/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "appenv.h" #include "buildmenu.h" #include "channels_dialog.h" #include "colormaps.h" #include "cursorutil.h" #include "disp_callbacks.h" #include "drawable.h" #include "gdisplay.h" #include "gdisplayP.h" #include "gdisplay_ops.h" #include "general.h" #include "gimage_mask.h" #include "gimprc.h" #include "gximage.h" #include "image_render.h" #include "info_window.h" #include "interface.h" #include "layers_dialog.h" #include "linked.h" #include "menus.h" #include "plug_in.h" #include "scale.h" #include "scroll.h" #include "tools.h" #include "undo.h" #include "tools.h" #include "layer_pvt.h" /* ick. */ #define OVERHEAD 25 /* in units of pixel area */ #define EPSILON 5 /* variable declarations */ link_ptr display_list = NULL; static int display_num = 1; static GdkCursorType default_gdisplay_cursor = GDK_TOP_LEFT_ARROW; #define ROUND(x) ((int) (x + 0.5)) #define MAX_TITLE_BUF 4096 static char *image_type_strs[] = { "RGB", "RGB-alpha", "grayscale", "grayscale-alpha", "indexed", "indexed-alpha" }; /* Local functions */ static void gdisplay_format_title (GImage *, char *); static void gdisplay_delete (GDisplay *); static link_ptr gdisplay_free_area_list (link_ptr); static link_ptr gdisplay_process_area_list(link_ptr, GArea *); static void gdisplay_add_update_area (GDisplay *, int, int, int, int); static void gdisplay_add_display_area (GDisplay *, int, int, int, int); static void gdisplay_paint_area (GDisplay *, int, int, int, int); static void gdisplay_display_area (GDisplay *, int, int, int, int); static guint gdisplay_hash (GDisplay *); static GHashTable *display_ht = NULL; GDisplay* gdisplay_new (GImage *gimage, unsigned int scale) { GDisplay *gdisp; char title [MAX_TITLE_BUF]; int instance; /* If there isn't an interface, never create a gdisplay */ if (no_interface) return NULL; /* format the title */ gdisplay_format_title (gimage, title); instance = gimage->instance_count; gimage->instance_count++; gimage->ref_count++; /* * Set all GDisplay parameters... */ gdisp = (GDisplay *) g_malloc (sizeof (GDisplay)); gdisp->offset_x = gdisp->offset_y = 0; gdisp->scale = scale; gdisp->gimage = gimage; gdisp->window_info_dialog = NULL; gdisp->depth = g_visual->depth; gdisp->select = NULL; gdisp->ID = display_num++; gdisp->instance = instance; gdisp->update_areas = NULL; gdisp->display_areas = NULL; gdisp->disp_xoffset = 0; gdisp->disp_yoffset = 0; gdisp->current_cursor = -1; gdisp->draw_guides = TRUE; gdisp->snap_to_guides = TRUE; /* add the new display to the list so that it isn't lost */ display_list = append_to_list (display_list, (void *) gdisp); /* create the shell for the image */ create_display_shell (gdisp->ID, gimage->width, gimage->height, title, gimage_base_type (gimage)); /* set the gdisplay colormap type and install the appropriate colormap */ gdisp->color_type = (gimage_base_type (gimage) == GRAY) ? GRAY : RGB; /* set the user data */ if (!display_ht) display_ht = g_hash_table_new ((GHashFunc) gdisplay_hash, NULL); g_hash_table_insert (display_ht, gdisp->shell, gdisp); g_hash_table_insert (display_ht, gdisp->canvas, gdisp); /* set the current tool cursor */ gdisplay_install_tool_cursor (gdisp, default_gdisplay_cursor); return gdisp; } static void gdisplay_format_title (GImage *gimage, char *title) { char *image_type_str; int empty; empty = gimage_is_empty (gimage); if (gimage_is_flat (gimage)) image_type_str = image_type_strs[drawable_type (gimage_active_drawable (gimage))]; else switch (gimage_base_type (gimage)) { case RGB: image_type_str = (empty) ? "RGB-empty" : "RGB-layered"; break; case GRAY: image_type_str = (empty) ? "grayscale-empty" : "grayscale-layered"; break; case INDEXED: image_type_str = (empty) ? "indexed-empty" : "indexed-layered"; break; default: image_type_str = NULL; } sprintf (title, "%s-%d.%d (%s)", prune_filename (gimage_filename (gimage)), gimage->ID, gimage->instance_count, image_type_str); } static void gdisplay_delete (GDisplay *gdisp) { g_hash_table_remove (display_ht, gdisp->shell); g_hash_table_remove (display_ht, gdisp->canvas); /* stop any active tool */ active_tool_control (HALT, (void *) gdisp); /* free the selection structure */ selection_free (gdisp->select); if (gdisp->scroll_gc) gdk_gc_destroy (gdisp->scroll_gc); /* free the area lists */ gdisplay_free_area_list (gdisp->update_areas); gdisplay_free_area_list (gdisp->display_areas); /* free the gimage */ gimage_delete (gdisp->gimage); /* insure that if a window information dialog exists, it is removed */ if (gdisp->window_info_dialog) info_window_free (gdisp->window_info_dialog); /* set popup_shell to NULL if appropriate */ if (popup_shell == gdisp->shell) popup_shell= NULL; g_free (gdisp); } static link_ptr gdisplay_free_area_list (link_ptr list) { link_ptr l = list; GArea *ga; while (l) { /* free the data */ ga = (GArea *) l->data; g_free (ga); l = next_item (l); } if (list) free_list (list); return NULL; } static link_ptr gdisplay_process_area_list (link_ptr list, GArea *ga1) { link_ptr new_list; link_ptr l = list; int area1, area2, area3; GArea *ga2; /* start new list off */ new_list = add_to_list (NULL, ga1); while (l) { /* process the data */ ga2 = (GArea *) l->data; area1 = (ga1->x2 - ga1->x1) * (ga1->y2 - ga1->y1) + OVERHEAD; area2 = (ga2->x2 - ga2->x1) * (ga2->y2 - ga2->y1) + OVERHEAD; area3 = (MAXIMUM (ga2->x2, ga1->x2) - MINIMUM (ga2->x1, ga1->x1)) * (MAXIMUM (ga2->y2, ga1->y2) - MINIMUM (ga2->y1, ga1->y1)) + OVERHEAD; if ((area1 + area2) < area3) new_list = add_to_list (new_list, ga2); else { ga1->x1 = MINIMUM (ga1->x1, ga2->x1); ga1->y1 = MINIMUM (ga1->y1, ga2->y1); ga1->x2 = MAXIMUM (ga1->x2, ga2->x2); ga1->y2 = MAXIMUM (ga1->y2, ga2->y2); g_free (ga2); } l = next_item (l); } if (list) free_list (list); return new_list; } void gdisplay_flush (GDisplay *gdisp) { GArea * ga; link_ptr list; /* Flush the items in the displays and updates lists-- * but only if gdisplay has been mapped and exposed */ if (!gdisp->select) return; /* First the updates... */ list = gdisp->update_areas; while (list) { /* Paint the area specified by the GArea */ ga = (GArea *) list->data; gdisplay_paint_area (gdisp, ga->x1, ga->y1, (ga->x2 - ga->x1), (ga->y2 - ga->y1)); list = next_item (list); } /* Free the update lists */ gdisp->update_areas = gdisplay_free_area_list (gdisp->update_areas); /* Next the displays... */ list = gdisp->display_areas; if (list) { /* stop the currently active tool */ active_tool_control (PAUSE, (void *) gdisp); while (list) { /* Paint the area specified by the GArea */ ga = (GArea *) list->data; gdisplay_display_area (gdisp, ga->x1, ga->y1, (ga->x2 - ga->x1), (ga->y2 - ga->y1)); list = next_item (list); } /* Free the update lists */ gdisp->display_areas = gdisplay_free_area_list (gdisp->display_areas); /* draw the guides */ gdisplay_draw_guides (gdisp); /* restart (and recalculate) the selection boundaries */ selection_start (gdisp->select, TRUE); /* start the currently active tool */ active_tool_control (RESUME, (void *) gdisp); } /* update the gdisplay's info dialog */ if (gdisp->window_info_dialog) info_window_update (gdisp->window_info_dialog, (void *) gdisp); } void gdisplay_draw_guides (GDisplay *gdisp) { GList *tmp_list; Guide *guide; if (gdisp->draw_guides) { tmp_list = gdisp->gimage->guides; while (tmp_list) { guide = tmp_list->data; tmp_list = tmp_list->next; gdisplay_draw_guide (gdisp, guide, FALSE); } } } void gdisplay_draw_guide (GDisplay *gdisp, Guide *guide, int active) { static GdkGC *normal_hgc = NULL; static GdkGC *active_hgc = NULL; static GdkGC *normal_vgc = NULL; static GdkGC *active_vgc = NULL; static int initialize = TRUE; int x1, x2; int y1, y2; int w, h; int x, y; if (guide->position < 0) return; if (initialize) { GdkGCValues values; char stipple[8] = { 0xF0, /* ####---- */ 0xE1, /* ###----# */ 0xC3, /* ##----## */ 0x87, /* #----### */ 0x0F, /* ----#### */ 0x1E, /* ---####- */ 0x3C, /* --####-- */ 0x78, /* -####--- */ }; initialize = FALSE; values.foreground.pixel = gdisplay_black_pixel (gdisp); values.background.pixel = g_normal_guide_pixel; values.fill = GDK_OPAQUE_STIPPLED; values.stipple = gdk_bitmap_create_from_data (gdisp->canvas->window, (char*) stipple, 8, 1); normal_hgc = gdk_gc_new_with_values (gdisp->canvas->window, &values, GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FILL | GDK_GC_STIPPLE); values.background.pixel = g_active_guide_pixel; active_hgc = gdk_gc_new_with_values (gdisp->canvas->window, &values, GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FILL | GDK_GC_STIPPLE); values.foreground.pixel = gdisplay_black_pixel (gdisp); values.background.pixel = g_normal_guide_pixel; values.fill = GDK_OPAQUE_STIPPLED; values.stipple = gdk_bitmap_create_from_data (gdisp->canvas->window, (char*) stipple, 1, 8); normal_vgc = gdk_gc_new_with_values (gdisp->canvas->window, &values, GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FILL | GDK_GC_STIPPLE); values.background.pixel = g_active_guide_pixel; active_vgc = gdk_gc_new_with_values (gdisp->canvas->window, &values, GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_FILL | GDK_GC_STIPPLE); } gdisplay_transform_coords (gdisp, 0, 0, &x1, &y1, FALSE); gdisplay_transform_coords (gdisp, gdisp->gimage->width, gdisp->gimage->height, &x2, &y2, FALSE); gdk_window_get_size (gdisp->canvas->window, &w, &h); if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 > w) x2 = w; if (y2 > h) y2 = h; if (guide->orientation == HORIZONTAL_GUIDE) { gdisplay_transform_coords (gdisp, 0, guide->position, &x, &y, FALSE); if (active) gdk_draw_line (gdisp->canvas->window, active_hgc, x1, y, x2, y); else gdk_draw_line (gdisp->canvas->window, normal_hgc, x1, y, x2, y); } else if (guide->orientation == VERTICAL_GUIDE) { gdisplay_transform_coords (gdisp, guide->position, 0, &x, &y, FALSE); if (active) gdk_draw_line (gdisp->canvas->window, active_vgc, x, y1, x, y2); else gdk_draw_line (gdisp->canvas->window, normal_vgc, x, y1, x, y2); } } Guide* gdisplay_find_guide (GDisplay *gdisp, int x, int y) { GList *tmp_list; Guide *guide; double scale; int offset_x, offset_y; int pos; if (gdisp->draw_guides) { offset_x = gdisp->offset_x - gdisp->disp_xoffset; offset_y = gdisp->offset_y - gdisp->disp_yoffset; scale = (SCALESRC (gdisp) == 1) ? SCALEDEST (gdisp) : 1.0 / SCALESRC (gdisp); tmp_list = gdisp->gimage->guides; while (tmp_list) { guide = tmp_list->data; tmp_list = tmp_list->next; switch (guide->orientation) { case HORIZONTAL_GUIDE: pos = (int) (scale * guide->position - offset_y); if ((pos > (y - EPSILON)) && (pos < (y + EPSILON))) return guide; break; case VERTICAL_GUIDE: pos = (int) (scale * guide->position - offset_x); if ((pos > (x - EPSILON)) && (pos < (x + EPSILON))) return guide; break; } } } return NULL; } void gdisplay_snap_point (GDisplay *gdisp, int x , int y, int *tx, int *ty) { GList *tmp_list; Guide *guide; double scale; int offset_x, offset_y; int minhdist, minvdist; int pos, dist; *tx = x; *ty = y; if (gdisp->draw_guides && gdisp->snap_to_guides && gdisp->gimage->guides) { offset_x = gdisp->offset_x - gdisp->disp_xoffset; offset_y = gdisp->offset_y - gdisp->disp_yoffset; scale = (SCALESRC (gdisp) == 1) ? SCALEDEST (gdisp) : 1.0 / SCALESRC (gdisp); minhdist = G_MAXINT; minvdist = G_MAXINT; tmp_list = gdisp->gimage->guides; while (tmp_list) { guide = tmp_list->data; tmp_list = tmp_list->next; switch (guide->orientation) { case HORIZONTAL_GUIDE: pos = (int) (scale * guide->position - offset_y); if ((pos > (y - EPSILON)) && (pos < (y + EPSILON))) { dist = pos - y; dist = ABS (dist); if (dist < minhdist) { minhdist = dist; *ty = pos; } } break; case VERTICAL_GUIDE: pos = (int) (scale * guide->position - offset_x); if ((pos > (x - EPSILON)) && (pos < (x + EPSILON))) { dist = pos - x; dist = ABS (dist); if (dist < minvdist) { minvdist = dist; *tx = pos; } } break; } } } } void gdisplay_snap_rectangle (GDisplay *gdisp, int x1, int y1, int x2, int y2, int *tx1, int *ty1) { int nx1, ny1; int nx2, ny2; *tx1 = x1; *ty1 = y1; if (gdisp->draw_guides && gdisp->snap_to_guides && gdisp->gimage->guides) { gdisplay_snap_point (gdisp, x1, y1, &nx1, &ny1); gdisplay_snap_point (gdisp, x2, y2, &nx2, &ny2); if (x1 != nx1) *tx1 = nx1; else if (x2 != nx2) *tx1 = x1 + (nx2 - x2); if (y1 != ny1) *ty1 = ny1; else if (y2 != ny2) *ty1 = y1 + (ny2 - y2); } } void gdisplay_remove_and_delete (GDisplay *gdisp) { /* remove the display from the list */ display_list = remove_from_list (display_list, (void *) gdisp); gdisplay_delete (gdisp); } static void gdisplay_add_update_area (GDisplay *gdisp, int x, int y, int w, int h) { GArea * ga; ga = (GArea *) g_malloc (sizeof (GArea)); ga->x1 = BOUNDS (x, 0, gdisp->gimage->width); ga->y1 = BOUNDS (y, 0, gdisp->gimage->height); ga->x2 = BOUNDS (x + w, 0, gdisp->gimage->width); ga->y2 = BOUNDS (y + h, 0, gdisp->gimage->height); gdisp->update_areas = gdisplay_process_area_list (gdisp->update_areas, ga); } static void gdisplay_add_display_area (GDisplay *gdisp, int x, int y, int w, int h) { GArea * ga; ga = (GArea *) g_malloc (sizeof (GArea)); ga->x1 = BOUNDS (x, 0, gdisp->disp_width); ga->y1 = BOUNDS (y, 0, gdisp->disp_height); ga->x2 = BOUNDS (x + w, 0, gdisp->disp_width); ga->y2 = BOUNDS (y + h, 0, gdisp->disp_height); gdisp->display_areas = gdisplay_process_area_list (gdisp->display_areas, ga); } static void gdisplay_paint_area (GDisplay *gdisp, int x, int y, int w, int h) { int x1, y1, x2, y2; /* Bounds check */ x1 = BOUNDS (x, 0, gdisp->gimage->width); y1 = BOUNDS (y, 0, gdisp->gimage->height); x2 = BOUNDS (x + w, 0, gdisp->gimage->width); y2 = BOUNDS (y + h, 0, gdisp->gimage->height); x = x1; y = y1; w = (x2 - x1); h = (y2 - y1); /* calculate the extents of the update as limited by what's visible */ gdisplay_untransform_coords (gdisp, 0, 0, &x1, &y1, FALSE, FALSE); gdisplay_untransform_coords (gdisp, gdisp->disp_width, gdisp->disp_height, &x2, &y2, FALSE, FALSE); gimage_invalidate (gdisp->gimage, x, y, w, h, x1, y1, x2, y2); /* display the area */ gdisplay_transform_coords (gdisp, x, y, &x1, &y1, FALSE); gdisplay_transform_coords (gdisp, x + w, y + h, &x2, &y2, FALSE); gdisplay_expose_area (gdisp, x1, y1, (x2 - x1), (y2 - y1)); } static void gdisplay_display_area (GDisplay *gdisp, int x, int y, int w, int h) { int sx, sy; int x1, y1; int x2, y2; int dx, dy; int i, j; sx = SCALE (gdisp, gdisp->gimage->width); sy = SCALE (gdisp, gdisp->gimage->height); /* Bounds check */ x1 = BOUNDS (x, 0, gdisp->disp_width); y1 = BOUNDS (y, 0, gdisp->disp_height); x2 = BOUNDS (x + w, 0, gdisp->disp_width); y2 = BOUNDS (y + h, 0, gdisp->disp_height); if (x1 < gdisp->disp_xoffset) { gdk_window_clear_area (gdisp->canvas->window, x, y, gdisp->disp_xoffset - x, h); x1 = gdisp->disp_xoffset; } if (y1 < gdisp->disp_yoffset) { gdk_window_clear_area (gdisp->canvas->window, x, y, w, gdisp->disp_yoffset - y); y1 = gdisp->disp_yoffset; } if (x2 > (gdisp->disp_xoffset + sx)) { gdk_window_clear_area (gdisp->canvas->window, gdisp->disp_xoffset + sx, y, x2 - (gdisp->disp_xoffset + sx), h); x2 = gdisp->disp_xoffset + sx; } if (y2 > (gdisp->disp_yoffset + sy)) { gdk_window_clear_area (gdisp->canvas->window, x, gdisp->disp_yoffset + sy, w, y2 - (gdisp->disp_yoffset + sy)); y2 = gdisp->disp_yoffset + sy; } /* display the image in GXIMAGE_WIDTH x GXIMAGE_HEIGHT sized chunks */ for (i = y1; i < y2; i += GXIMAGE_HEIGHT) for (j = x1; j < x2; j += GXIMAGE_WIDTH) { dx = (x2 - j < GXIMAGE_WIDTH) ? x2 - j : GXIMAGE_WIDTH; dy = (y2 - i < GXIMAGE_HEIGHT) ? y2 - i : GXIMAGE_HEIGHT; render_image (gdisp, j - gdisp->disp_xoffset, i - gdisp->disp_yoffset, dx, dy); gximage_put (gdisp->canvas->window, j, i, dx, dy); } } int gdisplay_mask_value (GDisplay *gdisp, int x, int y) { /* move the coordinates from screen space to image space */ gdisplay_untransform_coords (gdisp, x, y, &x, &y, FALSE, 0); return gimage_mask_value (gdisp->gimage, x, y); } int gdisplay_mask_bounds (GDisplay *gdisp, int *x1, int *y1, int *x2, int *y2) { Layer *layer; int off_x, off_y; /* If there is a floating selection, handle things differently */ if ((layer = gimage_floating_sel (gdisp->gimage))) { drawable_offsets (GIMP_DRAWABLE(layer), &off_x, &off_y); if (! channel_bounds (gimage_get_mask (gdisp->gimage), x1, y1, x2, y2)) { *x1 = off_x; *y1 = off_y; *x2 = off_x + drawable_width (GIMP_DRAWABLE(layer)); *y2 = off_y + drawable_height (GIMP_DRAWABLE(layer)); } else { *x1 = MINIMUM (off_x, *x1); *y1 = MINIMUM (off_y, *y1); *x2 = MAXIMUM (off_x + drawable_width (GIMP_DRAWABLE(layer)), *x2); *y2 = MAXIMUM (off_y + drawable_height (GIMP_DRAWABLE(layer)), *y2); } } else if (! channel_bounds (gimage_get_mask (gdisp->gimage), x1, y1, x2, y2)) return FALSE; gdisplay_transform_coords (gdisp, *x1, *y1, x1, y1, 0); gdisplay_transform_coords (gdisp, *x2, *y2, x2, y2, 0); /* Make sure the extents are within bounds */ *x1 = BOUNDS (*x1, 0, gdisp->disp_width); *y1 = BOUNDS (*y1, 0, gdisp->disp_height); *x2 = BOUNDS (*x2, 0, gdisp->disp_width); *y2 = BOUNDS (*y2, 0, gdisp->disp_height); return TRUE; } void gdisplay_transform_coords (GDisplay *gdisp, int x, int y, int *nx, int *ny, int use_offsets) { double scale; int offset_x, offset_y; /* transform from image coordinates to screen coordinates */ scale = (SCALESRC (gdisp) == 1) ? SCALEDEST (gdisp) : 1.0 / SCALESRC (gdisp); if (use_offsets) drawable_offsets (gimage_active_drawable (gdisp->gimage), &offset_x, &offset_y); else { offset_x = offset_y = 0; } *nx = (int) (scale * (x + offset_x) - gdisp->offset_x); *ny = (int) (scale * (y + offset_y) - gdisp->offset_y); *nx += gdisp->disp_xoffset; *ny += gdisp->disp_yoffset; } void gdisplay_untransform_coords (GDisplay *gdisp, int x, int y, int *nx, int *ny, int round, int use_offsets) { double scale; int offset_x, offset_y; x -= gdisp->disp_xoffset; y -= gdisp->disp_yoffset; /* transform from screen coordinates to image coordinates */ scale = (SCALESRC (gdisp) == 1) ? SCALEDEST (gdisp) : 1.0 / SCALESRC (gdisp); if (use_offsets) drawable_offsets (gimage_active_drawable (gdisp->gimage), &offset_x, &offset_y); else { offset_x = offset_y = 0; } if (round) { *nx = ROUND ((x + gdisp->offset_x) / scale - offset_x); *ny = ROUND ((y + gdisp->offset_y) / scale - offset_y); } else { *nx = (int) ((x + gdisp->offset_x) / scale - offset_x); *ny = (int) ((y + gdisp->offset_y) / scale - offset_y); } } void gdisplay_transform_coords_f (GDisplay *gdisp, double x, double y, double *nx, double *ny, int use_offsets) { double scale; int offset_x, offset_y; /* transform from gimp coordinates to screen coordinates */ scale = (SCALESRC (gdisp) == 1) ? SCALEDEST (gdisp) : 1.0 / SCALESRC (gdisp); if (use_offsets) drawable_offsets (gimage_active_drawable (gdisp->gimage), &offset_x, &offset_y); else { offset_x = offset_y = 0; } *nx = scale * (x + offset_x) - gdisp->offset_x; *ny = scale * (y + offset_y) - gdisp->offset_y; *nx += gdisp->disp_xoffset; *ny += gdisp->disp_yoffset; } void gdisplay_untransform_coords_f (GDisplay *gdisp, double x, double y, double *nx, double *ny, int use_offsets) { double scale; int offset_x, offset_y; x -= gdisp->disp_xoffset; y -= gdisp->disp_yoffset; /* transform from screen coordinates to gimp coordinates */ scale = (SCALESRC (gdisp) == 1) ? SCALEDEST (gdisp) : 1.0 / SCALESRC (gdisp); if (use_offsets) drawable_offsets (gimage_active_drawable (gdisp->gimage), &offset_x, &offset_y); else { offset_x = offset_y = 0; } *nx = (x + gdisp->offset_x) / scale - offset_x; *ny = (y + gdisp->offset_y) / scale - offset_y; } /* install and remove tool cursor from gdisplay... */ void gdisplay_install_tool_cursor (GDisplay *gdisp, GdkCursorType cursor_type) { if (gdisp->current_cursor != cursor_type) { gdisp->current_cursor = cursor_type; change_win_cursor (gdisp->canvas->window, cursor_type); } } void gdisplay_remove_tool_cursor (GDisplay *gdisp) { unset_win_cursor (gdisp->canvas->window); } void gdisplay_set_menu_sensitivity (GDisplay *gdisp) { Layer *layer; gint fs; gint aux; gint lm; gint lp; GimpDrawable *drawable; gint base_type; gint type; fs = (gimage_floating_sel (gdisp->gimage) != NULL); aux = (gimage_get_active_channel (gdisp->gimage) != NULL); if ((layer = gimage_get_active_layer (gdisp->gimage)) != NULL) lm = (layer->mask) ? TRUE : FALSE; else lm = FALSE; base_type = gimage_base_type (gdisp->gimage); lp = (gdisp->gimage->layers != NULL); type = -1; if (lp) { drawable = gimage_active_drawable (gdisp->gimage); type = drawable_type (drawable); } menus_set_sensitive ("/Layers/Raise Layer", !fs && !aux && lp); menus_set_sensitive ("/Layers/Lower Layer", !fs && !aux && lp); menus_set_sensitive ("/Layers/Anchor Layer", fs && !aux && lp); menus_set_sensitive ("/Layers/Merge Visible Layers", !fs && !aux && lp); menus_set_sensitive ("/Layers/Flatten Image", !fs && !aux && lp); menus_set_sensitive ("/Layers/Alpha To Selection", !aux && lp); menus_set_sensitive ("/Layers/Mask To Selection", !aux && lm && lp); menus_set_sensitive ("/Image/RGB", (base_type != RGB)); menus_set_sensitive ("/Image/Grayscale", (base_type != GRAY)); menus_set_sensitive ("/Image/Indexed", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Threshold", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Posterize", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Equalize", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Invert", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Color Balance", (base_type == RGB)); menus_set_sensitive ("/Image/Colors/Brightness-Contrast", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Hue-Saturation", (base_type == RGB)); menus_set_sensitive ("/Image/Colors/Curves", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Levels", (base_type != INDEXED)); menus_set_sensitive ("/Image/Colors/Desaturate", (base_type == RGB)); menus_set_sensitive ("/Select", lp); menus_set_sensitive ("/Edit/Cut", lp); menus_set_sensitive ("/Edit/Copy", lp); menus_set_sensitive ("/Edit/Paste Into", lp); menus_set_sensitive ("/Edit/Clear", lp); menus_set_sensitive ("/Edit/Fill", lp); menus_set_sensitive ("/Edit/Stroke", lp); menus_set_sensitive ("/Edit/Cut Named", lp); menus_set_sensitive ("/Edit/Copy Named", lp); menus_set_sensitive ("/Image/Colors", lp); menus_set_sensitive ("/Image/Channel Ops/Offset", lp); menus_set_sensitive ("/Image/Histogram", lp); menus_set_sensitive ("/Filters", lp); /* save selection to channel */ menus_set_sensitive ("/Select/Save To Channel", !fs); menus_set_state ("/View/Toggle Rulers", GTK_WIDGET_VISIBLE (gdisp->origin) ? 1 : 0); menus_set_state ("/View/Toggle Guides", gdisp->draw_guides); menus_set_state ("/View/Snap To Guides", gdisp->snap_to_guides); plug_in_set_menu_sensitivity (type); } void gdisplay_expose_area (GDisplay *gdisp, int x, int y, int w, int h) { gdisplay_add_display_area (gdisp, x, y, w, h); } void gdisplay_expose_guide (GDisplay *gdisp, Guide *guide) { int x1, y1; int x2, y2; int x, y; int w, h; if (guide->position < 0) return; gdisplay_transform_coords (gdisp, 0, 0, &x1, &y1, FALSE); gdisplay_transform_coords (gdisp, gdisp->disp_width, gdisp->disp_height, &x2, &y2, FALSE); gdisplay_transform_coords (gdisp, guide->position, guide->position, &x, &y, FALSE); gdk_window_get_size (gdisp->canvas->window, &w, &h); if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if (x2 > w) x2 = w; if (y2 > h) y2 = h; switch (guide->orientation) { case HORIZONTAL_GUIDE: gdisplay_expose_area (gdisp, x1, y, x2 - x1, 1); break; case VERTICAL_GUIDE: gdisplay_expose_area (gdisp, x, y1, 1, y2 - y1); break; } } void gdisplay_expose_full (GDisplay *gdisp) { gdisplay_add_display_area (gdisp, 0, 0, gdisp->disp_width, gdisp->disp_height); } /**************************************************/ /* Functions independent of a specific gdisplay */ /**************************************************/ GDisplay * gdisplay_active () { GtkWidget *event_widget; GtkWidget *toplevel_widget; GdkEvent *event; GDisplay *gdisp; /* If the popup shell is valid, then get the gdisplay associated with that shell */ event = gtk_get_current_event (); event_widget = gtk_get_event_widget (event); gdk_event_free (event); toplevel_widget = gtk_widget_get_toplevel (event_widget); gdisp = g_hash_table_lookup (display_ht, toplevel_widget); if (gdisp) return gdisp; if (popup_shell) { active_tool_control (DESTROY, gdisp); gdisp = gtk_object_get_user_data (GTK_OBJECT (popup_shell)); return gdisp; } return NULL; } GDisplay * gdisplay_get_ID (int ID) { GDisplay *gdisp; link_ptr list = display_list; /* Traverse the list of displays, returning the one that matches the ID */ /* If no display in the list is a match, return NULL. */ while (list) { gdisp = (GDisplay *) list->data; if (gdisp->ID == ID) return gdisp; list = next_item (list); } return NULL; } void gdisplays_update_title (int ID) { GDisplay *gdisp; link_ptr list = display_list; char title [MAX_TITLE_BUF]; /* traverse the linked list of displays, handling each one */ while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == ID) { /* format the title */ gdisplay_format_title (gdisp->gimage, title); gdk_window_set_title (gdisp->shell->window, title); } list = next_item (list); } } void gdisplays_update_area (int ID, int x, int y, int w, int h) { GDisplay *gdisp; link_ptr list = display_list; int x1, y1, x2, y2; int count = 0; /* traverse the linked list of displays */ while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == ID) { /* We only need to update the first instance that we find of this gimage ID. Otherwise, we would be reconverting the same region unnecessarily. */ if (! count) gdisplay_add_update_area (gdisp, x, y, w, h); else { gdisplay_transform_coords (gdisp, x, y, &x1, &y1, 0); gdisplay_transform_coords (gdisp, x + w, y + h, &x2, &y2, 0); gdisplay_add_display_area (gdisp, x1, y1, (x2 - x1), (y2 - y1)); } count++; } list = next_item (list); } } void gdisplays_expose_guides (int ID) { GDisplay *gdisp; GList *tmp_list; link_ptr list; /* traverse the linked list of displays, handling each one */ list = display_list; while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == ID) { tmp_list = gdisp->gimage->guides; while (tmp_list) { gdisplay_expose_guide (gdisp, tmp_list->data); tmp_list = tmp_list->next; } } list = next_item (list); } } void gdisplays_expose_guide (int ID, Guide *guide) { GDisplay *gdisp; link_ptr list; /* traverse the linked list of displays, handling each one */ list = display_list; while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == ID) gdisplay_expose_guide (gdisp, guide); list = next_item (list); } } void gdisplays_update_full (int ID) { GDisplay *gdisp; link_ptr list = display_list; int count = 0; /* traverse the linked list of displays, handling each one */ while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == ID) { if (! count) gdisplay_add_update_area (gdisp, 0, 0, gdisp->gimage->width, gdisp->gimage->height); else gdisplay_add_display_area (gdisp, 0, 0, gdisp->disp_width, gdisp->disp_height); count++; } list = next_item (list); } } void gdisplays_shrink_wrap (int ID) { GDisplay *gdisp; link_ptr list = display_list; /* traverse the linked list of displays, handling each one */ while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == ID) shrink_wrap_display (gdisp); list = next_item (list); } } void gdisplays_expose_full () { GDisplay *gdisp; link_ptr list = display_list; /* traverse the linked list of displays, handling each one */ while (list) { gdisp = (GDisplay *) list->data; gdisplay_expose_full (gdisp); list = next_item (list); } } void gdisplays_selection_visibility (int gimage_ID, SelectionControl function) { GDisplay *gdisp; link_ptr list = display_list; int count = 0; /* traverse the linked list of displays, handling each one */ while (list) { gdisp = (GDisplay *) list->data; if (gdisp->gimage->ID == gimage_ID && gdisp->select) { switch (function) { case SelectionOff: selection_invis (gdisp->select); break; case SelectionLayerOff: selection_layer_invis (gdisp->select); break; case SelectionOn: selection_start (gdisp->select, TRUE); break; case SelectionPause: selection_pause (gdisp->select); break; case SelectionResume: selection_resume (gdisp->select); break; } count++; } list = next_item (list); } } int gdisplays_dirty () { int dirty = 0; link_ptr list = display_list; /* traverse the linked list of displays */ while (list) { if (((GDisplay *) list->data)->gimage->dirty > 0) dirty = 1; list = next_item (list); } return dirty; } void gdisplays_delete () { link_ptr list = display_list; /* traverse the linked list of displays */ while (list) { gdisplay_delete ((GDisplay *) list->data); list = next_item (list); } /* free up linked list data */ free_list (display_list); } void gdisplays_flush () { static int flushing = FALSE; link_ptr list = display_list; /* no flushing necessary without an interface */ if (no_interface) return; /* this prevents multiple recursive calls to this procedure */ if (flushing == TRUE) return; flushing = TRUE; /* traverse the linked list of displays */ while (list) { gdisplay_flush ((GDisplay *) list->data); list = next_item (list); } /* for convenience, we call the layers dialog flush here */ layers_dialog_flush (); /* for convenience, we call the channels dialog flush here */ channels_dialog_flush (); flushing = FALSE; } static guint gdisplay_hash (GDisplay *display) { return (gulong) display; }