/* 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. */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_GDK_PIXBUF #include #else #include #endif #ifdef GNOME #include #endif #ifdef GNOME_PRINT #include "paginate_gnomeprint.h" #else #include "paginate_psprint.h" #endif #include "intl.h" #include "commands.h" #include "app_procs.h" #include "diagram.h" #include "display.h" #include "object_ops.h" #include "cut_n_paste.h" #include "load_save.h" #include "utils.h" #include "message.h" #include "grid.h" #include "properties.h" #include "preferences.h" #include "layer_dialog.h" #include "connectionpoint_ops.h" #include "undo.h" #include "pagesetup.h" #include "dia_dirs.h" #include "focus.h" #include "gdk/gdk.h" #include "gdk/gdkkeysyms.h" #include "lib/properties.h" #include "dia-props.h" #ifdef HAVE_GDK_PIXBUF GdkPixbuf *logo; #else GdkImlibImage *logo; #endif void file_quit_callback(GtkWidget *widget, gpointer data) { app_exit(); } static void file_import_from_xfig_dialog_ok_callback (GtkWidget *w, GtkFileSelection *fs) { char *filename; Diagram *dia; struct stat stat_struct; dia = gtk_object_get_user_data(GTK_OBJECT(fs)); filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)); if (stat(filename, &stat_struct)!=0) { message_error(_("No such file found\n%s\n"), filename); gtk_grab_remove(GTK_WIDGET(fs)); gtk_widget_destroy(GTK_WIDGET(fs)); return; } diagram_import_from_xfig(dia, filename); gtk_grab_remove(GTK_WIDGET(fs)); gtk_widget_destroy(GTK_WIDGET(fs)); } void file_import_from_xfig_callback (GtkWidget *widget, gpointer data) { DDisplay *ddisp; GtkWidget *window = NULL; ddisp = ddisplay_active(); window = gtk_file_selection_new (_("Import from XFig")); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE); gtk_object_set_user_data(GTK_OBJECT(window), ddisp->diagram); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (window)->ok_button), "clicked", GTK_SIGNAL_FUNC(file_import_from_xfig_dialog_ok_callback), window); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (window)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (window)); gtk_widget_show (window); /* Make dialog modal: */ gtk_widget_grab_focus(window); gtk_grab_add(window); } void file_pagesetup_callback(GtkWidget *widget, gpointer data) { Diagram *dia; dia = ddisplay_active()->diagram; create_page_setup_dlg(dia); } void file_print_callback(GtkWidget *widget, gpointer data) { Diagram *dia; dia = ddisplay_active()->diagram; #ifdef GNOME_PRINT diagram_print_gnome(dia); #else diagram_print_ps(dia); #endif } void file_close_callback(GtkWidget *widget, gpointer data) { ddisplay_close(ddisplay_active()); } void file_new_callback(GtkWidget *widget, gpointer data) { Diagram *dia; DDisplay *ddisp; static int untitled_nr = 1; char buffer[24]; g_snprintf(buffer, 24, _("Untitled-%d"), untitled_nr++); dia = new_diagram(buffer); ddisp = new_display(dia); } void file_preferences_callback(GtkWidget *widget, gpointer data) { prefs_show(); } void edit_copy_callback(GtkWidget *widget, gpointer data) { GList *copy_list; DDisplay *ddisp; ddisp = ddisplay_active(); copy_list = diagram_get_sorted_selected(ddisp->diagram); cnp_store_objects(object_copy_list(copy_list)); } void edit_cut_callback(GtkWidget *widget, gpointer data) { GList *cut_list; DDisplay *ddisp; Change *change; ddisp = ddisplay_active(); diagram_selected_break_external(ddisp->diagram); cut_list = diagram_get_sorted_selected(ddisp->diagram); cnp_store_objects(object_copy_list(cut_list)); change = undo_delete_objects(ddisp->diagram, cut_list); (change->apply)(change, ddisp->diagram); diagram_update_menu_sensitivity(ddisp->diagram); diagram_flush(ddisp->diagram); undo_set_transactionpoint(ddisp->diagram->undo); } void edit_paste_callback(GtkWidget *widget, gpointer data) { GList *paste_list; DDisplay *ddisp; Point paste_corner; Point delta; Change *change; ddisp = ddisplay_active(); if (!cnp_exist_stored_objects()) { message_warning(_("No existing object to paste.\n")); return; } paste_list = cnp_get_stored_objects(); /* Gets a copy */ paste_corner = object_list_corner(paste_list); delta.x = ddisp->visible.left - paste_corner.x; delta.y = ddisp->visible.top - paste_corner.y; /* Move down some 10% of the visible area. */ delta.x += (ddisp->visible.right - ddisp->visible.left)*0.1; delta.y += (ddisp->visible.bottom - ddisp->visible.top)*0.1; object_list_move_delta(paste_list, &delta); change = undo_insert_objects(ddisp->diagram, paste_list, 0); (change->apply)(change, ddisp->diagram); undo_set_transactionpoint(ddisp->diagram->undo); diagram_remove_all_selected(ddisp->diagram, TRUE); diagram_select_list(ddisp->diagram, paste_list); diagram_flush(ddisp->diagram); } /* Signal handler for getting the selection from whoever */ void received_selection_handler(GtkWidget *widget, GtkSelectionData *selection, gpointer data) { Focus *focus = active_focus(); gchar *text; int i; ObjectChange *change = NULL; int modified, any_modified = FALSE; Object *obj; DDisplay *ddisp = ddisplay_active(); if ((focus == NULL) || (!focus->has_focus)) return; obj = focus->obj; /* **** IMPORTANT **** Check to see if retrieval succeeded */ if (selection->length < 0) { g_print ("Selection retrieval failed\n"); return; } /* Make sure we got the data in the expected form */ if (selection->type != GDK_SELECTION_TYPE_STRING) { g_print ("Selection \"STRING\" was not returned as a string!\n"); return; } text = (gchar *)selection->data; for (i = 0; i < selection->length; i++) { if (text[i] == '\n') { modified = (*focus->key_event)(focus, GDK_Return, "\n", 1, &change); } else { modified = (*focus->key_event)(focus, GDK_A, &text[i], 1, &change); } { /* Make sure object updates its data: */ Point p = obj->position; (obj->ops->move)(obj,&p); } /* Perhaps this can be improved */ object_add_updates(obj, ddisp->diagram); if (modified && (change != NULL)) { undo_object_change(ddisp->diagram, obj, change); any_modified = TRUE; } diagram_flush(ddisp->diagram); } if (any_modified) undo_set_transactionpoint(ddisp->diagram->undo); } static char *current_clipboard; void get_selection_handler(GtkWidget *widget, GtkSelectionData *selection, gpointer data) { if (current_clipboard) { gtk_selection_data_set(selection, GDK_TARGET_STRING, 8, current_clipboard, strlen(current_clipboard)); } } void edit_copy_text_callback(GtkWidget *widget, gpointer data) { Focus *focus = active_focus(); DDisplay *ddisp; Object *obj; Property textprop; if ((focus == NULL) || (!focus->has_focus)) return; ddisp = ddisplay_active(); obj = focus->obj; if (obj->ops->get_props == NULL) return; textprop.name = "text"; textprop.type = PROP_TYPE_INVALID; PROP_VALUE_STRING(textprop) = NULL; /* Get the first text property */ obj->ops->get_props(obj, &textprop, 1); if (current_clipboard) g_free(current_clipboard); if (textprop.type != PROP_TYPE_STRING) return; current_clipboard = g_strdup(PROP_VALUE_STRING(textprop)); prop_free(&textprop); gtk_selection_owner_set(GTK_WIDGET(ddisp->shell), GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME); gtk_selection_add_target(GTK_WIDGET(ddisp->shell), GDK_SELECTION_PRIMARY, GDK_TARGET_STRING, 0); } void edit_cut_text_callback(GtkWidget *widget, gpointer data) { } void edit_paste_text_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; ddisp = ddisplay_active(); gtk_selection_convert(ddisp->shell, GDK_SELECTION_PRIMARY, GDK_TARGET_STRING, GDK_CURRENT_TIME); } void edit_delete_callback(GtkWidget *widget, gpointer data) { GList *delete_list; DDisplay *ddisp; Change *change; ddisp = ddisplay_active(); diagram_selected_break_external(ddisp->diagram); delete_list = diagram_get_sorted_selected(ddisp->diagram); change = undo_delete_objects(ddisp->diagram, delete_list); (change->apply)(change, ddisp->diagram); diagram_update_menu_sensitivity(ddisp->diagram); diagram_flush(ddisp->diagram); undo_set_transactionpoint(ddisp->diagram->undo); } void edit_undo_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; Diagram *dia; ddisp = ddisplay_active(); dia = ddisp->diagram; undo_revert_to_last_tp(dia->undo); diagram_flush(dia); } void edit_redo_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; Diagram *dia; ddisp = ddisplay_active(); dia = ddisp->diagram; undo_apply_to_next_tp(dia->undo); diagram_flush(dia); } void help_about_callback(GtkWidget *widget, gpointer data) { GtkWidget *dialog; GtkWidget *vbox; GtkWidget *table; GtkWidget *bbox; GtkWidget *frame; GtkWidget *label; GtkWidget *button; char str[100]; gint i; GtkWidget *gpixmap; static const gchar *contributors[] = { "Jerome Abela", "Hans Breuer", "Emmanuel Briot", "Cyrille Chepelov", "Lars R. Clausen", "Fredrik Hallenberg", "Francis J. Lacoste", "Steffen Macke", "Jacek Pliszka", "Henk Jan Priester", "Alejandro Aguilar Sierra", }; const gint ncontributors = sizeof(contributors) / sizeof(contributors[0]); dialog = gtk_dialog_new (); gtk_window_set_wmclass (GTK_WINDOW (dialog), "about_dialog", "Dia"); gtk_window_set_title (GTK_WINDOW (dialog), _("About Dia")); gtk_window_set_policy (GTK_WINDOW (dialog), FALSE, FALSE, FALSE); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); gtk_signal_connect (GTK_OBJECT (dialog), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (dialog)); vbox = gtk_vbox_new (FALSE, 1); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox); if (!logo) { gchar* datadir = dia_get_data_directory(""); g_snprintf(str, sizeof(str), "%s/dia_logo.png", datadir); #ifdef HAVE_GDK_PIXBUF logo = gdk_pixbuf_new_from_file(str); #else logo = gdk_imlib_load_image(str); #endif g_free(datadir); } if (logo) { #ifdef HAVE_GDK_PIXBUF GdkPixmap *pixmap; GdkBitmap *bitmap; frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (frame), 1); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 1); gdk_pixbuf_render_pixmap_and_mask(logo, &pixmap, &bitmap, 128); gpixmap = gtk_pixmap_new(pixmap, bitmap); gdk_pixmap_unref(pixmap); gdk_bitmap_unref(bitmap); #else gdk_imlib_render(logo, logo->rgb_width, logo->rgb_height); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (frame), 1); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 1); gpixmap = gtk_pixmap_new(logo->pixmap, logo->shape_mask); #endif gtk_container_add (GTK_CONTAINER(frame), gpixmap); } frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_set_border_width (GTK_CONTAINER (frame), 1); gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 1); table = gtk_table_new(3, 2, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), 1); gtk_container_add (GTK_CONTAINER (frame), table); g_snprintf(str, sizeof(str), _("Dia v %s by Alexander Larsson"), VERSION); label = gtk_label_new (str); gtk_table_attach(GTK_TABLE(table), label, 0,2, 0,1, GTK_FILL|GTK_EXPAND, GTK_FILL, 0,2); label = gtk_label_new(_("Maintainer: James Henstridge")); gtk_table_attach(GTK_TABLE(table), label, 0,2, 1,2, GTK_FILL|GTK_EXPAND, GTK_FILL, 0,2); label = gtk_label_new (_("Please visit http://www.lysator.liu.se/~alla/dia " "for more info")); gtk_table_attach(GTK_TABLE(table), label, 0,2, 2,3, GTK_FILL|GTK_EXPAND, GTK_FILL, 0,2); label = gtk_label_new (_("Contributors:")); gtk_table_attach(GTK_TABLE(table), label, 0,2, 3,4, GTK_FILL|GTK_EXPAND, GTK_FILL, 0,2); for (i = 0; i < ncontributors; i++) { label = gtk_label_new(contributors[i]); gtk_table_attach(GTK_TABLE(table), label, i%2,i%2+1, i/2+4,i/2+5, GTK_FILL|GTK_EXPAND, GTK_FILL, 0,0); } gtk_table_set_col_spacings(GTK_TABLE(table), 1); gtk_table_set_row_spacings(GTK_TABLE(table), 1); bbox = gtk_hbutton_box_new(); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), bbox, TRUE, TRUE, 5); gtk_button_box_set_child_size(GTK_BUTTON_BOX(bbox), 80, 0); gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 10); button = gtk_button_new_with_label(_("OK")); gtk_container_add(GTK_CONTAINER(bbox), button); gtk_signal_connect_object(GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(dialog)); gtk_widget_show_all (dialog); } void view_zoom_in_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; Point middle; Rectangle *visible; ddisp = ddisplay_active(); visible = &ddisp->visible; middle.x = visible->left*0.5 + visible->right*0.5; middle.y = visible->top*0.5 + visible->bottom*0.5; ddisplay_zoom(ddisp, &middle, M_SQRT2); } void view_zoom_out_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; Point middle; Rectangle *visible; ddisp = ddisplay_active(); visible = &ddisp->visible; middle.x = visible->left*0.5 + visible->right*0.5; middle.y = visible->top*0.5 + visible->bottom*0.5; ddisplay_zoom(ddisp, &middle, M_SQRT1_2); } void view_zoom_set_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; int percent; real scale; Point middle; Rectangle *visible; ddisp = ddisplay_active(); visible = &ddisp->visible; middle.x = visible->left*0.5 + visible->right*0.5; middle.y = visible->top*0.5 + visible->bottom*0.5; # if GNOME { /* XXX get the % out of the menu's label -- this is gross */ float v; GtkBin *mi = GTK_BIN (widget); GtkLabel *lbl = GTK_LABEL (mi->child); sscanf (lbl->label, "%f", &v); scale = ((real) v)/100.0 * DDISPLAY_NORMAL_ZOOM; } # else percent = (int) data; scale = ((real) percent)/1000.0 * DDISPLAY_NORMAL_ZOOM; # endif ddisplay_zoom(ddisp, &middle, scale / ddisp->zoom_factor); } #ifdef GNOME void view_show_cx_pts_callback(GtkWidget *widget, gpointer callback_data) #else /* GNOME */ void view_show_cx_pts_callback(gpointer callback_data, guint callback_action, GtkWidget *widget) #endif /* GNOME */ { DDisplay *ddisp; int old_val; ddisp = ddisplay_active(); old_val = ddisp->show_cx_pts; ddisp->show_cx_pts = GTK_CHECK_MENU_ITEM (widget)->active; if (old_val != ddisp->show_cx_pts) { ddisplay_add_update_all(ddisp); ddisplay_flush(ddisp); } } #ifdef GNOME void view_aa_callback(GtkWidget *widget, gpointer callback_data) #else /* GNOME */ void view_aa_callback(gpointer callback_data, guint callback_action, GtkWidget *widget) #endif /* GNOME */ { DDisplay *ddisp; int aa; static gboolean shown_warning = FALSE; ddisp = ddisplay_active(); aa = GTK_CHECK_MENU_ITEM (widget)->active; if (aa && !shown_warning) message_warning(_("The anti aliased renderer is buggy, and may cause\n" "crashes. We know there are bugs in it, so don't\n" "bother submitting another report if it crashes")); shown_warning = TRUE; if (aa != ddisp->aa_renderer) { ddisplay_set_renderer(ddisp, aa); ddisplay_add_update_all(ddisp); ddisplay_flush(ddisp); } } #ifdef GNOME void view_visible_grid_callback(GtkWidget *widget, gpointer callback_data) #else /* GNOME */ void view_visible_grid_callback(gpointer callback_data, guint callback_action, GtkWidget *widget) #endif /* GNOME */ { DDisplay *ddisp; int old_val; ddisp = ddisplay_active(); old_val = ddisp->grid.visible; ddisp->grid.visible = GTK_CHECK_MENU_ITEM (widget)->active; if (old_val != ddisp->grid.visible) { ddisplay_add_update_all(ddisp); ddisplay_flush(ddisp); } } #ifdef GNOME void view_snap_to_grid_callback(GtkWidget *widget, gpointer callback_data) #else /* GNOME */ void view_snap_to_grid_callback(gpointer callback_data, guint callback_action, GtkWidget *widget) #endif /* GNOME */ { DDisplay *ddisp; int old_val; ddisp = ddisplay_active(); old_val = ddisp->grid.snap; ddisp->grid.snap = GTK_CHECK_MENU_ITEM (widget)->active; } #ifdef GNOME void view_toggle_rulers_callback(GtkWidget *widget, gpointer callback_data) #else /* GNOME */ void view_toggle_rulers_callback(gpointer callback_data, guint callback_action, GtkWidget *widget) #endif /* GNOME */ { DDisplay *ddisp; ddisp = ddisplay_active(); /* The following is borrowed straight from the Gimp: */ /* This routine use promiscuous knowledge of gtk internals * in order to hide and show the rulers "smoothly". This * is kludgy and a hack and may break if gtk is changed * internally. */ if (!GTK_CHECK_MENU_ITEM (widget)->active) { if (GTK_WIDGET_VISIBLE (ddisp->origin)) { gtk_widget_unmap (ddisp->origin); gtk_widget_unmap (ddisp->hrule); gtk_widget_unmap (ddisp->vrule); GTK_WIDGET_UNSET_FLAGS (ddisp->origin, GTK_VISIBLE); GTK_WIDGET_UNSET_FLAGS (ddisp->hrule, GTK_VISIBLE); GTK_WIDGET_UNSET_FLAGS (ddisp->vrule, GTK_VISIBLE); gtk_widget_queue_resize (GTK_WIDGET (ddisp->origin->parent)); } } else { if (!GTK_WIDGET_VISIBLE (ddisp->origin)) { GTK_WIDGET_SET_FLAGS (ddisp->origin, GTK_VISIBLE); GTK_WIDGET_SET_FLAGS (ddisp->hrule, GTK_VISIBLE); GTK_WIDGET_SET_FLAGS (ddisp->vrule, GTK_VISIBLE); gtk_widget_map (ddisp->origin); gtk_widget_map (ddisp->hrule); gtk_widget_map (ddisp->vrule); gtk_widget_queue_resize (GTK_WIDGET (ddisp->origin->parent)); } } } extern void view_new_view_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; Diagram *dia; ddisp = ddisplay_active(); dia = ddisp->diagram; ddisp = new_display(dia); } void view_show_all_callback(GtkWidget *widget, gpointer data) { DDisplay *ddisp; Diagram *dia; real magnify_x, magnify_y; int width, height; Point middle; ddisp = ddisplay_active(); dia = ddisp->diagram; width = ddisp->renderer->pixel_width; height = ddisp->renderer->pixel_height; magnify_x = (real)width / (dia->data->extents.right - dia->data->extents.left) / ddisp->zoom_factor; magnify_y = (real)height / (dia->data->extents.bottom - dia->data->extents.top) / ddisp->zoom_factor; middle.x = dia->data->extents.left + (dia->data->extents.right - dia->data->extents.left) / 2.0; middle.y = dia->data->extents.top + (dia->data->extents.bottom - dia->data->extents.top) / 2.0; ddisplay_zoom (ddisp, &middle, (magnify_xdiagram); } void objects_place_over_callback(GtkWidget *widget, gpointer data) { diagram_place_over_selected(ddisplay_active()->diagram); } void objects_place_under_callback(GtkWidget *widget, gpointer data) { diagram_place_under_selected(ddisplay_active()->diagram); } void objects_group_callback(GtkWidget *widget, gpointer data) { diagram_group_selected(ddisplay_active()->diagram); } void objects_ungroup_callback(GtkWidget *widget, gpointer data) { diagram_ungroup_selected(ddisplay_active()->diagram); } void dialogs_properties_callback(GtkWidget *widget, gpointer data) { Diagram *dia; Object *selected; dia = ddisplay_active()->diagram; if (dia->data->selected != NULL) { selected = dia->data->selected->data; } else { selected = NULL; } properties_show(dia, selected); } void dialogs_layers_callback(GtkWidget *widget, gpointer data) { layer_dialog_set_diagram(ddisplay_active()->diagram); layer_dialog_show(); } void objects_align_h_callback(GtkWidget *widget, gpointer data) { int align; Diagram *dia; GList *objects; align = GPOINTER_TO_INT(data); dia = ddisplay_active()->diagram; objects = dia->data->selected; object_add_updates_list(objects, dia); object_list_align_h(objects, dia, align); diagram_update_connections_selection(dia); object_add_updates_list(objects, dia); diagram_flush(dia); undo_set_transactionpoint(dia->undo); } void objects_align_v_callback(GtkWidget *widget, gpointer data) { int align; Diagram *dia; GList *objects; align = GPOINTER_TO_INT(data); dia = ddisplay_active()->diagram; objects = dia->data->selected; object_add_updates_list(objects, dia); object_list_align_v(objects, dia, align); diagram_update_connections_selection(dia); object_add_updates_list(objects, dia); diagram_flush(dia); undo_set_transactionpoint(dia->undo); }