/* * Icon.c -- Implementation of Icon item. * * Authors : Patrick LECOANET * Creation date : Sat Mar 25 13:53:39 1995 */ /* * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet -- * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include "Item.h" #include "Geo.h" #include "Draw.h" #include "Types.h" #include "Image.h" #include "WidgetInfo.h" static const char rcsid[] = "$Id$"; static const char compile_id[] = "$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* ********************************************************************************** * * Specific Icon item record * ********************************************************************************** */ typedef struct _IconItemStruct { ItemStruct header; /* Public data */ ZnPoint pos; char *image_name; ZnAnchor anchor; ZnAnchor connection_anchor; Pixmap mask; /* Used only if the image is *NOT* specified */ ZnColor color; /* Used with the mask */ int alpha; /* Private data */ ZnPoint pos_dev; ZnImage image; } IconItemStruct, *IconItem; static ZnAttrConfig icon_attrs[] = { { ZN_CONFIG_UINT, "-alpha", NULL, Tk_Offset(IconItemStruct, alpha), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_ANCHOR, "-anchor", NULL, Tk_Offset(IconItemStruct, anchor), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_COLOR, "-color", NULL, Tk_Offset(IconItemStruct, color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(IconItemStruct, header.flags), COMPOSE_ROTATION_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(IconItemStruct, header.flags), COMPOSE_SCALE_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_ITEM, "-connecteditem", NULL, Tk_Offset(IconItemStruct, header.connected_item), 0, ZN_COORDS_FLAG|ZN_ITEM_FLAG, False }, { ZN_CONFIG_ANCHOR, "-connectionanchor", NULL, Tk_Offset(IconItemStruct, connection_anchor), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_IMAGE, "-image", NULL, Tk_Offset(IconItemStruct, image_name), 0, ZN_COORDS_FLAG|ZN_IMAGE_FLAG, False }, { ZN_CONFIG_PATTERN, "-mask", NULL, Tk_Offset(IconItemStruct, mask), 0, ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, { ZN_CONFIG_POINT, "-position", NULL, Tk_Offset(IconItemStruct, pos), 0, ZN_COORDS_FLAG, False}, { ZN_CONFIG_PRI, "-priority", NULL, Tk_Offset(IconItemStruct, header.priority), 0, ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(IconItemStruct, header.flags), SENSITIVE_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_TAGS, "-tags", NULL, Tk_Offset(IconItemStruct, header.tags), 0, 0, False }, { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(IconItemStruct, header.flags), VISIBLE_BIT, ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, { ZN_CONFIG_END, NULL, NULL, 0, 0, 0 } }; /* ********************************************************************************** * * IconImageChange -- * ********************************************************************************** */ static void IconImageChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height) { IconItem icon = (IconItem) client_data; InvalidateImage(icon->image_name); ITEM.Invalidate((Item) icon, ZN_COORDS_FLAG); } /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(Item item, int *argc, Tcl_Obj *CONST *args[]) { WidgetInfo *wi = item->wi; IconItem icon = (IconItem) item; /* Init attributes */ SET(item->flags, VISIBLE_BIT); SET(item->flags, SENSITIVE_BIT); SET(item->flags, COMPOSE_ROTATION_BIT); SET(item->flags, COMPOSE_SCALE_BIT); item->priority = DEFAULT_ICON_PRIORITY; icon->pos.x = icon->pos.y = 0.0; icon->image_name = ""; icon->image = ZnUnspecifiedImage; icon->anchor = ZnAnchorNW; icon->connection_anchor = ZnAnchorSW; icon->mask = ZnUnspecifiedPattern; icon->color = ZnGetColorByValue(wi->win, wi->fore_color); icon->alpha = 255; return ZN_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { IconItem icon = (IconItem) item; WidgetInfo *wi = item->wi; char *text; if (strlen(icon->image_name) != 0) { text = ZnMalloc((strlen(icon->image_name) + 1) * sizeof(char)); strcpy(text, icon->image_name); icon->image_name = text; icon->image = Tk_GetImage(wi->interp, wi->win, icon->image_name, IconImageChange, (ClientData) icon); } if (icon->mask != ZnUnspecifiedPattern) { icon->mask = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, icon->mask)); } icon->color = ZnGetColorByValue(wi->win, icon->color); } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { WidgetInfo *wi = item->wi; IconItem icon = (IconItem) item; if (icon->image != ZnUnspecifiedImage) { Tk_FreeImage(icon->image); icon->image = ZnUnspecifiedImage; } if (strlen(icon->image_name) != 0) { ZnFree(icon->image_name); } if (icon->mask != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, icon->mask); icon->mask = ZnUnspecifiedPattern; } ZnFreeColor(icon->color); } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static int Configure(Item item, int argc, Tcl_Obj *CONST argv[], int *flags) { IconItem icon = (IconItem) item; WidgetInfo *wi = item->wi; Item old_connected; old_connected = item->connected_item; if (ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags) == ZN_ERROR) { return ZN_ERROR; } if (ISSET(*flags, ZN_ITEM_FLAG)) { /* * If the new connected item is not appropriate back up * to the old one. */ if ((item->connected_item == ZN_NO_ITEM) || (item->connected_item->class->has_anchors && (item->parent == item->connected_item->parent))) { ITEM.UpdateItemDependency(item, old_connected); } else { item->connected_item = old_connected; } } if (ISSET(*flags, ZN_IMAGE_FLAG)) { Tk_Image image; if (strcmp(icon->image_name, "") != 0) { image = Tk_GetImage(wi->interp, wi->win, icon->image_name, IconImageChange, (ClientData) icon); if (image == NULL) { /* * The name will not be in sync with the image in * this case. */ return ZN_ERROR; } } else { image = ZnUnspecifiedImage; } if (icon->image != ZnUnspecifiedImage) { Tk_FreeImage(icon->image); } icon->image = image; } return ZN_OK; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(Item item, int argc, Tcl_Obj *CONST argv[]) { if (ITEM_P.QueryAttribute((char *) item, -1, argv[0]) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } /* ********************************************************************************** * * ComputeCoordinates -- * ********************************************************************************** */ static void ComputeCoordinates(Item item, ZnBool force) { WidgetInfo *wi = item->wi; IconItem icon = (IconItem) item; ZnDim width, height; int w, h; ResetBBox(&item->item_bounding_box); /* * If there is no image and no mask then nothing to show. */ if (icon->image == ZnUnspecifiedImage && icon->mask == ZnUnspecifiedPattern) { return; } if (icon->image != ZnUnspecifiedImage) { Tk_SizeOfImage(icon->image, &w, &h); } else { Tk_SizeOfBitmap(wi->dpy, icon->mask, &w,&h); } width = w; height = h; /* * The connected item support anchors, this is checked by * configure. */ if (item->connected_item != ZN_NO_ITEM) { item->connected_item->class->GetAnchor(item->connected_item, icon->connection_anchor, &icon->pos_dev); } else { ZnTransformPoint(wi->current_transfo, &icon->pos, &icon->pos_dev); } Anchor2Origin(&icon->pos_dev, width, height, icon->anchor, &icon->pos_dev); icon->pos_dev.x = REAL_TO_INT(icon->pos_dev.x); icon->pos_dev.y = REAL_TO_INT(icon->pos_dev.y); /* * Compute the bounding box. */ AddPointToBBox(&item->item_bounding_box, icon->pos_dev.x, icon->pos_dev.y); AddPointToBBox(&item->item_bounding_box, icon->pos_dev.x+width, icon->pos_dev.y+height); item->item_bounding_box.orig.x -= 1.0; item->item_bounding_box.orig.y -= 1.0; item->item_bounding_box.corner.x += 1.0; item->item_bounding_box.corner.y += 1.0; /* * Update connected items. */ SET(item->flags, UPDATE_DEPENDENT_BIT); } /* ********************************************************************************** * * ToArea -- * Tell if the object is entirely outside (-1), * entirely inside (1) or in between (0). * ********************************************************************************** */ static int ToArea(Item item, ZnBBox *area, Tk_Uid tag_uid, int enclosed, ZnBool report) { IconItem icon = (IconItem) item; ZnBBox box; int w, h; box.orig = icon->pos_dev; if (icon->image != ZnUnspecifiedImage) { Tk_SizeOfImage(icon->image, &w, &h); } else if (icon->mask != ZnUnspecifiedPattern) { Tk_SizeOfBitmap(item->wi->dpy, icon->mask, &w, &h); } box.corner.x = box.orig.x + w; box.corner.y = box.orig.y + h; return BBoxInBBox(&box, area); } /* ********************************************************************************** * * Draw -- * ********************************************************************************** */ static void Draw(Item item) { WidgetInfo *wi = item->wi; IconItem icon = (IconItem) item; XGCValues values; int gc_mask = 0; ImageBits *im_bits; Pixmap pmap, mask_pmap; int w, h; ZnBBox box, inter, *clip_box; Region clip_region; ZnBool simple; if (icon->image != ZnUnspecifiedImage) { /* * Added the case of a rectangular aligned clipping and the * simple case of the damaged area clip. These are done here * to avoid the use of Tk_RedrawImage which use a different * GC and lead to wrong clip behaviour. * * Non rectangular clipping. Two cases here: if the image * has no contour mask, simply use the clipping set in the * current gc, if not, we have to generate a bitmap mask * using the image mask and the current clip and use this * bitmap as the current clip. */ Tk_SizeOfImage(icon->image, &w, &h); box.orig = icon->pos_dev; box.corner.x = icon->pos_dev.x + w; box.corner.y = icon->pos_dev.y + h; IntersectBBox(&box, &wi->damaged_area, &inter); box = inter; im_bits = GetImageBits(wi->win, icon->image_name, icon->image); pmap = GetImagePixmap(wi->win, icon->image_name, icon->image, &mask_pmap); ITEM_P.CurrentClip(wi, &clip_region, NULL, &simple); if ((im_bits->mask == ZnUnspecifiedPattern) || simple) { if (im_bits->mask != ZnUnspecifiedPattern) { XSetClipMask(wi->dpy, wi->gc, mask_pmap); values.clip_x_origin = (int) icon->pos_dev.x; values.clip_y_origin = (int) icon->pos_dev.y; XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); } XCopyArea(wi->dpy, pmap, wi->draw_buffer, wi->gc, box.orig.x-icon->pos_dev.x, box.orig.y-icon->pos_dev.y, box.corner.x-box.orig.x, box.corner.y-box.orig.y, box.orig.x, box.orig.y); values.clip_x_origin = values.clip_y_origin = 0; XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); XSetRegion(wi->dpy, wi->gc, clip_region); } else { GC gc; /* * Build of the mask */ mask_pmap = XCreatePixmap(wi->dpy, ZnWindowId(wi->win), im_bits->width, im_bits->height, 1); gc = XCreateGC(wi->dpy, mask_pmap, 0, NULL); XFillRectangle(wi->dpy, mask_pmap, gc, 0, 0, im_bits->width, im_bits->height); XSetRegion(wi->dpy, gc, clip_region); values.foreground = 1; values.background = 0; values.clip_x_origin = (int) -icon->pos_dev.x; values.clip_y_origin = (int) -icon->pos_dev.y; XChangeGC(wi->dpy, gc, GCForeground|GCBackground|GCClipXOrigin|GCClipYOrigin, &values); XPutImage(wi->dpy, mask_pmap, gc, im_bits->mask, 0, 0, 0, 0, im_bits->width, im_bits->height); XFreeGC(wi->dpy, gc); /* * Drawing of the icon using the mask. */ XSetClipMask(wi->dpy, wi->gc, mask_pmap); values.clip_x_origin = (int) icon->pos_dev.x; values.clip_y_origin = (int) icon->pos_dev.y; XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); XCopyArea(wi->dpy, pmap, wi->draw_buffer, wi->gc, box.orig.x-icon->pos_dev.x, box.orig.y-icon->pos_dev.y, box.corner.x-box.orig.x, box.corner.y-box.orig.y, box.orig.x, box.orig.y); values.clip_x_origin = values.clip_y_origin = 0; XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); XSetRegion(wi->dpy, wi->gc, clip_region); XFreePixmap(wi->dpy, mask_pmap); } } else if (icon->mask != ZnUnspecifiedPattern) { Tk_SizeOfBitmap(wi->dpy, icon->mask, &w,&h); box.orig = icon->pos_dev; box.corner.x = icon->pos_dev.x + w; box.corner.y = icon->pos_dev.y + h; ITEM_P.CurrentClip(wi, NULL, &clip_box, &simple); if (simple) { IntersectBBox(&box, clip_box, &inter); box = inter; } values.fill_style = FillStippled; values.stipple = icon->mask; values.ts_x_origin = icon->pos_dev.x; values.ts_y_origin = icon->pos_dev.y; values.foreground = ZnPixel(icon->color); gc_mask |= GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin | GCForeground; XChangeGC(wi->dpy, wi->gc, gc_mask, &values); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, box.orig.x, box.orig.y, box.corner.x-box.orig.x, box.corner.y-box.orig.y); } } /* ********************************************************************************** * * Render -- * ********************************************************************************** */ static void Render(Item item) { WidgetInfo *wi = item->wi; IconItem icon = (IconItem) item; if (icon->image != ZnUnspecifiedImage) { ArtPixBuf *pixbuf; pixbuf = GetImagePixbuf(wi->win, icon->image_name, icon->image); rgb_image(wi->buf.buf, wi->buf.ox, wi->buf.oy, wi->buf.cx, wi->buf.cy, wi->buf.rowstride, pixbuf, icon->pos_dev.x, icon->pos_dev.y, icon->alpha); } else if (icon->mask != ZnUnspecifiedPattern) { BitmapBits *bitmap = GetBitmapMask(wi->dpy, icon->mask); art_u32 rgba; rgba = (((icon->color->red & 0xff00) << 16) | ((icon->color->green & 0xff00) << 8) | (icon->color->blue & 0xff00) | (icon->alpha & 0xff)); rgb_bitmap(wi->buf.buf, wi->buf.ox, wi->buf.oy, wi->buf.cx, wi->buf.cy, wi->buf.rowstride, bitmap->pixels, icon->pos_dev.x, icon->pos_dev.y, bitmap->width, bitmap->height, bitmap->rowstride, rgba); } } /* ********************************************************************************** * * IsSensitive -- * ********************************************************************************** */ static ZnBool IsSensitive(Item item, int item_part) { return (ISSET(item->flags, SENSITIVE_BIT) && item->parent->class->IsSensitive(item->parent, ZN_NO_PART)); } /* ********************************************************************************** * * Pick -- * ********************************************************************************** */ static double Pick(Item item, ZnPoint *p, Item start_item, int aperture, Item *a_item, int *part) { IconItem icon = (IconItem) item; WidgetInfo *wi = item->wi; double dist; int width, height; double off_dist = MAX(1, wi->pick_aperture+1); dist = RectangleToPointDist(&item->item_bounding_box, p); /* * If inside the bounding box, try to see if the point * is actually on the image or not. If it lies in an * area that is between pick_aperture+1 around the bbox * and the actual shape, the distance will be reported * as pick_aperture+1. Inside the actual shape it will be * reported as 0. This is a kludge, there is currently * no means to compute the real distance in the icon's * vicinity. */ if (dist <= 0.0) { BitmapBits *bitmap=NULL; ImageBits *im_bits; ZnPoint dp; dist = 0.0; dp.x = p->x - icon->pos_dev.x; dp.y = p->y - icon->pos_dev.y; if (icon->image != ZnUnspecifiedImage) { Tk_SizeOfImage(icon->image, &width, &height); if (dp.x >= width || dp.y >= height) { dist = off_dist; goto out_pick; } im_bits = GetImageBits(wi->win, icon->image_name, icon->image); bitmap = im_bits->b_bits; } else if (icon->mask != ZnUnspecifiedPattern) { Tk_SizeOfBitmap(wi->dpy, icon->mask, &width, &height); if (dp.x >= width || dp.y >= height) { dist = off_dist; goto out_pick; } bitmap = GetBitmapMask(wi->dpy, icon->mask); } if (bitmap != NULL) { int x = (int) dp.x; int y = (int) dp.y; if (! GetBitmapPixel(bitmap, x, y)) { dist = off_dist; } } } else if (dist < off_dist) { dist = off_dist; } out_pick: /*printf("dist = %g\n", dist);*/ return dist; } /* ********************************************************************************** * * PostScript -- * ********************************************************************************** */ static void PostScript(Item item, PostScriptInfo ps_info) { } /* ********************************************************************************** * * GetAnchor -- * ********************************************************************************** */ static void GetAnchor(Item item, ZnAnchor anchor, ZnPoint *p) { IconItem icon = (IconItem) item; int w=0, h=0; if ((icon->image != ZnUnspecifiedImage) || (icon->mask != ZnUnspecifiedPattern)) { if (icon->image != ZnUnspecifiedImage) { Tk_SizeOfImage(icon->image, &w, &h); } else { Tk_SizeOfBitmap(item->wi->dpy, icon->mask, &w, &h); } Origin2Anchor(&icon->pos_dev, w, h, anchor, p); } else { p->x = p->y = 0.0; } } /* ********************************************************************************** * * GetClipVertices -- * Get the clipping shape. * ********************************************************************************** */ static ZnBool GetClipVertices(Item item, ZnPoly *poly) { IconItem icon = (IconItem) item; int w=0, h=0; ZnPoint *points; ZnListAssertSize(item->wi->work_pts, 2); if (icon->image != ZnUnspecifiedImage) { Tk_SizeOfImage(icon->image, &w, &h); } else { Tk_SizeOfBitmap(item->wi->dpy, icon->mask, &w, &h); } points = (ZnPoint *) ZnListArray(item->wi->work_pts); POLY_CONTOUR1(poly, points, 2); points[0] = icon->pos_dev; points[1].x = points[0].x + w; points[1].y = points[0].y + h; return True; } /* ********************************************************************************** * * Coords -- * Return or edit the item origin. This doesn't take care of * the possible attachment. The change will be effective at the * end of the attachment. * ********************************************************************************** */ static int Coords(Item item, int contour, int index, int cmd, ZnPoint **pts, int *num_pts) { IconItem icon = (IconItem) item; if ((cmd == COORDS_ADD) || (cmd == COORDS_ADD_LAST) || (cmd == COORDS_REMOVE)) { Tcl_AppendResult(item->wi->interp, " icons can't add or remove vertices", NULL); return ZN_ERROR; } else if ((cmd == COORDS_REPLACE) || (cmd == COORDS_REPLACE_ALL)) { if (*num_pts == 0) { Tcl_AppendResult(item->wi->interp, " coords command need 1 point on icons", NULL); return ZN_ERROR; } icon->pos = (*pts)[0]; ITEM.Invalidate(item, ZN_COORDS_FLAG); } else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) { *num_pts = 1; *pts = &icon->pos; } return ZN_OK; } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct ICON_ITEM_CLASS = { sizeof(IconItemStruct), False, /* has_fields */ 0, /* num_parts */ True, /* has_anchors */ "icon", icon_attrs, Init, Clone, Destroy, Configure, Query, NULL, /* GetFieldSet */ GetAnchor, GetClipVertices, Coords, NULL, /* InsertChars */ NULL, /* DeleteChars */ NULL, /* Cursor */ NULL, /* Index */ NULL, /* Part */ NULL, /* Selection */ NULL, /* Contour */ ComputeCoordinates, ToArea, Draw, Render, IsSensitive, Pick, NULL, /* PickVertex */ PostScript }; ZnItemClassId ZnIcon = (ZnItemClassId) &ICON_ITEM_CLASS;