gimp/plug-ins/script-fu/script-fu-console.c

629 lines
17 KiB
C

/* The GIMP -- an 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#if HAVE_DIRENT_H
#include <dirent.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/stat.h>
#include "gdk/gdkkeysyms.h"
#include "gtk/gtk.h"
#include "libgimp/gimp.h"
#include "libgimp/gimpui.h"
#include "siod.h"
#include "script-fu-console.h"
#include <plug-ins/dbbrowser/dbbrowser.h>
#ifdef NATIVE_WIN32
#include <fcntl.h>
#include <io.h>
#endif
#ifndef NATIVE_WIN32
#define TEXT_WIDTH 400
#define TEXT_HEIGHT 400
#define ENTRY_WIDTH 400
#define BUFSIZE 256
typedef struct
{
GtkWidget *console;
GtkWidget *cc;
GtkAdjustment *vadj;
GdkFont *font_strong;
GdkFont *font_emphasis;
GdkFont *font_weak;
GdkFont *font;
gint32 input_id;
} ConsoleInterface;
/*
* Local Functions
*/
static void script_fu_console_interface (void);
static void script_fu_close_callback (GtkWidget *widget,
gpointer data);
static void script_fu_browse_callback (GtkWidget *widget,
gpointer data);
static void script_fu_siod_read (gpointer data,
gint id,
GdkInputCondition cond);
static gint script_fu_cc_is_empty (void);
static gint script_fu_cc_key_function (GtkWidget *widget,
GdkEventKey *event,
gpointer data);
static FILE *script_fu_open_siod_console (void);
static void script_fu_close_siod_console(void);
/*
* Local variables
*/
static ConsoleInterface cint =
{
NULL, /* console */
NULL, /* current command */
NULL, /* vertical adjustment */
NULL, /* strong font */
NULL, /* emphasis font */
NULL, /* weak font */
NULL, /* normal font */
-1 /* input id */
};
static char read_buffer[BUFSIZE];
static GList *history = NULL;
static int history_len = 0;
static int history_cur = 0;
static int history_max = 50;
static int siod_output_pipe[2];
extern int siod_verbose_level;
extern char siod_err_msg[];
extern FILE *siod_output;
#define message(string) printf("(%s): %d ::: %s\n", __PRETTY_FUNCTION__, __LINE__, string)
/*
* Function definitions
*/
void
script_fu_console_run (char *name,
int nparams,
GParam *params,
int *nreturn_vals,
GParam **return_vals)
{
static GParam values[1];
GStatusType status = STATUS_SUCCESS;
GRunModeType run_mode;
run_mode = params[0].data.d_int32;
switch (run_mode)
{
case RUN_INTERACTIVE:
/* Enable SIOD output */
script_fu_open_siod_console ();
/* Run the interface */
script_fu_console_interface ();
/* Clean up */
script_fu_close_siod_console ();
break;
case RUN_WITH_LAST_VALS:
case RUN_NONINTERACTIVE:
status = STATUS_CALLING_ERROR;
gimp_message ("Script-Fu console mode allows only interactive invocation");
break;
default:
break;
}
*nreturn_vals = 1;
*return_vals = values;
values[0].type = PARAM_STATUS;
values[0].data.d_status = status;
}
static void
script_fu_console_interface ()
{
GtkWidget *dlg;
GtkWidget *button;
GtkWidget *label;
GtkWidget *vsb;
GtkWidget *table;
GtkWidget *hbox;
gchar **argv;
gint argc;
argc = 1;
argv = g_new (gchar *, 1);
argv[0] = g_strdup ("script-fu");
gtk_init (&argc, &argv);
gtk_rc_parse (gimp_gtkrc ());
dlg = gtk_dialog_new ();
gtk_window_set_title (GTK_WINDOW (dlg), "Script-Fu Console");
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
(GtkSignalFunc) script_fu_close_callback,
NULL);
gtk_signal_connect (GTK_OBJECT (dlg),
"destroy",
GTK_SIGNAL_FUNC (gtk_widget_destroyed),
&dlg);
gtk_container_border_width (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), 2);
gtk_container_border_width (GTK_CONTAINER (GTK_DIALOG (dlg)->action_area), 2);
/* Action area */
button = gtk_button_new_with_label ("Close");
gtk_signal_connect (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) script_fu_close_callback,
NULL);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
gtk_widget_show (button);
/* The info vbox */
label = gtk_label_new ("SIOD Output");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), label, FALSE, TRUE, 0);
gtk_widget_show (label);
/* The output text widget */
cint.vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
vsb = gtk_vscrollbar_new (cint.vadj);
cint.console = gtk_text_new (NULL, cint.vadj);
gtk_text_set_editable (GTK_TEXT (cint.console), FALSE);
gtk_widget_set_usize (cint.console, TEXT_WIDTH, TEXT_HEIGHT);
table = gtk_table_new (1, 2, FALSE);
gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, TRUE, TRUE, 0);
gtk_table_attach (GTK_TABLE (table), vsb, 1, 2, 0, 1,
0, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
gtk_table_attach (GTK_TABLE (table), cint.console, 0, 1, 0, 1,
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
gtk_container_border_width (GTK_CONTAINER (table), 2);
cint.font_strong = gdk_font_load ("-*-helvetica-bold-r-normal-*-*-120-*-*-*-*-*-*");
cint.font_emphasis = gdk_font_load ("-*-helvetica-medium-o-normal-*-*-100-*-*-*-*-*-*");
cint.font_weak = gdk_font_load ("-*-helvetica-medium-r-normal-*-*-100-*-*-*-*-*-*");
cint.font = gdk_font_load ("-*-*-medium-r-normal-*-*-100-*-*-c-*-*-*");
/* Realize the widget before allowing new text to be inserted */
gtk_widget_realize (cint.console);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_strong, NULL, NULL,
"The GIMP - GNU Image Manipulation Program\n\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_emphasis, NULL, NULL,
"Copyright (C) 1995 Spencer Kimball and Peter Mattis\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"This program is free software; you can redistribute it and/or modify\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"it under the terms of the GNU General Public License as published by\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"the Free Software Foundation; either version 2 of the License, or\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"(at your option) any later version.\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"This program is distributed in the hope that it will be useful,\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"See the GNU General Public License for more details.\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"You should have received a copy of the GNU General Public License\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"along with this program; if not, write to the Free Software\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
"\n\n", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_strong, NULL, NULL,
"Script-Fu Console - ", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font_emphasis, NULL, NULL,
"Interactive Scheme Development\n\n", -1);
gtk_widget_show (vsb);
gtk_widget_show (cint.console);
gtk_widget_show (table);
/* The current command */
label = gtk_label_new ("Current Command");
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), label, FALSE, TRUE, 0);
gtk_widget_show (label);
hbox = gtk_hbox_new ( FALSE, 0 );
gtk_widget_set_usize (hbox, ENTRY_WIDTH, 0);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, FALSE, TRUE, 0);
gtk_widget_show (hbox);
cint.cc = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (hbox), cint.cc,
TRUE, TRUE, 0);
gtk_widget_set_usize (cint.cc, (ENTRY_WIDTH*5)/6, 0);
GTK_WIDGET_SET_FLAGS (cint.cc, GTK_CAN_DEFAULT);
gtk_widget_grab_default (cint.cc);
gtk_signal_connect (GTK_OBJECT (cint.cc), "key_press_event",
(GtkSignalFunc) script_fu_cc_key_function,
NULL);
button = gtk_button_new_with_label ("Browse...");
gtk_widget_set_usize (button, (ENTRY_WIDTH)/6, 0);
gtk_box_pack_start (GTK_BOX (hbox), button,
FALSE, TRUE, 0);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) script_fu_browse_callback,
NULL);
gtk_widget_show (button);
gtk_widget_show (cint.cc);
cint.input_id = gdk_input_add (siod_output_pipe[0],
GDK_INPUT_READ,
script_fu_siod_read,
NULL);
/* Initialize the history */
history = g_list_append (history, NULL);
history_len = 1;
gtk_widget_show (dlg);
gtk_main ();
gdk_input_remove (cint.input_id);
if (dlg)
gtk_widget_destroy (dlg);
gdk_flush ();
}
static void
script_fu_close_callback (GtkWidget *widget,
gpointer data)
{
gtk_main_quit ();
}
void apply_callback( gchar *proc_name,
gchar *scheme_proc_name,
gchar *proc_blurb,
gchar *proc_help,
gchar *proc_author,
gchar *proc_copyright,
gchar *proc_date,
int proc_type,
int nparams,
int nreturn_vals,
GParamDef *params,
GParamDef *return_vals )
{
gint i;
if (proc_name==NULL) return;
gtk_widget_hide(cint.cc);
gtk_entry_set_text( GTK_ENTRY(cint.cc), "(" );
gtk_entry_append_text( GTK_ENTRY(cint.cc), scheme_proc_name );
if ((nparams==0) || (params==NULL)) return;
for (i=0;i<nparams;i++) {
gtk_entry_append_text( GTK_ENTRY(cint.cc), " " );
gtk_entry_append_text( GTK_ENTRY(cint.cc), params[i].name);
}
gtk_entry_append_text( GTK_ENTRY(cint.cc), ")" );
gtk_widget_show(cint.cc);
}
static void
script_fu_browse_callback(GtkWidget *widget,
gpointer data)
{
gtk_quit_add_destroy (1, (GtkObject*) gimp_db_browser (apply_callback));
}
static gint
script_fu_console_scroll_end (gpointer data)
{
/* The Text widget in 1.0.1 doesn't like being scrolled before
* it is size-allocated, so we wait for it
*/
if ((cint.console->allocation.width > 1) &&
(cint.console->allocation.height > 1))
{
cint.vadj->value = cint.vadj->upper - cint.vadj->page_size;
gtk_signal_emit_by_name (GTK_OBJECT (cint.vadj), "changed");
}
else
gtk_idle_add (script_fu_console_scroll_end, NULL);
return FALSE;
}
static void
script_fu_siod_read (gpointer data,
gint id,
GdkInputCondition cond)
{
int count;
static int hack = 0;
if ((count = read (id, read_buffer, BUFSIZE - 1)) != 0)
{
if (!hack) /* this is a stupid hack, but as of 10/27/98
* the script-fu-console will hang on my system without it.
* the real cause of this needs to be tracked down.
* posibly a problem with the text widget, or the
* signal handlers not getting fully initialized or...
* no reports of hangs on other platforms, could just be a
* problem with my system.
*/
{
hack = 1;
return;
}
read_buffer[count] = '\0';
gtk_text_freeze (GTK_TEXT (cint.console));
gtk_text_insert (GTK_TEXT (cint.console), cint.font_weak, NULL, NULL,
read_buffer, -1);
gtk_text_thaw (GTK_TEXT (cint.console));
script_fu_console_scroll_end (NULL);
}
}
static gint
script_fu_cc_is_empty ()
{
char *str;
if ((str = gtk_entry_get_text (GTK_ENTRY (cint.cc))) == NULL)
return TRUE;
while (*str)
{
if (*str != ' ' && *str != '\t' && *str != '\n')
return FALSE;
str ++;
}
return TRUE;
}
static gint
script_fu_cc_key_function (GtkWidget *widget,
GdkEventKey *event,
gpointer data)
{
GList *list;
int direction = 0;
switch (event->keyval)
{
case GDK_Return:
gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
if (script_fu_cc_is_empty ())
return TRUE;
list = g_list_nth (history, (g_list_length (history) - 1));
if (list->data)
g_free (list->data);
list->data = g_strdup (gtk_entry_get_text (GTK_ENTRY (cint.cc)));
gtk_text_freeze (GTK_TEXT (cint.console));
gtk_text_insert (GTK_TEXT (cint.console), cint.font_strong, NULL, NULL, "=> ", -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font, NULL, NULL,
gtk_entry_get_text (GTK_ENTRY (cint.cc)), -1);
gtk_text_insert (GTK_TEXT (cint.console), cint.font, NULL, NULL, "\n\n", -1);
gtk_text_thaw (GTK_TEXT (cint.console));
cint.vadj->value = cint.vadj->upper - cint.vadj->page_size;
gtk_signal_emit_by_name (GTK_OBJECT (cint.vadj), "changed");
gtk_entry_set_text (GTK_ENTRY (cint.cc), "");
gdk_flush ();
repl_c_string ((char *) list->data, 0, 0, 1);
gimp_displays_flush ();
history = g_list_append (history, NULL);
if (history_len == history_max)
{
history = g_list_remove (history, history->data);
if (history->data)
g_free (history->data);
}
else
history_len++;
history_cur = g_list_length (history) - 1;
return TRUE;
break;
case GDK_KP_Up:
case GDK_Up:
gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
direction = -1;
break;
case GDK_KP_Down:
case GDK_Down:
gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
direction = 1;
break;
case GDK_P:
case GDK_p:
if (event->state & GDK_CONTROL_MASK)
{
gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
direction = -1;
}
break;
case GDK_N:
case GDK_n:
if (event->state & GDK_CONTROL_MASK)
{
gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
direction = 1;
}
break;
default:
break;
}
if (direction)
{
/* Make sure we keep track of the current one */
if (history_cur == g_list_length (history) - 1)
{
list = g_list_nth (history, history_cur);
if (list->data)
g_free (list->data);
list->data = g_strdup (gtk_entry_get_text (GTK_ENTRY (cint.cc)));
}
history_cur += direction;
if (history_cur < 0)
history_cur = 0;
if (history_cur >= history_len)
history_cur = history_len - 1;
gtk_entry_set_text (GTK_ENTRY (cint.cc), (char *) (g_list_nth (history, history_cur))->data);
return TRUE;
}
return FALSE;
}
static FILE *
script_fu_open_siod_console ()
{
if (siod_output == stdout)
{
if (pipe (siod_output_pipe))
{
gimp_message ("Unable to open SIOD output pipe");
}
else if ((siod_output = fdopen (siod_output_pipe [1], "w")) == NULL)
{
gimp_message ("Unable to open a stream on the SIOD output pipe");
siod_output = stdout;
}
else
{
siod_verbose_level = 2;
print_welcome ();
}
}
return siod_output;
}
static void
script_fu_close_siod_console ()
{
if (siod_output != stdout)
fclose (siod_output);
close (siod_output_pipe[0]);
close (siod_output_pipe[1]);
}
#endif /* !NATIVE_WIN32 */
void
script_fu_eval_run (char *name,
int nparams,
GParam *params,
int *nreturn_vals,
GParam **return_vals)
{
static GParam values[1];
GStatusType status = STATUS_SUCCESS;
GRunModeType run_mode;
run_mode = params[0].data.d_int32;
switch (run_mode)
{
case RUN_NONINTERACTIVE:
if (repl_c_string (params[1].data.d_string, 0, 0, 1) != 0)
status = STATUS_EXECUTION_ERROR;
break;
case RUN_INTERACTIVE:
case RUN_WITH_LAST_VALS:
status = STATUS_CALLING_ERROR;
gimp_message ("Script-Fu evaluate mode allows only noninteractive invocation");
break;
default:
break;
}
*nreturn_vals = 1;
*return_vals = values;
values[0].type = PARAM_STATUS;
values[0].data.d_status = status;
}