1 /*
2 
3     Minimum Profit - A Text Editor
4     GTK driver.
5 
6     ttcdt <dev@triptico.com> et al.
7 
8     This software is released into the public domain.
9     NO WARRANTY. See file LICENSE for details.
10 
11 */
12 
13 #include "config.h"
14 
15 #ifdef CONFOPT_GTK
16 
17 #include <stdio.h>
18 #include <wchar.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26 
27 #include "mpdm.h"
28 #include "mpsl.h"
29 
30 #include "mp.h"
31 
32 #include "mp.xpm"
33 
34 /** data **/
35 
36 /* global data */
37 
38 static GtkWidget *window = NULL;
39 static GtkWidget *file_tabs = NULL;
40 static GtkWidget *area = NULL;
41 static GtkWidget *scrollbar = NULL;
42 static GtkWidget *status = NULL;
43 static GtkWidget *menu_bar = NULL;
44 
45 /* font information */
46 static int font_width = 0;
47 static int font_height = 0;
48 static PangoFontDescription *font = NULL;
49 
50 /* the attributes */
51 #define MAX_COLORS 100
52 static GdkColor inks[MAX_COLORS];
53 static GdkColor papers[MAX_COLORS];
54 static int underlines[MAX_COLORS];
55 
56 #if CONFOPT_GTK == 2
57 static GdkColor normal_paper;
58 #endif
59 
60 #if CONFOPT_GTK == 3
61 static GdkRGBA normal_paper;
62 #endif
63 
64 /* global modal status */
65 /* code for the 'normal' attribute */
66 static int normal_attr = 0;
67 
68 /* mouse down flag */
69 static int mouse_down = 0;
70 
71 /* maximize wanted? */
72 static int maximize = 0;
73 
74 /* window state variables */
75 int ls_x, ls_y, ls_w, ls_h;
76 
77 
78 /** code **/
79 
80 /** support functions **/
81 
82 #define L(m) (translate_mbs(m))
83 #define LL(m) (m)
84 
translate_mbs(char * from)85 static char *translate_mbs(char *from)
86 {
87     mpdm_t h, v, w;
88 
89     if ((h = mpdm_get_wcs(mpdm_root(), L"__I18N_MBS__")) == NULL)
90         h = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"__I18N_MBS__");
91 
92     v = mpdm_gettext(MPDM_MBS(from));
93 
94     if ((w = mpdm_get(h, v)) == NULL)
95         w = mpdm_set(h, MPDM_2MBS(mpdm_string(v)), v);
96 
97     return (char *)w->data;
98 }
99 
100 
wcs_to_utf8(const wchar_t * wptr)101 static char *wcs_to_utf8(const wchar_t * wptr)
102 /* converts a wcs to utf-8 */
103 {
104     char *ptr;
105     gsize i, o;
106 
107     i = wcslen(wptr);
108 
109     /* do the conversion */
110     ptr = g_convert((const gchar *) wptr, (i + 1) * sizeof(wchar_t),
111                     "UTF-8", "WCHAR_T", NULL, &o, NULL);
112 
113     return ptr;
114 }
115 
116 
v_to_utf8(mpdm_t v)117 static char *v_to_utf8(mpdm_t v)
118 {
119     char *ptr = NULL;
120 
121     if (v != NULL) {
122         mpdm_ref(v);
123         ptr = wcs_to_utf8(mpdm_string(v));
124         mpdm_unref(v);
125     }
126 
127     return ptr;
128 }
129 
130 
update_window_size(void)131 static void update_window_size(void)
132 /* updates the viewport size in characters */
133 {
134     PangoLayout *pa;
135     int tx, ty;
136     mpdm_t v;
137 
138     /* get font metrics */
139     pa = gtk_widget_create_pango_layout(area, "m");
140     pango_layout_set_font_description(pa, font);
141     pango_layout_get_pixel_size(pa, &font_width, &font_height);
142     g_object_unref(pa);
143 
144     /* calculate the size in chars */
145 #if CONFOPT_GTK == 2
146     tx = (area->allocation.width / font_width);
147     ty = (area->allocation.height / font_height) + 1;
148 #endif
149 
150 #if CONFOPT_GTK == 3
151     tx = (gtk_widget_get_allocated_width(area) / font_width);
152     ty = (gtk_widget_get_allocated_height(area) / font_height) + 1;
153 #endif
154 
155     /* store the 'window' size */
156     v = mpdm_get_wcs(MP, L"window");
157     mpdm_set_wcs(v, MPDM_I(tx), L"tx");
158     mpdm_set_wcs(v, MPDM_I(ty), L"ty");
159 
160     /* rebuild the pixmap for the double buffer */
161 }
162 
163 
build_fonts(void)164 static void build_fonts(void)
165 /* builds the fonts */
166 {
167     char tmp[128];
168     mpdm_t c;
169     mpdm_t w = NULL;
170     int font_size           = 12;
171     const char *font_face   = "Mono";
172     double font_weight      = 0.0;
173 
174     if (font != NULL)
175         pango_font_description_free(font);
176 
177     /* get current configuration */
178     if ((c = mpdm_get_wcs(MP, L"config")) != NULL) {
179         mpdm_t v;
180 
181         if ((v = mpdm_get_wcs(c, L"font_size")) != NULL)
182             font_size = mpdm_ival(v);
183         else
184             mpdm_set_wcs(c, MPDM_I(font_size), L"font_size");
185 
186         if ((v = mpdm_get_wcs(c, L"font_weight")) != NULL)
187             font_weight = mpdm_rval(v) * 1000.0;
188         else
189             mpdm_set_wcs(c, MPDM_R(font_weight / 1000.0), L"font_weight");
190 
191         if ((v = mpdm_get_wcs(c, L"font_face")) != NULL) {
192             w = mpdm_ref(MPDM_2MBS(v->data));
193             font_face = w->data;
194         }
195         else
196             mpdm_set_wcs(c, MPDM_MBS(font_face), L"font_face");
197     }
198 
199     snprintf(tmp, sizeof(tmp) - 1, "%s Thin %d", font_face, font_size);
200     tmp[sizeof(tmp) - 1] = '\0';
201 
202     font = pango_font_description_from_string(tmp);
203 
204     if (font_weight > 0.0)
205         pango_font_description_set_weight(font, font_weight);
206 
207     update_window_size();
208 
209     mpdm_unref(w);
210 }
211 
212 
build_color(GdkColor * gdkcolor,int rgb)213 static void build_color(GdkColor * gdkcolor, int rgb)
214 /* builds a color */
215 {
216     gdkcolor->pixel = 0;
217     gdkcolor->blue  = (rgb & 0x000000ff) << 8;
218     gdkcolor->green = (rgb & 0x0000ff00);
219     gdkcolor->red   = (rgb & 0x00ff0000) >> 8;
220 
221 #if CONFOPT_GTK == 2
222     gdk_colormap_alloc_color(gdk_colormap_get_system(), gdkcolor, FALSE, TRUE);
223 #endif
224 }
225 
226 
build_colors(void)227 static void build_colors(void)
228 /* builds the colors */
229 {
230     mpdm_t colors;
231     mpdm_t v, i;
232     int n, c;
233 
234     /* gets the color definitions and attribute names */
235     colors = mpdm_get_wcs(MP, L"colors");
236 
237     /* loop the colors */
238     n = c = 0;
239     while (mpdm_iterator(colors, &c, &v, &i)) {
240         int ink, paper;
241 
242         mpdm_t w = mpdm_get_wcs(v, L"gui");
243 
244         /* store the 'normal' attribute */
245         if (wcscmp(mpdm_string(i), L"normal") == 0)
246             normal_attr = n;
247 
248         /* store the attr */
249         mpdm_set_wcs(v, MPDM_I(n), L"attr");
250 
251         ink   = mpdm_ival(mpdm_get_i(w, 0));
252         paper = mpdm_ival(mpdm_get_i(w, 1));
253 
254         /* flags */
255         w = mpdm_get_wcs(v, L"flags");
256         underlines[n] = mpdm_seek_wcs(w, L"underline", 1) != -1 ? 1 : 0;
257 
258         if (mpdm_seek_wcs(w, L"reverse", 1) != -1) {
259             int t;
260 
261             t = ink;
262             ink = paper;
263             paper = t;
264         }
265 
266         build_color(&inks[n],   ink);
267         build_color(&papers[n], paper);
268 
269         n++;
270     }
271 
272 #if CONFOPT_GTK == 2
273     normal_paper = papers[normal_attr];
274 #endif
275 
276 #if CONFOPT_GTK == 3
277     normal_paper.red   = papers[normal_attr].red   / 65535.0;
278     normal_paper.green = papers[normal_attr].green / 65535.0;
279     normal_paper.blue  = papers[normal_attr].blue  / 65535.0;
280     normal_paper.alpha = 1.0;
281 #endif
282 
283 }
284 
285 
286 /** menu functions **/
287 
menu_item_callback(mpdm_t action)288 static void menu_item_callback(mpdm_t action)
289 /* menu click callback */
290 {
291     mp_process_action(action);
292     gtk_widget_queue_draw(GTK_WIDGET(area));
293 
294 //    gtk_widget_hide(menu_bar);
295 
296     if (mp_exit_requested)
297         gtk_main_quit();
298 }
299 
300 
build_submenu(GtkWidget * menu,mpdm_t labels)301 static void build_submenu(GtkWidget * menu, mpdm_t labels)
302 /* build a submenu */
303 {
304     int n;
305     GtkWidget *menu_item;
306 
307     mpdm_ref(labels);
308 
309     for (n = 0; n < mpdm_size(labels); n++) {
310         /* get the action */
311         mpdm_t v = mpdm_get_i(labels, n);
312 
313         /* if the action is a separator... */
314         if (*((wchar_t *) v->data) == L'-')
315             menu_item = gtk_separator_menu_item_new();
316         else {
317             char *ptr;
318 
319             ptr = v_to_utf8(mp_menu_label(v));
320             menu_item = gtk_menu_item_new_with_label(ptr);
321             g_free(ptr);
322         }
323 
324 #if CONFOPT_GTK == 2
325         gtk_menu_append(GTK_MENU(menu), menu_item);
326 #endif
327 #if CONFOPT_GTK == 3
328         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
329 #endif
330 
331         g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
332                                  G_CALLBACK(menu_item_callback), v);
333         gtk_widget_show(menu_item);
334     }
335 
336     mpdm_unref(labels);
337 }
338 
339 
build_menu(void)340 static void build_menu(void)
341 /* builds the menu */
342 {
343     int n;
344     mpdm_t m;
345 
346     m = mpdm_get_wcs(MP, L"menu");
347 
348     /* create a new menu */
349     menu_bar = gtk_menu_bar_new();
350 
351     for (n = 0; n < mpdm_size(m); n++) {
352         char *ptr;
353         mpdm_t mi;
354         mpdm_t v;
355         GtkWidget *menu;
356         GtkWidget *menu_item;
357         int i;
358 
359         /* get the label and the items */
360         mi = mpdm_get_i(m, n);
361         v = mpdm_get_i(mi, 0);
362 
363         ptr = v_to_utf8(mpdm_gettext(v));
364 
365         /* change the & by _ for the mnemonic */
366         for (i = 0; ptr[i]; i++)
367             if (ptr[i] == '&')
368                 ptr[i] = '_';
369 
370         /* add the menu and the label */
371         menu = gtk_menu_new();
372         menu_item = gtk_menu_item_new_with_mnemonic(ptr);
373         g_free(ptr);
374 
375         gtk_widget_show(menu_item);
376         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
377 
378 #if CONFOPT_GTK == 2
379         gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_item);
380 #endif
381 #if CONFOPT_GTK == 3
382         gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_item);
383 #endif
384 
385         /* now loop the items */
386         build_submenu(menu, mpdm_get_i(mi, 1));
387     }
388 }
389 
390 
391 /** main area drawing functions **/
392 
switch_page(GtkNotebook * nb,gpointer * p,gint pg_num,gpointer d)393 static void switch_page(GtkNotebook *nb, gpointer *p, gint pg_num, gpointer d)
394 /* 'switch_page' handler (filetabs) */
395 {
396     /* sets the active one */
397     mpdm_set_wcs(MP, MPDM_I(pg_num), L"active_i");
398 
399     gtk_widget_grab_focus(area);
400     gtk_widget_queue_draw(GTK_WIDGET(area));
401 }
402 
403 
draw_filetabs(void)404 static void draw_filetabs(void)
405 /* draws the filetabs */
406 {
407     static mpdm_t prev = NULL;
408     mpdm_t names;
409     int n;
410 
411     names = mpdm_ref(mp_get_doc_names());
412 
413     /* disconnect redraw signal to avoid infinite loops */
414     g_signal_handlers_disconnect_by_func(G_OBJECT(file_tabs),
415                                          G_CALLBACK(switch_page), NULL);
416 
417     /* is the list different from the previous one? */
418     if (mpdm_cmp(names, prev) != 0) {
419         /* delete the current tabs */
420         for (n = 0; n < mpdm_size(prev); n++)
421             gtk_notebook_remove_page(GTK_NOTEBOOK(file_tabs), 0);
422 
423         /* create the new ones */
424         for (n = 0; n < mpdm_size(names); n++) {
425             GtkWidget *p;
426             GtkWidget *f;
427             char *ptr;
428             mpdm_t v = mpdm_get_i(names, n);
429 
430             if ((ptr = v_to_utf8(v)) != NULL) {
431                 p = gtk_label_new(ptr);
432                 gtk_widget_show(p);
433 
434                 f = gtk_frame_new(NULL);
435                 gtk_widget_show(f);
436 
437                 gtk_notebook_append_page(GTK_NOTEBOOK(file_tabs), f, p);
438 
439                 g_free(ptr);
440             }
441         }
442 
443         /* store for the next time */
444         mpdm_store(&prev, names);
445     }
446 
447     mpdm_unref(names);
448 
449     /* set the active one */
450     gtk_notebook_set_current_page(GTK_NOTEBOOK(file_tabs),
451                           mpdm_ival(mpdm_get_wcs(MP, L"active_i")));
452 
453     /* reconnect signal */
454     g_signal_connect(G_OBJECT(file_tabs), "switch_page",
455                      G_CALLBACK(switch_page), NULL);
456 
457     gtk_widget_grab_focus(area);
458 }
459 
460 
draw_status(void)461 static void draw_status(void)
462 /* draws the status line */
463 {
464     char *ptr;
465 
466     ptr = v_to_utf8(mp_build_status_line());
467     gtk_label_set_text(GTK_LABEL(status), ptr);
468     g_free(ptr);
469 }
470 
471 
scroll_event(GtkWidget * widget,GdkEventScroll * event)472 static gint scroll_event(GtkWidget *widget, GdkEventScroll *event)
473 /* 'scroll_event' handler (mouse wheel) */
474 {
475     double dx, dy;
476     wchar_t *ptr = NULL;
477 
478     switch (event->direction) {
479     case GDK_SCROLL_UP:
480         ptr = L"mouse-wheel-up";
481         break;
482     case GDK_SCROLL_DOWN:
483         ptr = L"mouse-wheel-down";
484         break;
485     case GDK_SCROLL_LEFT:
486         ptr = L"mouse-wheel-left";
487         break;
488     case GDK_SCROLL_RIGHT:
489         ptr = L"mouse-wheel-right";
490         break;
491 
492 #if CONFOPT_GTK == 3
493     case GDK_SCROLL_SMOOTH:
494         gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy);
495 
496         if (dy > 0)
497             ptr = L"mouse-wheel-down";
498         else
499         if (dy < 0)
500             ptr = L"mouse-wheel-up";
501 
502         break;
503 #endif
504 
505     default:
506         ptr = NULL;
507         break;
508     }
509 
510     if (ptr != NULL) {
511         mp_process_event(MPDM_S(ptr));
512         gtk_widget_queue_draw(GTK_WIDGET(area));
513     }
514 
515     return 0;
516 }
517 
518 
value_changed(GtkAdjustment * adj,gpointer * data)519 static void value_changed(GtkAdjustment * adj, gpointer * data)
520 /* 'value_changed' handler (scrollbar) */
521 {
522     int i = (int) gtk_adjustment_get_value(adj);
523     mpdm_t doc;
524     mpdm_t txt;
525     int y;
526 
527     /* get current y position */
528     doc = mp_active();
529     txt = mpdm_get_wcs(doc, L"txt");
530     y = mpdm_ival(mpdm_get_wcs(txt, L"y"));
531 
532     /* if it's different, set and redraw */
533     if (y != i) {
534         mp_set_y(doc, i);
535         mpdm_set_wcs(txt, MPDM_I(i), L"vy");
536         gtk_widget_queue_draw(GTK_WIDGET(area));
537     }
538 }
539 
540 
draw_scrollbar(void)541 static void draw_scrollbar(void)
542 /* updates the scrollbar */
543 {
544     GtkAdjustment *adjustment;
545     mpdm_t d;
546     mpdm_t v;
547     int pos, size, max;
548 
549     d = mp_active();
550 
551     /* get the coordinates */
552     v = mpdm_get_wcs(d, L"txt");
553     pos = mpdm_ival(mpdm_get_wcs(v, L"vy"));
554     max = mpdm_size(mpdm_get_wcs(v, L"lines"));
555 
556     v = mpdm_get_wcs(MP, L"window");
557     size = mpdm_ival(mpdm_get_wcs(v, L"ty"));
558 
559     adjustment = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
560 
561     /* disconnect to avoid infinite loops */
562     g_signal_handlers_disconnect_by_func(G_OBJECT(adjustment),
563                                          G_CALLBACK(value_changed), NULL);
564 
565     gtk_adjustment_set_step_increment(adjustment, (gdouble) 1);
566     gtk_adjustment_set_upper(adjustment, (gdouble) max);
567     gtk_adjustment_set_page_size(adjustment, (gdouble) size);
568     gtk_adjustment_set_page_increment(adjustment, (gdouble) size);
569     gtk_adjustment_set_value(adjustment, (gdouble) pos);
570 
571     gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment);
572 
573     /* reattach again */
574     g_signal_connect(G_OBJECT(gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
575                      "value_changed", G_CALLBACK(value_changed), NULL);
576 }
577 
578 
gtk_drv_render(mpdm_t doc,int optimize)579 static void gtk_drv_render(mpdm_t doc, int optimize)
580 /* GTK document draw function */
581 {
582     GdkRectangle gr;
583     cairo_t *cr;
584     mpdm_t d = NULL;
585     int n, m;
586 
587     if (maximize)
588         gtk_window_maximize(GTK_WINDOW(window));
589 
590     /* no gc? create it */
591     if (font == NULL)
592         build_fonts();
593 
594     d = mp_draw(doc, optimize);
595 
596     mpdm_ref(d);
597 
598     gr.x = 0;
599     gr.y = 0;
600 
601 #if CONFOPT_GTK == 2
602     gr.width = area->allocation.width;
603 #endif
604 #if CONFOPT_GTK == 3
605     gr.width = gtk_widget_get_allocated_width(area);
606 #endif
607 
608     gr.height = font_height;
609 
610 #if GDK_MAJOR_VERSION > 3 || GDK_MINOR_VERSION >= 22
611     GdkDrawingContext *gdc;
612     gdc = gdk_window_begin_draw_frame(gtk_widget_get_window(area),
613         cairo_region_create());
614     cr = gdk_drawing_context_get_cairo_context(gdc);
615 #else
616     cr = gdk_cairo_create(gtk_widget_get_window(area));
617 #endif
618 
619     for (n = 0; n < mpdm_size(d); n++) {
620         PangoLayout *pl;
621         PangoAttrList *pal;
622         mpdm_t l = mpdm_get_i(d, n);
623         char *str = NULL;
624         int u;
625         int p = 0;
626 
627         if (l == NULL)
628             continue;
629 
630         /* create the pango stuff */
631         pl = gtk_widget_create_pango_layout(area, NULL);
632         pango_layout_set_font_description(pl, font);
633         pal = pango_attr_list_new();
634 
635         for (m = u = p = 0; m < mpdm_size(l); m++, u = p) {
636             PangoAttribute *pa;
637             int attr;
638             mpdm_t s;
639             char *ptr;
640 
641             /* get the attribute and the string */
642             attr = mpdm_ival(mpdm_get_i(l, m++));
643             s = mpdm_get_i(l, m);
644 
645             /* convert the string to utf8 */
646             ptr = v_to_utf8(s);
647 
648             /* add to the full line */
649             str = mpdm_poke(str, &p, ptr, strlen(ptr), 1);
650 
651             g_free(ptr);
652 
653             /* create the background if it's
654                different from the default */
655             if (papers[attr].red   != papers[normal_attr].red ||
656                 papers[attr].green != papers[normal_attr].green ||
657                 papers[attr].blue  != papers[normal_attr].blue) {
658                 pa = pango_attr_background_new(papers[attr].red,
659                                                papers[attr].green,
660                                                papers[attr].blue);
661 
662                 pa->start_index = u;
663                 pa->end_index   = p;
664 
665                 pango_attr_list_insert(pal, pa);
666             }
667 
668             /* underline? */
669             if (underlines[attr]) {
670                 pa = pango_attr_underline_new(TRUE);
671 
672                 pa->start_index = u;
673                 pa->end_index   = p;
674 
675                 pango_attr_list_insert(pal, pa);
676             }
677 
678             /* foreground color */
679             pa = pango_attr_foreground_new(inks[attr].red,
680                                            inks[attr].green,
681                                            inks[attr].blue);
682 
683             pa->start_index = u;
684             pa->end_index   = p;
685 
686             pango_attr_list_insert(pal, pa);
687         }
688 
689         /* store the attributes */
690         pango_layout_set_attributes(pl, pal);
691         pango_attr_list_unref(pal);
692 
693         /* store and free the text */
694         pango_layout_set_text(pl, str, p);
695         free(str);
696 
697         /* draw the background */
698 #if CONFOPT_GTK == 2
699         gdk_cairo_set_source_color(cr, &normal_paper);
700 #endif
701 
702 #if CONFOPT_GTK == 3
703         gdk_cairo_set_source_rgba(cr, &normal_paper);
704 #endif
705 
706         cairo_rectangle(cr, 0, n * font_height, gr.width, gr.height);
707         cairo_fill(cr);
708 
709         /* draw the text */
710         cairo_move_to(cr, 2, n * font_height);
711         pango_cairo_show_layout(cr, pl);
712 
713         g_object_unref(pl);
714     }
715 
716 #if GDK_MAJOR_VERSION > 3 || GDK_MINOR_VERSION >= 22
717     gdk_window_end_draw_frame(gtk_widget_get_window(area), gdc);
718 #else
719     cairo_destroy(cr);
720 #endif
721 
722     mpdm_unref(d);
723 
724     draw_filetabs();
725     draw_scrollbar();
726     draw_status();
727 
728     gtk_window_get_position(GTK_WINDOW(window), &ls_x, &ls_y);
729 }
730 
731 
delete_event(GtkWidget * w,GdkEvent * e,gpointer data)732 static gint delete_event(GtkWidget * w, GdkEvent * e, gpointer data)
733 /* 'delete_event' handler */
734 {
735     mp_process_event(MPDM_S(L"close-window"));
736 
737     return mp_exit_requested ? FALSE : TRUE;
738 }
739 
740 
destroy(GtkWidget * w,gpointer data)741 static void destroy(GtkWidget * w, gpointer data)
742 /* 'destroy' handler */
743 {
744     gtk_main_quit();
745 }
746 
747 
748 #if 0
key_release_event(GtkWidget * w,GdkEventKey * e,gpointer d)749 static gint key_release_event(GtkWidget *w, GdkEventKey *e, gpointer d)
750 /* 'key_release_event' handler */
751 {
752     if (mp_keypress_throttle(0))
753         gtk_widget_queue_draw(GTK_WIDGET(area));
754 
755     return 0;
756 }
757 #endif
758 
759 
760 #if CONFOPT_GTK == 2
761 #define MP_KEY_Up GDK_Up
762 #define MP_KEY_Down GDK_Down
763 #define MP_KEY_Left GDK_Left
764 #define MP_KEY_Right GDK_Right
765 #define MP_KEY_Prior GDK_Prior
766 #define MP_KEY_Next GDK_Next
767 #define MP_KEY_Home GDK_Home
768 #define MP_KEY_End GDK_End
769 #define MP_KEY_space GDK_space
770 #define MP_KEY_KP_Add GDK_KP_Add
771 #define MP_KEY_KP_Subtract GDK_KP_Subtract
772 #define MP_KEY_KP_Multiply GDK_KP_Multiply
773 #define MP_KEY_KP_Divide GDK_KP_Divide
774 #define MP_KEY_F1 GDK_F1
775 #define MP_KEY_F2 GDK_F2
776 #define MP_KEY_F3 GDK_F3
777 #define MP_KEY_F4 GDK_F4
778 #define MP_KEY_F5 GDK_F5
779 #define MP_KEY_F6 GDK_F6
780 #define MP_KEY_F7 GDK_F7
781 #define MP_KEY_F8 GDK_F8
782 #define MP_KEY_F9 GDK_F9
783 #define MP_KEY_F10 GDK_F10
784 #define MP_KEY_F11 GDK_F11
785 #define MP_KEY_F12 GDK_F12
786 #define MP_KEY_KP_Enter GDK_KP_Enter
787 #define MP_KEY_Return GDK_Return
788 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
789 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
790 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
791 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
792 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
793 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
794 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
795 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
796 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
797 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
798 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
799 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
800 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
801 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
802 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
803 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
804 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
805 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
806 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
807 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
808 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
809 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
810 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
811 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
812 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
813 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
814 #define MP_KEY_Tab GDK_Tab
815 #define MP_KEY_ISO_Left_Tab GDK_ISO_Left_Tab
816 #define MP_KEY_Escape GDK_Escape
817 #define MP_KEY_Insert GDK_Insert
818 #define MP_KEY_BackSpace GDK_BackSpace
819 #define MP_KEY_Delete GDK_Delete
820 #endif /* CONFOPT_GTK == 2 */
821 
822 #if CONFOPT_GTK == 3
823 #define MP_KEY_Up GDK_KEY_Up
824 #define MP_KEY_Down GDK_KEY_Down
825 #define MP_KEY_Left GDK_KEY_Left
826 #define MP_KEY_Right GDK_KEY_Right
827 #define MP_KEY_Prior GDK_KEY_Prior
828 #define MP_KEY_Next GDK_KEY_Next
829 #define MP_KEY_Home GDK_KEY_Home
830 #define MP_KEY_End GDK_KEY_End
831 #define MP_KEY_space GDK_KEY_space
832 #define MP_KEY_KP_Add GDK_KEY_KP_Add
833 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
834 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
835 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
836 #define MP_KEY_F1 GDK_KEY_F1
837 #define MP_KEY_F2 GDK_KEY_F2
838 #define MP_KEY_F3 GDK_KEY_F3
839 #define MP_KEY_F4 GDK_KEY_F4
840 #define MP_KEY_F5 GDK_KEY_F5
841 #define MP_KEY_F6 GDK_KEY_F6
842 #define MP_KEY_F7 GDK_KEY_F7
843 #define MP_KEY_F8 GDK_KEY_F8
844 #define MP_KEY_F9 GDK_KEY_F9
845 #define MP_KEY_F10 GDK_KEY_F10
846 #define MP_KEY_F11 GDK_KEY_F11
847 #define MP_KEY_F12 GDK_KEY_F12
848 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
849 #define MP_KEY_Return GDK_KEY_Return
850 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
851 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
852 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
853 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
854 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
855 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
856 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
857 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
858 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
859 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
860 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
861 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
862 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
863 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
864 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
865 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
866 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
867 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
868 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
869 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
870 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
871 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
872 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
873 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
874 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
875 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
876 #define MP_KEY_Tab GDK_KEY_Tab
877 #define MP_KEY_ISO_Left_Tab GDK_KEY_ISO_Left_Tab
878 #define MP_KEY_Escape GDK_KEY_Escape
879 #define MP_KEY_Insert GDK_KEY_Insert
880 #define MP_KEY_BackSpace GDK_KEY_BackSpace
881 #define MP_KEY_Delete GDK_KEY_Delete
882 #endif /* CONFOPT_GTK == 3 */
883 
im_commit(GtkIMContext * i,char * str,gpointer u)884 static void im_commit(GtkIMContext *i, char *str, gpointer u)
885 /* GtkIM 'commit' handler */
886 {
887     wchar_t *im_char = (wchar_t *)u;
888     int s = 0;
889 
890     /* convert the utf-8 string to a wchar_t */
891     while (*str)
892         mpdm_utf8_to_wc(im_char, &s, *str++);
893 
894     im_char[1] = L'\0';
895 }
896 
897 
key_press_event(GtkWidget * widget,GdkEventKey * event,gpointer data)898 static gint key_press_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
899 /* 'key_press_event' handler */
900 {
901     static GtkIMContext *im = NULL;
902     static wchar_t im_char[2] = L"";
903     wchar_t *ptr = NULL;
904 
905     if (im == NULL) {
906         im = gtk_im_multicontext_new();
907         g_signal_connect(im, "commit", G_CALLBACK(im_commit), im_char);
908         gtk_im_context_set_client_window(im, gtk_widget_get_window(widget));
909     }
910 
911     gtk_im_context_filter_keypress(im, event);
912 
913     /* set mp.shift_pressed */
914     if (event->state & (GDK_SHIFT_MASK))
915         mpdm_set_wcs(MP, MPDM_I(1), L"shift_pressed");
916 
917     if (event->state & (GDK_SHIFT_MASK)) {
918         switch (event->keyval) {
919         case MP_KEY_F1:
920             ptr = L"shift-f1";
921             break;
922         case MP_KEY_F2:
923             ptr = L"shift-f2";
924             break;
925         case MP_KEY_F3:
926             ptr = L"shift-f3";
927             break;
928         case MP_KEY_F4:
929             ptr = L"shift-f4";
930             break;
931         case MP_KEY_F5:
932             ptr = L"shift-f5";
933             break;
934         case MP_KEY_F6:
935             ptr = L"shift-f6";
936             break;
937         case MP_KEY_F7:
938             ptr = L"shift-f7";
939             break;
940         case MP_KEY_F8:
941             ptr = L"shift-f8";
942             break;
943         case MP_KEY_F9:
944             ptr = L"shift-f9";
945             break;
946         case MP_KEY_F10:
947             ptr = L"shift-f10";
948             break;
949         case MP_KEY_F11:
950             ptr = L"shift-f11";
951             break;
952         case MP_KEY_F12:
953             ptr = L"shift-f12";
954             break;
955         }
956     }
957 
958     /* reserve alt for menu mnemonics */
959 /*  if (GDK_MOD1_MASK & event->state)
960         return(0);*/
961 
962     if (ptr == NULL && (event->state & (GDK_CONTROL_MASK))) {
963         switch (event->keyval) {
964         case MP_KEY_Up:
965             ptr = L"ctrl-cursor-up";
966             break;
967         case MP_KEY_Down:
968             ptr = L"ctrl-cursor-down";
969             break;
970         case MP_KEY_Left:
971             ptr = L"ctrl-cursor-left";
972             break;
973         case MP_KEY_Right:
974             ptr = L"ctrl-cursor-right";
975             break;
976         case MP_KEY_Prior:
977             ptr = L"ctrl-page-up";
978             break;
979         case MP_KEY_Next:
980             ptr = L"ctrl-page-down";
981             break;
982         case MP_KEY_Home:
983             ptr = L"ctrl-home";
984             break;
985         case MP_KEY_End:
986             ptr = L"ctrl-end";
987             break;
988         case MP_KEY_space:
989             ptr = L"ctrl-space";
990             break;
991         case MP_KEY_KP_Add:
992             ptr = L"ctrl-kp-plus";
993             break;
994         case MP_KEY_KP_Subtract:
995             ptr = L"ctrl-kp-minus";
996             break;
997         case MP_KEY_KP_Multiply:
998             ptr = L"ctrl-kp-multiply";
999             break;
1000         case MP_KEY_KP_Divide:
1001             ptr = L"ctrl-kp-divide";
1002             break;
1003         case MP_KEY_F1:
1004             ptr = L"ctrl-f1";
1005             break;
1006         case MP_KEY_F2:
1007             ptr = L"ctrl-f2";
1008             break;
1009         case MP_KEY_F3:
1010             ptr = L"ctrl-f3";
1011             break;
1012         case MP_KEY_F4:
1013             ptr = L"ctrl-f4";
1014             break;
1015         case MP_KEY_F5:
1016             ptr = L"ctrl-f5";
1017             break;
1018         case MP_KEY_F6:
1019             ptr = L"ctrl-f6";
1020             break;
1021         case MP_KEY_F7:
1022             ptr = L"ctrl-f7";
1023             break;
1024         case MP_KEY_F8:
1025             ptr = L"ctrl-f8";
1026             break;
1027         case MP_KEY_F9:
1028             ptr = L"ctrl-f9";
1029             break;
1030         case MP_KEY_F10:
1031             ptr = L"ctrl-f10";
1032             break;
1033         case MP_KEY_F11:
1034             ptr = L"ctrl-f11";
1035             break;
1036         case MP_KEY_F12:
1037             ptr = L"ctrl-f12";
1038             break;
1039         case MP_KEY_KP_Enter:
1040         case MP_KEY_Return:
1041             ptr = L"ctrl-enter";
1042             break;
1043         case MP_KEY_Cyrillic_ve:
1044             ptr = L"ctrl-d";
1045             break;
1046         case MP_KEY_Cyrillic_a:
1047             ptr = L"ctrl-f";
1048             break;
1049         case MP_KEY_Cyrillic_tse:
1050             ptr = L"ctrl-w";
1051             break;
1052         case MP_KEY_Cyrillic_de:
1053             ptr = L"ctrl-l";
1054             break;
1055         case MP_KEY_Cyrillic_ie:
1056             ptr = L"ctrl-t";
1057             break;
1058         case MP_KEY_Cyrillic_ef:
1059             ptr = L"ctrl-a";
1060             break;
1061         case MP_KEY_Cyrillic_ghe:
1062             ptr = L"ctrl-u";
1063             break;
1064         case MP_KEY_Cyrillic_i:
1065             ptr = L"ctrl-b";
1066             break;
1067         case MP_KEY_Cyrillic_shorti:
1068             ptr = L"ctrl-q";
1069             break;
1070         case MP_KEY_Cyrillic_ka:
1071             ptr = L"ctrl-r";
1072             break;
1073         case MP_KEY_Cyrillic_el:
1074             ptr = L"ctrl-k";
1075             break;
1076         case MP_KEY_Cyrillic_em:
1077             ptr = L"ctrl-v";
1078             break;
1079         case MP_KEY_Cyrillic_en:
1080             ptr = L"ctrl-y";
1081             break;
1082         case MP_KEY_Cyrillic_o:
1083             ptr = L"ctrl-j";
1084             break;
1085         case MP_KEY_Cyrillic_pe:
1086             ptr = L"ctrl-g";
1087             break;
1088         case MP_KEY_Cyrillic_ya:
1089             ptr = L"ctrl-z";
1090             break;
1091         case MP_KEY_Cyrillic_er:
1092             ptr = L"ctrl-h";
1093             break;
1094         case MP_KEY_Cyrillic_es:
1095             ptr = L"ctrl-c";
1096             break;
1097         case MP_KEY_Cyrillic_te:
1098             ptr = L"ctrl-n";
1099             break;
1100         case MP_KEY_Cyrillic_softsign:
1101             ptr = L"ctrl-m";
1102             break;
1103         case MP_KEY_Cyrillic_yeru:
1104             ptr = L"ctrl-s";
1105             break;
1106         case MP_KEY_Cyrillic_ze:
1107             ptr = L"ctrl-p";
1108             break;
1109         case MP_KEY_Cyrillic_sha:
1110             ptr = L"ctrl-i";
1111             break;
1112         case MP_KEY_Cyrillic_e:
1113             ptr = L"ctrl-t";
1114             break;
1115         case MP_KEY_Cyrillic_shcha:
1116             ptr = L"ctrl-o";
1117             break;
1118         case MP_KEY_Cyrillic_che:
1119             ptr = L"ctrl-x";
1120             break;
1121         }
1122 
1123         if (ptr == NULL) {
1124             char c = event->keyval & 0xdf;
1125 
1126             switch (c) {
1127             case 'A':
1128                 ptr = L"ctrl-a";
1129                 break;
1130             case 'B':
1131                 ptr = L"ctrl-b";
1132                 break;
1133             case 'C':
1134                 ptr = L"ctrl-c";
1135                 break;
1136             case 'D':
1137                 ptr = L"ctrl-d";
1138                 break;
1139             case 'E':
1140                 ptr = L"ctrl-e";
1141                 break;
1142             case 'F':
1143                 ptr = L"ctrl-f";
1144                 break;
1145             case 'G':
1146                 ptr = L"ctrl-g";
1147                 break;
1148             case 'H':
1149                 ptr = L"ctrl-h";
1150                 break;
1151             case 'I':
1152                 ptr = L"ctrl-i";
1153                 break;
1154             case 'J':
1155                 ptr = L"ctrl-j";
1156                 break;
1157             case 'K':
1158                 ptr = L"ctrl-k";
1159                 break;
1160             case 'L':
1161                 ptr = L"ctrl-l";
1162                 break;
1163             case 'M':
1164                 ptr = L"ctrl-m";
1165                 break;
1166             case 'N':
1167                 ptr = L"ctrl-n";
1168                 break;
1169             case 'O':
1170                 ptr = L"ctrl-o";
1171                 break;
1172             case 'P':
1173                 ptr = L"ctrl-p";
1174                 break;
1175             case 'Q':
1176                 ptr = L"ctrl-q";
1177                 break;
1178             case 'R':
1179                 ptr = L"ctrl-r";
1180                 break;
1181             case 'S':
1182                 ptr = L"ctrl-s";
1183                 break;
1184             case 'T':
1185                 ptr = L"ctrl-t";
1186                 break;
1187             case 'U':
1188                 ptr = L"ctrl-u";
1189                 break;
1190             case 'V':
1191                 ptr = L"ctrl-v";
1192                 break;
1193             case 'W':
1194                 ptr = L"ctrl-w";
1195                 break;
1196             case 'X':
1197                 ptr = L"ctrl-x";
1198                 break;
1199             case 'Y':
1200                 ptr = L"ctrl-y";
1201                 break;
1202             case 'Z':
1203                 ptr = L"ctrl-z";
1204                 break;
1205             }
1206         }
1207     }
1208     else
1209     if (ptr == NULL && (event->state & (GDK_MOD1_MASK))) {
1210         switch (event->keyval) {
1211         case MP_KEY_Up:
1212             ptr = L"alt-cursor-up";
1213             break;
1214         case MP_KEY_Down:
1215             ptr = L"alt-cursor-down";
1216             break;
1217         case MP_KEY_Left:
1218             ptr = L"alt-cursor-left";
1219             break;
1220         case MP_KEY_Right:
1221             ptr = L"alt-cursor-right";
1222             break;
1223         case MP_KEY_Prior:
1224             ptr = L"alt-page-up";
1225             break;
1226         case MP_KEY_Next:
1227             ptr = L"alt-page-down";
1228             break;
1229         case MP_KEY_Home:
1230             ptr = L"alt-home";
1231             break;
1232         case MP_KEY_End:
1233             ptr = L"alt-end";
1234             break;
1235         case MP_KEY_space:
1236             ptr = L"alt-space";
1237             break;
1238         case MP_KEY_KP_Add:
1239             ptr = L"alt-kp-plus";
1240             break;
1241         case MP_KEY_KP_Subtract:
1242             ptr = L"alt-kp-minus";
1243             break;
1244         case MP_KEY_KP_Multiply:
1245             ptr = L"alt-kp-multiply";
1246             break;
1247         case MP_KEY_KP_Divide:
1248             ptr = L"alt-kp-divide";
1249             break;
1250         case MP_KEY_F1:
1251             ptr = L"alt-f1";
1252             break;
1253         case MP_KEY_F2:
1254             ptr = L"alt-f2";
1255             break;
1256         case MP_KEY_F3:
1257             ptr = L"alt-f3";
1258             break;
1259         case MP_KEY_F4:
1260             ptr = L"alt-f4";
1261             break;
1262         case MP_KEY_F5:
1263             ptr = L"alt-f5";
1264             break;
1265         case MP_KEY_F6:
1266             ptr = L"alt-f6";
1267             break;
1268         case MP_KEY_F7:
1269             ptr = L"alt-f7";
1270             break;
1271         case MP_KEY_F8:
1272             ptr = L"alt-f8";
1273             break;
1274         case MP_KEY_F9:
1275             ptr = L"alt-f9";
1276             break;
1277         case MP_KEY_F10:
1278             ptr = L"alt-f10";
1279             break;
1280         case MP_KEY_F11:
1281             ptr = L"alt-f11";
1282             break;
1283         case MP_KEY_F12:
1284             ptr = L"alt-f12";
1285             break;
1286         case MP_KEY_KP_Enter:
1287         case MP_KEY_Return:
1288             ptr = L"alt-enter";
1289             break;
1290         case MP_KEY_Cyrillic_ve:
1291             ptr = L"alt-d";
1292             break;
1293         case MP_KEY_Cyrillic_a:
1294             ptr = L"alt-f";
1295             break;
1296         case MP_KEY_Cyrillic_tse:
1297             ptr = L"alt-w";
1298             break;
1299         case MP_KEY_Cyrillic_de:
1300             ptr = L"alt-l";
1301             break;
1302         case MP_KEY_Cyrillic_ie:
1303             ptr = L"alt-t";
1304             break;
1305         case MP_KEY_Cyrillic_ef:
1306             ptr = L"alt-a";
1307             break;
1308         case MP_KEY_Cyrillic_ghe:
1309             ptr = L"alt-u";
1310             break;
1311         case MP_KEY_Cyrillic_i:
1312             ptr = L"alt-b";
1313             break;
1314         case MP_KEY_Cyrillic_shorti:
1315             ptr = L"alt-q";
1316             break;
1317         case MP_KEY_Cyrillic_ka:
1318             ptr = L"alt-r";
1319             break;
1320         case MP_KEY_Cyrillic_el:
1321             ptr = L"alt-k";
1322             break;
1323         case MP_KEY_Cyrillic_em:
1324             ptr = L"alt-v";
1325             break;
1326         case MP_KEY_Cyrillic_en:
1327             ptr = L"alt-y";
1328             break;
1329         case MP_KEY_Cyrillic_o:
1330             ptr = L"alt-j";
1331             break;
1332         case MP_KEY_Cyrillic_pe:
1333             ptr = L"alt-g";
1334             break;
1335         case MP_KEY_Cyrillic_ya:
1336             ptr = L"alt-z";
1337             break;
1338         case MP_KEY_Cyrillic_er:
1339             ptr = L"alt-h";
1340             break;
1341         case MP_KEY_Cyrillic_es:
1342             ptr = L"alt-c";
1343             break;
1344         case MP_KEY_Cyrillic_te:
1345             ptr = L"alt-n";
1346             break;
1347         case MP_KEY_Cyrillic_softsign:
1348             ptr = L"alt-m";
1349             break;
1350         case MP_KEY_Cyrillic_yeru:
1351             ptr = L"alt-s";
1352             break;
1353         case MP_KEY_Cyrillic_ze:
1354             ptr = L"alt-p";
1355             break;
1356         case MP_KEY_Cyrillic_sha:
1357             ptr = L"alt-i";
1358             break;
1359         case MP_KEY_Cyrillic_e:
1360             ptr = L"alt-t";
1361             break;
1362         case MP_KEY_Cyrillic_shcha:
1363             ptr = L"alt-o";
1364             break;
1365         case MP_KEY_Cyrillic_che:
1366             ptr = L"alt-x";
1367             break;
1368         }
1369 
1370         if (ptr == NULL) {
1371             char c = event->keyval & 0xdf;
1372 
1373             switch (c) {
1374             case 'A':
1375                 ptr = L"alt-a";
1376                 break;
1377             case 'B':
1378                 ptr = L"alt-b";
1379                 break;
1380             case 'C':
1381                 ptr = L"alt-c";
1382                 break;
1383             case 'D':
1384                 ptr = L"alt-d";
1385                 break;
1386             case 'E':
1387                 ptr = L"alt-e";
1388                 break;
1389             case 'F':
1390                 ptr = L"alt-f";
1391                 break;
1392             case 'G':
1393                 ptr = L"alt-g";
1394                 break;
1395             case 'H':
1396                 ptr = L"alt-h";
1397                 break;
1398             case 'I':
1399                 ptr = L"alt-i";
1400                 break;
1401             case 'J':
1402                 ptr = L"alt-j";
1403                 break;
1404             case 'K':
1405                 ptr = L"alt-k";
1406                 break;
1407             case 'L':
1408                 ptr = L"alt-l";
1409                 break;
1410             case 'M':
1411                 ptr = L"alt-m";
1412                 break;
1413             case 'N':
1414                 ptr = L"alt-n";
1415                 break;
1416             case 'O':
1417                 ptr = L"alt-o";
1418                 break;
1419             case 'P':
1420                 ptr = L"alt-p";
1421                 break;
1422             case 'Q':
1423                 ptr = L"alt-q";
1424                 break;
1425             case 'R':
1426                 ptr = L"alt-r";
1427                 break;
1428             case 'S':
1429                 ptr = L"alt-s";
1430                 break;
1431             case 'T':
1432                 ptr = L"alt-t";
1433                 break;
1434             case 'U':
1435                 ptr = L"alt-u";
1436                 break;
1437             case 'V':
1438                 ptr = L"alt-v";
1439                 break;
1440             case 'W':
1441                 ptr = L"alt-w";
1442                 break;
1443             case 'X':
1444                 ptr = L"alt-x";
1445                 break;
1446             case 'Y':
1447                 ptr = L"alt-y";
1448                 break;
1449             case 'Z':
1450                 ptr = L"alt-z";
1451                 break;
1452             case '-':
1453                 ptr = L"alt-minus";
1454                 break;
1455             }
1456         }
1457     }
1458     else
1459     if (ptr == NULL) {
1460         switch (event->keyval) {
1461         case MP_KEY_Up:
1462         case GDK_KEY_KP_Up:
1463             ptr = L"cursor-up";
1464             break;
1465         case MP_KEY_Down:
1466         case GDK_KEY_KP_Down:
1467             ptr = L"cursor-down";
1468             break;
1469         case MP_KEY_Left:
1470         case GDK_KEY_KP_Left:
1471             ptr = L"cursor-left";
1472             break;
1473         case MP_KEY_Right:
1474         case GDK_KEY_KP_Right:
1475             ptr = L"cursor-right";
1476             break;
1477         case MP_KEY_Prior:
1478         case GDK_KEY_KP_Prior:
1479             ptr = L"page-up";
1480             break;
1481         case MP_KEY_Next:
1482         case GDK_KEY_KP_Next:
1483             ptr = L"page-down";
1484             break;
1485         case MP_KEY_Home:
1486         case GDK_KEY_KP_Home:
1487             ptr = L"home";
1488             break;
1489         case MP_KEY_End:
1490         case GDK_KEY_KP_End:
1491             ptr = L"end";
1492             break;
1493         case MP_KEY_space:
1494             ptr = L"space";
1495             break;
1496         case MP_KEY_KP_Add:
1497             ptr = L"kp-plus";
1498             break;
1499         case MP_KEY_KP_Subtract:
1500             ptr = L"kp-minus";
1501             break;
1502         case MP_KEY_KP_Multiply:
1503             ptr = L"kp-multiply";
1504             break;
1505         case MP_KEY_KP_Divide:
1506             ptr = L"kp-divide";
1507             break;
1508         case MP_KEY_F1:
1509             ptr = L"f1";
1510             break;
1511         case MP_KEY_F2:
1512             ptr = L"f2";
1513             break;
1514         case MP_KEY_F3:
1515             ptr = L"f3";
1516             break;
1517         case MP_KEY_F4:
1518             ptr = L"f4";
1519             break;
1520         case MP_KEY_F5:
1521             ptr = L"f5";
1522             break;
1523         case MP_KEY_F6:
1524             ptr = L"f6";
1525             break;
1526         case MP_KEY_F7:
1527             ptr = L"f7";
1528             break;
1529         case MP_KEY_F8:
1530             ptr = L"f8";
1531             break;
1532         case MP_KEY_F9:
1533             ptr = L"f9";
1534             break;
1535         case MP_KEY_F10:
1536             ptr = L"f10";
1537             break;
1538         case MP_KEY_F11:
1539             ptr = L"f11";
1540             break;
1541         case MP_KEY_F12:
1542             ptr = L"f12";
1543             break;
1544         case MP_KEY_Insert:
1545             ptr = L"insert";
1546             break;
1547         case MP_KEY_BackSpace:
1548             ptr = L"backspace";
1549             break;
1550         case MP_KEY_Delete:
1551             ptr = L"delete";
1552             break;
1553         case MP_KEY_KP_Enter:
1554         case MP_KEY_Return:
1555             ptr = L"enter";
1556             break;
1557         case MP_KEY_Tab:
1558             ptr = L"tab";
1559             break;
1560         case MP_KEY_ISO_Left_Tab:
1561             ptr = L"shift-tab";
1562             break;
1563         case MP_KEY_Escape:
1564             ptr = L"escape";
1565             break;
1566         }
1567     }
1568 
1569     /* if there is a pending char in im_char, use it */
1570     if (ptr == NULL && im_char[0] != L'\0')
1571         ptr = im_char;
1572 
1573 //    gtk_widget_hide(menu_bar);
1574 
1575     /* finally process */
1576     if (ptr != NULL)
1577         mp_process_event(MPDM_S(ptr));
1578 
1579     /* delete the pending char */
1580     im_char[0] = L'\0';
1581 
1582     if (mp_exit_requested)
1583         gtk_main_quit();
1584 
1585     if (ptr)
1586         gtk_widget_queue_draw(GTK_WIDGET(area));
1587 
1588     return 0;
1589 }
1590 
1591 
button_press_event(GtkWidget * w,GdkEventButton * event,gpointer d)1592 static gint button_press_event(GtkWidget *w, GdkEventButton *event, gpointer d)
1593 /* 'button_press_event' handler (mouse buttons) */
1594 {
1595     int x, y;
1596     wchar_t *ptr = NULL;
1597 
1598     mouse_down = 1;
1599 
1600     /* mouse instant positioning */
1601     x = ((int) event->x) / font_width;
1602     y = ((int) event->y) / font_height;
1603 
1604     mpdm_set_wcs(MP, MPDM_I(x), L"mouse_x");
1605     mpdm_set_wcs(MP, MPDM_I(y), L"mouse_y");
1606 
1607     switch (event->button) {
1608     case 1:
1609         if (event->type == GDK_2BUTTON_PRESS)
1610             ptr = L"mouse-left-dblclick";
1611         else
1612             ptr = L"mouse-left-button";
1613         break;
1614     case 2:
1615         ptr = L"mouse-middle-button";
1616         break;
1617     case 3:
1618         ptr = L"mouse-right-button";
1619         break;
1620     case 4:
1621         ptr = L"mouse-wheel-up";
1622         break;
1623     case 5:
1624         ptr = L"mouse-wheel-down";
1625         break;
1626     }
1627 
1628     if (ptr != NULL)
1629         mp_process_event(MPDM_S(ptr));
1630 
1631     gtk_widget_queue_draw(GTK_WIDGET(area));
1632 
1633 //    gtk_widget_hide(menu_bar);
1634 
1635     return 0;
1636 }
1637 
1638 
button_release_event(GtkWidget * w,GdkEventButton * e,gpointer d)1639 static gint button_release_event(GtkWidget *w, GdkEventButton *e, gpointer d)
1640 /* 'button_release_event' handle (mouse buttons) */
1641 {
1642     mouse_down = 0;
1643 
1644     return TRUE;
1645 }
1646 
1647 
motion_notify_event(GtkWidget * w,GdkEventMotion * event,gpointer d)1648 static gint motion_notify_event(GtkWidget *w, GdkEventMotion *event, gpointer d)
1649 /* 'motion_notify_event' handler (mouse movement) */
1650 {
1651     if (mouse_down) {
1652         int x, y;
1653 
1654         /* mouse dragging */
1655         x = ((int) event->x) / font_width;
1656         y = ((int) event->y) / font_height;
1657 
1658         mpdm_set_wcs(MP, MPDM_I(x), L"mouse_to_x");
1659         mpdm_set_wcs(MP, MPDM_I(y), L"mouse_to_y");
1660 
1661         mp_process_event(MPDM_S(L"mouse-drag"));
1662 
1663         gtk_widget_queue_draw(GTK_WIDGET(area));
1664     }
1665 
1666     return TRUE;
1667 }
1668 
1669 
drag_data_received(GtkWidget * w,GdkDragContext * dc,gint x,gint y,GtkSelectionData * data,guint info,guint time)1670 static void drag_data_received(GtkWidget *w, GdkDragContext *dc,
1671                                gint x, gint y, GtkSelectionData *data,
1672                                guint info, guint time)
1673 /* 'drag_data_received' handler */
1674 {
1675     mpdm_t v;
1676 
1677     /* get data */
1678     v = MPDM_MBS((char *)gtk_selection_data_get_text(data));
1679 
1680     /* strip URI crap */
1681     v = mpdm_sregex(v, MPDM_S(L"!file://!g"), NULL, 0);
1682 
1683     /* split */
1684     v = mpdm_split_wcs(v, L"\n");
1685 
1686     /* drop last element, as it's an empty string */
1687     mpdm_del_i(v, -1);
1688 
1689     mpdm_set_wcs(MP, v, L"dropped_files");
1690 
1691     mp_process_event(MPDM_S(L"dropped-files"));
1692 
1693     gtk_widget_queue_draw(GTK_WIDGET(area));
1694 
1695     gtk_drag_finish(dc, TRUE, TRUE, time);
1696 }
1697 
1698 
1699 /** clipboard functions **/
1700 
realize(GtkWidget * w)1701 static void realize(GtkWidget *w)
1702 /* 'realize' handler */
1703 {
1704 }
1705 
1706 
expose_event(GtkWidget * w,cairo_t * e)1707 static gint expose_event(GtkWidget *w, cairo_t *e)
1708 /* 'expose_event' handler */
1709 {
1710     static int first_time = 1;
1711 
1712     if (!first_time || mpdm_size(mpdm_get_wcs(MP, L"docs"))) {
1713         gtk_drv_render(mp_active(), 0);
1714         first_time = 0;
1715     }
1716 
1717     return FALSE;
1718 }
1719 
1720 
configure_event(GtkWidget * w,GdkEventConfigure * event)1721 static gint configure_event(GtkWidget *w, GdkEventConfigure *event)
1722 /* 'configure_event' handler */
1723 {
1724     static GdkEventConfigure o;
1725 
1726     if (memcmp(&o, event, sizeof(o)) != 0) {
1727         memcpy(&o, event, sizeof(o));
1728 
1729         update_window_size();
1730         gtk_widget_queue_draw(GTK_WIDGET(area));
1731 
1732         gtk_window_get_size(GTK_WINDOW(window), &ls_w, &ls_h);
1733     }
1734 
1735     return TRUE;
1736 }
1737 
1738 
gtk_drv_clip_to_sys(mpdm_t a,mpdm_t ctxt)1739 static mpdm_t gtk_drv_clip_to_sys(mpdm_t a, mpdm_t ctxt)
1740 /* driver-dependent mp to system clipboard */
1741 {
1742     GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
1743     mpdm_t d = mpdm_join_wcs(mpdm_get_wcs(MP, L"clipboard"), L"\n");
1744     char *ptr = v_to_utf8(d);
1745 
1746     gtk_clipboard_set_text(clip, ptr, -1);
1747     g_free(ptr);
1748 
1749     return NULL;
1750 }
1751 
1752 
gtk_drv_sys_to_clip(mpdm_t a,mpdm_t ctxt)1753 static mpdm_t gtk_drv_sys_to_clip(mpdm_t a, mpdm_t ctxt)
1754 /* driver-dependent system to mp clipboard */
1755 {
1756     GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
1757     gchar * clip_txt = gtk_clipboard_wait_for_text(clip);
1758 
1759     if (clip_txt) {
1760         mpdm_t v = MPDM_MBS(clip_txt);
1761 
1762         /* split and set as the clipboard */
1763         mpdm_set_wcs(MP, mpdm_split_wcs(v, L"\n"), L"clipboard");
1764     }
1765 
1766     return NULL;
1767 }
1768 
1769 
1770 /** interface functions **/
1771 
retrieve_form_data(mpdm_t form_args,GtkWidget ** form_widgets)1772 static mpdm_t retrieve_form_data(mpdm_t form_args, GtkWidget **form_widgets)
1773 /* called when confirmation of a form */
1774 {
1775     int n;
1776     mpdm_t ret = MPDM_A(mpdm_size(form_args));
1777 
1778     for (n = 0; n < mpdm_size(form_args); n++) {
1779         GtkWidget *widget = form_widgets[n];
1780         mpdm_t w = mpdm_get_i(form_args, n);
1781         wchar_t *wptr = mpdm_string(mpdm_get_wcs(w, L"type"));
1782         mpdm_t v = NULL;
1783 
1784         if (wcscmp(wptr, L"text") == 0 || wcscmp(wptr, L"password") == 0) {
1785             char *ptr;
1786             GtkWidget *gw = widget;
1787             mpdm_t h;
1788 
1789             if (wcscmp(wptr, L"text") == 0)
1790 #if CONFOPT_GTK == 2
1791                 gw = GTK_COMBO(widget)->entry;
1792 #endif
1793 #if CONFOPT_GTK == 3
1794                 gw = gtk_bin_get_child(GTK_BIN(widget));
1795 #endif
1796 
1797             if ((ptr = gtk_editable_get_chars(GTK_EDITABLE(gw), 0, -1)) != NULL) {
1798                 v = MPDM_MBS(ptr);
1799                 g_free(ptr);
1800             }
1801 
1802             mpdm_ref(v);
1803 
1804             /* if it has history, fill it */
1805             if (v && (h = mpdm_get_wcs(w, L"history")) && mpdm_cmp_wcs(v, L"")) {
1806                 h = mp_get_history(h);
1807 
1808                 if (mpdm_cmp(v, mpdm_get_i(h, -1)) != 0)
1809                     mpdm_push(h, v);
1810             }
1811 
1812             mpdm_unrefnd(v);
1813         }
1814         else
1815         if (wcscmp(wptr, L"checkbox") == 0) {
1816             v = MPDM_I(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
1817         }
1818         else
1819         if (wcscmp(wptr, L"list") == 0) {
1820             GtkWidget *list = gtk_bin_get_child(GTK_BIN(widget));
1821             GtkTreeSelection *selection =
1822                               gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
1823             GList *selected = gtk_tree_selection_get_selected_rows(selection, NULL);
1824             GtkTreePath *path = selected->data;
1825 
1826             v = MPDM_I(gtk_tree_path_get_indices(path)[0]);
1827             gtk_tree_path_free(path);
1828             g_list_free(selected);
1829         }
1830 
1831         mpdm_set_i(ret, v, n);
1832     }
1833 
1834     return ret;
1835 }
1836 
1837 
idle_callback(gpointer data)1838 static gint idle_callback(gpointer data)
1839 {
1840     mp_process_event(MPDM_S(L"idle"));
1841 
1842     return TRUE;
1843 }
1844 
1845 
1846 /** dialog functions **/
1847 
gtk_drv_alert(mpdm_t a,mpdm_t ctxt)1848 static mpdm_t gtk_drv_alert(mpdm_t a, mpdm_t ctxt)
1849 /* alert driver function */
1850 {
1851     gchar *ptr;
1852     GtkWidget *dlg;
1853 
1854     /* 1# arg: prompt */
1855     ptr = v_to_utf8(mpdm_get_i(a, 0));
1856 
1857     dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
1858                                  GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "%s", ptr);
1859     gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
1860     g_free(ptr);
1861 
1862     gtk_dialog_run(GTK_DIALOG(dlg));
1863     gtk_widget_destroy(dlg);
1864 
1865     return NULL;
1866 }
1867 
1868 
gtk_drv_confirm(mpdm_t a,mpdm_t ctxt)1869 static mpdm_t gtk_drv_confirm(mpdm_t a, mpdm_t ctxt)
1870 /* confirm driver function */
1871 {
1872     char *ptr;
1873     GtkWidget *dlg;
1874     gint response;
1875 
1876     /* 1# arg: prompt */
1877     ptr = v_to_utf8(mpdm_get_i(a, 0));
1878 
1879     dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
1880                                  GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1881                                  "%s", ptr);
1882     gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
1883     g_free(ptr);
1884 
1885     gtk_dialog_add_button(GTK_DIALOG(dlg), L("Yes"), 1);
1886     gtk_dialog_add_button(GTK_DIALOG(dlg), L("No"), 2);
1887     gtk_dialog_add_button(GTK_DIALOG(dlg), L("Cancel"), 0);
1888 
1889     response = gtk_dialog_run(GTK_DIALOG(dlg));
1890     gtk_widget_destroy(dlg);
1891 
1892     if (response == GTK_RESPONSE_DELETE_EVENT)
1893         response = 0;
1894 
1895     return MPDM_I(response);
1896 }
1897 
1898 
gtk_drv_form(mpdm_t a,mpdm_t ctxt)1899 static mpdm_t gtk_drv_form(mpdm_t a, mpdm_t ctxt)
1900 /* 'form' driver function */
1901 {
1902     GtkWidget *dlg;
1903     GtkWidget *table;
1904     GtkWidget *content_area;
1905     GtkWidget **form_widgets;
1906     mpdm_t form_args;
1907     int n;
1908     mpdm_t ret = NULL;
1909 
1910     /* first argument: list of widgets */
1911     form_args    = mpdm_get_i(a, 0);
1912     form_widgets = (GtkWidget **) calloc(mpdm_size(form_args), sizeof(GtkWidget *));
1913 
1914     dlg = gtk_dialog_new_with_buttons("mp " VERSION, GTK_WINDOW(window),
1915                                       GTK_DIALOG_MODAL,
1916                                       L("Cancel"), GTK_RESPONSE_CANCEL,
1917                                       L("OK"), GTK_RESPONSE_OK,
1918                                       NULL);
1919     gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
1920     gtk_container_set_border_width(GTK_CONTAINER(dlg), 5);
1921 
1922     content_area = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
1923     gtk_box_set_spacing(GTK_BOX(content_area), 2);
1924 
1925 #if CONFOPT_GTK == 2
1926     table = gtk_table_new(mpdm_size(a), 2, FALSE);
1927     gtk_table_set_col_spacings(GTK_TABLE(table), 12);
1928     gtk_table_set_row_spacings(GTK_TABLE(table), 6);
1929 #endif
1930 
1931 #if CONFOPT_GTK == 3
1932     table = gtk_grid_new();
1933     gtk_grid_set_column_spacing(GTK_GRID(table), 12);
1934     gtk_grid_set_row_spacing(GTK_GRID(table), 6);
1935 #endif
1936 
1937     gtk_container_set_border_width(GTK_CONTAINER(table), 5);
1938 
1939     for (n = 0; n < mpdm_size(form_args); n++) {
1940         mpdm_t w = mpdm_get_i(form_args, n);
1941         GtkWidget *widget = NULL;
1942         GtkWidget *label = NULL;
1943         wchar_t *type;
1944         char *ptr;
1945         mpdm_t t;
1946         int col = 0;
1947 
1948         type = mpdm_string(mpdm_get_wcs(w, L"type"));
1949 
1950         if ((t = mpdm_get_wcs(w, L"label")) != NULL) {
1951             ptr = v_to_utf8(mpdm_gettext(t));
1952             label = gtk_label_new(ptr);
1953 
1954 #if CONFOPT_GTK == 2
1955             gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
1956 #endif
1957 
1958 #if CONFOPT_GTK == 3
1959             gtk_label_set_xalign(GTK_LABEL(label), 0.0);
1960             gtk_label_set_yalign(GTK_LABEL(label), 0.5);
1961 #endif
1962 
1963             g_free(ptr);
1964 
1965             col++;
1966         }
1967 
1968         t = mpdm_get_wcs(w, L"value");
1969 
1970         if (wcscmp(type, L"text") == 0) {
1971             GList *combo_items = NULL;
1972             mpdm_t h;
1973 
1974 #if CONFOPT_GTK == 2
1975             widget = gtk_combo_new();
1976             gtk_combo_set_use_arrows_always(GTK_COMBO(widget), TRUE);
1977             gtk_combo_set_case_sensitive(GTK_COMBO(widget), TRUE);
1978             gtk_entry_set_activates_default(GTK_ENTRY(GTK_COMBO(widget)->entry), TRUE);
1979 #endif
1980 
1981 #if CONFOPT_GTK == 3
1982             widget = gtk_combo_box_text_new_with_entry();
1983             gtk_entry_set_activates_default(
1984                         GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget))), TRUE);
1985 #endif
1986 
1987             gtk_widget_set_size_request(widget, 300, -1);
1988 
1989             if ((h = mpdm_get_wcs(w, L"history")) != NULL) {
1990                 int i;
1991 
1992                 /* has history; fill it */
1993                 h = mp_get_history(h);
1994 
1995                 for (i = 0; i < mpdm_size(h); i++) {
1996                     ptr = v_to_utf8(mpdm_get_i(h, i));
1997 
1998                     combo_items = g_list_prepend(combo_items, ptr);
1999 
2000 #if CONFOPT_GTK == 3
2001                     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(widget), ptr);
2002 #endif
2003 
2004                     g_free(ptr);
2005                 }
2006             }
2007 
2008             if (t != NULL) {
2009                 ptr = v_to_utf8(t);
2010 
2011                 combo_items = g_list_prepend(combo_items, ptr);
2012 #if CONFOPT_GTK == 3
2013                 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget), ptr);
2014                 gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
2015 #endif
2016 
2017                 g_free(ptr);
2018             }
2019 
2020 #if CONFOPT_GTK == 2
2021             gtk_combo_set_popdown_strings(GTK_COMBO(widget), combo_items);
2022 #endif
2023             g_list_free(combo_items);
2024         }
2025         else
2026         if (wcscmp(type, L"password") == 0) {
2027             widget = gtk_entry_new();
2028             gtk_widget_set_size_request(widget, 300, -1);
2029             gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
2030             gtk_entry_set_activates_default(GTK_ENTRY(widget), TRUE);
2031         }
2032         else
2033         if (wcscmp(type, L"checkbox") == 0) {
2034             widget = gtk_check_button_new();
2035 
2036             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
2037                                          mpdm_ival(t) ? TRUE : FALSE);
2038         }
2039         else
2040         if (wcscmp(type, L"list") == 0) {
2041             GtkWidget *list;
2042             GtkListStore *list_store;
2043             GtkCellRenderer *renderer;
2044             GtkTreeViewColumn *column;
2045             GtkTreePath *path;
2046             mpdm_t l;
2047             gint i;
2048 
2049             if ((i = 450 / mpdm_size(form_args)) < 100)
2050                 i = 100;
2051 
2052             widget = gtk_scrolled_window_new(NULL, NULL);
2053             gtk_widget_set_size_request(widget, 500, i);
2054             gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
2055                                            GTK_POLICY_NEVER,
2056                                            GTK_POLICY_AUTOMATIC);
2057             gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
2058                                                 (widget), GTK_SHADOW_IN);
2059 
2060             list_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
2061             list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
2062             gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2063 
2064             column = gtk_tree_view_column_new();
2065             gtk_tree_view_column_set_title(column, "");
2066             gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2067 
2068             renderer = gtk_cell_renderer_text_new();
2069             gtk_tree_view_column_pack_start(column, renderer, FALSE);
2070             gtk_tree_view_column_set_attributes(column, renderer, "text", 0, NULL);
2071 
2072             gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2073 
2074             column = gtk_tree_view_column_new();
2075             gtk_tree_view_column_set_title(column, "");
2076             gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
2077 
2078             renderer = gtk_cell_renderer_text_new();
2079             g_object_set(renderer, "xalign", 1.0, NULL);
2080             gtk_tree_view_column_pack_start(column, renderer, FALSE);
2081             gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
2082 
2083             gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2084 
2085             gtk_container_add(GTK_CONTAINER(widget), list);
2086 
2087             l = mpdm_get_wcs(w, L"list");
2088 
2089             for (i = 0; i < mpdm_size(l); i++) {
2090                 GtkTreeIter iter;
2091                 char *ptr2;
2092 
2093                 ptr = v_to_utf8(mpdm_get_i(l, i));
2094 
2095                 /* if there is a tab inside the text,
2096                    split in two columns */
2097                 if ((ptr2 = strchr(ptr, '\t')) == NULL)
2098                     ptr2 = "";
2099                 else {
2100                     *ptr2 = '\0';
2101                     ptr2++;
2102                 }
2103 
2104                 gtk_list_store_append(list_store, &iter);
2105                 gtk_list_store_set(list_store, &iter, 0, ptr, 1, ptr2, -1);
2106                 g_free(ptr);
2107             }
2108 
2109             /* initial position */
2110             i = mpdm_ival(t);
2111 
2112             path = gtk_tree_path_new_from_indices(i, -1);
2113             gtk_tree_view_set_cursor(GTK_TREE_VIEW(list), path, NULL,
2114                                      FALSE);
2115             gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(list), path, NULL,
2116                                     FALSE, 0, 0);
2117             gtk_tree_path_free(path);
2118 
2119             g_signal_connect_swapped(G_OBJECT(list), "row-activated",
2120                                      G_CALLBACK(gtk_window_activate_default), dlg);
2121         }
2122 
2123         if (widget != NULL) {
2124             form_widgets[n] = widget;
2125 
2126 #if CONFOPT_GTK == 2
2127             if (label)
2128                 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, n, n + 1);
2129 
2130             gtk_table_attach_defaults(GTK_TABLE(table), widget, col, 2, n, n + 1);
2131 #endif
2132 
2133 #if CONFOPT_GTK == 3
2134             if (label)
2135                 gtk_grid_attach(GTK_GRID(table), label, 0, n, 1, 1);
2136 
2137             if (mpdm_size(form_args) == 1)
2138                 gtk_grid_attach(GTK_GRID(table), widget, 0, n + 1, 1, 1);
2139             else
2140                 gtk_grid_attach(GTK_GRID(table), widget, 1, n, 1, 1);
2141 #endif
2142         }
2143     }
2144 
2145     gtk_widget_show_all(table);
2146 
2147     gtk_box_pack_start(GTK_BOX(content_area), table, TRUE, TRUE, 0);
2148 
2149     if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK)
2150         ret = retrieve_form_data(form_args, form_widgets);
2151 
2152     gtk_widget_destroy(dlg);
2153 
2154     free(form_widgets);
2155 
2156     return ret;
2157 }
2158 
2159 
2160 enum {
2161     FC_OPEN,
2162     FC_SAVE,
2163     FC_FOLDER
2164 };
2165 
2166 
run_filechooser(mpdm_t a,int type)2167 static mpdm_t run_filechooser(mpdm_t a, int type)
2168 /* openfile driver function */
2169 {
2170     GtkWidget *dlg;
2171     char *ptr;
2172     mpdm_t ret = NULL;
2173     gint response;
2174 
2175     /* 1# arg: prompt */
2176     ptr = v_to_utf8(mpdm_get_i(a, 0));
2177 
2178     switch (type) {
2179     case FC_OPEN:
2180         dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2181                                           GTK_FILE_CHOOSER_ACTION_OPEN,
2182                                           L("Cancel"),
2183                                           GTK_RESPONSE_CANCEL,
2184                                           L("OK"), GTK_RESPONSE_OK,
2185                                           NULL);
2186         break;
2187 
2188     case FC_SAVE:
2189         dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2190                                           GTK_FILE_CHOOSER_ACTION_SAVE,
2191                                           L("Cancel"),
2192                                           L("Cancel"), L("OK"),
2193                                           GTK_RESPONSE_OK, NULL);
2194         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER
2195                                                        (dlg), TRUE);
2196         break;
2197 
2198     case FC_FOLDER:
2199         dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2200                                           GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2201                                           L("Cancel"),
2202                                           GTK_RESPONSE_CANCEL,
2203                                           L("OK"), GTK_RESPONSE_OK,
2204                                           NULL);
2205         break;
2206     }
2207 
2208     g_free(ptr);
2209 
2210     /* override stupid GTK3 "optimal" current folder */
2211     char tmp[2048];
2212     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dlg), getcwd(tmp, sizeof(tmp)));
2213 
2214     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), TRUE);
2215     response = gtk_dialog_run(GTK_DIALOG(dlg));
2216 
2217     if (response == GTK_RESPONSE_OK) {
2218         gchar *filename;
2219         gchar *utf8name;
2220 
2221         filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
2222         utf8name = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
2223         g_free(filename);
2224 
2225         ret = MPDM_MBS(utf8name);
2226         g_free(utf8name);
2227     }
2228 
2229     gtk_widget_destroy(dlg);
2230 
2231     return ret;
2232 }
2233 
2234 
gtk_drv_openfile(mpdm_t a,mpdm_t ctxt)2235 static mpdm_t gtk_drv_openfile(mpdm_t a, mpdm_t ctxt)
2236 /* openfile driver function */
2237 {
2238     return run_filechooser(a, FC_OPEN);
2239 }
2240 
2241 
gtk_drv_savefile(mpdm_t a,mpdm_t ctxt)2242 static mpdm_t gtk_drv_savefile(mpdm_t a, mpdm_t ctxt)
2243 /* savefile driver function */
2244 {
2245     return run_filechooser(a, FC_SAVE);
2246 }
2247 
2248 
gtk_drv_openfolder(mpdm_t a,mpdm_t ctxt)2249 static mpdm_t gtk_drv_openfolder(mpdm_t a, mpdm_t ctxt)
2250 /* openfolder driver function */
2251 {
2252     return run_filechooser(a, FC_FOLDER);
2253 }
2254 
2255 
gtk_drv_update_ui(mpdm_t a,mpdm_t ctxt)2256 static mpdm_t gtk_drv_update_ui(mpdm_t a, mpdm_t ctxt)
2257 {
2258     build_fonts();
2259     build_colors();
2260     build_menu();
2261 
2262     gtk_widget_queue_draw(GTK_WIDGET(area));
2263 
2264     return NULL;
2265 }
2266 
2267 
gtk_drv_idle(mpdm_t a,mpdm_t ctxt)2268 static mpdm_t gtk_drv_idle(mpdm_t a, mpdm_t ctxt)
2269 {
2270     static guint timer_id = 0;
2271     int idle_msecs = (int) (mpdm_rval(mpdm_get_i(a, 0)) * 1000);
2272 
2273     if (timer_id)
2274         g_source_remove(timer_id);
2275 
2276     if (idle_msecs > 0)
2277         timer_id = g_timeout_add(idle_msecs, idle_callback, NULL);
2278     else
2279         timer_id = 0;
2280 
2281     return NULL;
2282 }
2283 
2284 
gtk_drv_busy(mpdm_t a,mpdm_t ctxt)2285 static mpdm_t gtk_drv_busy(mpdm_t a, mpdm_t ctxt)
2286 {
2287     int onoff = mpdm_ival(mpdm_get_i(a, 0));
2288 
2289     gdk_window_set_cursor(gtk_widget_get_window(window),
2290 #if CONFOPT_GTK == 2
2291                           gdk_cursor_new(
2292 #else
2293                           gdk_cursor_new_for_display(gdk_display_get_default(),
2294 #endif
2295                           onoff ? GDK_WATCH : GDK_LEFT_PTR));
2296 
2297     while (gtk_events_pending())
2298         gtk_main_iteration();
2299 
2300     return NULL;
2301 }
2302 
2303 
gtk_drv_main_loop(mpdm_t a,mpdm_t ctxt)2304 static mpdm_t gtk_drv_main_loop(mpdm_t a, mpdm_t ctxt)
2305 /* main loop */
2306 {
2307     if (!mp_exit_requested) {
2308         gtk_drv_render(mp_active(), 0);
2309 
2310         gtk_main();
2311     }
2312 
2313     return NULL;
2314 }
2315 
2316 
gtk_drv_shutdown(mpdm_t a,mpdm_t ctxt)2317 static mpdm_t gtk_drv_shutdown(mpdm_t a, mpdm_t ctxt)
2318 /* shutdown */
2319 {
2320     mpdm_t v;
2321 
2322     v = mpdm_get_wcs(MP, L"state");
2323     v = mpdm_set_wcs(v, MPDM_O(), L"window");
2324     mpdm_set_wcs(v, MPDM_I(ls_x), L"x");
2325     mpdm_set_wcs(v, MPDM_I(ls_y), L"y");
2326     mpdm_set_wcs(v, MPDM_I(ls_w), L"w");
2327     mpdm_set_wcs(v, MPDM_I(ls_h), L"h");
2328 
2329     if ((v = mpdm_get_wcs(MP, L"exit_message")) != NULL) {
2330         mpdm_write_wcs(stdout, mpdm_string(v));
2331         printf("\n");
2332     }
2333 
2334     return NULL;
2335 }
2336 
2337 
gtk_drv_menu(void)2338 static mpdm_t gtk_drv_menu(void)
2339 {
2340     if (!gtk_widget_get_visible(menu_bar))
2341         gtk_widget_show(menu_bar);
2342 
2343     return NULL;
2344 }
2345 
2346 
gtk_register_functions(void)2347 static void gtk_register_functions(void)
2348 {
2349     mpdm_t drv;
2350 
2351     drv = mpdm_get_wcs(mpdm_root(), L"mp_drv");
2352     mpdm_set_wcs(drv, MPDM_X(gtk_drv_main_loop),    L"main_loop");
2353     mpdm_set_wcs(drv, MPDM_X(gtk_drv_shutdown),     L"shutdown");
2354     mpdm_set_wcs(drv, MPDM_X(gtk_drv_clip_to_sys),  L"clip_to_sys");
2355     mpdm_set_wcs(drv, MPDM_X(gtk_drv_sys_to_clip),  L"sys_to_clip");
2356     mpdm_set_wcs(drv, MPDM_X(gtk_drv_update_ui),    L"update_ui");
2357     mpdm_set_wcs(drv, MPDM_X(gtk_drv_idle),         L"idle");
2358     mpdm_set_wcs(drv, MPDM_X(gtk_drv_busy),         L"busy");
2359     mpdm_set_wcs(drv, MPDM_X(gtk_drv_alert),        L"alert");
2360     mpdm_set_wcs(drv, MPDM_X(gtk_drv_confirm),      L"confirm");
2361     mpdm_set_wcs(drv, MPDM_X(gtk_drv_openfile),     L"openfile");
2362     mpdm_set_wcs(drv, MPDM_X(gtk_drv_savefile),     L"savefile");
2363     mpdm_set_wcs(drv, MPDM_X(gtk_drv_form),         L"form");
2364     mpdm_set_wcs(drv, MPDM_X(gtk_drv_openfolder),   L"openfolder");
2365     mpdm_set_wcs(drv, MPDM_X(gtk_drv_menu),         L"menu");
2366 }
2367 
2368 
gtk_drv_startup(mpdm_t a,mpdm_t ctxt)2369 static mpdm_t gtk_drv_startup(mpdm_t a, mpdm_t ctxt)
2370 /* driver initialization */
2371 {
2372     GtkWidget *vbox;
2373     GtkWidget *hbox;
2374 #if CONFOPT_GTK == 2
2375     GdkPixmap *pixmap;
2376     GdkPixmap *mask;
2377     GdkScreen *screen;
2378 #endif
2379 #if CONFOPT_GTK == 3
2380     GdkPixbuf *pixmap;
2381     GdkDisplay *display;
2382     GdkMonitor *monitor;
2383     GdkRectangle monitor_one_size;
2384 #endif
2385     mpdm_t v;
2386     int w, h;
2387     GtkTargetEntry targets[] = {
2388         {"text/plain", 0, 0},
2389         {"text/uri-list", 0, 1}
2390     };
2391 
2392     gtk_register_functions();
2393 
2394     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2395 
2396     gtk_window_set_title(GTK_WINDOW(window), "mp " VERSION);
2397 
2398     /* get real screen and pick a usable size for the main area */
2399 #if CONFOPT_GTK == 3
2400     display = gdk_display_get_default();
2401 
2402     monitor = gdk_display_get_monitor(display, 0);
2403 
2404     gdk_monitor_get_geometry(monitor, &monitor_one_size);
2405 
2406     w = (monitor_one_size.width * 3) / 4;
2407     h = (monitor_one_size.height * 2) / 3;
2408 #endif
2409 
2410 #if CONFOPT_GTK == 2
2411     screen = gtk_window_get_screen(GTK_WINDOW(window));
2412     if (gdk_screen_get_n_monitors(screen) > 1) {
2413        GdkRectangle monitor_one_size;
2414        gdk_screen_get_monitor_geometry(screen, 0, &monitor_one_size);
2415 
2416        w = (monitor_one_size.width * 3) / 4;
2417        h = (monitor_one_size.height * 2) / 3;
2418     }
2419     else {
2420        w = (gdk_screen_get_width(screen) * 3) / 4;
2421        h = (gdk_screen_get_height(screen) * 2) / 3;
2422     }
2423 #endif
2424 
2425     v = mpdm_get_wcs(MP, L"state");
2426     if ((v = mpdm_get_wcs(v, L"window")) == NULL) {
2427         v = mpdm_set_wcs(mpdm_get_wcs(MP, L"state"), MPDM_O(), L"window");
2428         mpdm_set_wcs(v, MPDM_I(0), L"x");
2429         mpdm_set_wcs(v, MPDM_I(0), L"y");
2430         mpdm_set_wcs(v, MPDM_I(w), L"w");
2431         mpdm_set_wcs(v, MPDM_I(h), L"h");
2432     }
2433 
2434     gtk_window_move(GTK_WINDOW(window),
2435         mpdm_ival(mpdm_get_wcs(v, L"x")), mpdm_ival(mpdm_get_wcs(v, L"y")));
2436     gtk_window_set_default_size(GTK_WINDOW(window),
2437         mpdm_ival(mpdm_get_wcs(v, L"w")), mpdm_ival(mpdm_get_wcs(v, L"h")));
2438 
2439     g_signal_connect(G_OBJECT(window), "delete_event",
2440                      G_CALLBACK(delete_event), NULL);
2441 
2442     g_signal_connect(G_OBJECT(window), "destroy",
2443                      G_CALLBACK(destroy), NULL);
2444 
2445     /* file tabs */
2446     file_tabs = gtk_notebook_new();
2447     gtk_notebook_set_tab_pos(GTK_NOTEBOOK(file_tabs), GTK_POS_TOP);
2448 
2449 #if CONFOPT_GTK == 2
2450     GTK_WIDGET_UNSET_FLAGS(file_tabs, GTK_CAN_FOCUS);
2451 #endif
2452 #if CONFOPT_GTK == 3
2453     gtk_widget_set_can_focus(file_tabs, FALSE);
2454 #endif
2455     gtk_notebook_set_scrollable(GTK_NOTEBOOK(file_tabs), 1);
2456 
2457 #if CONFOPT_GTK == 2
2458     vbox = gtk_vbox_new(FALSE, 2);
2459 #endif
2460 #if CONFOPT_GTK == 3
2461     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
2462 #endif
2463     gtk_container_add(GTK_CONTAINER(window), vbox);
2464 
2465     build_menu();
2466 
2467 #if CONFOPT_GTK == 2
2468     hbox = gtk_hbox_new(FALSE, 0);
2469 #endif
2470 #if CONFOPT_GTK == 3
2471     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2472 #endif
2473     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2474     gtk_box_pack_start(GTK_BOX(hbox), menu_bar, FALSE, FALSE, 0);
2475     gtk_box_pack_start(GTK_BOX(vbox), file_tabs, FALSE, FALSE, 0);
2476 
2477     gtk_notebook_popup_enable(GTK_NOTEBOOK(file_tabs));
2478 
2479     /* horizontal box holding the text and the scrollbar */
2480 #if CONFOPT_GTK == 2
2481     hbox = gtk_hbox_new(FALSE, 2);
2482 #endif
2483 #if CONFOPT_GTK == 3
2484     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
2485 #endif
2486     gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
2487 
2488     /* the Minimum Profit area */
2489     area = gtk_drawing_area_new();
2490     gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
2491     gtk_widget_set_events(GTK_WIDGET(area), GDK_BUTTON_PRESS_MASK |
2492                           GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
2493                           | GDK_LEAVE_NOTIFY_MASK
2494 #if CONFOPT_GTK == 3
2495                           | GDK_SMOOTH_SCROLL_MASK
2496 #endif
2497     );
2498 
2499     g_signal_connect(G_OBJECT(area), "configure_event",
2500                      G_CALLBACK(configure_event), NULL);
2501 
2502 #if CONFOPT_GTK == 2
2503     g_signal_connect(G_OBJECT(area), "expose_event",
2504 #endif
2505 #if CONFOPT_GTK == 3
2506     g_signal_connect(G_OBJECT(area), "draw",
2507 #endif
2508                      G_CALLBACK(expose_event), NULL);
2509 
2510     g_signal_connect(G_OBJECT(area), "realize", G_CALLBACK(realize), NULL);
2511 
2512     g_signal_connect(G_OBJECT(window), "key_press_event",
2513                      G_CALLBACK(key_press_event), NULL);
2514 
2515 #if 0
2516     g_signal_connect(G_OBJECT(window), "key_release_event",
2517                      G_CALLBACK(key_release_event), NULL);
2518 #endif
2519 
2520     g_signal_connect(G_OBJECT(area), "button_press_event",
2521                      G_CALLBACK(button_press_event), NULL);
2522 
2523     g_signal_connect(G_OBJECT(area), "button_release_event",
2524                      G_CALLBACK(button_release_event), NULL);
2525 
2526     g_signal_connect(G_OBJECT(area), "motion_notify_event",
2527                      G_CALLBACK(motion_notify_event), NULL);
2528 
2529     g_signal_connect(G_OBJECT(area), "scroll_event",
2530                      G_CALLBACK(scroll_event), NULL);
2531 
2532     gtk_drag_dest_set(area, GTK_DEST_DEFAULT_ALL, targets,
2533                       sizeof(targets) / sizeof(GtkTargetEntry),
2534                       GDK_ACTION_COPY);
2535     g_signal_connect(G_OBJECT(area), "drag_data_received",
2536                      G_CALLBACK(drag_data_received), NULL);
2537 
2538     g_signal_connect(G_OBJECT(file_tabs), "switch_page",
2539                      G_CALLBACK(switch_page), NULL);
2540 
2541     /* the scrollbar */
2542 #if CONFOPT_GTK == 2
2543     scrollbar = gtk_vscrollbar_new(NULL);
2544 #endif
2545 #if CONFOPT_GTK == 3
2546     scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
2547 #endif
2548     gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0);
2549 
2550     g_signal_connect(G_OBJECT
2551                      (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
2552                      "value_changed", G_CALLBACK(value_changed), NULL);
2553 
2554     /* the status bar */
2555     status = gtk_label_new("mp " VERSION);
2556     gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
2557 
2558 #if CONFOPT_GTK == 2
2559     gtk_misc_set_alignment(GTK_MISC(status), 0, .5);
2560 #endif
2561 #if CONFOPT_GTK == 3
2562     gtk_label_set_xalign(GTK_LABEL(status), 0.0);
2563     gtk_label_set_yalign(GTK_LABEL(status), 0.5);
2564 #endif
2565 
2566     gtk_label_set_justify(GTK_LABEL(status), GTK_JUSTIFY_LEFT);
2567 
2568     gtk_widget_show_all(window);
2569 
2570     /* set application icon */
2571 #if CONFOPT_GTK == 2
2572     pixmap = gdk_pixmap_create_from_xpm_d(window->window,
2573                                           &mask, NULL, mp_xpm);
2574     gdk_window_set_icon(window->window, NULL, pixmap, mask);
2575 #endif
2576 #if CONFOPT_GTK == 3
2577     pixmap = gdk_pixbuf_new_from_xpm_data((const char **)mp_xpm);
2578     gtk_window_set_icon(GTK_WINDOW(window), pixmap);
2579 #endif
2580 
2581     build_colors();
2582 
2583     if ((v = mpdm_get_wcs(MP, L"config")) != NULL &&
2584         mpdm_ival(mpdm_get_wcs(v, L"maximize")) > 0)
2585         maximize = 1;
2586 
2587 //    gtk_widget_hide(menu_bar);
2588 
2589     return NULL;
2590 }
2591 
2592 int gtk_drv_detect(int *argc, char ***argv)
2593 {
2594     int n, ret = 1;
2595 
2596     for (n = 0; n < *argc; n++) {
2597         if (strcmp(argv[0][n], "-txt") == 0)
2598             ret = 0;
2599     }
2600 
2601     if (ret) {
2602         if (gtk_init_check(argc, argv)) {
2603             mpdm_t drv;
2604 
2605             drv = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"mp_drv");
2606 
2607 #if CONFOPT_GTK == 3
2608             mpdm_set_wcs(drv, MPDM_S(L"gtk3"), L"id");
2609 #else
2610             mpdm_set_wcs(drv, MPDM_S(L"gtk2"), L"id");
2611 #endif
2612             mpdm_set_wcs(drv, MPDM_X(gtk_drv_startup), L"startup");
2613         }
2614         else
2615             ret = 0;
2616     }
2617 
2618     return ret;
2619 }
2620 
2621 #endif                          /* CONFOPT_GTK */
2622