Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View File

@ -0,0 +1,39 @@
# library for script-fu-console
#
# Contains just the run_func for ScriptFu console plugin.
# The plugin is a PDB procedure of type temporary.
# The PDB procedure is registered by script-fu plugin,
# which passes the run_func as a closure at registration time.
#
# Static library: just an archive of object files.
# The library is not installed,
# only linked with the script-fu plugin which references it.
# uses libscriptfu
libscriptfuconsoleInclude = include_directories('.')
libscriptfuconsole_sources = [
'script-fu-console.c',
'script-fu-console-editor.c',
'script-fu-console-history.c',
'script-fu-console-total.c',
]
libscriptfuconsole = static_library('scriptfu-console',
libscriptfuconsole_sources,
include_directories: [
libscriptfuInclude,
rootInclude,
appInclude,
],
c_args: [
'-DG_LOG_DOMAIN="scriptfu-console"',
],
dependencies: [
libpikaui_dep,
math,
],
link_with: libscriptfu,
install: false,
)

View File

@ -0,0 +1,97 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 <gtk/gtk.h>
#include "script-fu-console-editor.h"
/* ConsoleEditor
* An API that abstracts these GtkWidgets for editing text:
* - GtkTextEntry is single line,
* - GtkTextView is multiline.
*
* So that we can swap out or enhance widget without affecting main logic.
*
* Not a defined class but methods conform to naming and calling conventions.
*
* Is-a GtkWidget.
*/
/* FUTURE
* GtkTextEntry => GtkTextView (multiline)
*
* Possibly: an editor that understands syntax and highlighting.
*/
GtkWidget *
console_editor_new (void)
{
return gtk_entry_new ();
}
/* Set editor's text and position the cursor.
* @position conforms to GtkEditable interface.
*/
void
console_editor_set_text_and_position (GtkWidget *self,
const gchar *text,
gint position)
{
/* gtk_entry_set_text not allow NULL */
if (text != NULL)
gtk_entry_set_text (GTK_ENTRY (self), text);
gtk_editable_set_position (GTK_EDITABLE (self), position);
}
void
console_editor_clear (GtkWidget *self)
{
console_editor_set_text_and_position (self, "", -1);
}
const gchar *
console_editor_get_text (GtkWidget *self)
{
return gtk_entry_get_text (GTK_ENTRY (self));
}
gboolean
console_editor_is_empty (GtkWidget *self)
{
const gchar *str;
if ((str = console_editor_get_text (self)) == NULL)
return TRUE;
while (*str)
{
if (*str != ' ' && *str != '\t' && *str != '\n')
return FALSE;
str ++;
}
return TRUE;
}

View File

@ -0,0 +1,36 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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/>.
*/
#ifndef __SCRIPT_FU_CONSOLE_EDITOR_H__
#define __SCRIPT_FU_CONSOLE_EDITOR_H__
GtkWidget *console_editor_new (void);
void console_editor_set_text_and_position (GtkWidget *self,
const gchar *text,
gint position);
const gchar *console_editor_get_text (GtkWidget *self);
gboolean console_editor_is_empty (GtkWidget *self);
void console_editor_clear (GtkWidget *self);
#endif /* __SCRIPT_FU_CONSOLE_EDITOR_H__ */

View File

@ -0,0 +1,262 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 "libpika/pika.h"
#include "script-fu-console-history.h"
static gint console_history_tail_position (CommandHistory *self);
static GStrv console_history_to_strv (CommandHistory *self);
/* CommandHistory
*
* Not a true class, just a struct with methods.
* Does not inherit GObject.
*
* Is a model that affects a view of the "console history."
* The model is a sequence of ExecutedCommands.
* The sequence is a time-ordered queue.
* You can only append to the end, called the tail.
* An ExecutedCommand does not contain the result of interpretation,
* only the string that was interpreted.
*
* The sequence is finite.
* When you append to the tail,
* commands might be discarded from the head.
*
* Has a cursor.
* You can only get the command at the cursor.
* The user scrolling through the history moves the cursor.
* This scrolling is arrow keys in the editor pane,
* (not scroll bars in the history view pane.)
* See the main console logic:
* when user hits arrow keys in the editor,
* move cursor in the history, get the command at the cursor,
* and display it in the editor, ready to execute.
*
* A CommandHistory is a model,
* but there is also a distinct TotalHistory model for a scrolling view of the history
* (e.g. a GtkTextBuffer model for a GtkTextView.)
*
* CommandHistory <-supersets- TotalHistory <-views- ConsoleView
*
* TotalHistory contains more than the commands in CommandHistory.
* TotalHistory contains e.g. splash, prompts, and interpretation results.
*
* !!! Self does not currently write TotalHistory;
* The main console logic writes TotalHistory,
*
* CommandHistory is persistent across sessions of the ScriptFu Console,
* and across sessions of Pika.
* When the SFConsole starts, the TotalHistory,
* is just the CommandHistory, without results of eval.
* Old results are not meaningful since the environment changed.
* Specifically, a new session of SFConsole has a new initialized interpreter.
* Similarly, when the user closes the console,
* only the CommandHistory is saved as settings.
*/
void
console_history_init (CommandHistory *self)
{
self->model = g_list_append (self->model, NULL);
self->model_len = 1;
self->model_max = 100;
}
/* Store the command in tail of CommandHistory.
* The tail is the most recent added element, which was created prior by new_tail.
*
* @commmand transfer full
*
* !!! The caller is executing the command.
* The caller updates TotalHistory, with a prompt string and the command string.
* Self does not update TotalHistory, the model of the view.
*/
void
console_history_set_tail (CommandHistory *self,
const gchar *command)
{
GList *list;
list = g_list_nth (self->model,
console_history_tail_position (self));
if (list->data)
g_free (list->data);
/* Discarding const. */
list->data = (gpointer) command;
}
/* Remove the head of the history and free its string.
*
* GList doesn't have such a direct method.
* Search web to find this solution.
* !!! g_list_remove does not free the data of the removed element.
*
* Remove the element whose data (a string)
* matches the data of the first element.
* Then free the data of the first element.
*/
static void
console_history_remove_head (CommandHistory *self)
{
gpointer * data;
g_return_if_fail (self->model != NULL);
data = self->model->data;
self->model = g_list_remove (self->model, data);
g_free (data);
}
/* Append NULL string at tail of CommandHistory.
* Prune head when max exceeded, freeing the string.
* Position the cursor at last element.
*/
void
console_history_new_tail (CommandHistory *self)
{
self->model = g_list_append (self->model, NULL);
if (self->model_len == self->model_max)
{
console_history_remove_head (self);
}
else
{
self->model_len++;
}
self->model_cursor = console_history_tail_position (self);
}
void
console_history_cursor_to_tail (CommandHistory *self)
{
self->model_cursor = console_history_tail_position (self);
}
gboolean
console_history_is_cursor_at_tail (CommandHistory *self)
{
return self->model_cursor == console_history_tail_position (self);
}
void
console_history_move_cursor (CommandHistory *self,
gint direction)
{
self->model_cursor += direction;
/* Clamp cursor in range [0, model_len-1] */
if (self->model_cursor < 0)
self->model_cursor = 0;
if (self->model_cursor >= self->model_len)
self->model_cursor = self->model_len - 1;
}
const gchar *
console_history_get_at_cursor (CommandHistory *self)
{
return g_list_nth (self->model, self->model_cursor)->data;
}
/* Methods for persisting history as a setting. */
/* Return a GStrv of the history from settings.
* The Console knows how to put GStrv to both models!
*
* !!! Handle attack on settings file.
* The returned cardinality of the set of strings
* may be zero or very many.
* Elsewhere ensure we don't overflow models.
*/
GStrv
console_history_from_settings (CommandHistory *self,
PikaProcedureConfig *config)
{
GStrv in_history;
/* Get aux arg from property of config. */
g_object_get (config,
"history", &in_history,
NULL);
return in_history;
}
void
console_history_to_settings (CommandHistory *self,
PikaProcedureConfig *config)
{
GStrv out_history;
out_history = console_history_to_strv (self);
/* set an aux arg in config. */
g_object_set (config,
"history", out_history,
NULL);
}
/* Return history model as GStrv.
* Converts from interal list into a string array.
*
* !!! The exported history may have a tail
* which is user's edits to the command line,
* that the user never evaluated.
* Exported history does not have an empty tail.
*
* Caller must g_strfreev the returned GStrv.
*/
static GStrv
console_history_to_strv (CommandHistory *self)
{
GStrv history_strv;
GStrvBuilder *builder;
builder = g_strv_builder_new ();
/* Order is earliest first. */
for (GList *l = self->model; l != NULL; l = l->next)
{
/* Don't write an empty pre-allocated tail. */
if (l->data != NULL)
g_strv_builder_add (builder, l->data);
}
history_strv = g_strv_builder_end (builder);
g_strv_builder_unref (builder);
return history_strv;
}
static gint
console_history_tail_position (CommandHistory *self)
{
return g_list_length (self->model) - 1;
}

View File

@ -0,0 +1,53 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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/>.
*/
#ifndef __SCRIPT_FU_CONSOLE_HISTORY_H__
#define __SCRIPT_FU_CONSOLE_HISTORY_H__
typedef struct
{
GList *model;
gint model_len;
gint model_cursor;
gint model_max;
} CommandHistory;
void console_history_init (CommandHistory *self);
void console_history_new_tail (CommandHistory *self);
void console_history_set_tail (CommandHistory *self,
const gchar *command);
void console_history_move_cursor (CommandHistory *self,
gint direction);
void console_history_cursor_to_tail (CommandHistory *self);
gboolean console_history_is_cursor_at_tail (CommandHistory *self);
const gchar *console_history_get_at_cursor (CommandHistory *self);
GStrv console_history_from_settings (CommandHistory *self,
PikaProcedureConfig *config);
void console_history_to_settings (CommandHistory *self,
PikaProcedureConfig *config);
#endif /* __SCRIPT_FU_CONSOLE_HISTORY_H__ */

View File

@ -0,0 +1,169 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 <gtk/gtk.h>
#include "script-fu-console-total.h"
#include "script-fu-intl.h"
/* TotalHistory
* Model for the history pane of SFConsole.
*
* Shows welcome, prompts repr, commands executed, and results of evaluation.
*
* A thin wrapper around GtkTextBuffer
*
* All changes to the model update the view via signals of the underlying Gtk classes.
*/
GtkTextBuffer *
console_total_history_new (void)
{
return gtk_text_buffer_new (NULL);
}
/* Clear TotalHistory.
*
* !!! Not clear CommandHistory, only TotalHistory.
* So TotalHistory is not always a superset of CommandHistory.
* FUTURE: also clear CommandHistory??
*
* !!! Not affect cursor of CommandHistory
* FUTURE: reset cursor to bottom?
*/
void
console_total_history_clear (GtkTextBuffer *self)
{
GtkTextIter start, end;
gtk_text_buffer_get_start_iter (self, &start);
gtk_text_buffer_get_end_iter (self, &end);
gtk_text_buffer_delete (self, &start, &end);
}
/* Get all the text in self, including text
* that is not in CommandHistory, i.e. splash, prompts, and results.
*/
gchar *
console_total_history_get_text (GtkTextBuffer *self)
{
GtkTextIter start, end;
gtk_text_buffer_get_start_iter (self, &start);
gtk_text_buffer_get_end_iter (self, &end);
return gtk_text_buffer_get_text (self, &start, &end, FALSE);
}
void
console_total_append_welcome (GtkTextBuffer *self)
{
gtk_text_buffer_create_tag (self, "strong",
"weight", PANGO_WEIGHT_BOLD,
"scale", PANGO_SCALE_LARGE,
NULL);
gtk_text_buffer_create_tag (self, "emphasis",
"style", PANGO_STYLE_OBLIQUE,
NULL);
{
const gchar * const greetings[] =
{
"emphasis", N_("Welcome to TinyScheme"),
NULL, "\n",
"emphasis", "Copyright (c) Dimitrios Souflis",
NULL, "\n",
"emphasis", N_("Scripting PIKA in the Scheme language"),
NULL, "\n"
};
GtkTextIter cursor;
gint i;
gtk_text_buffer_get_end_iter (self, &cursor);
for (i = 0; i < G_N_ELEMENTS (greetings); i += 2)
{
if (greetings[i])
gtk_text_buffer_insert_with_tags_by_name (self, &cursor,
gettext (greetings[i + 1]),
-1, greetings[i],
NULL);
else
gtk_text_buffer_insert (self, &cursor,
gettext (greetings[i + 1]), -1);
}
}
}
void
console_total_append_text_normal (GtkTextBuffer *self,
const gchar *text,
gint len)
{
GtkTextIter cursor;
gtk_text_buffer_get_end_iter (self, &cursor);
gtk_text_buffer_insert (self, &cursor, text, len);
gtk_text_buffer_insert (self, &cursor, "\n", 1);
}
void
console_total_append_text_emphasize (GtkTextBuffer *self,
const gchar *text,
gint len)
{
GtkTextIter cursor;
gtk_text_buffer_get_end_iter (self, &cursor);
gtk_text_buffer_insert_with_tags_by_name (self,
&cursor,
text, len, "emphasis",
NULL);
gtk_text_buffer_insert (self, &cursor, "\n", 1);
}
/* Write newlines, prompt, and command. */
void
console_total_append_command (GtkTextBuffer *self,
const gchar *command)
{
GtkTextIter cursor;
gtk_text_buffer_get_end_iter (self, &cursor);
/* assert we are just after a newline. */
/* Write repr of a prompt.
* SFConsole doesn't have a prompt in it's command line,
* But we show one in the history view to distinguish commands.
*/
gtk_text_buffer_insert_with_tags_by_name (self, &cursor,
"> ", 2,
"strong",
NULL);
gtk_text_buffer_insert (self, &cursor, command, -1);
gtk_text_buffer_insert (self, &cursor, "\n", 1);
}

View File

@ -0,0 +1,41 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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/>.
*/
#ifndef __SCRIPT_FU_CONSOLE_TOTAL_H__
#define __SCRIPT_FU_CONSOLE_TOTAL_H__
GtkTextBuffer *console_total_history_new (void);
void console_total_history_clear (GtkTextBuffer *self);
gchar *console_total_history_get_text (GtkTextBuffer *self);
void console_total_append_welcome (GtkTextBuffer *self);
void console_total_append_text_normal (GtkTextBuffer *self,
const gchar *text,
gint len);
void console_total_append_text_emphasize (GtkTextBuffer *self,
const gchar *text,
gint len);
void console_total_append_command (GtkTextBuffer *self,
const gchar *command);
#endif /* __SCRIPT_FU_CONSOLE_TOTAL_H__ */

View File

@ -0,0 +1,646 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 "libpika/pika.h"
#include "libpika/pikaui.h"
#include <gdk/gdkkeysyms.h>
#include "script-fu-console.h"
#include "script-fu-console-editor.h"
#include "script-fu-console-history.h"
#include "script-fu-console-total.h"
#include "script-fu-lib.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 *total_history;
GtkWidget *editor;
GtkWidget *history_view;
GtkWidget *proc_browser;
GtkWidget *save_dialog;
CommandHistory history;
} ConsoleInterface;
enum
{
RESPONSE_CLEAR,
RESPONSE_SAVE
};
/*
* Local Functions
*/
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_editor_key_function (GtkWidget *widget,
GdkEventKey *event,
ConsoleInterface *console);
static void script_fu_console_scroll_end (GtkWidget *view);
static void script_fu_output_to_console (gboolean is_error,
const gchar *text,
gint len,
gpointer user_data);
static void script_fu_models_from_settings (ConsoleInterface *console,
PikaProcedureConfig *config);
static void script_fu_command_to_history (ConsoleInterface *console,
const gchar *command);
/*
* Function definitions
*/
PikaValueArray *
script_fu_console_run (PikaProcedure *procedure,
const PikaValueArray *args)
{
ConsoleInterface console = { 0, };
GtkWidget *vbox;
GtkWidget *button;
GtkWidget *scrolled_window;
GtkWidget *hbox;
PikaProcedureConfig *config;
script_fu_set_print_flag (1);
pika_ui_init ("script-fu");
/* Create model early so we can fill from settings. */
console.total_history = console_total_history_new ();
console_history_init (&console.history);
console_total_append_welcome (console.total_history);
/* Get previous or default settings into config. */
config = pika_procedure_create_config (procedure);
pika_procedure_config_begin_run (config, NULL, PIKA_RUN_INTERACTIVE, args);
script_fu_models_from_settings (&console, config);
console.dialog = pika_dialog_new (_("Script Console"),
"pika-script-fu-console",
NULL, 0,
pika_standard_help_func, PROC_NAME,
_("_Save"), RESPONSE_SAVE,
_("C_lear"), RESPONSE_CLEAR,
_("_Close"), GTK_RESPONSE_CLOSE,
NULL);
gtk_window_set_default_size (GTK_WINDOW (console.dialog), TEXT_WIDTH,
TEXT_HEIGHT);
pika_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);
/* A view of the total history. */
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.history_view = gtk_text_view_new_with_buffer (console.total_history);
/* View keeps reference. Unref our ref so buffer is destroyed with view. */
g_object_unref (console.total_history);
gtk_text_view_set_editable (GTK_TEXT_VIEW (console.history_view), FALSE);
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (console.history_view),
GTK_WRAP_WORD);
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (console.history_view), 6);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (console.history_view), 6);
gtk_widget_set_size_request (console.history_view, TEXT_WIDTH, TEXT_HEIGHT);
gtk_container_add (GTK_CONTAINER (scrolled_window), console.history_view);
gtk_widget_show (console.history_view);
/* An editor of a command to be executed. */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
console.editor = console_editor_new ();
gtk_box_pack_start (GTK_BOX (hbox), console.editor, TRUE, TRUE, 0);
gtk_widget_grab_focus (console.editor);
gtk_widget_show (console.editor);
g_signal_connect (console.editor, "key-press-event",
G_CALLBACK (script_fu_editor_key_function),
&console);
button = gtk_button_new_with_mnemonic (_("_Browse..."));
g_object_set (gtk_bin_get_child (GTK_BIN (button)),
"margin-start", 2,
"margin-end", 2,
NULL);
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);
gtk_widget_show (console.dialog);
/* The history model may fill the view, scroll. */
script_fu_console_scroll_end (console.history_view);
gtk_main ();
if (console.save_dialog)
gtk_widget_destroy (console.save_dialog);
if (console.dialog)
gtk_widget_destroy (console.dialog);
/* Update config with user's change to history */
console_history_to_settings (&console.history, config);
/* Persist config */
pika_procedure_config_end_run (config, PIKA_PDB_SUCCESS);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static void
script_fu_console_response (GtkWidget *widget,
gint response_id,
ConsoleInterface *console)
{
switch (response_id)
{
case RESPONSE_CLEAR:
console_total_history_clear (console->total_history);
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);
pika_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)
{
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"),
pika_filename_to_utf8 (filename),
g_strerror (errno));
g_free (filename);
return;
}
str = console_total_history_get_text (console->total_history);
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 =
pika_proc_browser_dialog_new (_("Script-Fu Procedure Browser"),
"script-fu-procedure-browser",
pika_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);
pika_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)
{
PikaProcBrowserDialog *dialog = PIKA_PROC_BROWSER_DIALOG (widget);
PikaProcedure *procedure;
gchar *proc_name;
GParamSpec **pspecs;
gint n_pspecs;
gint i;
GString *text;
if (response_id != GTK_RESPONSE_APPLY)
{
gtk_widget_destroy (widget);
return;
}
proc_name = pika_proc_browser_dialog_get_selected (dialog);
if (proc_name == NULL)
return;
procedure = pika_pdb_lookup_procedure (pika_get_pdb (), proc_name);
pspecs = pika_procedure_get_arguments (procedure, &n_pspecs);
text = g_string_new ("(");
text = g_string_append (text, proc_name);
for (i = 0; i < n_pspecs; i++)
{
text = g_string_append_c (text, ' ');
text = g_string_append (text, pspecs[i]->name);
}
text = g_string_append_c (text, ')');
gtk_window_set_focus (GTK_WINDOW (console->dialog), console->editor);
console_editor_set_text_and_position (console->editor,
text->str,
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);
}
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);
}
/* Write result of eval to the console view.
* But not put results in the history model.
*/
static void
script_fu_output_to_console (gboolean is_error_msg,
const gchar *result_text,
gint len,
gpointer user_data)
{
ConsoleInterface *console = user_data;
if (console && console->history_view)
{
if (! is_error_msg)
console_total_append_text_normal (console->total_history, result_text, len);
else
console_total_append_text_emphasize (console->total_history, result_text, len);
script_fu_console_scroll_end (console->history_view);
}
}
static gboolean
script_fu_editor_key_function (GtkWidget *widget,
GdkEventKey *event,
ConsoleInterface *console)
{
gint direction = 0;
GString *output;
gboolean is_error;
const gchar *command;
switch (event->keyval)
{
case GDK_KEY_Return:
case GDK_KEY_KP_Enter:
case GDK_KEY_ISO_Enter:
if (console_editor_is_empty (console->editor))
return TRUE;
command = g_strdup (console_editor_get_text (console->editor));
script_fu_command_to_history (console, command);
/* Assert history advanced to new, empty tail. */
script_fu_console_scroll_end (console->history_view);
console_editor_clear (console->editor);
output = g_string_new (NULL);
script_fu_redirect_output_to_gstr (output);
pika_plug_in_set_pdb_error_handler (pika_get_plug_in (),
PIKA_PDB_ERROR_HANDLER_PLUGIN);
is_error = script_fu_interpret_string (command);
script_fu_output_to_console (is_error,
output->str,
output->len,
console);
pika_plug_in_set_pdb_error_handler (pika_get_plug_in (),
PIKA_PDB_ERROR_HANDLER_INTERNAL);
g_string_free (output, TRUE);
pika_displays_flush ();
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:
/* Any other key is the user editing.
* Set cursor to tail: user is done scrolling history.
* Must do this to ensure edited command line is saved in history.
*/
console_history_cursor_to_tail (&console->history);
break;
}
if (direction)
{
/* Tail was preallocated and usually empty.
* Keep the editor contents in the tail as cursor is moved away from tail.
* So any edited text is not lost if user moves cursor back to tail.
*/
command = console_editor_get_text (console->editor);
/* command can be NULL */
if (console_history_is_cursor_at_tail (&console->history))
console_history_set_tail (&console->history, g_strdup (command));
/* Now move cursor and replace editor contents. */
console_history_move_cursor (&console->history, direction);
command = console_history_get_at_cursor (&console->history);
/* command can be NULL. */
console_editor_set_text_and_position (console->editor,
command,
-1);
return TRUE;
}
return FALSE;
}
/* Restore models from settings.
* This understands how to get history as a GStrv from settings
* and how to put GStrv into both models.
*
* Just the model. The view does not exist yet.
*/
static void
script_fu_models_from_settings (ConsoleInterface *console,
PikaProcedureConfig *config)
{
GStrv strings_in;
/* Assert the History model is empty, recently init. */
strings_in = console_history_from_settings (&console->history, config);
/* The history setting can be empty, and GStrv can be NULL.
* !!! But g_strv_length requires its arg!=NULL
*/
if (strings_in==NULL)
return;
/* Adding requires a new tail. */
console_history_new_tail (&console->history);
/* Order of the GStrv is earliest command first.
* Iterate ascending, i.e. earliest command to history first.
* Not concerned with performance.
*/
for (gint i = 0; i < g_strv_length (strings_in); i++)
script_fu_command_to_history (console, g_strdup (strings_in[i]));
g_strfreev (strings_in);
}
/* Append a command to history.
*
* Knows to put to both TotalHistory and History models.
*
* Transfers ownership of command to History model.
* Caller may retain a reference, for a short time.
*
* !!! The History model is finite and limits itself.
* While the TotalHistory model is nearly unlimited.
* More commands in the view than in the History model.
*/
static void
script_fu_command_to_history (ConsoleInterface *console,
const gchar *command)
{
/* Require new_tail called previously. */
/* To History model. */
console_history_set_tail (&console->history, command);
/* Advance history, editor wants preallocated tail. */
console_history_new_tail (&console->history);
/* Decorated command to TotalHistory model. */
console_total_append_command (console->total_history, command);
/* Ensure there is a new tail. */
}

View File

@ -0,0 +1,30 @@
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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/>.
*/
#ifndef __SCRIPT_FU_CONSOLE_H__
#define __SCRIPT_FU_CONSOLE_H__
PikaValueArray * script_fu_console_run (PikaProcedure *procedure,
const PikaValueArray *args);
#endif /* __SCRIPT_FU_CONSOLE_H__ */