/* * 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; ZnImage image; ZnAnchor anchor; ZnAnchor connection_anchor; ZnGradient *color; /* Used only if the image is a bitmap (in GLX alpha part * is always meaningful). */ /* Private data */ ZnPoint dev[4]; } IconItemStruct, *IconItem; static ZnAttrConfig icon_attrs[] = { { ZN_CONFIG_ANCHOR, "-anchor", NULL, Tk_Offset(IconItemStruct, anchor), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_GRADIENT, "-color", NULL, Tk_Offset(IconItemStruct, color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-composealpha", NULL, Tk_Offset(IconItemStruct, header.flags), COMPOSE_ALPHA_BIT, 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), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_BITMAP, "-mask", NULL, Tk_Offset(IconItemStruct, image), 0, ZN_COORDS_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_TAG_LIST, "-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 } }; /* ********************************************************************************** * * 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_ALPHA_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 = ZnUnspecifiedImage; icon->anchor = ZnAnchorNW; icon->connection_anchor = ZnAnchorSW; icon->color = ZnGetGradientByValue(wi->fore_color); return ZN_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { IconItem icon = (IconItem) item; if (icon->image != ZnUnspecifiedImage) { icon->image = ZnGetImageByValue(icon->image); } icon->color = ZnGetGradientByValue(icon->color); } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { IconItem icon = (IconItem) item; if (icon->image != ZnUnspecifiedImage) { ZnFreeImage(icon->image); icon->image = ZnUnspecifiedImage; } ZnFreeGradient(icon->color); } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static int Configure(Item item, int argc, Tcl_Obj *CONST argv[], int *flags) { Item old_connected; old_connected = item->connected_item; if (ZnConfigureAttributes(item->wi, item, icon_attrs, 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; } } return ZN_OK; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(Item item, int argc, Tcl_Obj *CONST argv[]) { if (ZnQueryAttribute(item->wi, item, icon_attrs, 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; int width, height; ResetBBox(&item->item_bounding_box); /* * If there is no image then nothing to show. */ if (icon->image == ZnUnspecifiedImage) { return; } if (icon->image != ZnUnspecifiedImage) { ZnSizeOfImage(icon->image, &width, &height); } if (wi->render) { ZnPoint pos, quad[4]; int i; /* * The connected item support anchors, this is checked by * configure. */ if (item->connected_item != ZN_NO_ITEM) { ZnTransfo t; item->connected_item->class->GetAnchor(item->connected_item, icon->connection_anchor, quad); ZnTransfoInvert(wi->current_transfo, &t); ZnTransformPoint(&t, quad, &pos); } else { pos = icon->pos; } Anchor2Origin(&pos, width, height, icon->anchor, quad); quad[1].x = quad[0].x; quad[1].y = quad[0].y + height; quad[2].x = quad[0].x + width; quad[2].y = quad[1].y; quad[3].x = quad[2].x; quad[3].y = quad[0].y; ZnTransformPoints(wi->current_transfo, quad, icon->dev, 4); for (i = 0; i < 4; i++) { icon->dev[i].x = REAL_TO_INT(icon->dev[i].x); icon->dev[i].y = REAL_TO_INT(icon->dev[i].y); } /* * Compute the bounding box. */ AddPointsToBBox(&item->item_bounding_box, icon->dev, 4); } else { if (item->connected_item != ZN_NO_ITEM) { item->connected_item->class->GetAnchor(item->connected_item, icon->connection_anchor, icon->dev); } else { ZnTransformPoint(wi->current_transfo, &icon->pos, icon->dev); } Anchor2Origin(icon->dev, width, height, icon->anchor, icon->dev); icon->dev->x = REAL_TO_INT(icon->dev->x); icon->dev->y = REAL_TO_INT(icon->dev->y); /* * Compute the bounding box. */ AddPointToBBox(&item->item_bounding_box, icon->dev->x, icon->dev->y); AddPointToBBox(&item->item_bounding_box, icon->dev->x+width, icon->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, ZnToArea ta) { IconItem icon = (IconItem) item; ZnBBox box, *area = ta->area; if (icon->image == ZnUnspecifiedImage) { return -1; } if (item->wi->render) { return PolygonInBBox(icon->dev, 4, ta->area, NULL); } else { int w, h; box.orig = *icon->dev; ZnSizeOfImage(icon->image, &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; int w, h; ZnBBox box, inter, *clip_box; Region clip_region; ZnBool simple; Pixmap pixmap, mask_pmap; if (icon->image == ZnUnspecifiedImage) { return; } ZnSizeOfImage(icon->image, &w, &h); box.orig = *icon->dev; box.corner.x = icon->dev->x + w; box.corner.y = icon->dev->y + h; if (!ZnImageIsBitmap(icon->image)) { /* * 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. */ IntersectBBox(&box, &wi->damaged_area, &inter); box = inter; ZnCurrentClip(wi, &clip_region, NULL, &simple); pixmap = ZnImagePixmap(icon->image, &mask_pmap); if (mask_pmap == ZnUnspecifiedImage || simple) { if (mask_pmap != ZnUnspecifiedImage) { XSetClipMask(wi->dpy, wi->gc, mask_pmap); values.clip_x_origin = (int) icon->dev->x; values.clip_y_origin = (int) icon->dev->y; XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); } XCopyArea(wi->dpy, pixmap, wi->draw_buffer, wi->gc, box.orig.x-icon->dev->x, box.orig.y-icon->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 */ Pixmap mask_pmap = XCreatePixmap(wi->dpy, ZnWindowId(wi->win), w, h, 1); gc = XCreateGC(wi->dpy, mask_pmap, 0, NULL); XFillRectangle(wi->dpy, mask_pmap, gc, 0, 0, w, h); XSetRegion(wi->dpy, gc, clip_region); values.foreground = 1; values.background = 0; values.clip_x_origin = (int) -icon->dev->x; values.clip_y_origin = (int) -icon->dev->y; XChangeGC(wi->dpy, gc, GCForeground|GCBackground|GCClipXOrigin|GCClipYOrigin, &values); XPutImage(wi->dpy, mask_pmap, gc, ZnImageMask(icon->image), 0, 0, 0, 0, w, h); XFreeGC(wi->dpy, gc); /* * Drawing of the icon using the mask. */ XSetClipMask(wi->dpy, wi->gc, mask_pmap); values.clip_x_origin = (int) icon->dev->x; values.clip_y_origin = (int) icon->dev->y; XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); XCopyArea(wi->dpy, pixmap, wi->draw_buffer, wi->gc, box.orig.x-icon->dev->x, box.orig.y-icon->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 { pixmap = ZnImagePixmap(icon->image, NULL); ZnCurrentClip(wi, NULL, &clip_box, &simple); if (simple) { IntersectBBox(&box, clip_box, &inter); box = inter; } values.fill_style = FillStippled; values.stipple = pixmap; values.ts_x_origin = icon->dev->x; values.ts_y_origin = icon->dev->y; values.foreground = ZnPixel(ZnGetGradientColor(icon->color, 0, NULL)); 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) { #ifdef GLX WidgetInfo *wi = item->wi; IconItem icon = (IconItem) item; if (icon->image != ZnUnspecifiedImage) { ZnRenderImage(wi, icon->image, icon->color, icon->dev, ZnImageIsBitmap(icon->image)); } #endif } /* ********************************************************************************** * * 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, ZnPick ps) { IconItem icon = (IconItem) item; WidgetInfo *wi = item->wi; double dist; double off_dist = MAX(1, wi->pick_aperture+1); ZnPoint *p = ps->point; dist = RectangleToPointDist(&item->item_bounding_box, p); /* * If inside the icon rectangle, 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 external * rectangle 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) { ZnPoint dp; int x, y, w, h, stride; char *bpixels; dist = 0.0; dp.x = p->x - icon->dev->x; dp.y = p->y - icon->dev->y; if (icon->image != ZnUnspecifiedImage) { ZnSizeOfImage(icon->image, &w, &h); bpixels = ZnImagePattern(icon->image, &stride); if (!bpixels) { /* * The image has no bitmap pattern * (i.e, it is rectangular and not a bitmap). */ return dist; } else if ((dp.x >= w) || (dp.y >= h)) { return off_dist; } } else { return dist; } /* BUG: when images can be scaled/rotated (openGL) this doesn't. * work. We must compute the invert transform and find the point * coordinate in the image space. */ x = (int) dp.x; y = (int) dp.y; if (! ZnGetBitmapPixel(bpixels, stride, x, y)) { dist = off_dist; } } else if (dist < off_dist) { dist = off_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; if (icon->image == ZnUnspecifiedImage) { *p = *icon->dev; } else { ZnBBox *bbox = &item->item_bounding_box; Origin2Anchor(&bbox->orig, bbox->corner.x - bbox->orig.x, bbox->corner.y - bbox->orig.y, anchor, p); } } /* ********************************************************************************** * * GetClipVertices -- * Get the clipping shape. * Never ever call TRI_FREE on the tristrip returned by GetClipVertices. * ********************************************************************************** */ static ZnBool GetClipVertices(Item item, ZnTriStrip *tristrip) { IconItem icon = (IconItem) item; int w=0, h=0; ZnPoint *points; if (item->wi->render) { TRI_STRIP1(tristrip, icon->dev, 4, False); return False; } else { ZnListAssertSize(item->wi->work_pts, 2); if (icon->image != ZnUnspecifiedImage) { ZnSizeOfImage(icon->image, &w, &h); } points = ZnListArray(item->wi->work_pts); TRI_STRIP1(tristrip, points, 2, False); points[0] = *icon->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, char **controls, 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), 0, /* num_parts */ True, /* has_anchors */ "icon", icon_attrs, Init, Clone, Destroy, Configure, Query, NULL, /* GetFieldSet */ GetAnchor, GetClipVertices, NULL, /* GetContours */ 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;