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