diff options
-rw-r--r-- | generic/Icon.c | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/generic/Icon.c b/generic/Icon.c new file mode 100644 index 0000000..b5f4de0 --- /dev/null +++ b/generic/Icon.c @@ -0,0 +1,736 @@ +/* + * 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 <malloc.h> + +#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 */ + RadarPoint pos; + char *image_name; + RadarAnchor anchor; + RadarAnchor connection_anchor; + Pixmap mask; /* Used only if the image is *NOT* specified */ + RadarColor color; /* Used with the mask */ + + /* Private data */ + RadarPoint pos_dev; + RadarImage image; +} IconItemStruct, *IconItem; + + +static RadarAttrConfig icon_attrs[] = { + { RADAR_CONFIG_ANCHOR, "-anchor", NULL, + Tk_Offset(IconItemStruct, anchor), 0, RADAR_COORDS_FLAG, False }, + { RADAR_CONFIG_COLOR, "-color", NULL, + Tk_Offset(IconItemStruct, color), 0, RADAR_DRAW_FLAG, False }, + { RADAR_CONFIG_BOOL, "-composerotation", NULL, + Tk_Offset(IconItemStruct, header.flags), COMPOSE_ROTATION_BIT, + RADAR_COORDS_FLAG, False }, + { RADAR_CONFIG_BOOL, "-composescale", NULL, + Tk_Offset(IconItemStruct, header.flags), COMPOSE_SCALE_BIT, + RADAR_COORDS_FLAG, False }, + { RADAR_CONFIG_ITEM, "-connecteditem", NULL, + Tk_Offset(IconItemStruct, header.connected_item), 0, + RADAR_COORDS_FLAG|RADAR_ITEM_FLAG, False }, + { RADAR_CONFIG_ANCHOR, "-connectionanchor", NULL, + Tk_Offset(IconItemStruct, connection_anchor), 0, RADAR_COORDS_FLAG, False }, + { RADAR_CONFIG_IMAGE, "-image", NULL, + Tk_Offset(IconItemStruct, image_name), 0, + RADAR_COORDS_FLAG|RADAR_IMAGE_FLAG, False }, + { RADAR_CONFIG_PATTERN, "-mask", NULL, + Tk_Offset(IconItemStruct, mask), 0, RADAR_DRAW_FLAG|RADAR_REPICK_FLAG, False }, + { RADAR_CONFIG_POINT, "-position", NULL, Tk_Offset(IconItemStruct, pos), 0, + RADAR_COORDS_FLAG, False}, + { RADAR_CONFIG_PRI, "-priority", NULL, + Tk_Offset(IconItemStruct, header.priority), 0, + RADAR_DRAW_FLAG|RADAR_REPICK_FLAG, False }, + { RADAR_CONFIG_BOOL, "-sensitive", NULL, + Tk_Offset(IconItemStruct, header.flags), SENSITIVE_BIT, + RADAR_REPICK_FLAG, False }, + { RADAR_CONFIG_TAGS, "-tags", NULL, + Tk_Offset(IconItemStruct, header.tags), 0, 0, False }, + { RADAR_CONFIG_BOOL, "-visible", NULL, + Tk_Offset(IconItemStruct, header.flags), VISIBLE_BIT, + RADAR_DRAW_FLAG|RADAR_REPICK_FLAG|RADAR_VIS_FLAG, False }, + + { RADAR_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); + ITEM.Invalidate((Item) icon, RADAR_COORDS_FLAG); +} + + +/* + ********************************************************************************** + * + * Init -- + * + ********************************************************************************** + */ +static int +Init(Item item, + int *argc, + Arg **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 = RadarUnspecifiedImage; + icon->anchor = RadarAnchorNW; + icon->connection_anchor = RadarAnchorSW; + icon->mask = RadarUnspecifiedPattern; + icon->color = RadarGetColorByValue(wi->win, wi->fore_color); + + return RADAR_OK; +} + + +/* + ********************************************************************************** + * + * Clone -- + * + ********************************************************************************** + */ +static void +Clone(Item item) +{ + IconItem icon = (IconItem) item; + WidgetInfo *wi = item->wi; + char *text; + + if (strlen(icon->image_name) != 0) { + text = RadarMalloc((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 != RadarUnspecifiedPattern) { + icon->mask = Tk_GetBitmap(wi->interp, wi->win, + Tk_NameOfBitmap(wi->dpy, icon->mask)); + } + icon->color = RadarGetColorByValue(wi->win, icon->color); +} + + +/* + ********************************************************************************** + * + * Destroy -- + * + ********************************************************************************** + */ +static void +Destroy(Item item) +{ + WidgetInfo *wi = item->wi; + IconItem icon = (IconItem) item; + + if (strlen(icon->image_name) != 0) { + RadarFree(icon->image_name); + } + if (icon->image != RadarUnspecifiedImage) { + Tk_FreeImage(icon->image); + icon->image = RadarUnspecifiedImage; + } + if (icon->mask != RadarUnspecifiedPattern) { + Tk_FreeBitmap(wi->dpy, icon->mask); + icon->mask = RadarUnspecifiedPattern; + } + RadarFreeColor(icon->color); +} + + +/* + ********************************************************************************** + * + * Configure -- + * + ********************************************************************************** + */ +static int +Configure(Item item, + int argc, + RadarAttrList 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) == RADAR_ERROR) { + return RADAR_ERROR; + } + + if (ISSET(*flags, RADAR_ITEM_FLAG)) { + /* + * If the new connected item is not appropriate back up + * to the old one. + */ + if ((item->connected_item == RADAR_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, RADAR_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 RADAR_ERROR; + } + } + else { + image = RadarUnspecifiedImage; + } + if (icon->image != RadarUnspecifiedImage) { + Tk_FreeImage(icon->image); + } + icon->image = image; + } + + return RADAR_OK; +} + + +/* + ********************************************************************************** + * + * Query -- + * + ********************************************************************************** + */ +static int +Query(Item item, + int argc, + RadarAttrList argv) +{ + if (ITEM_P.QueryAttribute((char *) item, -1, argv[0]) == RADAR_ERROR) { + return RADAR_ERROR; + } + + return RADAR_OK; +} + + +/* + ********************************************************************************** + * + * ComputeCoordinates -- + * + ********************************************************************************** + */ +static void +ComputeCoordinates(Item item, + RadarBool force) +{ + WidgetInfo *wi = item->wi; + IconItem icon = (IconItem) item; + RadarDim 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 == RadarUnspecifiedImage && + icon->mask == RadarUnspecifiedPattern) { + return; + } + + if (icon->image != RadarUnspecifiedImage) { + 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 != RADAR_NO_ITEM) { + item->connected_item->class->GetAnchor(item->connected_item, + icon->connection_anchor, + &icon->pos_dev); + } + else { + RadarTransformPoint(wi->current_transfo, &icon->pos, &icon->pos_dev); + } + + Anchor2Origin(&icon->pos_dev, width, height, icon->anchor, + &icon->pos_dev); + + /* + * 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; + item->item_bounding_box.orig.y -= 1; + item->item_bounding_box.corner.x += 1; + item->item_bounding_box.corner.y += 1; + + /* + * 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, + RadarBBox *area, + Tk_Uid tag_uid, + int enclosed, + RadarBool report) +{ + /* IconItem icon = (IconItem) item;*/ + + return BBoxInBBox(&item->item_bounding_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; + + if (icon->image != RadarUnspecifiedImage) { + if (wi->current_clip) { + if (wi->current_clip->simple) { + Tk_RedrawImage(icon->image, + item->item_bounding_box.orig.x-icon->pos_dev.x, + item->item_bounding_box.orig.y-icon->pos_dev.y, + item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, + item->item_bounding_box.corner.y-item->item_bounding_box.orig.y, + wi->draw_buffer, + item->item_bounding_box.orig.x, + item->item_bounding_box.orig.y); + } + else { + /* + * 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. + */ + im_bits = GetImageBits(wi->win, icon->image); + pmap = GetImagePixmap(wi->win, icon->image); + if (im_bits->mask == RadarUnspecifiedPattern) { + XCopyArea(wi->dpy, pmap, wi->draw_buffer, wi->gc, + item->item_bounding_box.orig.x-icon->pos_dev.x, + item->item_bounding_box.orig.y-icon->pos_dev.y, + item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, + item->item_bounding_box.corner.y-item->item_bounding_box.orig.y, + item->item_bounding_box.orig.x, + item->item_bounding_box.orig.y); + } + else { + GC gc; + /* + * Build of the mask + */ + mask_pmap = XCreatePixmap(wi->dpy, RadarWindowId(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, wi->current_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, + item->item_bounding_box.orig.x-icon->pos_dev.x, + item->item_bounding_box.orig.y-icon->pos_dev.y, + item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, + item->item_bounding_box.corner.y-item->item_bounding_box.orig.y, + item->item_bounding_box.orig.x, + item->item_bounding_box.orig.y); + XSetRegion(wi->dpy, wi->gc, wi->current_clip->region); + values.clip_x_origin = 0; + values.clip_y_origin = 0; + XChangeGC(wi->dpy, wi->gc, GCClipXOrigin|GCClipYOrigin, &values); + XFreePixmap(wi->dpy, mask_pmap); + } + } + } + else { /* !current_clip */ + Tk_RedrawImage(icon->image, 0, 0, + item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, + item->item_bounding_box.corner.y-item->item_bounding_box.orig.y, + wi->draw_buffer, + item->item_bounding_box.orig.x, + item->item_bounding_box.orig.y); + } + } + else if (icon->mask != RadarUnspecifiedPattern) { + 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 = RadarPixel(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, + item->item_bounding_box.orig.x, + item->item_bounding_box.orig.y, + item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, + item->item_bounding_box.corner.y-item->item_bounding_box.orig.y); + } +} + + +/* + ********************************************************************************** + * + * IsSensitive -- + * + ********************************************************************************** + */ +static RadarBool +IsSensitive(Item item, + int item_part) +{ + return (ISSET(item->flags, SENSITIVE_BIT) && + item->parent->class->IsSensitive(item->parent, RADAR_NO_PART)); +} + + +/* + ********************************************************************************** + * + * Pick -- + * + ********************************************************************************** + */ +static double +Pick(Item item, + RadarPoint *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) { + XImage *bitmap=NULL; + ImageBits *im_bits; + RadarPoint dp; + + dist = 0.0; + dp.x = p->x - item->item_bounding_box.orig.x; + dp.y = p->y - item->item_bounding_box.orig.y; + if (icon->image != RadarUnspecifiedImage) { + 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); + bitmap = im_bits->mask; + } + else if (icon->mask != RadarUnspecifiedPattern) { + 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 != RadarUnspecifiedPattern) { + /*printf("dpx=%g, dpy=%g, width=%d, height=%d, pixel=%ld\n", dp.x, dp.y, + width, height, XGetPixel(bitmap, (int) dp.x, (int) dp.y));*/ + if (! XGetPixel(bitmap, (int) dp.x, (int) dp.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, + RadarAnchor anchor, + RadarPoint *p) +{ + IconItem icon = (IconItem) item; + + if ((icon->image != RadarUnspecifiedImage) || + (icon->mask != RadarUnspecifiedPattern)) { + Origin2Anchor(&icon->pos_dev, + item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, + item->item_bounding_box.corner.y-item->item_bounding_box.orig.y, + anchor, p); + } + else { + p->x = p->y = 0.0; + } +} + + +/* + ********************************************************************************** + * + * GetClipVertices -- + * Get the clipping shape. + * + ********************************************************************************** + */ +static RadarBool +GetClipVertices(Item item, + RadarPoint **points, + int *num_points) +{ + RadarListAssertSize(item->wi->work_pts, 2); + *points = (RadarPoint *) RadarListArray(item->wi->work_pts); + *num_points = 2; + (*points)[0] = item->item_bounding_box.orig; + (*points)[1] = item->item_bounding_box.corner; + + 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 index, + int cmd, + RadarPoint **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 RADAR_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 RADAR_ERROR; + } + icon->pos = (*pts)[0]; + ITEM.Invalidate(item, RADAR_COORDS_FLAG); + } + else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) { + *num_pts = 1; + *pts = &icon->pos; + } + return RADAR_OK; +} + + +/* + ********************************************************************************** + * + * Exported functions struct -- + * + ********************************************************************************** + */ +static ItemClassStruct ICON_ITEM_CLASS = { + sizeof(IconItemStruct), + False, + False, + True, + "icon", + icon_attrs, + Init, + Clone, + Destroy, + Configure, + Query, + NULL, + GetAnchor, + GetClipVertices, + Coords, + ComputeCoordinates, + ToArea, + Draw, + IsSensitive, + Pick, + PostScript +}; + +RadarItemClassId RadarIcon = (RadarItemClassId) &ICON_ITEM_CLASS; |