diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/script-fu/script-fu-console.c | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/script-fu/script-fu-console.c')
-rw-r--r-- | plug-ins/script-fu/script-fu-console.c | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/plug-ins/script-fu/script-fu-console.c b/plug-ins/script-fu/script-fu-console.c new file mode 100644 index 0000000..835b3db --- /dev/null +++ b/plug-ins/script-fu/script-fu-console.c @@ -0,0 +1,703 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 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 <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <errno.h> +#include <string.h> + +#include <glib/gstdio.h> + +#include "libgimp/gimp.h" +#include "libgimp/gimpui.h" + +#include <gdk/gdkkeysyms.h> + +#include "scheme-wrapper.h" +#include "script-fu-console.h" + +#include "script-fu-intl.h" + + +#define TEXT_WIDTH 480 +#define TEXT_HEIGHT 400 + +#define PROC_NAME "plug-in-script-fu-console" + +typedef struct +{ + GtkWidget *dialog; + GtkTextBuffer *console; + GtkWidget *cc; + GtkWidget *text_view; + GtkWidget *proc_browser; + GtkWidget *save_dialog; + + GList *history; + gint history_len; + gint history_cur; + gint history_max; +} ConsoleInterface; + +enum +{ + RESPONSE_CLEAR, + RESPONSE_SAVE +}; + +/* + * Local Functions + */ +static void script_fu_console_interface (void); +static void script_fu_console_response (GtkWidget *widget, + gint response_id, + ConsoleInterface *console); +static void script_fu_console_save_dialog (ConsoleInterface *console); +static void script_fu_console_save_response (GtkWidget *dialog, + gint response_id, + ConsoleInterface *console); + +static void script_fu_browse_callback (GtkWidget *widget, + ConsoleInterface *console); +static void script_fu_browse_response (GtkWidget *widget, + gint response_id, + ConsoleInterface *console); +static void script_fu_browse_row_activated (GtkDialog *dialog); + +static gboolean script_fu_cc_is_empty (ConsoleInterface *console); +static gboolean script_fu_cc_key_function (GtkWidget *widget, + GdkEventKey *event, + ConsoleInterface *console); + +static void script_fu_output_to_console (TsOutputType type, + const gchar *text, + gint len, + gpointer user_data); + +/* + * Function definitions + */ + +void +script_fu_console_run (const gchar *name, + gint nparams, + const GimpParam *params, + gint *nreturn_vals, + GimpParam **return_vals) +{ + static GimpParam values[1]; + + ts_set_print_flag (1); + script_fu_console_interface (); + + *nreturn_vals = 1; + *return_vals = values; + + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = GIMP_PDB_SUCCESS; +} + +static void +script_fu_console_interface (void) +{ + ConsoleInterface console = { 0, }; + GtkWidget *vbox; + GtkWidget *button; + GtkWidget *scrolled_window; + GtkWidget *hbox; + + gimp_ui_init ("script-fu", FALSE); + + console.history_max = 50; + + console.dialog = gimp_dialog_new (_("Script-Fu Console"), + "gimp-script-fu-console", + NULL, 0, + gimp_standard_help_func, PROC_NAME, + + _("_Save"), RESPONSE_SAVE, + _("C_lear"), RESPONSE_CLEAR, + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (console.dialog), + GTK_RESPONSE_CLOSE, + RESPONSE_CLEAR, + RESPONSE_SAVE, + -1); + + g_object_add_weak_pointer (G_OBJECT (console.dialog), + (gpointer) &console.dialog); + + g_signal_connect (console.dialog, "response", + G_CALLBACK (script_fu_console_response), + &console); + + /* The main vbox */ + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (console.dialog))), + vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + /* The output text widget */ + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_ALWAYS); + gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0); + gtk_widget_show (scrolled_window); + + console.console = gtk_text_buffer_new (NULL); + console.text_view = gtk_text_view_new_with_buffer (console.console); + g_object_unref (console.console); + + gtk_text_view_set_editable (GTK_TEXT_VIEW (console.text_view), FALSE); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (console.text_view), + GTK_WRAP_WORD); + gtk_text_view_set_left_margin (GTK_TEXT_VIEW (console.text_view), 6); + gtk_text_view_set_right_margin (GTK_TEXT_VIEW (console.text_view), 6); + gtk_widget_set_size_request (console.text_view, TEXT_WIDTH, TEXT_HEIGHT); + gtk_container_add (GTK_CONTAINER (scrolled_window), console.text_view); + gtk_widget_show (console.text_view); + + gtk_text_buffer_create_tag (console.console, "strong", + "weight", PANGO_WEIGHT_BOLD, + "scale", PANGO_SCALE_LARGE, + NULL); + gtk_text_buffer_create_tag (console.console, "emphasis", + "style", PANGO_STYLE_OBLIQUE, + NULL); + + { + const gchar * const greetings[] = + { + "strong", N_("Welcome to TinyScheme"), + NULL, "\n", + NULL, "Copyright (c) Dimitrios Souflis", + NULL, "\n", + "strong", N_("Script-Fu Console"), + NULL, " - ", + "emphasis", N_("Interactive Scheme Development"), + NULL, "\n" + }; + + GtkTextIter cursor; + gint i; + + gtk_text_buffer_get_end_iter (console.console, &cursor); + + for (i = 0; i < G_N_ELEMENTS (greetings); i += 2) + { + if (greetings[i]) + gtk_text_buffer_insert_with_tags_by_name (console.console, &cursor, + gettext (greetings[i + 1]), + -1, greetings[i], + NULL); + else + gtk_text_buffer_insert (console.console, &cursor, + gettext (greetings[i + 1]), -1); + } + } + + /* The current command */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + console.cc = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (hbox), console.cc, TRUE, TRUE, 0); + gtk_widget_grab_focus (console.cc); + gtk_widget_show (console.cc); + + g_signal_connect (console.cc, "key-press-event", + G_CALLBACK (script_fu_cc_key_function), + &console); + + button = gtk_button_new_with_mnemonic (_("_Browse...")); + gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))), 2, 0); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (script_fu_browse_callback), + &console); + + /* Initialize the history */ + console.history = g_list_append (console.history, NULL); + console.history_len = 1; + + gtk_widget_show (console.dialog); + + gtk_main (); + + if (console.save_dialog) + gtk_widget_destroy (console.save_dialog); + + if (console.dialog) + gtk_widget_destroy (console.dialog); +} + +static void +script_fu_console_response (GtkWidget *widget, + gint response_id, + ConsoleInterface *console) +{ + GtkTextIter start, end; + + switch (response_id) + { + case RESPONSE_CLEAR: + gtk_text_buffer_get_start_iter (console->console, &start); + gtk_text_buffer_get_end_iter (console->console, &end); + gtk_text_buffer_delete (console->console, &start, &end); + break; + + case RESPONSE_SAVE: + script_fu_console_save_dialog (console); + break; + + default: + gtk_main_quit (); + break; + } +} + + +static void +script_fu_console_save_dialog (ConsoleInterface *console) +{ + if (! console->save_dialog) + { + console->save_dialog = + gtk_file_chooser_dialog_new (_("Save Script-Fu Console Output"), + GTK_WINDOW (console->dialog), + GTK_FILE_CHOOSER_ACTION_SAVE, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (console->save_dialog), + GTK_RESPONSE_OK); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (console->save_dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (console->save_dialog), + TRUE); + + g_object_add_weak_pointer (G_OBJECT (console->save_dialog), + (gpointer) &console->save_dialog); + + g_signal_connect (console->save_dialog, "response", + G_CALLBACK (script_fu_console_save_response), + console); + } + + gtk_window_present (GTK_WINDOW (console->save_dialog)); +} + +static void +script_fu_console_save_response (GtkWidget *dialog, + gint response_id, + ConsoleInterface *console) +{ + GtkTextIter start, end; + + if (response_id == GTK_RESPONSE_OK) + { + gchar *filename; + gchar *str; + FILE *fh; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + + fh = g_fopen (filename, "w"); + + if (! fh) + { + g_message (_("Could not open '%s' for writing: %s"), + gimp_filename_to_utf8 (filename), + g_strerror (errno)); + + g_free (filename); + return; + } + + gtk_text_buffer_get_start_iter (console->console, &start); + gtk_text_buffer_get_end_iter (console->console, &end); + + str = gtk_text_buffer_get_text (console->console, &start, &end, FALSE); + + fputs (str, fh); + fclose (fh); + + g_free (str); + } + + gtk_widget_hide (dialog); +} + +static void +script_fu_browse_callback (GtkWidget *widget, + ConsoleInterface *console) +{ + if (! console->proc_browser) + { + console->proc_browser = + gimp_proc_browser_dialog_new (_("Script-Fu Procedure Browser"), + "script-fu-procedure-browser", + gimp_standard_help_func, PROC_NAME, + + _("_Apply"), GTK_RESPONSE_APPLY, + _("_Close"), GTK_RESPONSE_CLOSE, + + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (console->proc_browser), + GTK_RESPONSE_APPLY); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (console->proc_browser), + GTK_RESPONSE_CLOSE, + GTK_RESPONSE_APPLY, + -1); + + g_object_add_weak_pointer (G_OBJECT (console->proc_browser), + (gpointer) &console->proc_browser); + + g_signal_connect (console->proc_browser, "response", + G_CALLBACK (script_fu_browse_response), + console); + g_signal_connect (console->proc_browser, "row-activated", + G_CALLBACK (script_fu_browse_row_activated), + console); + } + + gtk_window_present (GTK_WINDOW (console->proc_browser)); +} + +static void +script_fu_browse_response (GtkWidget *widget, + gint response_id, + ConsoleInterface *console) +{ + GimpProcBrowserDialog *dialog = GIMP_PROC_BROWSER_DIALOG (widget); + gchar *proc_name; + gchar *proc_blurb; + gchar *proc_help; + gchar *proc_author; + gchar *proc_copyright; + gchar *proc_date; + GimpPDBProcType proc_type; + gint n_params; + gint n_return_vals; + GimpParamDef *params; + GimpParamDef *return_vals; + gint i; + GString *text; + + if (response_id != GTK_RESPONSE_APPLY) + { + gtk_widget_destroy (widget); + return; + } + + proc_name = gimp_proc_browser_dialog_get_selected (dialog); + + if (proc_name == NULL) + return; + + gimp_procedural_db_proc_info (proc_name, + &proc_blurb, + &proc_help, + &proc_author, + &proc_copyright, + &proc_date, + &proc_type, + &n_params, + &n_return_vals, + ¶ms, + &return_vals); + + text = g_string_new ("("); + text = g_string_append (text, proc_name); + + for (i = 0; i < n_params; i++) + { + text = g_string_append_c (text, ' '); + text = g_string_append (text, params[i].name); + } + + text = g_string_append_c (text, ')'); + + gtk_window_set_focus (GTK_WINDOW (console->dialog), console->cc); + + gtk_entry_set_text (GTK_ENTRY (console->cc), text->str); + gtk_editable_set_position (GTK_EDITABLE (console->cc), + g_utf8_pointer_to_offset (text->str, + text->str + + strlen (proc_name) + 2)); + + g_string_free (text, TRUE); + + gtk_window_present (GTK_WINDOW (console->dialog)); + + g_free (proc_name); + g_free (proc_blurb); + g_free (proc_help); + g_free (proc_author); + g_free (proc_copyright); + g_free (proc_date); + + gimp_destroy_paramdefs (params, n_params); + gimp_destroy_paramdefs (return_vals, n_return_vals); +} + +static void +script_fu_browse_row_activated (GtkDialog *dialog) +{ + gtk_dialog_response (dialog, GTK_RESPONSE_APPLY); +} + +static gboolean +script_fu_console_idle_scroll_end (GtkWidget *view) +{ + GtkWidget *parent = gtk_widget_get_parent (view); + + if (parent) + { + GtkAdjustment *adj; + + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (parent)); + + gtk_adjustment_set_value (adj, + gtk_adjustment_get_upper (adj) - + gtk_adjustment_get_page_size (adj)); + } + + g_object_unref (view); + + return FALSE; +} + +static void +script_fu_console_scroll_end (GtkWidget *view) +{ + /* the text view idle updates, so we need to idle scroll too + */ + g_object_ref (view); + + g_idle_add ((GSourceFunc) script_fu_console_idle_scroll_end, view); +} + +static void +script_fu_output_to_console (TsOutputType type, + const gchar *text, + gint len, + gpointer user_data) +{ + ConsoleInterface *console = user_data; + + if (console && console->text_view) + { + GtkTextBuffer *buffer; + GtkTextIter cursor; + + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (console->text_view)); + + gtk_text_buffer_get_end_iter (buffer, &cursor); + + if (type == TS_OUTPUT_NORMAL) + { + gtk_text_buffer_insert (buffer, &cursor, text, len); + } + else + { + gtk_text_buffer_insert_with_tags_by_name (console->console, &cursor, + text, len, "emphasis", + NULL); + } + + script_fu_console_scroll_end (console->text_view); + } +} + +static gboolean +script_fu_cc_is_empty (ConsoleInterface *console) +{ + const gchar *str; + + if ((str = gtk_entry_get_text (GTK_ENTRY (console->cc))) == NULL) + return TRUE; + + while (*str) + { + if (*str != ' ' && *str != '\t' && *str != '\n') + return FALSE; + + str ++; + } + + return TRUE; +} + +static gboolean +script_fu_cc_key_function (GtkWidget *widget, + GdkEventKey *event, + ConsoleInterface *console) +{ + GList *list; + gint direction = 0; + GtkTextIter cursor; + GString *output; + + switch (event->keyval) + { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + if (script_fu_cc_is_empty (console)) + return TRUE; + + list = g_list_nth (console->history, + (g_list_length (console->history) - 1)); + + if (list->data) + g_free (list->data); + + list->data = g_strdup (gtk_entry_get_text (GTK_ENTRY (console->cc))); + + gtk_text_buffer_get_end_iter (console->console, &cursor); + + gtk_text_buffer_insert (console->console, &cursor, "\n", 1); + gtk_text_buffer_insert_with_tags_by_name (console->console, &cursor, + "> ", 2, + "strong", + NULL); + + gtk_text_buffer_insert (console->console, &cursor, + gtk_entry_get_text (GTK_ENTRY (console->cc)), -1); + gtk_text_buffer_insert (console->console, &cursor, "\n", 1); + + script_fu_console_scroll_end (console->text_view); + + gtk_entry_set_text (GTK_ENTRY (console->cc), ""); + + output = g_string_new (NULL); + ts_register_output_func (ts_gstring_output_func, output); + + gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_PLUGIN); + + if (ts_interpret_string (list->data) != 0) + { + script_fu_output_to_console (TS_OUTPUT_ERROR, + output->str, + output->len, + console); + } + else + { + script_fu_output_to_console (TS_OUTPUT_NORMAL, + output->str, + output->len, + console); + } + + gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_INTERNAL); + + g_string_free (output, TRUE); + + gimp_displays_flush (); + + console->history = g_list_append (console->history, NULL); + + if (console->history_len == console->history_max) + { + console->history = g_list_remove (console->history, + console->history->data); + if (console->history->data) + g_free (console->history->data); + } + else + { + console->history_len++; + } + + console->history_cur = g_list_length (console->history) - 1; + + return TRUE; + break; + + case GDK_KEY_KP_Up: + case GDK_KEY_Up: + direction = -1; + break; + + case GDK_KEY_KP_Down: + case GDK_KEY_Down: + direction = 1; + break; + + case GDK_KEY_P: + case GDK_KEY_p: + if (event->state & GDK_CONTROL_MASK) + direction = -1; + break; + + case GDK_KEY_N: + case GDK_KEY_n: + if (event->state & GDK_CONTROL_MASK) + direction = 1; + break; + + default: + break; + } + + if (direction) + { + /* Make sure we keep track of the current one */ + if (console->history_cur == g_list_length (console->history) - 1) + { + list = g_list_nth (console->history, console->history_cur); + + g_free (list->data); + list->data = g_strdup (gtk_entry_get_text (GTK_ENTRY (console->cc))); + } + + console->history_cur += direction; + + if (console->history_cur < 0) + console->history_cur = 0; + + if (console->history_cur >= console->history_len) + console->history_cur = console->history_len - 1; + + gtk_entry_set_text (GTK_ENTRY (console->cc), + (gchar *) (g_list_nth (console->history, + console->history_cur))->data); + + gtk_editable_set_position (GTK_EDITABLE (console->cc), -1); + + return TRUE; + } + + return FALSE; +} |