don't disable use of deprecated APIs unless asked to do so. add Meta case
[vte.git] / src / reflect.c
1 /*
2  * Copyright (C) 2003 Red Hat, Inc.
3  *
4  * This is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Library General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #ident "$Id$"
20 #include "../config.h"
21 #include <sys/types.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <gtk/gtk.h>
27 #include <atk/atk.h>
28 #ifdef USE_VTE
29 #include "vte.h"
30 #endif
31 #ifdef USE_ZVT
32 #ifdef HAVE_ZVT
33 #include <libzvt/libzvt.h>
34 #endif
35 #endif
36
37 static GArray *contents = NULL;
38
39 #ifdef USE_TEXT_VIEW
40 /*
41  * Implementation for a TextView widget.
42  */
43 static void
44 terminal_init_text_view(GtkWidget **widget)
45 {
46         *widget = gtk_text_view_new();
47         gtk_text_view_set_editable(GTK_TEXT_VIEW(*widget), TRUE);
48 }
49 static void
50 terminal_shell_text_view(GtkWidget *widget)
51 {
52         /* no-op */
53 }
54 static GtkAdjustment *
55 terminal_adjustment_text_view(GtkWidget *terminal)
56 {
57         return (GTK_TEXT_VIEW(terminal))->vadjustment;
58 }
59 #endif
60 #ifdef USE_VTE
61 /*
62  * Implementation for a VteTerminal widget.
63  */
64 static void
65 terminal_init_vte(GtkWidget **terminal)
66 {
67         *terminal = vte_terminal_new();
68         g_signal_connect(G_OBJECT(*terminal), "eof",
69                          G_CALLBACK(gtk_main_quit), NULL);
70         g_signal_connect(G_OBJECT(*terminal), "child-exited",
71                          G_CALLBACK(gtk_main_quit), NULL);
72 }
73 static void
74 terminal_shell_vte(GtkWidget *terminal)
75 {
76         vte_terminal_fork_command(VTE_TERMINAL(terminal),
77                                   getenv("SHELL") ? getenv("SHELL") : "/bin/sh",
78                                   NULL,
79                                   NULL,
80                                   g_get_home_dir() ? g_get_home_dir() : NULL,
81                                   FALSE,
82                                   FALSE,
83                                   FALSE);
84 }
85 static GtkAdjustment *
86 terminal_adjustment_vte(GtkWidget *terminal)
87 {
88         return (VTE_TERMINAL(terminal))->adjustment;
89 }
90 #endif
91 #ifdef USE_ZVT
92 #ifdef HAVE_ZVT
93 /*
94  * Implementation for a ZvtTerm widget.
95  */
96 static void
97 terminal_hint_zvt(GtkWidget *widget, gpointer data)
98 {
99         ZvtTerm *terminal;
100         GtkStyle *style;
101         GdkGeometry hints;
102         GtkWidget *toplevel;
103
104         terminal = ZVT_TERM(widget);
105
106         toplevel = gtk_widget_get_toplevel(widget);
107         g_assert(toplevel != NULL);
108
109         gtk_widget_ensure_style(widget);
110         style = widget->style;
111         hints.base_width = style->xthickness * 2 + 2;
112         hints.base_height = style->ythickness * 2;
113
114         hints.width_inc = terminal->charwidth;
115         hints.height_inc = terminal->charheight;
116         hints.min_width = hints.base_width + hints.width_inc;
117         hints.min_height = hints.base_height + hints.height_inc;
118
119         gtk_window_set_geometry_hints(GTK_WINDOW(toplevel),
120                                       widget,
121                                       &hints,
122                                       GDK_HINT_RESIZE_INC |
123                                       GDK_HINT_MIN_SIZE |
124                                       GDK_HINT_BASE_SIZE);
125         gtk_widget_queue_resize(widget);
126 }
127 static void
128 terminal_init_zvt(GtkWidget **terminal)
129 {
130         *terminal = zvt_term_new();
131         g_signal_connect_after(G_OBJECT(*terminal), "realize",
132                                G_CALLBACK(terminal_hint_zvt), NULL);
133 }
134 static void
135 terminal_shell_zvt(GtkWidget *terminal)
136 {
137         const char *shell;
138         shell = getenv("SHELL") ? getenv("SHELL") : "/bin/sh";
139         g_signal_connect(G_OBJECT(terminal), "child-died",
140                          G_CALLBACK(gtk_main_quit), NULL);
141         if (zvt_term_forkpty(ZVT_TERM(terminal), 0) == 0) {
142                 execlp(shell, shell, NULL);
143                 g_assert_not_reached();
144         }
145 }
146 static GtkAdjustment *
147 terminal_adjustment_zvt(GtkWidget *terminal)
148 {
149         return (ZVT_TERM(terminal))->adjustment;
150 }
151 #else
152 /*
153  * Implementation for broken setups.
154  */
155 static void
156 terminal_init_broken(GtkWidget **terminal)
157 {
158         g_error("libzvt not found at compile-time");
159         _exit(1);
160 }
161 static void
162 terminal_shell_broken(GtkWidget *terminal)
163 {
164         g_error("libzvt not found at compile-time");
165         _exit(1);
166 }
167 static GtkAdjustment *
168 terminal_adjustment_broken(GtkWidget *terminal)
169 {
170         g_error("libzvt not found at compile-time");
171         _exit(1);
172 }
173 #endif
174 #endif
175
176 /*
177  * Update the contents of the widget with the data from our contents array.
178  */
179 static void
180 update_contents(AtkObject *obj, GtkWidget *widget)
181 {
182         int caret, i;
183         GString *s;
184
185         caret = atk_text_get_caret_offset(ATK_TEXT(obj));
186         s = g_string_new("");
187         for (i = 0; i < contents->len; i++) {
188                 if (i == caret) {
189                         s = g_string_append(s, "[CARET]");
190                 }
191                 s = g_string_append_unichar(s,
192                                             g_array_index(contents,
193                                                           gunichar,
194                                                           i));
195         }
196         if (i == caret) {
197                 s = g_string_append(s, "[CARET]");
198         }
199         if (GTK_IS_LABEL(widget)) {
200                 gtk_label_set_text(GTK_LABEL(widget), s->str);
201                 gtk_label_set_selectable(GTK_LABEL(widget),
202                                          atk_text_get_n_selections(ATK_TEXT(obj)) > 0);
203                 if (gtk_label_get_selectable(GTK_LABEL(widget))) {
204                         int selection_start, selection_end;
205                         atk_text_get_selection(ATK_TEXT(obj), 0,
206                                                &selection_start,
207                                                &selection_end);
208                         gtk_label_select_region(GTK_LABEL(widget),
209                                                 selection_start, selection_end);
210                 }
211         }
212         g_string_free(s, TRUE);
213 }
214
215 /* Handle inserted text by inserting the text into our gunichar array. */
216 static void
217 text_changed_insert(AtkObject *obj, gint offset, gint length, gpointer data)
218 {
219         char *inserted, *p;
220         gunichar c;
221         int i;
222
223         inserted = atk_text_get_text(ATK_TEXT(obj), offset, offset + length);
224
225         if (!g_utf8_validate(inserted, -1, NULL)) {
226                 g_free(inserted);
227                 g_error("UTF-8 validation error");
228                 return;
229         }
230
231         p = inserted;
232         i = 0;
233         while (i < length) {
234                 c = g_utf8_get_char(p);
235                 if (offset + i >= contents->len) {
236                         g_array_append_val(contents, c);
237                 } else {
238                         g_array_insert_val(contents, offset + i, c);
239                 }
240                 i++;
241                 p = g_utf8_next_char(p);
242         }
243
244 #ifdef VTE_DEBUG
245         if ((getenv("REFLECT_VERBOSE") != NULL) &&
246             (atol(getenv("REFLECT_VERBOSE")) != 0)) {
247                 fprintf(stderr, "Inserted %d chars ('%.*s') at %d,",
248                         length, (int)(p - inserted), inserted, offset);
249                 fprintf(stderr, " buffer contains %d characters.\n",
250                         contents->len);
251         }
252 #endif
253
254         g_free(inserted);
255
256         update_contents(obj, GTK_WIDGET(data));
257 }
258
259 /* Handle deleted text by removing the text from our gunichar array. */
260 static void
261 text_changed_delete(AtkObject *obj, gint offset, gint length, gpointer data)
262 {
263         int i;
264         for (i = offset + length - 1; i >= offset; i--) {
265                 if (i > contents->len - 1) {
266                         g_warning("Invalid character %d was deleted.\n", i);
267                 }
268                 g_array_remove_index(contents, i);
269         }
270 #ifdef VTE_DEBUG
271         if ((getenv("REFLECT_VERBOSE") != NULL) &&
272             (atol(getenv("REFLECT_VERBOSE")) != 0)) {
273                 fprintf(stderr, "Deleted %d chars at %d.\n", length, offset);
274         }
275 #endif
276         update_contents(obj, GTK_WIDGET(data));
277 }
278
279 static void
280 text_caret_moved(AtkObject *obj, gint offset, gpointer data)
281 {
282         update_contents(obj, GTK_WIDGET(data));
283 }
284
285 static void
286 text_selection_changed(AtkObject *obj, gpointer data)
287 {
288         update_contents(obj, GTK_WIDGET(data));
289 }
290
291 /* Wrapper versions. */
292 static void
293 terminal_init(GtkWidget **terminal)
294 {
295         *terminal = NULL;
296 #ifdef USE_ZVT
297 #ifdef HAVE_ZVT
298         terminal_init_zvt(terminal);
299         return;
300 #else
301         terminal_init_broken(terminal);
302         return;
303 #endif
304 #endif
305 #ifdef USE_TEXT_VIEW
306         terminal_init_text_view(terminal);
307         return;
308 #endif
309 #ifdef USE_VTE
310         terminal_init_vte(terminal);
311         return;
312 #endif
313         g_assert_not_reached();
314 }
315 static void
316 terminal_shell(GtkWidget *terminal)
317 {
318 #ifdef USE_ZVT
319 #ifdef HAVE_ZVT
320         terminal_shell_zvt(terminal);
321         return;
322 #else
323         terminal_shell_broken(terminal);
324         return;
325 #endif
326 #endif
327 #ifdef USE_TEXT_VIEW
328         terminal_shell_text_view(terminal);
329         return;
330 #endif
331 #ifdef USE_VTE
332         terminal_shell_vte(terminal);
333         return;
334 #endif
335         g_assert_not_reached();
336 }
337 static GtkAdjustment *
338 terminal_adjustment(GtkWidget *terminal)
339 {
340 #ifdef USE_ZVT
341 #ifdef HAVE_ZVT
342         return terminal_adjustment_zvt(terminal);
343 #else
344         return terminal_adjustment_broken(terminal);
345 #endif
346 #endif
347 #ifdef USE_TEXT_VIEW
348         return terminal_adjustment_text_view(terminal);
349 #endif
350 #ifdef USE_VTE
351         return terminal_adjustment_vte(terminal);
352 #endif
353         g_assert_not_reached();
354 }
355
356 int
357 main(int argc, char **argv)
358 {
359         GtkWidget *label, *terminal, *tophalf, *pane, *window, *scrollbar, *sw;
360         AtkObject *obj;
361         char *text, *p;
362         gunichar c;
363         gint count;
364
365         gtk_init(&argc, &argv);
366
367         contents = g_array_new(TRUE, FALSE, sizeof(gunichar));
368
369         terminal_init(&terminal);
370
371 #ifdef USE_TEXT_VIEW
372         tophalf = gtk_scrolled_window_new(NULL, terminal_adjustment(terminal));
373         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tophalf),
374                                        GTK_POLICY_AUTOMATIC,
375                                        GTK_POLICY_AUTOMATIC);
376         scrollbar = NULL;
377         gtk_container_add(GTK_CONTAINER(tophalf), terminal);
378 #else
379         tophalf = gtk_hbox_new(FALSE, 0);
380
381         gtk_box_pack_start(GTK_BOX(tophalf), terminal, TRUE, TRUE, 0);
382         gtk_widget_show(terminal);
383
384         scrollbar = gtk_vscrollbar_new(terminal_adjustment(terminal));
385         gtk_box_pack_start(GTK_BOX(tophalf), scrollbar, FALSE, TRUE, 0);
386         gtk_widget_show(scrollbar);
387 #endif
388         gtk_widget_show(terminal);
389
390         label = gtk_label_new("");
391         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
392         gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
393
394         sw = gtk_scrolled_window_new(NULL, NULL);
395         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), label);
396         gtk_widget_show(label);
397
398         pane = gtk_vpaned_new();
399         gtk_paned_pack1(GTK_PANED(pane), tophalf, TRUE, FALSE);
400         gtk_paned_pack2(GTK_PANED(pane), sw, TRUE, FALSE);
401         gtk_widget_show(tophalf);
402         gtk_widget_show(sw);
403
404         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
405         g_signal_connect(G_OBJECT(window), "delete_event",
406                          G_CALLBACK(gtk_main_quit), NULL);
407         gtk_container_add(GTK_CONTAINER(window), pane);
408         gtk_widget_show(pane);
409
410         obj = gtk_widget_get_accessible(terminal);
411         g_assert(obj != NULL);
412         g_signal_connect(G_OBJECT(obj), "text-changed::insert",
413                          G_CALLBACK(text_changed_insert), label);
414         g_signal_connect(G_OBJECT(obj), "text-changed::delete",
415                          G_CALLBACK(text_changed_delete), label);
416         g_signal_connect(G_OBJECT(obj), "text-caret-moved",
417                          G_CALLBACK(text_caret_moved), label);
418         g_signal_connect(G_OBJECT(obj), "text-selection-changed",
419                          G_CALLBACK(text_selection_changed), label);
420
421         count = atk_text_get_character_count(ATK_TEXT(obj));
422         if (count > 0) {
423                 text = atk_text_get_text(ATK_TEXT(obj), 0, count);
424                 if (text != NULL) {
425                         for (p = text;
426                              contents->len < count;
427                              p = g_utf8_next_char(p)) {
428                                 c = g_utf8_get_char(p);
429                                 g_array_append_val(contents, c);
430                         }
431                         g_free(text);
432                 }
433         }
434         terminal_shell(terminal);
435
436         gtk_window_set_default_size(GTK_WINDOW(window), 600, 450);
437         gtk_widget_show(window);
438
439         update_contents(obj, terminal);
440
441         gtk_main();
442
443         g_array_free(contents, TRUE);
444         contents = NULL;
445
446         return 0;
447 }