summaryrefslogtreecommitdiffstats
path: root/plug-ins/script-fu/script-fu-console.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/script-fu/script-fu-console.c703
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,
+ &params,
+ &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;
+}