/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This is a plug-in for GIMP. * * Copyright (C) 2000 Michael Natterer * * 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 #include "libgimp/stdplugins-intl.h" #define PLUG_IN_PROC "plug-in-unit-editor" #define PLUG_IN_BINARY "unit-editor" #define PLUG_IN_ROLE "gimp-unit-editor" #define RESPONSE_REFRESH 1 enum { SAVE, IDENTIFIER, FACTOR, DIGITS, SYMBOL, ABBREVIATION, SINGULAR, PLURAL, UNIT, USER_UNIT, BG_COLOR, NUM_COLUMNS }; typedef struct { const gchar *title; const gchar *help; } UnitColumn; static void query (void); static void run (const gchar *name, gint n_params, const GimpParam *param, gint *n_return_vals, GimpParam **return_vals); static GimpUnit new_unit_dialog (GtkWidget *main_dialog, GimpUnit template); static void unit_editor_dialog (void); static void unit_editor_response (GtkWidget *widget, gint response_id, gpointer data); static void new_callback (GtkAction *action, GtkTreeView *tv); static void duplicate_callback (GtkAction *action, GtkTreeView *tv); static void saved_toggled_callback (GtkCellRendererToggle *celltoggle, gchar *path_string, GtkListStore *list_store); static void unit_list_init (GtkTreeView *tv); const GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static const UnitColumn columns[] = { { N_("Saved"), N_("A unit definition will only be saved before " "GIMP exits if this column is checked.") }, { N_("ID"), N_("This string will be used to identify a " "unit in GIMP's configuration files.") }, { N_("Factor"), N_("How many units make up an inch.") }, { N_("Digits"), N_("This field is a hint for numerical input " "fields. It specifies how many decimal digits " "the input field should provide to get " "approximately the same accuracy as an " "\"inch\" input field with two decimal digits.") }, { N_("Symbol"), N_("The unit's symbol if it has one (e.g. \" " "for inches). The unit's abbreviation is used " "if doesn't have a symbol.") }, { N_("Abbreviation"), N_("The unit's abbreviation (e.g. \"cm\" for " "centimeters).") }, { N_("Singular"), N_("The unit's singular form.") }, { N_("Plural"), N_("The unit's plural form.") } }; static GtkActionEntry actions[] = { { "unit-editor-toolbar", NULL, "Unit Editor Toolbar", NULL, NULL, NULL }, { "unit-editor-new", GIMP_ICON_DOCUMENT_NEW, NULL, "N", N_("Create a new unit from scratch"), G_CALLBACK (new_callback) }, { "unit-editor-duplicate", GIMP_ICON_OBJECT_DUPLICATE, NULL, "D", N_("Create a new unit using the currently selected unit as template"), G_CALLBACK (duplicate_callback) } }; MAIN () static void query (void) { static const GimpParamDef args[] = { { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" } }; gimp_install_procedure (PLUG_IN_PROC, N_("Create or alter units used in GIMP"), "The GIMP unit editor", "Michael Natterer ", "Michael Natterer ", "2000", N_("U_nits"), "", GIMP_PLUGIN, G_N_ELEMENTS (args), 0, args, NULL); gimp_plugin_menu_register (PLUG_IN_PROC, "/Edit/Preferences"); gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_ICON_NAME, (const guint8 *) GIMP_ICON_TOOL_MEASURE); } static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[2]; INIT_I18N (); *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = GIMP_PDB_CALLING_ERROR; if (strcmp (name, PLUG_IN_PROC) == 0) { values[0].data.d_status = GIMP_PDB_SUCCESS; unit_editor_dialog (); } } static GimpUnit new_unit_dialog (GtkWidget *main_dialog, GimpUnit template) { GtkWidget *dialog; GtkWidget *table; GtkWidget *entry; GtkWidget *spinbutton; GtkWidget *identifier_entry; GtkAdjustment *factor_adj; GtkAdjustment *digits_adj; GtkWidget *symbol_entry; GtkWidget *abbreviation_entry; GtkWidget *singular_entry; GtkWidget *plural_entry; GimpUnit unit = GIMP_UNIT_PIXEL; dialog = gimp_dialog_new (_("Add a New Unit"), PLUG_IN_ROLE, main_dialog, GTK_DIALOG_MODAL, gimp_standard_help_func, PLUG_IN_PROC, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Add"), GTK_RESPONSE_OK, NULL); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); table = gtk_table_new (7, 2, FALSE); gtk_table_set_col_spacings (GTK_TABLE (table), 6); gtk_table_set_row_spacings (GTK_TABLE (table), 6); gtk_container_set_border_width (GTK_CONTAINER (table), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), table, FALSE, FALSE, 0); gtk_widget_show (table); entry = identifier_entry = gtk_entry_new (); if (template != GIMP_UNIT_PIXEL) { gtk_entry_set_text (GTK_ENTRY (entry), gimp_unit_get_identifier (template)); } gimp_table_attach_aligned (GTK_TABLE (table), 0, 0, _("_ID:"), 0.0, 0.5, entry, 1, FALSE); gimp_help_set_help_data (entry, gettext (columns[IDENTIFIER].help), NULL); factor_adj = (GtkAdjustment *) gtk_adjustment_new ((template != GIMP_UNIT_PIXEL) ? gimp_unit_get_factor (template) : 1.0, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION, 0.01, 0.1, 0.0); spinbutton = gimp_spin_button_new (factor_adj, 0.01, 5); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 1, _("_Factor:"), 0.0, 0.5, spinbutton, 1, TRUE); gimp_help_set_help_data (spinbutton, gettext (columns[FACTOR].help), NULL); digits_adj = (GtkAdjustment *) gtk_adjustment_new ((template != GIMP_UNIT_PIXEL) ? gimp_unit_get_digits (template) : 2.0, 0, 5, 1, 1, 0); spinbutton = gimp_spin_button_new (digits_adj, 0, 0); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); gimp_table_attach_aligned (GTK_TABLE (table), 0, 2, _("_Digits:"), 0.0, 0.5, spinbutton, 1, TRUE); gimp_help_set_help_data (spinbutton, gettext (columns[DIGITS].help), NULL); entry = symbol_entry = gtk_entry_new (); if (template != GIMP_UNIT_PIXEL) { gtk_entry_set_text (GTK_ENTRY (entry), gimp_unit_get_symbol (template)); } gimp_table_attach_aligned (GTK_TABLE (table), 0, 3, _("_Symbol:"), 0.0, 0.5, entry, 1, FALSE); gimp_help_set_help_data (entry, gettext (columns[SYMBOL].help), NULL); entry = abbreviation_entry = gtk_entry_new (); if (template != GIMP_UNIT_PIXEL) { gtk_entry_set_text (GTK_ENTRY (entry), gimp_unit_get_abbreviation (template)); } gimp_table_attach_aligned (GTK_TABLE (table), 0, 4, _("_Abbreviation:"), 0.0, 0.5, entry, 1, FALSE); gimp_help_set_help_data (entry, gettext (columns[ABBREVIATION].help), NULL); entry = singular_entry = gtk_entry_new (); if (template != GIMP_UNIT_PIXEL) { gtk_entry_set_text (GTK_ENTRY (entry), gimp_unit_get_singular (template)); } gimp_table_attach_aligned (GTK_TABLE (table), 0, 5, _("Si_ngular:"), 0.0, 0.5, entry, 1, FALSE); gimp_help_set_help_data (entry, gettext (columns[SINGULAR].help), NULL); entry = plural_entry = gtk_entry_new (); if (template != GIMP_UNIT_PIXEL) { gtk_entry_set_text (GTK_ENTRY (entry), gimp_unit_get_plural (template)); } gimp_table_attach_aligned (GTK_TABLE (table), 0, 6, _("_Plural:"), 0.0, 0.5, entry, 1, FALSE); gimp_help_set_help_data (entry, gettext (columns[PLURAL].help), NULL); gtk_widget_show (dialog); while (TRUE) { gchar *identifier; gdouble factor; gint digits; gchar *symbol; gchar *abbreviation; gchar *singular; gchar *plural; if (gimp_dialog_run (GIMP_DIALOG (dialog)) != GTK_RESPONSE_OK) break; identifier = g_strdup (gtk_entry_get_text (GTK_ENTRY (identifier_entry))); factor = gtk_adjustment_get_value (GTK_ADJUSTMENT (factor_adj)); digits = gtk_adjustment_get_value (GTK_ADJUSTMENT (digits_adj)); symbol = g_strdup (gtk_entry_get_text (GTK_ENTRY (symbol_entry))); abbreviation = g_strdup (gtk_entry_get_text (GTK_ENTRY (abbreviation_entry))); singular = g_strdup (gtk_entry_get_text (GTK_ENTRY (singular_entry))); plural = g_strdup (gtk_entry_get_text (GTK_ENTRY (plural_entry))); identifier = g_strstrip (identifier); symbol = g_strstrip (symbol); abbreviation = g_strstrip (abbreviation); singular = g_strstrip (singular); plural = g_strstrip (plural); if (! strlen (identifier) || ! strlen (symbol) || ! strlen (abbreviation) || ! strlen (singular) || ! strlen (plural)) { GtkWidget *msg = gtk_message_dialog_new (GTK_WINDOW (dialog), 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Incomplete input")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msg), _("Please fill in all text fields.")); gtk_dialog_run (GTK_DIALOG (msg)); gtk_widget_destroy (msg); continue; } unit = gimp_unit_new (identifier, factor, digits, symbol, abbreviation, singular, plural); g_free (identifier); g_free (symbol); g_free (abbreviation); g_free (singular); g_free (plural); break; } gtk_widget_destroy (dialog); return unit; } static void unit_editor_dialog (void) { GtkWidget *dialog; GtkWidget *scrolled_win; GtkUIManager *ui_manager; GtkActionGroup *group; GtkWidget *toolbar; GtkListStore *list_store; GtkWidget *tv; GtkTreeViewColumn *col; GtkWidget *col_widget; GtkWidget *button; GtkCellRenderer *rend; gint i; gimp_ui_init (PLUG_IN_BINARY, FALSE); list_store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_BOOLEAN, /* SAVE */ G_TYPE_STRING, /* IDENTIFIER */ G_TYPE_DOUBLE, /* FACTOR */ G_TYPE_INT, /* DIGITS */ G_TYPE_STRING, /* SYMBOL */ G_TYPE_STRING, /* ABBREVIATION */ G_TYPE_STRING, /* SINGULAR */ G_TYPE_STRING, /* PLURAL */ GIMP_TYPE_UNIT, /* UNIT */ G_TYPE_BOOLEAN, /* USER_UNIT */ GDK_TYPE_COLOR); /* BG_COLOR */ tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store)); g_object_unref (list_store); dialog = gimp_dialog_new (_("Unit Editor"), PLUG_IN_ROLE, NULL, 0, gimp_standard_help_func, PLUG_IN_PROC, _("_Refresh"), RESPONSE_REFRESH, _("_Close"), GTK_RESPONSE_CLOSE, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); g_signal_connect (dialog, "response", G_CALLBACK (unit_editor_response), tv); g_signal_connect (dialog, "destroy", G_CALLBACK (gtk_main_quit), NULL); /* the toolbar */ ui_manager = gtk_ui_manager_new (); group = gtk_action_group_new ("unit-editor"); gtk_action_group_set_translation_domain (group, NULL); gtk_action_group_add_actions (group, actions, G_N_ELEMENTS (actions), tv); gtk_window_add_accel_group (GTK_WINDOW (dialog), gtk_ui_manager_get_accel_group (ui_manager)); gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager)); gtk_ui_manager_insert_action_group (ui_manager, group, -1); g_object_unref (group); gtk_ui_manager_add_ui_from_string (ui_manager, "\n" " \n" " \n" " \n" " \n" "\n", -1, NULL); toolbar = gtk_ui_manager_get_widget (ui_manager, "/unit-editor-toolbar"); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), toolbar, FALSE, FALSE, 0); gtk_widget_show (toolbar); scrolled_win = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 12); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scrolled_win, TRUE, TRUE, 0); gtk_widget_show (scrolled_win); gtk_widget_set_size_request (tv, -1, 220); gtk_container_add (GTK_CONTAINER (scrolled_win), tv); gtk_widget_show (tv); rend = gtk_cell_renderer_toggle_new (); col = gtk_tree_view_column_new_with_attributes (gettext (columns[SAVE].title), rend, "active", SAVE, "activatable", USER_UNIT, "cell-background-gdk", BG_COLOR, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col); col_widget = gtk_tree_view_column_get_widget (col); if (col_widget) { button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON); if (button) gimp_help_set_help_data (button, gettext (columns[SAVE].help), NULL); } g_signal_connect (rend, "toggled", G_CALLBACK (saved_toggled_callback), list_store); for (i = 0; i < G_N_ELEMENTS (columns); i++) { if (i == SAVE) continue; col = gtk_tree_view_column_new_with_attributes (gettext (columns[i].title), gtk_cell_renderer_text_new (), "text", i, "cell-background-gdk", BG_COLOR, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col); col_widget = gtk_tree_view_column_get_widget (col); if (col_widget) { button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON); if (button) gimp_help_set_help_data (button, gettext (columns[i].help), NULL); } } unit_list_init (GTK_TREE_VIEW (tv)); gtk_widget_show (dialog); gtk_main (); } static void unit_editor_response (GtkWidget *widget, gint response_id, gpointer data) { switch (response_id) { case RESPONSE_REFRESH: unit_list_init (GTK_TREE_VIEW (data)); break; default: gtk_widget_destroy (widget); break; } } static void new_callback (GtkAction *action, GtkTreeView *tv) { GimpUnit unit; unit = new_unit_dialog (gtk_widget_get_toplevel (GTK_WIDGET (tv)), GIMP_UNIT_PIXEL); if (unit != GIMP_UNIT_PIXEL) { GtkTreeModel *model; GtkTreeIter iter; unit_list_init (tv); model = gtk_tree_view_get_model (tv); if (gtk_tree_model_get_iter_first (model, &iter) && gtk_tree_model_iter_nth_child (model, &iter, NULL, unit - GIMP_UNIT_INCH)) { GtkAdjustment *adj; gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv), &iter); adj = gtk_tree_view_get_vadjustment (tv); gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj)); } } } static void duplicate_callback (GtkAction *action, GtkTreeView *tv) { GtkTreeModel *model; GtkTreeSelection *sel; GtkTreeIter iter; model = gtk_tree_view_get_model (tv); sel = gtk_tree_view_get_selection (tv); if (gtk_tree_selection_get_selected (sel, NULL, &iter)) { GimpUnit unit; gtk_tree_model_get (model, &iter, UNIT, &unit, -1); unit = new_unit_dialog (gtk_widget_get_toplevel (GTK_WIDGET (tv)), unit); if (unit != GIMP_UNIT_PIXEL) { GtkTreeIter iter; unit_list_init (tv); if (gtk_tree_model_get_iter_first (model, &iter) && gtk_tree_model_iter_nth_child (model, &iter, NULL, unit - GIMP_UNIT_INCH)) { GtkAdjustment *adj; gtk_tree_selection_select_iter (sel, &iter); adj = gtk_tree_view_get_vadjustment (tv); gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj)); } } } } static void saved_toggled_callback (GtkCellRendererToggle *celltoggle, gchar *path_string, GtkListStore *list_store) { GtkTreePath *path; GtkTreeIter iter; gboolean saved; GimpUnit unit; path = gtk_tree_path_new_from_string (path_string); if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &iter, path)) { g_warning ("%s: bad tree path?", G_STRLOC); return; } gtk_tree_path_free (path); gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, SAVE, &saved, UNIT, &unit, -1); if (unit >= gimp_unit_get_number_of_built_in_units ()) { gimp_unit_set_deletion_flag (unit, saved); gtk_list_store_set (GTK_LIST_STORE (list_store), &iter, SAVE, ! saved, -1); } } static void unit_list_init (GtkTreeView *tv) { GtkListStore *list_store; GtkTreeIter iter; gint num_units; GimpUnit unit; GdkColor color; list_store = GTK_LIST_STORE (gtk_tree_view_get_model (tv)); gtk_list_store_clear (list_store); num_units = gimp_unit_get_number_of_units (); color.red = 0xdddd; color.green = 0xdddd; color.blue = 0xffff; for (unit = GIMP_UNIT_INCH; unit < num_units; unit++) { gboolean user_unit = (unit >= gimp_unit_get_number_of_built_in_units ()); gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, SAVE, ! gimp_unit_get_deletion_flag (unit), IDENTIFIER, gimp_unit_get_identifier (unit), FACTOR, gimp_unit_get_factor (unit), DIGITS, gimp_unit_get_digits (unit), SYMBOL, gimp_unit_get_symbol (unit), ABBREVIATION, gimp_unit_get_abbreviation (unit), SINGULAR, gimp_unit_get_singular (unit), PLURAL, gimp_unit_get_plural (unit), UNIT, unit, USER_UNIT, user_unit, user_unit ? -1 : BG_COLOR, &color, -1); } if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter)) gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv), &iter); }