/* * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for PIKA. * * Generates images containing vector type drawings. * * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk * * 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 3 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, see . */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef G_OS_WIN32 #include #endif #include #include #include "libpika/stdplugins-intl.h" #include "gfig.h" #include "gfig-style.h" #include "gfig-dialog.h" #include "gfig-arc.h" #include "gfig-bezier.h" #include "gfig-circle.h" #include "gfig-dobject.h" #include "gfig-ellipse.h" #include "gfig-grid.h" #include "gfig-icons.h" #include "gfig-line.h" #include "gfig-poly.h" #include "gfig-preview.h" #include "gfig-rectangle.h" #include "gfig-spiral.h" #include "gfig-star.h" #define SEL_BUTTON_WIDTH 100 #define SEL_BUTTON_HEIGHT 20 #define GRID_TYPE_MENU 1 #define GRID_RENDER_MENU 2 #define PAINT_BGS_MENU 2 #define PAINT_TYPE_MENU 3 #define OBJ_SELECT_GT 1 #define OBJ_SELECT_LT 2 #define OBJ_SELECT_EQ 4 #define UPDATE_DELAY 300 /* From GtkRange in GTK+ 2.22 */ /* Globals */ gint undo_level; /* Last slot filled in -1 = no undo */ GList *undo_table[MAX_UNDO]; /* Values when first invoked */ SelectItVals selvals = { { MIN_GRID + (MAX_GRID - MIN_GRID)/2, /* Gridspacing */ RECT_GRID, /* Default to rectangle type */ FALSE, /* drawgrid */ FALSE, /* snap2grid */ FALSE, /* lockongrid */ TRUE, /* show control points */ 0.0, /* grid_radius_min */ 10.0, /* grid_radius_interval */ 0.0, /* grid_rotation */ 5.0, /* grid_granularity */ 120 /* grid_sectors_desired */ }, FALSE, /* show image */ MIN_UNDO + (MAX_UNDO - MIN_UNDO)/2, /* Max level of undos */ TRUE, /* Show pos updates */ 0.0, /* Brush fade */ 0.0, /* Brush gradient */ 20.0, /* Air brush pressure */ ORIGINAL_LAYER, /* Draw all objects on one layer */ LAYER_TRANS_BG, /* New layers background */ PAINT_BRUSH_TYPE, /* Default to use brushes */ FALSE, /* reverse lines */ TRUE, /* Scale to image when painting */ 1.0, /* Scale to image fp */ BRUSH_BRUSH_TYPE, /* Default to use a brush */ LINE /* Initial object type */ }; selection_option selopt = { PIKA_CHANNEL_OP_ADD, /* type */ FALSE, /* Antia */ FALSE, /* Feather */ 10.0, /* feather radius */ ARC_SEGMENT, /* Arc as a segment */ FILL_PATTERN, /* Fill as pattern */ 100.0, /* Max opacity */ }; /* Should be kept in sync with GfigOpts */ typedef struct { GtkWidget *gridspacing; GtkWidget *grid_sectors_desired; GtkWidget *grid_radius_interval; GtkWidget *gridtypemenu; GtkWidget *drawgrid; GtkWidget *snap2grid; GtkWidget *lockongrid; GtkWidget *showcontrol; } GfigOptWidgets; static GfigOptWidgets gfig_opt_widget = { NULL, NULL, NULL, NULL, NULL, NULL }; static gchar *gfig_path = NULL; static GtkWidget *page_menu_bg; static GtkWidget *tool_options_notebook; static GtkWidget *fill_type_notebook; static guint paint_timeout = 0; static void shape_change_state (GSimpleAction *action, GVariant *new_state, gpointer user_data); static void gfig_response (GtkWidget *widget, gint response_id, gpointer data); static void gfig_load_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void gfig_save_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void gfig_list_load_all (const gchar *path); static void gfig_list_free_all (void); static void create_notebook_pages (GtkWidget *notebook); static void select_filltype_callback (GtkWidget *widget); static void gfig_close_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void gfig_undo_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void gfig_clear_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void gfig_grid_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void gfig_prefs_action (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void toggle_show_image (void); static void gridtype_combo_callback (GtkWidget *widget, gpointer data); static void load_file_chooser_response (GtkFileChooser *chooser, gint response_id, gpointer data); static void save_file_chooser_response (GtkFileChooser *chooser, gint response_id, GFigObj *obj); static void paint_combo_callback (GtkWidget *widget, gpointer data); static void select_button_clicked (gint type); static void select_button_clicked_lt (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void select_button_clicked_gt (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void select_button_clicked_eq (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void raise_selected_obj_to_top (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void lower_selected_obj_to_bottom (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void raise_selected_obj (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void lower_selected_obj (GSimpleAction *action, GVariant *parameter, gpointer user_data); static void toggle_obj_type (DobjType new_type); static void gfig_scale_entry_update_double (PikaLabelSpin *entry, gdouble *value); static void gfig_scale_entry_update_int (PikaLabelSpin *entry, gint *value); /* GAction helper methods */ static GtkWidget * add_tool_button (GtkWidget *toolbar, const char *action, const char *icon, const char *label, const char *tooltip); static GtkWidget * add_toggle_button (GtkWidget *toolbar, const char *action, const char *icon, const char *label, const char *tooltip); static void add_tool_separator (GtkWidget *toolbar, gboolean expand); static const GActionEntry ACTIONS[] = { /* Sub-menu options */ { "open", gfig_load_action }, { "save", gfig_save_action }, { "close", gfig_close_action }, { "undo", gfig_undo_action }, { "clear", gfig_clear_action }, { "grid", gfig_grid_action }, { "preferences", gfig_prefs_action }, /* Toolbar buttons */ { "raise", raise_selected_obj }, { "lower", lower_selected_obj }, { "top", raise_selected_obj_to_top }, { "bottom", lower_selected_obj_to_bottom }, { "show-prev", select_button_clicked_lt }, { "show-next", select_button_clicked_gt }, { "show-all", select_button_clicked_eq }, /* RadioButtons - only the default state is shown here. */ { "shape", shape_change_state, "s", "'line'", NULL }, }; gboolean gfig_dialog (PikaGfig *gfig) { GAction *action; GtkWidget *main_hbox; GtkWidget *vbox; GFigObj *gfig_obj; PikaParasite *parasite; PikaLayer *newlayer; GtkWidget *menubar; GMenuModel *model; GtkWidget *toolbar; GtkWidget *combo; GtkWidget *frame; gint img_width; gint img_height; PikaImageType img_type; GtkWidget *toggle; GtkWidget *right_vbox; GtkWidget *hbox; GtkWidget *empty_label; gchar *path; pika_ui_init (PLUG_IN_BINARY); img_width = pika_drawable_get_width (gfig_context->drawable); img_height = pika_drawable_get_height (gfig_context->drawable); img_type = pika_drawable_type_with_alpha (gfig_context->drawable); /* * See if there is a "gfig" parasite. If so, this is a gfig layer, * and we start by clearing it to transparent. * If not, we create a new transparent layer. */ gfig_list = NULL; undo_level = -1; parasite = pika_item_get_parasite (PIKA_ITEM (gfig_context->drawable), "gfig"); gfig_context->enable_repaint = FALSE; /* debug */ gfig_context->debug_styles = FALSE; /* initial default style */ gfig_read_pika_style (&gfig_context->default_style, "Base"); gfig_context->default_style.paint_type = selvals.painttype; if (parasite) { pika_drawable_fill (gfig_context->drawable, PIKA_FILL_TRANSPARENT); gfig_context->using_new_layer = FALSE; pika_parasite_free (parasite); } else { newlayer = pika_layer_new (gfig_context->image, "GFig", img_width, img_height, img_type, 100.0, pika_image_get_default_new_layer_mode (gfig_context->image)); pika_drawable_fill (PIKA_DRAWABLE (newlayer), PIKA_FILL_TRANSPARENT); pika_image_insert_layer (gfig_context->image, newlayer, NULL, -1); gfig_context->drawable = PIKA_DRAWABLE (newlayer); gfig_context->using_new_layer = TRUE; } gfig_icons_init (); path = pika_pikarc_query ("gfig-path"); if (path) { gfig_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL); g_free (path); } else { GFile *pikarc = pika_directory_file ("pikarc", NULL); gchar *full_path = pika_config_build_data_path ("gfig"); gchar *esc_path = g_strescape (full_path, NULL); g_free (full_path); g_message (_("No %s in pikarc:\n" "You need to add an entry like\n" "(%s \"%s\")\n" "to your %s file."), "gfig-path", "gfig-path", esc_path, pika_file_get_utf8_name (pikarc)); g_object_unref (pikarc); g_free (esc_path); } /* Start building the dialog up */ gfig->top_level_dlg = pika_dialog_new (_("Gfig"), PLUG_IN_ROLE, NULL, 0, pika_standard_help_func, PLUG_IN_PROC, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Close"), GTK_RESPONSE_OK, NULL); gtk_window_set_application (GTK_WINDOW (gfig->top_level_dlg), gfig->app); pika_window_set_transient (GTK_WINDOW (gfig->top_level_dlg)); pika_dialog_set_alternative_button_order (GTK_DIALOG (gfig->top_level_dlg), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); g_signal_connect (gfig->top_level_dlg, "response", G_CALLBACK (gfig_response), gfig); /* build the menu */ g_action_map_add_action_entries (G_ACTION_MAP (gfig->app), ACTIONS, G_N_ELEMENTS (ACTIONS), gfig); model = G_MENU_MODEL (gtk_builder_get_object (gfig->builder, "gfig-menubar")); menubar = gtk_menu_bar_new_from_model (model); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (gfig->top_level_dlg))), menubar, FALSE, FALSE, 0); gtk_widget_show (menubar); toolbar = gtk_toolbar_new (); add_toggle_button (toolbar, "app.shape::line", GFIG_ICON_LINE, _("Line"), _("Create line")); add_toggle_button (toolbar, "app.shape::rectangle", GFIG_ICON_RECTANGLE, _("Rectangle"), _("Create rectangle")); add_toggle_button (toolbar, "app.shape::circle", GFIG_ICON_CIRCLE, _("Circle"), _("Create circle")); add_toggle_button (toolbar, "app.shape::ellipse", GFIG_ICON_ELLIPSE, _("Ellipse"), _("Create ellipse")); add_toggle_button (toolbar, "app.shape::arc", GFIG_ICON_CURVE, _("Arc"), _("Create arc")); add_toggle_button (toolbar, "app.shape::polygon", GFIG_ICON_POLYGON, _("Polygon"), _("Create reg polygon")); add_toggle_button (toolbar, "app.shape::star", GFIG_ICON_STAR, _("Star"), _("Create star")); add_toggle_button (toolbar, "app.shape::spiral", GFIG_ICON_SPIRAL, _("Spiral"), _("Create spiral")); add_toggle_button (toolbar, "app.shape::bezier", GFIG_ICON_BEZIER, _("Bezier"), _("Create bezier curve. " "Shift + Button ends object creation.")); add_toggle_button (toolbar, "app.shape::move-obj", GFIG_ICON_MOVE_OBJECT, _("Move Object"), _("Move an object")); add_toggle_button (toolbar, "app.shape::move-point", GFIG_ICON_MOVE_POINT, _("Move Point"), _("Move a single point")); add_toggle_button (toolbar, "app.shape::copy", GFIG_ICON_COPY_OBJECT, _("Copy"), _("Copy an object")); add_toggle_button (toolbar, "app.shape::delete", GFIG_ICON_DELETE_OBJECT, _("Delete"), _("Delete an object")); add_toggle_button (toolbar, "app.shape::select", GFIG_ICON_SELECT_OBJECT, _("Select"), _("Select an object")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.raise", PIKA_ICON_GO_UP, _("Raise"), _("Raise selected object")); add_tool_button (toolbar, "app.lower", PIKA_ICON_GO_DOWN, _("Lower"), _("Lower selected object")); add_tool_button (toolbar, "app.raise", PIKA_ICON_GO_TOP, _("To Top"), _("Raise selected object to top")); add_tool_button (toolbar, "app.lower", PIKA_ICON_GO_BOTTOM, _("To Bottom"), _("Lower selected object to bottom")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.show-prev", PIKA_ICON_GO_PREVIOUS, _("Show Previous"), _("Show previous object")); add_tool_button (toolbar, "app.show-next", PIKA_ICON_GO_NEXT, _("Show Next"), _("Show next object")); add_tool_button (toolbar, "app.show-all", GFIG_ICON_SHOW_ALL, _("Show All"), _("Show all objects")); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (gfig->top_level_dlg))), toolbar, FALSE, FALSE, 0); gtk_widget_show (toolbar); action = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "undo"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), undo_level >= 0); /* Main box */ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12); gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (gfig->top_level_dlg))), main_hbox, TRUE, TRUE, 0); /* Preview itself */ gtk_box_pack_start (GTK_BOX (main_hbox), make_preview (gfig), FALSE, FALSE, 0); gtk_widget_show (gfig_context->preview); right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); gtk_widget_set_hexpand (right_vbox, FALSE); gtk_widget_set_halign (right_vbox, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, FALSE, FALSE, 0); gtk_widget_show (right_vbox); /* Tool options notebook */ frame = pika_frame_new ( _("Tool Options")); gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); tool_options_notebook = gtk_notebook_new (); gtk_container_add (GTK_CONTAINER (frame), tool_options_notebook); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (tool_options_notebook), FALSE); gtk_notebook_set_show_border (GTK_NOTEBOOK (tool_options_notebook), FALSE); gtk_widget_show (tool_options_notebook); create_notebook_pages (tool_options_notebook); /* Stroke frame on right side */ frame = pika_frame_new (NULL); gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); gfig_context->paint_type_toggle = toggle = gtk_check_button_new_with_mnemonic (_("_Stroke")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.painttype); gtk_frame_set_label_widget (GTK_FRAME (frame), toggle); gtk_widget_show (toggle); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_add (GTK_CONTAINER (frame), hbox); gtk_widget_show (hbox); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); gtk_widget_set_sensitive (vbox, selvals.painttype); g_signal_connect (toggle, "toggled", G_CALLBACK (set_paint_type_callback), vbox); /* foreground color button in Stroke frame*/ gfig_context->fg_color = g_new0 (PikaRGB, 1); gfig_context->fg_color_button = pika_color_button_new ("Foreground", SEL_BUTTON_WIDTH, SEL_BUTTON_HEIGHT, gfig_context->fg_color, PIKA_COLOR_AREA_SMALL_CHECKS); g_signal_connect (gfig_context->fg_color_button, "color-changed", G_CALLBACK (set_foreground_callback), gfig_context->fg_color); pika_color_button_set_color (PIKA_COLOR_BUTTON (gfig_context->fg_color_button), &gfig_context->default_style.foreground); gtk_box_pack_start (GTK_BOX (vbox), gfig_context->fg_color_button, FALSE, FALSE, 0); gtk_widget_show (gfig_context->fg_color_button); /* brush selector in Stroke frame */ gfig_context->brush_select = pika_brush_chooser_new ("Brush", "Brush", PIKA_RESOURCE (gfig_context->default_style.brush)); g_signal_connect (gfig_context->brush_select, "resource-set", G_CALLBACK (gfig_brush_changed_callback), NULL); gtk_box_pack_start (GTK_BOX (vbox), gfig_context->brush_select, FALSE, FALSE, 0); gtk_widget_show (gfig_context->brush_select); /* Fill frame on right side */ frame = pika_frame_new (_("Fill")); gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0); gtk_widget_show (frame); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_add (GTK_CONTAINER (frame), hbox); gtk_widget_show (hbox); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); /* fill style combo box in Style frame */ gfig_context->fillstyle_combo = combo = pika_int_combo_box_new (_("No fill"), FILL_NONE, _("Color fill"), FILL_COLOR, _("Pattern fill"), FILL_PATTERN, _("Shape gradient"), FILL_GRADIENT, _("Vertical gradient"), FILL_VERTICAL, _("Horizontal gradient"), FILL_HORIZONTAL, NULL); pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), 0); g_signal_connect (combo, "changed", G_CALLBACK (select_filltype_callback), NULL); gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); gtk_widget_show (combo); fill_type_notebook = gtk_notebook_new (); gtk_box_pack_start (GTK_BOX (vbox), fill_type_notebook, FALSE, FALSE, 0); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (fill_type_notebook), FALSE); gtk_notebook_set_show_border (GTK_NOTEBOOK (fill_type_notebook), FALSE); gtk_widget_show (fill_type_notebook); /* An empty page for "No fill" */ empty_label = gtk_label_new (""); gtk_widget_show (empty_label); gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), empty_label, NULL); /* A page for the fill color button */ gfig_context->bg_color = g_new0 (PikaRGB, 1); gfig_context->bg_color_button = pika_color_button_new ("Background", SEL_BUTTON_WIDTH, SEL_BUTTON_HEIGHT, gfig_context->bg_color, PIKA_COLOR_AREA_SMALL_CHECKS); g_signal_connect (gfig_context->bg_color_button, "color-changed", G_CALLBACK (set_background_callback), gfig_context->bg_color); pika_color_button_set_color (PIKA_COLOR_BUTTON (gfig_context->bg_color_button), &gfig_context->default_style.background); gtk_widget_show (gfig_context->bg_color_button); gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), gfig_context->bg_color_button, NULL); /* A page for the pattern selector */ gfig_context->pattern_select = pika_pattern_chooser_new ("Pattern", "Pattern", PIKA_RESOURCE (gfig_context->default_style.pattern)); g_signal_connect (gfig_context->pattern_select, "resource-set", G_CALLBACK (gfig_pattern_changed_callback), NULL); gtk_widget_show (gfig_context->pattern_select); gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), gfig_context->pattern_select, NULL); /* A page for the gradient selector */ gfig_context->gradient_select = pika_gradient_chooser_new ("Gradient", "Gradient", PIKA_RESOURCE (gfig_context->default_style.gradient)); g_signal_connect (gfig_context->gradient_select, "resource-set", G_CALLBACK (gfig_gradient_changed_callback), NULL); gtk_widget_show (gfig_context->gradient_select); gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook), gfig_context->gradient_select, NULL); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_box_pack_start (GTK_BOX (right_vbox), vbox, FALSE, FALSE, 0); gtk_widget_show (vbox); /* "show image" checkbutton at bottom of style frame */ toggle = gtk_check_button_new_with_label (_("Show image")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gfig_context->show_background); gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &gfig_context->show_background); g_signal_connect_swapped (toggle, "toggled", G_CALLBACK (gtk_widget_queue_draw), gfig_context->preview); gtk_widget_show (toggle); /* "snap to grid" checkbutton at bottom of style frame */ toggle = gtk_check_button_new_with_label (C_("checkbutton", "Snap to grid")); gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &selvals.opts.snap2grid); gtk_widget_show (toggle); gfig_opt_widget.snap2grid = toggle; /* "show grid" checkbutton at bottom of style frame */ toggle = gtk_check_button_new_with_label (_("Show grid")); gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &selvals.opts.drawgrid); g_signal_connect (toggle, "toggled", G_CALLBACK (draw_grid_clear), NULL); gtk_widget_show (toggle); gfig_opt_widget.drawgrid = toggle; /* Load saved objects */ gfig_list_load_all (gfig_path); /* Setup initial brush settings */ set_context_bdesc (pika_context_get_brush ()); gtk_widget_show (main_hbox); gtk_widget_show (gfig->top_level_dlg); gfig_obj = gfig_load_from_parasite (gfig); if (gfig_obj) { gfig_list_insert (gfig_obj); new_obj_2edit (gfig, gfig_obj); gfig_style_set_context_from_style (&gfig_context->default_style); gfig_style_apply (&gfig_context->default_style); } gfig_context->enable_repaint = TRUE; gfig_paint_callback (); /* FIXME */ return TRUE; } static void shape_change_state (GSimpleAction *action, GVariant *new_state, gpointer user_data) { gchar *str; DobjType new_type = LINE; str = g_strdup_printf ("%s", g_variant_get_string (new_state, NULL)); if (! strcmp (str, "line")) new_type = LINE; else if (! strcmp (str, "rectangle")) new_type = RECTANGLE; else if (! strcmp (str, "circle")) new_type = CIRCLE; else if (! strcmp (str, "ellipse")) new_type = ELLIPSE; else if (! strcmp (str, "arc")) new_type = ARC; else if (! strcmp (str, "polygon")) new_type = POLY; else if (! strcmp (str, "star")) new_type = STAR; else if (! strcmp (str, "spiral")) new_type = SPIRAL; else if (! strcmp (str, "bezier")) new_type = BEZIER; else if (! strcmp (str, "move-obj")) new_type = MOVE_OBJ; else if (! strcmp (str, "move-point")) new_type = MOVE_POINT; else if (! strcmp (str, "copy")) new_type = COPY_OBJ; else if (! strcmp (str, "delete")) new_type = DEL_OBJ; else if (! strcmp (str, "select")) new_type = SELECT_OBJ; g_free (str); g_simple_action_set_state (action, new_state); toggle_obj_type (new_type); } static void gfig_response (GtkWidget *widget, gint response_id, gpointer data) { GFigObj *gfig_obj; switch (response_id) { case GTK_RESPONSE_DELETE_EVENT: case GTK_RESPONSE_CANCEL: /* if we created a new layer, delete it */ if (gfig_context->using_new_layer) { pika_image_remove_layer (gfig_context->image, PIKA_LAYER (gfig_context->drawable)); } else /* revert back to the original figure */ { free_all_objs (gfig_context->current_obj->obj_list); gfig_context->current_obj->obj_list = NULL; gfig_obj = gfig_load_from_parasite (PIKA_GFIG (data)); if (gfig_obj) { gfig_list_insert (gfig_obj); new_obj_2edit (PIKA_GFIG (data), gfig_obj); } gfig_context->enable_repaint = TRUE; gfig_paint_callback (); } break; case GTK_RESPONSE_OK: /* Close button */ gfig_save_as_parasite (); break; default: break; } gtk_application_remove_window ((PIKA_GFIG (data))->app, GTK_WINDOW ((PIKA_GFIG (data))->top_level_dlg)); } static gchar * gfig_get_user_writable_dir (void) { if (gfig_path) { GList *list; gchar *dir; list = pika_path_parse (gfig_path, 256, FALSE, NULL); dir = pika_path_get_user_writable_dir (list); pika_path_free (list); return dir; } return g_strdup (pika_directory ()); } static void gfig_load_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { static GtkWidget *dialog = NULL; if (! dialog) { gchar *dir; dialog = gtk_file_chooser_dialog_new (_("Load Gfig Object Collection"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_OK, NULL); pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); g_signal_connect (dialog, "response", G_CALLBACK (load_file_chooser_response), user_data); dir = gfig_get_user_writable_dir (); if (dir) { gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), dir); g_free (dir); } gtk_widget_show (dialog); } else { gtk_window_present (GTK_WINDOW (dialog)); } } static void gfig_save_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { static GtkWidget *dialog = NULL; if (!dialog) { gchar *dir; dialog = gtk_file_chooser_dialog_new (_("Save Gfig Drawing"), NULL, GTK_FILE_CHOOSER_ACTION_SAVE, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Save"), GTK_RESPONSE_OK, NULL); pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); /* FIXME: GFigObj should be a GObject and g_signal_connect_object() * should be used here. */ g_signal_connect (dialog, "response", G_CALLBACK (save_file_chooser_response), gfig_context->current_obj); dir = gfig_get_user_writable_dir (); if (dir) { gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), dir); g_free (dir); } gtk_widget_show (dialog); } else { gtk_window_present (GTK_WINDOW (dialog)); } } static void gfig_close_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { PikaGfig *gfig = PIKA_GFIG (user_data); gtk_dialog_response (GTK_DIALOG (gfig->top_level_dlg), GTK_RESPONSE_OK); } static void gfig_undo_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GAction *action_undo; PikaGfig *gfig = PIKA_GFIG (user_data); if (undo_level >= 0) { /* Free current objects an reinstate previous */ free_all_objs (gfig_context->current_obj->obj_list); gfig_context->current_obj->obj_list = NULL; tmp_bezier = tmp_line = obj_creating = NULL; gfig_context->current_obj->obj_list = undo_table[undo_level]; undo_level--; /* Update the screen */ gtk_widget_queue_draw (gfig_context->preview); /* And preview */ gfig_context->current_obj->obj_status |= GFIG_MODIFIED; if (gfig_context->current_obj->obj_list) gfig_context->selected_obj = gfig_context->current_obj->obj_list->data; else gfig_context->selected_obj = NULL; } action_undo = g_action_map_lookup_action (G_ACTION_MAP (gfig->app), "undo"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action_undo), undo_level >= 0); gfig_paint_callback (); } static void gfig_clear_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { /* Make sure we can get back - if we have some objects to get back to */ if (!gfig_context->current_obj->obj_list) return; setup_undo (PIKA_GFIG (user_data)); /* Free all objects */ free_all_objs (gfig_context->current_obj->obj_list); gfig_context->current_obj->obj_list = NULL; gfig_context->selected_obj = NULL; obj_creating = NULL; tmp_line = NULL; tmp_bezier = NULL; gtk_widget_queue_draw (gfig_context->preview); gfig_paint_callback (); } void draw_item (cairo_t *cr, gboolean fill) { cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); if (fill) { cairo_fill_preserve (cr); } else { cairo_set_line_width (cr, 3.0); cairo_stroke_preserve (cr); } cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); cairo_set_line_width (cr, 1.0); cairo_stroke (cr); } /* Given a point x, y draw a circle */ void draw_circle (GdkPoint *p, gboolean selected, cairo_t *cr) { if (!selvals.opts.showcontrol) return; cairo_arc (cr, gfig_scale_x (p->x) + .5, gfig_scale_y (p->y) + .5, SQ_SIZE / 2, 0, 2 * G_PI); draw_item (cr, selected); } /* Given a point x, y draw a square around it */ void draw_sqr (GdkPoint *p, gboolean selected, cairo_t *cr) { if (!selvals.opts.showcontrol) return; cairo_rectangle (cr, gfig_scale_x (p->x) + .5 - SQ_SIZE / 2, gfig_scale_y (p->y) + .5 - SQ_SIZE / 2, SQ_SIZE, SQ_SIZE); draw_item (cr, selected); } static void gfig_list_load_all (const gchar *path) { /* Make sure to clear any existing gfigs */ gfig_context->current_obj = NULL; gfig_list_free_all (); if (! gfig_list) { GFigObj *gfig; /* lets have at least one! */ gfig = gfig_new (); gfig->draw_name = g_strdup (_("First Gfig")); gfig_list_insert (gfig); } gfig_context->current_obj = gfig_list->data; /* set to first entry */ } static void gfig_list_free_all (void) { g_list_free_full (gfig_list, (GDestroyNotify) gfig_free); gfig_list = NULL; } static void tool_option_no_option (GtkWidget *notebook) { GtkWidget *label; label = gtk_label_new (_("This tool has no options")); gtk_widget_show (label); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label, NULL); } static void create_notebook_pages (GtkWidget *notebook) { tool_option_no_option (notebook); /* Line */ tool_option_no_option (notebook); /* Rectangle */ tool_option_no_option (notebook); /* Circle */ tool_option_no_option (notebook); /* Ellipse */ tool_option_no_option (notebook); /* Arc */ tool_options_poly (notebook); /* Polygon */ tool_options_star (notebook); /* Star */ tool_options_spiral (notebook); /* Spiral */ tool_options_bezier (notebook); /* Bezier */ tool_option_no_option (notebook); /* Dummy */ tool_option_no_option (notebook); /* Move Object */ tool_option_no_option (notebook); /* Move Point */ tool_option_no_option (notebook); /* Copy Object */ tool_option_no_option (notebook); /* Delete Object */ } static void raise_selected_obj_to_top (GSimpleAction *action, GVariant *parameter, gpointer user_data) { if (!gfig_context->selected_obj) return; if (g_list_find (gfig_context->current_obj->obj_list, gfig_context->selected_obj)) { gfig_context->current_obj->obj_list = g_list_remove (gfig_context->current_obj->obj_list, gfig_context->selected_obj); gfig_context->current_obj->obj_list = g_list_append (gfig_context->current_obj->obj_list, gfig_context->selected_obj); } else { g_message ("Trying to raise object that does not exist."); return; } gfig_paint_callback (); } static void lower_selected_obj_to_bottom (GSimpleAction *action, GVariant *parameter, gpointer user_data) { if (!gfig_context->selected_obj) return; if (g_list_find (gfig_context->current_obj->obj_list, gfig_context->selected_obj)) { gfig_context->current_obj->obj_list = g_list_remove (gfig_context->current_obj->obj_list, gfig_context->selected_obj); gfig_context->current_obj->obj_list = g_list_prepend (gfig_context->current_obj->obj_list, gfig_context->selected_obj); } else { g_message ("Trying to lower object that does not exist."); return; } gfig_paint_callback (); } static void raise_selected_obj (GSimpleAction *action, GVariant *parameter, gpointer user_data) { if (!gfig_context->selected_obj) return; if (g_list_find (gfig_context->current_obj->obj_list, gfig_context->selected_obj)) { int position; position = g_list_index (gfig_context->current_obj->obj_list, gfig_context->selected_obj); gfig_context->current_obj->obj_list = g_list_remove (gfig_context->current_obj->obj_list, gfig_context->selected_obj); gfig_context->current_obj->obj_list = g_list_insert (gfig_context->current_obj->obj_list, gfig_context->selected_obj, position + 1); } else { g_message ("Trying to raise object that does not exist."); return; } gfig_paint_callback (); } static void lower_selected_obj (GSimpleAction *action, GVariant *parameter, gpointer user_data) { if (!gfig_context->selected_obj) return; if (g_list_find (gfig_context->current_obj->obj_list, gfig_context->selected_obj)) { int position; position = g_list_index (gfig_context->current_obj->obj_list, gfig_context->selected_obj); gfig_context->current_obj->obj_list = g_list_remove (gfig_context->current_obj->obj_list, gfig_context->selected_obj); gfig_context->current_obj->obj_list = g_list_insert (gfig_context->current_obj->obj_list, gfig_context->selected_obj, MAX (0, position - 1)); } else { g_message ("Trying to lower object that does not exist."); return; } gfig_paint_callback (); } static void select_filltype_callback (GtkWidget *widget) { gint value; pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (widget), &value); gtk_notebook_set_current_page (GTK_NOTEBOOK (fill_type_notebook), MIN (value, FILL_GRADIENT)); gfig_context_get_current_style ()->fill_type = (FillType) value; gfig_paint_callback (); } static gboolean gfig_paint_timeout (gpointer data) { gfig_paint_callback (); paint_timeout = 0; return FALSE; } static void gfig_paint_delayed (void) { if (paint_timeout) g_source_remove (paint_timeout); paint_timeout = g_timeout_add (UPDATE_DELAY, gfig_paint_timeout, NULL); } static void gfig_prefs_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { static GtkWidget *dialog = NULL; if (!dialog) { GtkWidget *main_vbox; GtkWidget *grid; GtkWidget *toggle; GtkWidget *undo_scale; GtkWidget *scale; GtkAdjustment *scale_data; dialog = pika_dialog_new (_("Options"), "pika-gfig-options", NULL, 0, NULL, NULL, _("_Close"), GTK_RESPONSE_CLOSE, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); /* Put buttons in */ toggle = gtk_check_button_new_with_label (_("Show position")); gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.showpos); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &selvals.showpos); g_signal_connect_after (toggle, "toggled", G_CALLBACK (gfig_pos_enable), NULL); gtk_widget_show (toggle); toggle = gtk_check_button_new_with_label (_("Show control points")); gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.opts.showcontrol); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &selvals.opts.showcontrol); g_signal_connect (toggle, "toggled", G_CALLBACK (toggle_show_image), NULL); gtk_widget_show (toggle); gfig_opt_widget.showcontrol = toggle; g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.showcontrol), (gpointer) &gfig_opt_widget.showcontrol); toggle = gtk_check_button_new_with_label (_("Antialiasing")); gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selopt.antia); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &selopt.antia); g_signal_connect (toggle, "toggled", G_CALLBACK (gfig_paint_callback), NULL); gtk_widget_show (toggle); grid = gtk_grid_new (); gtk_grid_set_row_spacing (GTK_GRID (grid), 6); gtk_grid_set_column_spacing (GTK_GRID (grid), 6); gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 6); gtk_widget_show (grid); undo_scale = pika_scale_entry_new (_("Max undo:"), selvals.maxundo, MIN_UNDO, MAX_UNDO, 0); g_signal_connect (undo_scale, "value-changed", G_CALLBACK (gfig_scale_entry_update_int), &selvals.maxundo); gtk_grid_attach (GTK_GRID (grid), undo_scale, 0, 0, 3, 1); gtk_widget_show (undo_scale); page_menu_bg = pika_int_combo_box_new (_("Transparent"), LAYER_TRANS_BG, _("Background"), LAYER_BG_BG, _("Foreground"), LAYER_FG_BG, _("White"), LAYER_WHITE_BG, _("Copy"), LAYER_COPY_BG, NULL); pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (page_menu_bg), selvals.onlayerbg); g_signal_connect (page_menu_bg, "changed", G_CALLBACK (paint_combo_callback), GINT_TO_POINTER (PAINT_BGS_MENU)); pika_help_set_help_data (page_menu_bg, _("Layer background type. Copy causes the " "previous layer to be copied before the " "draw is performed."), NULL); pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, _("Background:"), 0.0, 0.5, page_menu_bg, 2); toggle = gtk_check_button_new_with_label (_("Feather")); gtk_grid_attach (GTK_GRID (grid), toggle, 2, 2, 1, 1); g_signal_connect (toggle, "toggled", G_CALLBACK (pika_toggle_button_update), &selopt.feather); g_signal_connect (toggle, "toggled", G_CALLBACK (gfig_paint_callback), NULL); gtk_widget_show (toggle); scale_data = gtk_adjustment_new (selopt.feather_radius, 0.0, 100.0, 1.0, 1.0, 0.0); scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_data); gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP); g_signal_connect (scale_data, "value-changed", G_CALLBACK (pika_double_adjustment_update), &selopt.feather_radius); g_signal_connect (scale_data, "value-changed", G_CALLBACK (gfig_paint_delayed), NULL); pika_grid_attach_aligned (GTK_GRID (grid), 0, 2, _("Radius:"), 0.0, 1.0, scale, 1); gtk_widget_show (dialog); } else { gtk_window_present (GTK_WINDOW (dialog)); } } static void gfig_grid_action (GSimpleAction *action, GVariant *parameter, gpointer user_data) { static GtkWidget *dialog = NULL; if (!dialog) { GtkWidget *main_vbox; GtkWidget *hbox; GtkWidget *grid; GtkWidget *combo; GtkWidget *grid_scale; GtkWidget *sectors_scale; GtkWidget *radius_scale; dialog = pika_dialog_new (_("Grid"), "pika-gfig-grid", NULL, 0, NULL, NULL, _("_Close"), GTK_RESPONSE_CLOSE, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), main_vbox, TRUE, TRUE, 0); gtk_widget_show (main_vbox); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); grid = gtk_grid_new (); gtk_grid_set_row_spacing (GTK_GRID (grid), 6); gtk_grid_set_column_spacing (GTK_GRID (grid), 6); gtk_box_pack_start (GTK_BOX (main_vbox), grid, FALSE, FALSE, 0); gtk_widget_show (grid); grid_scale = pika_scale_entry_new (_("Grid spacing:"), selvals.opts.gridspacing, MIN_GRID, MAX_GRID, 0); g_signal_connect (grid_scale, "value-changed", G_CALLBACK (gfig_scale_entry_update_int), &selvals.opts.gridspacing); g_signal_connect (grid_scale, "value-changed", G_CALLBACK (draw_grid_clear), NULL); gtk_grid_attach (GTK_GRID (grid), grid_scale, 0, 0, 3, 1); gtk_widget_show (grid_scale); gfig_opt_widget.gridspacing = grid_scale; g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridspacing), (gpointer) &gfig_opt_widget.gridspacing); sectors_scale = pika_scale_entry_new (_("Polar grid sectors desired:"), selvals.opts.grid_sectors_desired, 5, 360, 0); g_signal_connect (sectors_scale, "value-changed", G_CALLBACK (gfig_scale_entry_update_int), &selvals.opts.grid_sectors_desired); g_signal_connect (sectors_scale, "value-changed", G_CALLBACK (draw_grid_clear), NULL); gtk_grid_attach (GTK_GRID (grid), sectors_scale, 0, 3, 3, 1); gtk_widget_show (sectors_scale); gfig_opt_widget.grid_sectors_desired = sectors_scale; g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_sectors_desired), (gpointer) &gfig_opt_widget.grid_sectors_desired); radius_scale = pika_scale_entry_new (_("Polar grid radius interval:"), selvals.opts.grid_radius_interval, 5, 50, 0); g_signal_connect (radius_scale, "value-changed", G_CALLBACK (gfig_scale_entry_update_double), &selvals.opts.grid_radius_interval); g_signal_connect (radius_scale, "value-changed", G_CALLBACK (draw_grid_clear), NULL); gtk_grid_attach (GTK_GRID (grid), radius_scale, 0, 4, 3, 1); gtk_widget_show (radius_scale); gfig_opt_widget.grid_radius_interval = radius_scale; g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_radius_interval), (gpointer) &gfig_opt_widget.grid_radius_interval); combo = pika_int_combo_box_new (_("Rectangle"), RECT_GRID, _("Polar"), POLAR_GRID, _("Isometric"), ISO_GRID, NULL); pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), selvals.opts.gridtype); g_signal_connect (combo, "changed", G_CALLBACK (gridtype_combo_callback), GINT_TO_POINTER (GRID_TYPE_MENU)); pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, _("Grid type:"), 0.0, 0.5, combo, 2); gfig_opt_widget.gridtypemenu = combo; g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridtypemenu), (gpointer) &gfig_opt_widget.gridtypemenu); combo = pika_int_combo_box_new (_("Normal"), GFIG_NORMAL_GC, _("Black"), GFIG_BLACK_GC, _("White"), GFIG_WHITE_GC, _("Grey"), GFIG_GREY_GC, _("Darker"), GFIG_DARKER_GC, _("Lighter"), GFIG_LIGHTER_GC, _("Very dark"), GFIG_VERY_DARK_GC, NULL); pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), grid_gc_type); g_signal_connect (combo, "changed", G_CALLBACK (gridtype_combo_callback), GINT_TO_POINTER (GRID_RENDER_MENU)); pika_grid_attach_aligned (GTK_GRID (grid), 0, 2, _("Grid color:"), 0.0, 0.5, combo, 2); gtk_widget_show (dialog); } else { gtk_window_present (GTK_WINDOW (dialog)); } } void options_update (GFigObj *old_obj) { /* Save old vals */ if (selvals.opts.gridspacing != old_obj->opts.gridspacing) { old_obj->opts.gridspacing = selvals.opts.gridspacing; } if (selvals.opts.grid_sectors_desired != old_obj->opts.grid_sectors_desired) { old_obj->opts.grid_sectors_desired = selvals.opts.grid_sectors_desired; } if (selvals.opts.grid_radius_interval != old_obj->opts.grid_radius_interval) { old_obj->opts.grid_radius_interval = selvals.opts.grid_radius_interval; } if (selvals.opts.gridtype != old_obj->opts.gridtype) { old_obj->opts.gridtype = selvals.opts.gridtype; } if (selvals.opts.drawgrid != old_obj->opts.drawgrid) { old_obj->opts.drawgrid = selvals.opts.drawgrid; } if (selvals.opts.snap2grid != old_obj->opts.snap2grid) { old_obj->opts.snap2grid = selvals.opts.snap2grid; } if (selvals.opts.lockongrid != old_obj->opts.lockongrid) { old_obj->opts.lockongrid = selvals.opts.lockongrid; } if (selvals.opts.showcontrol != old_obj->opts.showcontrol) { old_obj->opts.showcontrol = selvals.opts.showcontrol; } /* New vals */ if (selvals.opts.gridspacing != gfig_context->current_obj->opts.gridspacing) { if (gfig_opt_widget.gridspacing) pika_label_spin_set_value (PIKA_LABEL_SPIN (gfig_opt_widget.gridspacing), gfig_context->current_obj->opts.gridspacing); } if (selvals.opts.grid_sectors_desired != gfig_context->current_obj->opts.grid_sectors_desired) { if (gfig_opt_widget.grid_sectors_desired) pika_label_spin_set_value (PIKA_LABEL_SPIN (gfig_opt_widget.grid_sectors_desired), gfig_context->current_obj->opts.grid_sectors_desired); } if (selvals.opts.grid_radius_interval != gfig_context->current_obj->opts.grid_radius_interval) { if (gfig_opt_widget.grid_radius_interval) pika_label_spin_set_value (PIKA_LABEL_SPIN (gfig_opt_widget.grid_radius_interval), gfig_context->current_obj->opts.grid_radius_interval); } if (selvals.opts.gridtype != gfig_context->current_obj->opts.gridtype) { if (gfig_opt_widget.gridtypemenu) pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (gfig_opt_widget.gridtypemenu), gfig_context->current_obj->opts.gridtype); } if (selvals.opts.drawgrid != gfig_context->current_obj->opts.drawgrid) { if (gfig_opt_widget.drawgrid) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.drawgrid), gfig_context->current_obj->opts.drawgrid); } if (selvals.opts.snap2grid != gfig_context->current_obj->opts.snap2grid) { if (gfig_opt_widget.snap2grid) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.snap2grid), gfig_context->current_obj->opts.snap2grid); } if (selvals.opts.lockongrid != gfig_context->current_obj->opts.lockongrid) { if (gfig_opt_widget.lockongrid) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.lockongrid), gfig_context->current_obj->opts.lockongrid); } if (selvals.opts.showcontrol != gfig_context->current_obj->opts.showcontrol) { if (gfig_opt_widget.showcontrol) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.showcontrol), gfig_context->current_obj->opts.showcontrol); } } static void save_file_chooser_response (GtkFileChooser *chooser, gint response_id, GFigObj *obj) { if (response_id == GTK_RESPONSE_OK) { gchar *filename; filename = gtk_file_chooser_get_filename (chooser); obj->filename = filename; gfig_context->current_obj = obj; gfig_save_callbk (); } gtk_widget_destroy (GTK_WIDGET (chooser)); } static GfigObject * gfig_select_obj_by_number (gint count) { GList *objs; GfigObject *object = NULL; gint k; gfig_context->selected_obj = NULL; for (objs = gfig_context->current_obj->obj_list, k = 0; objs; objs = g_list_next (objs), k++) { if (k == obj_show_single) { object = objs->data; gfig_context->selected_obj = object; gfig_style_set_context_from_style (&object->style); break; } } return object; } static void select_button_clicked (gint type) { gint count = 0; if (gfig_context->current_obj) { count = g_list_length (gfig_context->current_obj->obj_list); } switch (type) { case OBJ_SELECT_LT: obj_show_single--; if (obj_show_single < 0) obj_show_single = count - 1; break; case OBJ_SELECT_GT: obj_show_single++; if (obj_show_single >= count) obj_show_single = 0; break; case OBJ_SELECT_EQ: obj_show_single = -1; /* Reset to show all */ break; default: break; } if (obj_show_single >= 0) gfig_select_obj_by_number (obj_show_single); draw_grid_clear (); gfig_paint_callback (); } static void select_button_clicked_lt (GSimpleAction *action, GVariant *parameter, gpointer user_data) { select_button_clicked (OBJ_SELECT_LT); } static void select_button_clicked_gt (GSimpleAction *action, GVariant *parameter, gpointer user_data) { select_button_clicked (OBJ_SELECT_GT); } static void select_button_clicked_eq (GSimpleAction *action, GVariant *parameter, gpointer user_data) { select_button_clicked (OBJ_SELECT_EQ); } /* Special case for now - options on poly/star/spiral button */ GtkWidget * num_sides_widget (const gchar *d_title, gint *num_sides, gint *which_way, gint adj_min, gint adj_max) { GtkWidget *grid; GtkWidget *scale; grid = gtk_grid_new (); gtk_grid_set_row_spacing (GTK_GRID (grid), 6); gtk_grid_set_column_spacing (GTK_GRID (grid), 6); gtk_container_set_border_width (GTK_CONTAINER (grid), 12); gtk_widget_show (grid); scale = pika_scale_entry_new (_("Sides:"), *num_sides, adj_min, adj_max, 0); g_signal_connect (scale, "value-changed", G_CALLBACK (gfig_scale_entry_update_int), num_sides); gtk_grid_attach (GTK_GRID (grid), scale, 0, 0, 3, 1); gtk_widget_show (scale); if (which_way) { GtkWidget *combo = pika_int_combo_box_new (_("Right"), 0, _("Left"), 1, NULL); pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), *which_way); g_signal_connect (combo, "changed", G_CALLBACK (pika_int_combo_box_get_active), which_way); pika_grid_attach_aligned (GTK_GRID (grid), 0, 1, _("Orientation:"), 0.0, 0.5, combo, 1); } return grid; } void gfig_paint (BrushType brush_type, PikaDrawable *drawable, gint seg_count, gdouble line_pnts[]) { switch (brush_type) { case BRUSH_BRUSH_TYPE: pika_paintbrush (drawable, selvals.brushfade, seg_count, line_pnts, PIKA_PAINT_CONSTANT, selvals.brushgradient); break; case BRUSH_PENCIL_TYPE: pika_pencil (drawable, seg_count, line_pnts); break; case BRUSH_AIRBRUSH_TYPE: pika_airbrush (drawable, selvals.airbrushpressure, seg_count, line_pnts); break; case BRUSH_PATTERN_TYPE: pika_clone (drawable, drawable, PIKA_CLONE_PATTERN, 0.0, 0.0, seg_count, line_pnts); break; } } static void paint_combo_callback (GtkWidget *widget, gpointer data) { gint mtype = GPOINTER_TO_INT (data); gint value; pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (widget), &value); switch (mtype) { case PAINT_BGS_MENU: selvals.onlayerbg = (LayersBGType) value; break; default: g_return_if_reached (); break; } gfig_paint_callback (); } static void gridtype_combo_callback (GtkWidget *widget, gpointer data) { gint mtype = GPOINTER_TO_INT (data); gint value; pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (widget), &value); switch (mtype) { case GRID_TYPE_MENU: selvals.opts.gridtype = value; break; case GRID_RENDER_MENU: grid_gc_type = value; break; default: g_return_if_reached (); break; } draw_grid_clear (); } /* * The edit gfig name attributes dialog * Modified from Pika source - layer edit. */ typedef struct _GfigListOptions { GtkWidget *query_box; GtkWidget *name_entry; GtkWidget *list_entry; GFigObj *obj; gboolean created; } GfigListOptions; static void load_file_chooser_response (GtkFileChooser *chooser, gint response_id, gpointer data) { if (response_id == GTK_RESPONSE_OK) { gchar *filename; GFigObj *gfig_obj; GFigObj *current_saved; filename = gtk_file_chooser_get_filename (chooser); if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) { /* Hack - current object MUST be NULL to prevent setup_undo () * from kicking in. */ current_saved = gfig_context->current_obj; gfig_context->current_obj = NULL; gfig_obj = gfig_load (PIKA_GFIG (data), filename, filename); gfig_context->current_obj = current_saved; if (gfig_obj) { /* Read only ?*/ if (access (filename, W_OK)) gfig_obj->obj_status |= GFIG_READONLY; gfig_list_insert (gfig_obj); new_obj_2edit (PIKA_GFIG (data), gfig_obj); } } g_free (filename); } gtk_widget_destroy (GTK_WIDGET (chooser)); gfig_paint_callback (); } void paint_layer_fill (gdouble x1, gdouble y1, gdouble x2, gdouble y2) { PikaFillType fill_type = PIKA_FILL_FOREGROUND; Style *current_style; current_style = gfig_context_get_current_style (); pika_context_push (); pika_context_set_paint_mode (PIKA_LAYER_MODE_NORMAL_LEGACY); pika_context_set_opacity (100.0); pika_context_set_gradient_repeat_mode (PIKA_REPEAT_NONE); pika_context_set_gradient_reverse (FALSE); switch (current_style->fill_type) { case FILL_NONE: return; case FILL_COLOR: fill_type = PIKA_FILL_BACKGROUND; break; case FILL_PATTERN: fill_type = PIKA_FILL_PATTERN; break; case FILL_GRADIENT: pika_drawable_edit_gradient_fill (gfig_context->drawable, PIKA_GRADIENT_SHAPEBURST_DIMPLED, 0.0, /* offset */ FALSE, /* supersampling */ 0, /* max_depth */ 0.0, /* threshold */ FALSE, /* dither */ 0.0, 0.0, /* (x1, y1) - ignored */ 0.0, 0.0); /* (x2, y2) - ignored */ return; case FILL_VERTICAL: pika_drawable_edit_gradient_fill (gfig_context->drawable, PIKA_GRADIENT_LINEAR, 0.0, FALSE, 0, 0.0, FALSE, x1, y1, x1, y2); return; case FILL_HORIZONTAL: pika_drawable_edit_gradient_fill (gfig_context->drawable, PIKA_GRADIENT_LINEAR, 0.0, FALSE, 0, 0.0, FALSE, x1, y1, x2, y1); return; } pika_context_set_opacity (current_style->fill_opacity); pika_drawable_edit_fill (gfig_context->drawable, fill_type); pika_context_pop (); } void gfig_paint_callback (void) { GList *objs; gint ccount = 0; GfigObject *object; if (!gfig_context->enable_repaint || !gfig_context->current_obj) return; objs = gfig_context->current_obj->obj_list; pika_drawable_fill (gfig_context->drawable, PIKA_FILL_TRANSPARENT); while (objs) { if (ccount == obj_show_single || obj_show_single == -1) { FillType saved_filltype; object = objs->data; gfig_style_apply (&object->style); saved_filltype = gfig_context_get_current_style ()->fill_type; gfig_context_get_current_style ()->fill_type = object->style.fill_type; object->class->paintfunc (object); gfig_context_get_current_style ()->fill_type = saved_filltype; } objs = g_list_next (objs); ccount++; } pika_displays_flush (); if (back_pixbuf) { g_object_unref (back_pixbuf); back_pixbuf = NULL; } gtk_widget_queue_draw (gfig_context->preview); } /* Draw the grid on the screen */ void draw_grid_clear (void) { /* wipe slate and start again */ gtk_widget_queue_draw (gfig_context->preview); } static void toggle_show_image (void) { /* wipe slate and start again */ draw_grid_clear (); } static void toggle_obj_type (DobjType new_type) { /* cache of cursors. * Must be larger than DobjType values, i.e. NULL_OPER. * Test by clicking the "select object" icon. * C ensures is initialized to NULL. */ static GdkCursor *p_cursors[NULL_OPER]; GdkCursorType ctype = GDK_LAST_CURSOR; if (selvals.otype != new_type) { /* Mem leak */ obj_creating = NULL; tmp_line = NULL; tmp_bezier = NULL; if (new_type < MOVE_OBJ) /* Eeeeek */ { g_debug ("%s new_type < MOVE_OBJ", G_STRFUNC); obj_show_single = -1; /* Cancel select preview */ } /* Update draw areas */ gtk_widget_queue_draw (gfig_context->preview); } g_debug ("%s: old and new obj type %d %d", G_STRFUNC, selvals.otype, new_type); selvals.otype = new_type; gtk_notebook_set_current_page (GTK_NOTEBOOK (tool_options_notebook), new_type - 1); switch (selvals.otype) { case LINE: case RECTANGLE: case CIRCLE: case ELLIPSE: case ARC: case POLY: case STAR: case SPIRAL: case BEZIER: ctype = GDK_CROSSHAIR; break; case MOVE_OBJ: case MOVE_POINT: case COPY_OBJ: case MOVE_COPY_OBJ: ctype = GDK_DIAMOND_CROSS; break; case DEL_OBJ: ctype = GDK_PIRATE; break; case OBJ_TYPE_NONE: ctype = GDK_CROSSHAIR; break; default: g_debug ("%s: default cursor for object type %d.", G_STRFUNC, selvals.otype); ctype = GDK_CROSSHAIR; break; } /* Get cached cursor. */ if (!p_cursors[selvals.otype]) { GdkCursor *cursor; GdkDisplay *display = gtk_widget_get_display (gfig_context->preview); cursor = gdk_cursor_new_for_display (display, ctype); p_cursors[selvals.otype] = cursor; } /* Require cursor (possibly from cache) is-a cursor. */ g_assert (GDK_IS_CURSOR (p_cursors[selvals.otype])); gdk_window_set_cursor (gtk_widget_get_window (gfig_context->preview), p_cursors[selvals.otype]); } /* This could belong in a separate file ... but makes it easier to lump into * one when compiling the plugin. */ /* Given a number of float co-ords adjust for scaling back to org size */ /* Size is number of PAIRS of points */ /* FP + int variants */ static void scale_to_orginal_x (gdouble *list) { *list *= scale_x_factor; } gint gfig_scale_x (gint x) { if (!selvals.scaletoimage) return (gint) (x * (1 / scale_x_factor)); else return x; } static void scale_to_orginal_y (gdouble *list) { *list *= scale_y_factor; } gint gfig_scale_y (gint y) { if (!selvals.scaletoimage) return (gint) (y * (1 / scale_y_factor)); else return y; } /* Pairs x followed by y */ void scale_to_original_xy (gdouble *list, gint size) { gint i; for (i = 0; i < size * 2; i += 2) { scale_to_orginal_x (&list[i]); scale_to_orginal_y (&list[i + 1]); } } /* Pairs x followed by y */ void scale_to_xy (gdouble *list, gint size) { gint i; for (i = 0; i < size * 2; i += 2) { list[i] *= (org_scale_x_factor / scale_x_factor); list[i + 1] *= (org_scale_y_factor / scale_y_factor); } } void gfig_draw_arc (gint x, gint y, gint width, gint height, gint angle1, gint angle2, cairo_t *cr) { cairo_save (cr); cairo_translate (cr, gfig_scale_x (x), gfig_scale_y (y)); cairo_scale (cr, gfig_scale_x (width), gfig_scale_y (height)); if (angle1 < angle2) cairo_arc (cr, 0., 0., 1., angle1 * G_PI / 180, angle2 * G_PI / 180); else cairo_arc_negative (cr, 0., 0., 1., angle1 * G_PI / 180, angle2 * G_PI / 180); cairo_restore (cr); draw_item (cr, FALSE); } void gfig_draw_line (gint x0, gint y0, gint x1, gint y1, cairo_t *cr) { cairo_move_to (cr, gfig_scale_x (x0) + .5, gfig_scale_y (y0) + .5); cairo_line_to (cr, gfig_scale_x (x1) + .5, gfig_scale_y (y1) + .5); draw_item (cr, FALSE); } static void gfig_scale_entry_update_double (PikaLabelSpin *entry, gdouble *value) { *value = pika_label_spin_get_value (entry); } static void gfig_scale_entry_update_int (PikaLabelSpin *entry, gint *value) { *value = (gint) pika_label_spin_get_value (entry); } static GtkWidget * add_tool_button (GtkWidget *toolbar, const char *action, const char *icon, const char *label, const char *tooltip) { GtkWidget *tool_icon; GtkToolItem *tool_button; tool_icon = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON); gtk_widget_show (GTK_WIDGET (tool_icon)); tool_button = gtk_tool_button_new (tool_icon, label); gtk_widget_show (GTK_WIDGET (tool_button)); gtk_tool_item_set_tooltip_text (tool_button, tooltip); gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (tool_button), action); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_button, -1); return GTK_WIDGET (tool_button); } GtkWidget * add_toggle_button (GtkWidget *toolbar, const char *action, const char *icon, const char *label, const char *tooltip) { GtkWidget *tool_icon; GtkToolItem *toggle_tool_button; tool_icon = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON); gtk_widget_show (GTK_WIDGET (tool_icon)); toggle_tool_button = gtk_toggle_tool_button_new (); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toggle_tool_button), tool_icon); gtk_tool_button_set_label (GTK_TOOL_BUTTON (toggle_tool_button), label); gtk_widget_show (GTK_WIDGET (toggle_tool_button)); gtk_tool_item_set_tooltip_text (toggle_tool_button, tooltip); gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (toggle_tool_button), action); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toggle_tool_button, -1); return GTK_WIDGET (toggle_tool_button); } static void add_tool_separator (GtkWidget *toolbar, gboolean expand) { GtkToolItem *item; item = gtk_separator_tool_item_new (); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1); gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE); gtk_tool_item_set_expand (item, expand); gtk_widget_show (GTK_WIDGET (item)); }