1 /*
2 * py-command-line.c: Simple wrapper around GtkEntry with history support
3 *
4 * Author: Zbigniew Chyla (cyba@gnome.pl)
5 */
6
7 #include <gnumeric-config.h>
8 #include "py-command-line.h"
9 #include "gnm-python.h"
10 #include <gnumeric.h>
11 #include <gutils.h>
12 #include <goffice/goffice.h>
13 #include <goffice/app/module-plugin-defs.h>
14 #include <gsf/gsf-impl-utils.h>
15 #include <glib-object.h>
16 #include <gdk/gdkkeysyms.h>
17 #include <string.h>
18
19 #define MAX_HISTORY_SIZE 100
20
21 struct _GnmPyCommandLine {
22 GtkEntry parent;
23
24 GList *history, *history_tail, *history_cur;
25 gboolean editing;
26 int history_size;
27 };
28
29 typedef struct {
30 GtkEntryClass parent_class;
31
32 void (*entered) (GnmPyCommandLine *cline);
33 } GnmPyCommandLineClass;
34
35 enum {
36 ENTERED_SIGNAL,
37 LAST_SIGNAL
38 };
39
40 static guint signals[LAST_SIGNAL] = { 0 };
41 static GObjectClass *parent_class = NULL;
42
43 static void
gnm_py_command_line_changed(GnmPyCommandLine * cline)44 gnm_py_command_line_changed (GnmPyCommandLine *cline)
45 {
46 cline->editing = TRUE;
47 }
48
49 static gint
gnm_py_command_line_keypress(GnmPyCommandLine * cline,GdkEventKey * event,gpointer user_data)50 gnm_py_command_line_keypress (GnmPyCommandLine *cline, GdkEventKey *event, gpointer user_data)
51 {
52 switch (event->keyval) {
53 case GDK_KEY_Return: {
54 const char *text;
55
56 text = gtk_entry_get_text (GTK_ENTRY (cline));
57 if (cline->history_tail == NULL) {
58 cline->history = g_list_append (NULL, g_strdup (text));
59 cline->history_tail = cline->history;
60 } else if (text[0] != '\0' && strcmp (text, cline->history_tail->data) != 0) {
61 cline->history_tail =
62 g_list_append (cline->history_tail,
63 g_strdup (text))->next;
64 }
65 if (cline->history_size == MAX_HISTORY_SIZE) {
66 g_free (cline->history->data);
67 cline->history = g_list_delete_link (cline->history, cline->history);
68 } else {
69 cline->history_size++;
70 }
71 g_signal_emit (cline, signals[ENTERED_SIGNAL], 0);
72 gtk_entry_set_text (GTK_ENTRY (cline), "");
73 cline->editing = TRUE;
74 break;
75 }
76 case GDK_KEY_Up:
77 if (cline->editing) {
78 if (cline->history_tail != NULL) {
79 cline->history_cur = cline->history_tail;
80 gtk_entry_set_text (GTK_ENTRY (cline), cline->history_cur->data);
81 gtk_editable_set_position (
82 GTK_EDITABLE (cline), strlen (cline->history_cur->data));
83 cline->editing = FALSE;
84 }
85 } else {
86 if (cline->history_cur->prev != NULL) {
87 cline->history_cur = cline->history_cur->prev;
88 gtk_entry_set_text (GTK_ENTRY (cline), cline->history_cur->data);
89 gtk_editable_set_position (
90 GTK_EDITABLE (cline), strlen (cline->history_cur->data));
91 cline->editing = FALSE;
92 }
93 }
94 break;
95 case GDK_KEY_Down:
96 if (!cline->editing) {
97 if (cline->history_cur->next != NULL) {
98 cline->history_cur = cline->history_cur->next;
99 gtk_entry_set_text (GTK_ENTRY (cline), cline->history_cur->data);
100 gtk_editable_set_position (
101 GTK_EDITABLE (cline), strlen (cline->history_cur->data));
102 cline->editing = FALSE;
103 } else {
104 gtk_entry_set_text (GTK_ENTRY (cline), "");
105 cline->editing = TRUE;
106 }
107 }
108 break;
109 default:
110 return FALSE;
111 }
112
113 g_signal_stop_emission_by_name (cline, "key_press_event");
114 return TRUE;
115 }
116
117 static void
gnm_py_command_line_init(GnmPyCommandLine * cline)118 gnm_py_command_line_init (GnmPyCommandLine *cline)
119 {
120 g_signal_connect (
121 cline, "key_press_event",
122 G_CALLBACK (gnm_py_command_line_keypress), NULL);
123 g_signal_connect (
124 cline, "changed",
125 G_CALLBACK (gnm_py_command_line_changed), NULL);
126 cline->history = cline->history_tail = NULL;
127 cline->history_cur = NULL;
128 cline->editing = TRUE;
129 cline->history_size = 0;
130 }
131
132 static void
gnm_py_command_line_finalize(GObject * obj)133 gnm_py_command_line_finalize (GObject *obj)
134 {
135 GnmPyCommandLine *cline = GNM_PY_COMMAND_LINE (obj);
136
137 g_list_free_full (cline->history, g_free);
138 cline->history = NULL;
139
140 parent_class->finalize (obj);
141 }
142
143 static void
gnm_py_command_line_class_init(GObjectClass * gobject_class)144 gnm_py_command_line_class_init (GObjectClass *gobject_class)
145 {
146 parent_class = g_type_class_peek_parent (gobject_class);
147
148 gobject_class->finalize = gnm_py_command_line_finalize;
149
150 signals[ENTERED_SIGNAL] =
151 g_signal_new (
152 "entered",
153 G_TYPE_FROM_CLASS (gobject_class),
154 G_SIGNAL_RUN_FIRST,
155 G_STRUCT_OFFSET (GnmPyCommandLineClass, entered),
156 NULL, NULL,
157 g_cclosure_marshal_VOID__VOID,
158 G_TYPE_NONE, 0);
159 }
160
161 GtkWidget *
gnm_py_command_line_new(void)162 gnm_py_command_line_new (void)
163 {
164 return g_object_new (GNM_PY_COMMAND_LINE_TYPE, NULL);
165 }
166
167 GSF_DYNAMIC_CLASS (GnmPyCommandLine, gnm_py_command_line,
168 gnm_py_command_line_class_init, gnm_py_command_line_init,
169 GTK_TYPE_ENTRY)
170
171