/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998 Alexander Larsson * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef GNOME #include #endif #include "intl.h" #include "display.h" #include "group.h" #include "interface.h" #include "focus.h" #include "color.h" #include "object.h" #include "handle_ops.h" #include "connectionpoint_ops.h" #include "menus.h" #include "cut_n_paste.h" #include "message.h" #include "preferences.h" #include "app_procs.h" #include "layer_dialog.h" #include "load_save.h" #include "dia-props.h" #include "render_gdk.h" #include "render_libart.h" static GHashTable *display_ht = NULL; static GdkCursor *current_cursor = NULL; GdkCursor *default_cursor = NULL; static DDisplay *active_display = NULL; typedef struct _IRectangle { int top, bottom; int left,right; } IRectangle; static guint display_hash(DDisplay *ddisp); static void update_zoom_status(DDisplay *ddisp) { GtkWidget *zoomcombo; gchar* zoom_text; zoomcombo = ddisp->zoom_status; zoom_text = g_strdup_printf("%.1f%%", ddisp->zoom_factor * 100.0 / DDISPLAY_NORMAL_ZOOM); gtk_entry_set_text(GTK_ENTRY(gtk_object_get_user_data(GTK_OBJECT(zoomcombo))), zoom_text); g_free(zoom_text); /* Copied by gtk_entry_set_text */ } static void update_modified_status(DDisplay *ddisp) { GtkStatusbar *statusbar; guint context_id; if (diagram_is_modified(ddisp->diagram)) { statusbar = GTK_STATUSBAR (ddisp->modified_status); context_id = gtk_statusbar_get_context_id (statusbar, "Changed"); gtk_statusbar_pop (statusbar, context_id); gtk_statusbar_push (statusbar, context_id, _("Diagram modified!")); } else { statusbar = GTK_STATUSBAR (ddisp->modified_status); context_id = gtk_statusbar_get_context_id (statusbar, "Changed"); gtk_statusbar_pop (statusbar, context_id); } } static void selection_changed (Diagram* dia, int n, DDisplay* ddisp) { GtkStatusbar *statusbar; guint context_id; statusbar = GTK_STATUSBAR (ddisp->modified_status); context_id = gtk_statusbar_get_context_id (statusbar, "Selection"); if (n > 1) { gchar *msg; /* http://www.gnu.org/software/gettext/manual/html_chapter/gettext_10.html#SEC150 * Althoug the single objects wont get triggered here some languages have variations on the other numbers */ msg = g_strdup_printf (ngettext ("Selection of %d object", "Selection of %d objects", n), n); gtk_statusbar_pop (statusbar, context_id); gtk_statusbar_push (statusbar, context_id, msg); g_free (msg); } else if (n == 1) { /* find the selected objects name - and display it */ DiaObject *object = (DiaObject *)ddisp->diagram->data->selected->data; gchar *name = object_get_displayname (object); gchar *msg = g_strdup_printf (_("Selected '%s'"), name); gtk_statusbar_pop (statusbar, context_id); gtk_statusbar_push (statusbar, context_id, msg); g_free (name); g_free (msg); } else { gtk_statusbar_pop (statusbar, context_id); } } static void append_im_menu (DDisplay* ddisp, GtkAction* action) { GSList *proxies; GtkWidget *im_menu; /* GtkWidget *im_menu_tearoff; */ proxies = gtk_action_get_proxies (action); while (proxies) { if (GTK_IS_MENU_ITEM (proxies->data)) { im_menu = gtk_menu_new (); /* tearoff should be added depending on gtk settings im_menu_tearoff = gtk_tearoff_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL(im_menu), im_menu_tearoff); */ gtk_im_multicontext_append_menuitems ( GTK_IM_MULTICONTEXT(ddisp->im_context), GTK_MENU_SHELL(im_menu)); gtk_menu_item_set_submenu (GTK_MENU_ITEM(proxies->data), im_menu); gtk_widget_show (GTK_WIDGET (proxies->data)); gtk_widget_show (GTK_WIDGET (im_menu)); } proxies = proxies->next; } } /** Initialize the various GTK-level thinks in a display after the internal * data has been set. * @param ddisp A display with all non-GTK/GDK items set. */ static void initialize_display_widgets(DDisplay *ddisp) { GtkAction* im_menu_item; static gboolean input_methods_done = FALSE; Diagram *dia = ddisp->diagram; gchar *filename; /* ddisp->renderer = new_gdk_renderer(ddisp);*/ ddisp->im_context = gtk_im_multicontext_new(); g_signal_connect (G_OBJECT (ddisp->im_context), "commit", G_CALLBACK (ddisplay_im_context_commit), ddisp); ddisp->preedit_string = NULL; g_signal_connect (G_OBJECT (ddisp->im_context), "preedit_changed", G_CALLBACK (ddisplay_im_context_preedit_changed), ddisp); ddisp->preedit_attrs = NULL; filename = strrchr(dia->filename, G_DIR_SEPARATOR); if (filename==NULL) { filename = dia->filename; } else { filename++; } create_display_shell(ddisp, prefs.new_view.width, prefs.new_view.height, filename, prefs.new_view.use_menu_bar, !app_is_embedded()); ddisplay_update_statusbar (ddisp); ddisplay_set_origo(ddisp, ddisp->visible.left, ddisp->visible.top); ddisplay_update_scrollbars(ddisp); ddisplay_add_update_all(ddisp); if (!display_ht) display_ht = g_hash_table_new ((GHashFunc) display_hash, NULL); if (!app_is_embedded()) ddisplay_set_cursor(ddisp, current_cursor); g_hash_table_insert (display_ht, ddisp->shell, ddisp); g_hash_table_insert (display_ht, ddisp->canvas, ddisp); if (!input_methods_done) { im_menu_item = menus_get_action ("InputMethods"); g_assert (im_menu_item); append_im_menu (ddisp, im_menu_item); input_methods_done = TRUE; } /* the diagram menubar gets recreated for every diagram */ if (ddisp->menu_bar) { im_menu_item = gtk_action_group_get_action (ddisp->actions, "InputMethods"); g_assert (im_menu_item); append_im_menu (ddisp, im_menu_item); } } /** Make a copy of an existing display. The original does not need to have * the various GTK-related fields initialized, and so can just have been read * from a savefile. * Note that size and position are not handled here yet, but taken from prefs. * @param A display object with non-GTK/GDK fields initialized (same fields as * new_display initializes before calling initialize_display_widgets()). * @returns A newly allocated display, inserted into the diagram list, with * same basic layout as the original. */ DDisplay * copy_display(DDisplay *orig_ddisp) { DDisplay *ddisp; Diagram *dia = orig_ddisp->diagram; ddisp = g_new0(DDisplay,1); /* initialize the whole struct to 0 so that we are sure to catch errors.*/ memset (&ddisp->updatable_menu_items, 0, sizeof (UpdatableMenuItems)); ddisp->diagram = orig_ddisp->diagram; /* Every display has its own reference */ g_object_ref(dia); ddisp->grid = orig_ddisp->grid; ddisp->show_cx_pts = orig_ddisp->show_cx_pts; ddisp->autoscroll = orig_ddisp->autoscroll; ddisp->mainpoint_magnetism = orig_ddisp->mainpoint_magnetism; ddisp->aa_renderer = orig_ddisp->aa_renderer; ddisp->update_areas = orig_ddisp->update_areas; ddisp->display_areas = orig_ddisp->display_areas; ddisp->update_id = 0; diagram_add_ddisplay(dia, ddisp); g_signal_connect (dia, "selection_changed", G_CALLBACK(selection_changed), ddisp); ddisp->origo = orig_ddisp->origo; ddisp->zoom_factor = orig_ddisp->zoom_factor; ddisp->visible = orig_ddisp->visible; initialize_display_widgets(ddisp); return ddisp; /* set the user data */ } /** Create a new display for a diagram, using prefs settings. * @param dia Otherwise initialize diagram to create a display for. * @returns A newly created display. */ DDisplay * new_display(Diagram *dia) { DDisplay *ddisp; Rectangle visible; ddisp = g_new0(DDisplay,1); /* initialize the whole struct to 0 so that we are sure to catch errors.*/ memset (&ddisp->updatable_menu_items, 0, sizeof (UpdatableMenuItems)); ddisp->diagram = dia; /* Every display has it's own reference */ g_object_ref(dia); ddisp->grid.visible = prefs.grid.visible; ddisp->grid.snap = prefs.grid.snap; ddisp->show_cx_pts = prefs.show_cx_pts; ddisp->autoscroll = TRUE; ddisp->mainpoint_magnetism = TRUE; ddisp->aa_renderer = 0; ddisp->update_areas = NULL; ddisp->display_areas = NULL; ddisp->update_id = 0; diagram_add_ddisplay(dia, ddisp); g_signal_connect (dia, "selection_changed", G_CALLBACK(selection_changed), ddisp); ddisp->origo.x = 0.0; ddisp->origo.y = 0.0; ddisp->zoom_factor = prefs.new_view.zoom/100.0*DDISPLAY_NORMAL_ZOOM; if ((ddisp->diagram) && (ddisp->diagram->data)) { Rectangle *extents = &ddisp->diagram->data->extents; visible.left = extents->left; visible.top = extents->top; } else { visible.left = 0.0; visible.top = 0.0; } visible.right = visible.left + prefs.new_view.width/ddisp->zoom_factor; visible.bottom = visible.top + prefs.new_view.height/ddisp->zoom_factor; ddisp->visible = visible; initialize_display_widgets(ddisp); return ddisp; /* set the user data */ } static guint display_hash(DDisplay *ddisp) { return (gulong) ddisp; } void ddisplay_transform_coords_double(DDisplay *ddisp, coord x, coord y, double *xi, double *yi) { Rectangle *visible = &ddisp->visible; double width = dia_renderer_get_width_pixels (ddisp->renderer); double height = dia_renderer_get_height_pixels (ddisp->renderer); *xi = (x - visible->left) * (real)width / (visible->right - visible->left); *yi = (y - visible->top) * (real)height / (visible->bottom - visible->top); } void ddisplay_transform_coords(DDisplay *ddisp, coord x, coord y, int *xi, int *yi) { Rectangle *visible = &ddisp->visible; int width = dia_renderer_get_width_pixels (ddisp->renderer); int height = dia_renderer_get_height_pixels (ddisp->renderer); *xi = ROUND ( (x - visible->left) * (real)width / (visible->right - visible->left) ); *yi = ROUND ( (y - visible->top) * (real)height / (visible->bottom - visible->top) ); } /* Takes real length and returns pixel length */ real ddisplay_transform_length(DDisplay *ddisp, real len) { return len * ddisp->zoom_factor; } /* Takes pixel length and returns real length */ real ddisplay_untransform_length(DDisplay *ddisp, real len) { return len / ddisp->zoom_factor; } void ddisplay_untransform_coords(DDisplay *ddisp, int xi, int yi, coord *x, coord *y) { Rectangle *visible = &ddisp->visible; int width = dia_renderer_get_width_pixels (ddisp->renderer); int height = dia_renderer_get_height_pixels (ddisp->renderer); *x = visible->left + xi*(visible->right - visible->left) / (real)width; *y = visible->top + yi*(visible->bottom - visible->top) / (real)height; } void ddisplay_add_update_pixels(DDisplay *ddisp, Point *point, int pixel_width, int pixel_height) { Rectangle rect; real size_x, size_y; size_x = ddisplay_untransform_length(ddisp, pixel_width+1); size_y = ddisplay_untransform_length(ddisp, pixel_height+1); rect.left = point->x - size_x/2.0; rect.top = point->y - size_y/2.0; rect.right = point->x + size_x/2.0; rect.bottom = point->y + size_y/2.0; ddisplay_add_update(ddisp, &rect); } /** Free display_areas list */ static void ddisplay_free_display_areas(DDisplay *ddisp) { GSList *l; l = ddisp->display_areas; while(l!=NULL) { g_free(l->data); l = g_slist_next(l); } g_slist_free(ddisp->display_areas); ddisp->display_areas = NULL; } /** Free update_areas list */ static void ddisplay_free_update_areas(DDisplay *ddisp) { GSList *l; l = ddisp->update_areas; while(l!=NULL) { g_free(l->data); l = g_slist_next(l); } g_slist_free(ddisp->update_areas); ddisp->update_areas = NULL; } /** Marks the entire visible area for update. * Throws out old updates, since everything will be updated anyway. */ void ddisplay_add_update_all(DDisplay *ddisp) { if (ddisp->update_areas != NULL) { ddisplay_free_update_areas(ddisp); } if (ddisp->display_areas != NULL) { ddisplay_free_display_areas(ddisp); } ddisplay_add_update(ddisp, &ddisp->visible); } /** Marks a rectangle for update, with a pixel border around it. */ void ddisplay_add_update_with_border(DDisplay *ddisp, Rectangle *rect, int pixel_border) { Rectangle r; real size = ddisplay_untransform_length(ddisp, pixel_border+1); r.left = rect->left-size; r.top = rect->top-size; r.right = rect->right+size; r.bottom = rect->bottom+size; ddisplay_add_update(ddisp, &r); } void ddisplay_add_update(DDisplay *ddisp, Rectangle *rect) { Rectangle *r; int top,bottom,left,right; Rectangle *visible; int width, height; if (!ddisp->renderer) return; /* can happen at creation time of the diagram */ width = dia_renderer_get_width_pixels (ddisp->renderer); height = dia_renderer_get_height_pixels (ddisp->renderer); if (!rectangle_intersects(rect, &ddisp->visible)) return; /* Temporarily just do a union of all rectangles: */ if (ddisp->update_areas==NULL) { r = g_new(Rectangle,1); *r = *rect; rectangle_intersection(r, &ddisp->visible); ddisp->update_areas = g_slist_prepend(ddisp->update_areas, r); } else { r = (Rectangle *) ddisp->update_areas->data; rectangle_union(r, rect); rectangle_intersection(r, &ddisp->visible); } visible = &ddisp->visible; left = floor( (r->left - visible->left) * (real)width / (visible->right - visible->left) ) - 1; top = floor( (r->top - visible->top) * (real)height / (visible->bottom - visible->top) ) - 1; right = ceil( (r->right - visible->left) * (real)width / (visible->right - visible->left) ) + 1; bottom = ceil( (r->bottom - visible->top) * (real)height / (visible->bottom - visible->top) ) + 1; ddisplay_add_display_area(ddisp, left, top, right, bottom); } void ddisplay_add_display_area(DDisplay *ddisp, int left, int top, int right, int bottom) { IRectangle *r; if (left < 0) left = 0; if (top < 0) top = 0; if (right > dia_renderer_get_width_pixels (ddisp->renderer)) right = dia_renderer_get_width_pixels (ddisp->renderer); if (bottom > dia_renderer_get_height_pixels (ddisp->renderer)) bottom = dia_renderer_get_height_pixels (ddisp->renderer); /* draw some rectangles to show where updates are...*/ /* gdk_draw_rectangle(ddisp->canvas->window, ddisp->canvas->style->black_gc, TRUE, left, top, right-left,bottom-top); */ /* Temporarily just do a union of all Irectangles: */ if (ddisp->display_areas==NULL) { r = g_new(IRectangle,1); r->top = top; r->bottom = bottom; r->left = left; r->right = right; ddisp->display_areas = g_slist_prepend(ddisp->display_areas, r); } else { r = (IRectangle *) ddisp->display_areas->data; r->top = MIN( r->top, top ); r->bottom = MAX( r->bottom, bottom ); r->left = MIN( r->left, left ); r->right = MAX( r->right, right ); } } static gboolean ddisplay_update_handler(DDisplay *ddisp) { GSList *l; IRectangle *ir; Rectangle *r, totrect; DiaInteractiveRendererInterface *renderer; /* Renders updates to pixmap + copies display_areas to canvas(screen) */ renderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (ddisp->renderer); /* Only update if update_areas exist */ l = ddisp->update_areas; if (l != NULL) { totrect = *(Rectangle *) l->data; g_return_val_if_fail ( renderer->clip_region_clear != NULL && renderer->clip_region_add_rect != NULL, FALSE); renderer->clip_region_clear (ddisp->renderer); while(l!=NULL) { r = (Rectangle *) l->data; rectangle_union(&totrect, r); renderer->clip_region_add_rect (ddisp->renderer, r); l = g_slist_next(l); } /* Free update_areas list: */ ddisplay_free_update_areas(ddisp); totrect.left -= 0.1; totrect.right += 0.1; totrect.top -= 0.1; totrect.bottom += 0.1; ddisplay_render_pixmap(ddisp, &totrect); } l = ddisp->display_areas; while(l!=NULL) { ir = (IRectangle *) l->data; g_return_val_if_fail (renderer->copy_to_window, FALSE); renderer->copy_to_window(ddisp->renderer, ddisp->canvas->window, ir->left, ir->top, ir->right - ir->left, ir->bottom - ir->top); l = g_slist_next(l); } ddisplay_free_display_areas(ddisp); ddisp->update_id = 0; return FALSE; } void ddisplay_flush(DDisplay *ddisp) { /* if no update is queued, queue update */ if (!ddisp->update_id) ddisp->update_id = gtk_idle_add((GtkFunction) ddisplay_update_handler, ddisp); } static void ddisplay_obj_render(DiaObject *obj, DiaRenderer *renderer, int active_layer, gpointer data) { DDisplay *ddisp = (DDisplay *)data; int i; DIA_RENDERER_GET_CLASS(renderer)->draw_object(renderer, obj); if (ddisp->show_cx_pts && obj->parent_layer != NULL && obj->parent_layer->connectable) { for (i=0;inum_connections;i++) { connectionpoint_draw(obj->connections[i], ddisp); } } } void ddisplay_render_pixmap(DDisplay *ddisp, Rectangle *update) { GList *list; DiaObject *obj; int i; DiaInteractiveRendererInterface *renderer; #ifdef TRACES GTimer *timer; #endif if (ddisp->renderer==NULL) { printf("ERROR! Renderer was NULL!!\n"); return; } renderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (ddisp->renderer); /* Erase background */ g_return_if_fail (renderer->fill_pixel_rect != NULL); DIA_RENDERER_GET_CLASS(ddisp->renderer)->begin_render(ddisp->renderer); renderer->fill_pixel_rect (ddisp->renderer, 0, 0, dia_renderer_get_width_pixels (ddisp->renderer), dia_renderer_get_height_pixels (ddisp->renderer), &ddisp->diagram->data->bg_color); /* Draw grid */ grid_draw(ddisp, update); pagebreak_draw(ddisp, update); #ifdef TRACES timer = g_timer_new(); #endif data_render(ddisp->diagram->data, ddisp->renderer, update, ddisplay_obj_render, (gpointer) ddisp); #ifdef TRACES g_print ("data_render(%g%%) took %g seconds\n", ddisp->zoom_factor * 5.0, g_timer_elapsed (timer, NULL)); g_timer_destroy (timer); #endif /* Draw handles for all selected objects */ list = ddisp->diagram->data->selected; while (list!=NULL) { obj = (DiaObject *) list->data; for (i=0;inum_handles;i++) { handle_draw(obj->handles[i], ddisp); } list = g_list_next(list); } DIA_RENDERER_GET_CLASS(ddisp->renderer)->end_render(ddisp->renderer); } void ddisplay_update_scrollbars(DDisplay *ddisp) { Rectangle *extents = &ddisp->diagram->data->extents; Rectangle *visible = &ddisp->visible; GtkAdjustment *hsbdata, *vsbdata; hsbdata = ddisp->hsbdata; /* Horizontal: */ hsbdata->lower = MIN(extents->left, visible->left); hsbdata->upper = MAX(extents->right, visible->right); hsbdata->page_size = visible->right - visible->left - 0.0001; /* remove some to fix strange behaviour in gtk_range_adjustment_changed */ hsbdata->page_increment = (visible->right - visible->left) / 2.0; hsbdata->step_increment = (visible->right - visible->left) / 10.0; hsbdata->value = visible->left; g_signal_emit_by_name (G_OBJECT (ddisp->hsbdata), "changed"); /* Vertical: */ vsbdata = ddisp->vsbdata; vsbdata->lower = MIN(extents->top, visible->top); vsbdata->upper = MAX(extents->bottom, visible->bottom); vsbdata->page_size = visible->bottom - visible->top - 0.00001; /* remove some to fix strange behaviour in gtk_range_adjustment_changed */ vsbdata->page_increment = (visible->bottom - visible->top) / 2.0; vsbdata->step_increment = (visible->bottom - visible->top) / 10.0; vsbdata->value = visible->top; g_signal_emit_by_name (G_OBJECT (ddisp->vsbdata), "changed"); } void ddisplay_set_origo(DDisplay *ddisp, coord x, coord y) { Rectangle *extents = &ddisp->diagram->data->extents; Rectangle *visible = &ddisp->visible; int width, height; /* updaterar origo+visible+rulers */ ddisp->origo.x = x; ddisp->origo.y = y; if (ddisp->zoom_factorzoom_factor = DDISPLAY_MIN_ZOOM; if (ddisp->zoom_factor > DDISPLAY_MAX_ZOOM) ddisp->zoom_factor = DDISPLAY_MAX_ZOOM; width = dia_renderer_get_width_pixels (ddisp->renderer); height = dia_renderer_get_height_pixels (ddisp->renderer); visible->left = ddisp->origo.x; visible->top = ddisp->origo.y; visible->right = ddisp->origo.x + ddisplay_untransform_length(ddisp, width); visible->bottom = ddisp->origo.y + ddisplay_untransform_length(ddisp, height); gtk_ruler_set_range (GTK_RULER (ddisp->hrule), visible->left, visible->right, 0.0f /* position*/, MAX(extents->right, visible->right)/* max_size*/); gtk_ruler_set_range (GTK_RULER (ddisp->vrule), visible->top, visible->bottom, 0.0f /* position*/, MAX(extents->bottom, visible->bottom)/* max_size*/); } void ddisplay_zoom(DDisplay *ddisp, Point *point, real magnify) { Rectangle *visible; real width, height; visible = &ddisp->visible; width = (visible->right - visible->left)/magnify; height = (visible->bottom - visible->top)/magnify; if ((ddisp->zoom_factor <= DDISPLAY_MIN_ZOOM) && (magnify<=1.0)) return; if ((ddisp->zoom_factor >= DDISPLAY_MAX_ZOOM) && (magnify>=1.0)) return; ddisp->zoom_factor *= magnify; ddisplay_set_origo(ddisp, point->x - width/2.0, point->y - height/2.0); ddisplay_update_scrollbars(ddisp); ddisplay_add_update_all(ddisp); ddisplay_flush(ddisp); update_zoom_status (ddisp); } /** Set the display's snap-to-grid setting, updating menu and button * in the process */ void ddisplay_set_snap_to_grid(DDisplay *ddisp, gboolean snap) { GtkToggleAction *snap_to_grid; ddisp->grid.snap = snap; if (ddisp->menu_bar == NULL) { snap_to_grid = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptogrid")); } else { snap_to_grid = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewSnaptogrid")); } /* Currently, this can cause double emit, but that's a small problem. */ gtk_toggle_action_set_active (snap_to_grid, ddisp->grid.snap); ddisplay_update_statusbar(ddisp); } /** Update the button showing whether snap-to-grid is on */ static void update_snap_grid_status(DDisplay *ddisp) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ddisp->grid_status), ddisp->grid.snap); } /** Set the display's mainpoint magnetism setting, updating menu and button * in the process */ void ddisplay_set_snap_to_objects(DDisplay *ddisp, gboolean magnetic) { GtkToggleAction *mainpoint_magnetism; ddisp->mainpoint_magnetism = magnetic; if (ddisp->menu_bar == NULL) { mainpoint_magnetism = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptoobjects")); } else { mainpoint_magnetism = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewSnaptoobjects")); } /* Currently, this can cause double emit, but that's a small problem. */ gtk_toggle_action_set_active (mainpoint_magnetism, ddisp->mainpoint_magnetism); ddisplay_update_statusbar(ddisp); } /** Update the button showing whether mainpoint magnetism is on */ static void update_mainpoint_status(DDisplay *ddisp) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ddisp->mainpoint_status), ddisp->mainpoint_magnetism); } /** Scroll display to where point x,y (window coords) is visible */ gboolean ddisplay_autoscroll(DDisplay *ddisp, int x, int y) { guint16 width, height; Point scroll; if (! ddisp->autoscroll) return FALSE; scroll.x = scroll.y = 0; width = GTK_WIDGET(ddisp->canvas)->allocation.width; height = GTK_WIDGET(ddisp->canvas)->allocation.height; if (x < 0) { scroll.x = x; } else if ( x > width) { scroll.x = x - width; } if (y < 0) { scroll.y = y; } else if (y > height) { scroll.y = y - height; } if ((scroll.x != 0) || (scroll.y != 0)) { gboolean scrolled; scroll.x = ddisplay_untransform_length(ddisp, scroll.x); scroll.y = ddisplay_untransform_length(ddisp, scroll.y); scrolled = ddisplay_scroll(ddisp, &scroll); if (scrolled) { ddisplay_flush(ddisp); return TRUE; } } return FALSE; } /** Scroll the display by delta (diagram coords) */ gboolean ddisplay_scroll(DDisplay *ddisp, Point *delta) { Rectangle *visible = &ddisp->visible; real width = visible->right - visible->left; real height = visible->bottom - visible->top; Rectangle extents = ddisp->diagram->data->extents; real ex_width = extents.right - extents.left; real ex_height = extents.bottom - extents.top; Point new_origo = ddisp->origo; point_add(&new_origo, delta); rectangle_union(&extents, visible); if (new_origo.x < extents.left - ex_width) new_origo.x = extents.left - ex_width; if (new_origo.x+width > extents.right + ex_width) new_origo.x = extents.right - width + ex_width; if (new_origo.y < extents.top - ex_height) new_origo.y = extents.top - ex_height; if (new_origo.y+height > extents.bottom + ex_height) new_origo.y = extents.bottom - height + ex_height; if ( (new_origo.x != ddisp->origo.x) || (new_origo.y != ddisp->origo.y) ) { ddisplay_set_origo(ddisp, new_origo.x, new_origo.y); ddisplay_update_scrollbars(ddisp); ddisplay_add_update_all(ddisp); return TRUE; } return FALSE; } void ddisplay_scroll_up(DDisplay *ddisp) { Point delta; delta.x = 0; delta.y = -(ddisp->visible.bottom - ddisp->visible.top)/4.0; ddisplay_scroll(ddisp, &delta); } void ddisplay_scroll_down(DDisplay *ddisp) { Point delta; delta.x = 0; delta.y = (ddisp->visible.bottom - ddisp->visible.top)/4.0; ddisplay_scroll(ddisp, &delta); } void ddisplay_scroll_left(DDisplay *ddisp) { Point delta; delta.x = -(ddisp->visible.right - ddisp->visible.left)/4.0; delta.y = 0; ddisplay_scroll(ddisp, &delta); } void ddisplay_scroll_right(DDisplay *ddisp) { Point delta; delta.x = (ddisp->visible.right - ddisp->visible.left)/4.0; delta.y = 0; ddisplay_scroll(ddisp, &delta); } /** Scroll display to have the diagram point p at the center. * Returns TRUE if anything changed. */ gboolean ddisplay_scroll_center_point(DDisplay *ddisp, Point *p) { Point center; /* Find current center */ center.x = (ddisp->visible.right+ddisp->visible.left)/2; center.y = (ddisp->visible.top+ddisp->visible.bottom)/2; point_sub(p, ¢er); return ddisplay_scroll(ddisp, p); } /** Scroll display so that obj is centered. * Returns TRUE if anything changed. */ gboolean ddisplay_scroll_to_object(DDisplay *ddisp, DiaObject *obj) { Rectangle r = obj->bounding_box; Point p; p.x = (r.left+r.right)/2; p.y = (r.top+r.bottom)/2; return ddisplay_scroll_center_point(ddisp, &p); } void ddisplay_set_renderer(DDisplay *ddisp, int aa_renderer) { int width, height; if (ddisp->renderer) g_object_unref (ddisp->renderer); ddisp->aa_renderer = aa_renderer; width = ddisp->canvas->allocation.width; height = ddisp->canvas->allocation.height; if (ddisp->aa_renderer){ ddisp->renderer = new_libart_renderer( dia_transform_new (&ddisp->visible, &ddisp->zoom_factor), 1); } else { ddisp->renderer = new_gdk_renderer(ddisp); } dia_renderer_set_size(ddisp->renderer, ddisp->canvas->window, width, height); } void ddisplay_resize_canvas(DDisplay *ddisp, int width, int height) { if (ddisp->renderer==NULL) { if (ddisp->aa_renderer) ddisp->renderer = new_libart_renderer( dia_transform_new (&ddisp->visible, &ddisp->zoom_factor), 1); else ddisp->renderer = new_gdk_renderer(ddisp); } dia_renderer_set_size(ddisp->renderer, ddisp->canvas->window, width, height); ddisplay_set_origo(ddisp, ddisp->origo.x, ddisp->origo.y); ddisplay_add_update_all(ddisp); ddisplay_flush(ddisp); } DDisplay * ddisplay_active(void) { return active_display; } Diagram * ddisplay_active_diagram(void) { DDisplay *ddisp = ddisplay_active (); if (!ddisp) return NULL; return ddisp->diagram; } static void ddisp_destroy(DDisplay *ddisp) { if (ddisp->update_id) { gtk_idle_remove(ddisp->update_id); ddisp->update_id = 0; } g_signal_handlers_disconnect_by_func (ddisp->diagram, selection_changed, ddisp); g_object_unref (G_OBJECT (ddisp->im_context)); ddisp->im_context = NULL; ddisplay_im_context_preedit_reset(ddisp, active_focus()); /* This calls ddisplay_really_destroy */ gtk_widget_destroy (ddisp->shell); } static void are_you_sure_close_dialog_respond(GtkWidget *widget, /* the dialog */ gint response_id, gpointer user_data) /* the display */ { DDisplay *ddisp = (DDisplay *)user_data; switch (response_id) { case GTK_RESPONSE_YES : /* save changes */ diagram_save(ddisp->diagram, ddisp->diagram->filename); if (ddisp->update_id) { gtk_idle_remove(ddisp->update_id); ddisp->update_id = 0; } /* fall through */ case GTK_RESPONSE_NO : ddisp_destroy (ddisp); /* fall through */ case GTK_RESPONSE_CANCEL : case GTK_RESPONSE_NONE : case GTK_RESPONSE_DELETE_EVENT : /* closing via window manager */ gtk_widget_destroy(widget); break; default : g_assert_not_reached(); } } void ddisplay_close(DDisplay *ddisp) { Diagram *dia; GtkWidget *dialog, *button; gchar *fname; g_return_if_fail(ddisp != NULL); dia = ddisp->diagram; if ( (dia->display_count > 1) || (!diagram_is_modified(dia)) ) { ddisp_destroy(ddisp); return; } fname = dia->filename; if (!fname) fname = _(""); dialog = gtk_message_dialog_new(GTK_WINDOW (ddisp->shell), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, /* no standard buttons */ _("Closing diagram without saving"), NULL); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), _("The diagram '%s'\n" "has not been saved. Save changes now?"), fname); gtk_window_set_title (GTK_WINDOW(dialog), _("Close Diagram")); button = gtk_button_new_from_stock (GTK_STOCK_CANCEL); gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_CANCEL); button = gtk_button_new_with_mnemonic (_("_Discard Changes")); gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_NO); /* button = gtk_button_new_with_label (_("Save and Close")); */ button = gtk_button_new_from_stock (GTK_STOCK_SAVE); gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_YES); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_YES); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK(are_you_sure_close_dialog_respond), ddisp); gtk_widget_show_all(dialog); } void display_update_menu_state(DDisplay *ddisp) { GtkToggleAction *rulers; GtkToggleAction *visible_grid; GtkToggleAction *snap_to_grid; GtkToggleAction *show_cx_pts; #ifdef HAVE_LIBART GtkToggleAction *antialiased; #endif if (ddisp->menu_bar == NULL) { rulers = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowrulers")); visible_grid = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowgrid")); snap_to_grid = GTK_TOGGLE_ACTION (menus_get_action ("ViewSnaptogrid")); show_cx_pts = GTK_TOGGLE_ACTION (menus_get_action ("ViewShowconnectionpoints")); #ifdef HAVE_LIBART antialiased = GTK_TOGGLE_ACTION (menus_get_action ("ViewAntialiased")); #else /* the action is registered to avoid crashing, disable the entry */ gtk_action_set_sensitive (menus_get_action ("ViewAntialiased"), FALSE); #endif } else { rulers = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewShowrulers")); visible_grid = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewShowgrid")); snap_to_grid = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewSnaptogrid")); show_cx_pts = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewShowconnectionpoints")); #ifdef HAVE_LIBART antialiased = GTK_TOGGLE_ACTION (gtk_action_group_get_action (ddisp->actions, "ViewAntialiased")); #else gtk_action_set_sensitive (gtk_action_group_get_action (ddisp->actions, "ViewAntialiased"), FALSE); #endif } ddisplay_do_update_menu_sensitivity (ddisp); gtk_toggle_action_set_active (rulers, GTK_WIDGET_VISIBLE (ddisp->hrule) ? 1 : 0); gtk_toggle_action_set_active (visible_grid, ddisp->grid.visible); gtk_toggle_action_set_active (snap_to_grid, ddisp->grid.snap); gtk_toggle_action_set_active (show_cx_pts, ddisp->show_cx_pts); #ifdef HAVE_LIBART gtk_toggle_action_set_active (antialiased, ddisp->aa_renderer); #endif } void ddisplay_do_update_menu_sensitivity (DDisplay *ddisp) { Diagram *dia; dia = ddisp->diagram; if (ddisp->menu_bar) { diagram_update_menubar_sensitivity(dia, &ddisp->updatable_menu_items); } else { diagram_update_popupmenu_sensitivity(dia); } } /* This is called when ddisp->shell is destroyed... */ void ddisplay_really_destroy(DDisplay *ddisp) { if (active_display == ddisp) display_set_active(NULL); if (ddisp->diagram) { diagram_remove_ddisplay(ddisp->diagram, ddisp); /* if we are the last user of the diagram it will be unref'ed */ g_object_unref(ddisp->diagram); ddisp->diagram = NULL; } g_object_unref (ddisp->renderer); ddisp->renderer = NULL; g_hash_table_remove(display_ht, ddisp->shell); g_hash_table_remove(display_ht, ddisp->canvas); /* Free update_areas list: */ ddisplay_free_update_areas(ddisp); /* Free display_areas list */ ddisplay_free_display_areas(ddisp); g_free(ddisp); } void ddisplay_set_title(DDisplay *ddisp, char *title) { gtk_window_set_title (GTK_WINDOW (ddisp->shell), title); } void ddisplay_set_all_cursor(GdkCursor *cursor) { Diagram *dia; DDisplay *ddisp; GList *list; GSList *slist; current_cursor = cursor; list = dia_open_diagrams(); while (list != NULL) { dia = (Diagram *) list->data; slist = dia->displays; while (slist != NULL) { ddisp = (DDisplay *) slist->data; ddisplay_set_cursor(ddisp, cursor); slist = g_slist_next(slist); } list = g_list_next(list); } } void ddisplay_set_cursor(DDisplay *ddisp, GdkCursor *cursor) { gdk_window_set_cursor(ddisp->canvas->window, cursor); } void ddisplay_update_statusbar(DDisplay *ddisp) { update_zoom_status (ddisp); update_snap_grid_status (ddisp); update_mainpoint_status (ddisp); update_modified_status (ddisp); } void display_set_active(DDisplay *ddisp) { if (ddisp != active_display) { active_display = ddisp; /* perform notification here (such as switch layers dialog) */ layer_dialog_set_diagram(ddisp ? ddisp->diagram : NULL); diagram_properties_set_diagram(ddisp ? ddisp->diagram : NULL); if (ddisp) { display_update_menu_state(ddisp); if (prefs.toolbox_on_top) { gtk_window_set_transient_for(GTK_WINDOW(interface_get_toolbox_shell()), GTK_WINDOW(ddisp->shell)); } else { gtk_window_set_transient_for(GTK_WINDOW(interface_get_toolbox_shell()), NULL); } } else { gtk_window_set_transient_for(GTK_WINDOW(interface_get_toolbox_shell()), NULL); } } } void ddisplay_im_context_preedit_reset(DDisplay *ddisp, Focus *focus) { if (ddisp->preedit_string != NULL) { if (focus != NULL) { int i; ObjectChange *change; for (i = 0; i < g_utf8_strlen(ddisp->preedit_string, -1); i++) { (focus->key_event)(focus, GDK_BackSpace, NULL, 0, &change); } } g_free(ddisp->preedit_string); ddisp->preedit_string = NULL; } if (ddisp->preedit_attrs != NULL) { pango_attr_list_unref(ddisp->preedit_attrs); ddisp->preedit_attrs = NULL; } }