Call mp.test_mtime() from inside mp.process_event().
[mp-5.x.git] / mpv_gtk.c
blob3abba2432af167399aa6ad775a0f14b348058ba2
1 /*
3 Minimum Profit - Programmer Text Editor
5 GTK driver.
7 Copyright (C) 1991-2011 Angel Ortega <angel@triptico.com>
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 http://www.triptico.com
27 #include "config.h"
29 #ifdef CONFOPT_GTK
31 #include <stdio.h>
32 #include <wchar.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <sys/stat.h>
38 #include <gtk/gtk.h>
39 #include <gdk/gdkkeysyms.h>
41 #include "mpdm.h"
42 #include "mpsl.h"
44 #include "mp.h"
46 #include "mp.xpm"
48 /** data **/
50 /* global data */
52 static GtkWidget *window = NULL;
53 static GtkWidget *file_tabs = NULL;
54 static GtkWidget *area = NULL;
55 static GtkWidget *scrollbar = NULL;
56 static GtkWidget *status = NULL;
57 static GtkWidget *menu_bar = NULL;
58 static GtkIMContext *im = NULL;
60 /* character read from the keyboard */
61 static wchar_t im_char[2];
63 /* font information */
64 static int font_width = 0;
65 static int font_height = 0;
66 static PangoFontDescription *font = NULL;
68 /* the attributes */
69 static GdkColor *inks = NULL;
70 static GdkColor *papers = NULL;
71 static int *underlines = NULL;
73 /* true if the selection is ours */
74 static int got_selection = 0;
76 /* hack for active waiting for the selection */
77 static int wait_for_selection = 0;
79 /* global modal status */
80 /* code for the 'normal' attribute */
81 static int normal_attr = 0;
83 /* mp.drv.form() controls */
85 static GtkWidget **form_widgets = NULL;
86 static mpdm_t form_args = NULL;
87 static mpdm_t form_values = NULL;
89 /* mouse down flag */
90 static int mouse_down = 0;
92 /* timer function */
93 static mpdm_t timer_func = NULL;
95 /* maximize wanted? */
96 static int maximize = 0;
98 /** code **/
100 /** support functions **/
102 #define LL(m) (m)
104 static char *wcs_to_utf8(const wchar_t * wptr)
105 /* converts a wcs to utf-8 */
107 char *ptr;
108 gsize i, o;
110 i = wcslen(wptr);
112 /* do the conversion */
113 ptr = g_convert((const gchar *) wptr, (i + 1) * sizeof(wchar_t),
114 "UTF-8", "WCHAR_T", NULL, &o, NULL);
116 return ptr;
120 static char *v_to_utf8(mpdm_t v)
122 char *ptr = NULL;
124 if (v != NULL) {
125 mpdm_ref(v);
126 ptr = wcs_to_utf8(mpdm_string(v));
127 mpdm_unref(v);
130 return ptr;
133 static wchar_t *utf8_to_wcs(const char *ptr)
134 /* converts utf-8 to wcs */
136 wchar_t *wptr;
137 gsize i, o;
139 i = strlen(ptr);
141 /* do the conversion */
142 wptr = (wchar_t *) g_convert((gchar *) ptr, i + 1,
143 "WCHAR_T", "UTF-8", NULL, &o, NULL);
145 return wptr;
149 static void update_window_size(void)
150 /* updates the viewport size in characters */
152 PangoLayout *pa;
153 int tx, ty;
154 mpdm_t v;
156 /* get font metrics */
157 pa = gtk_widget_create_pango_layout(area, "m");
158 pango_layout_set_font_description(pa, font);
159 pango_layout_get_pixel_size(pa, &font_width, &font_height);
160 g_object_unref(pa);
162 /* calculate the size in chars */
163 #if CONFOPT_GTK == 2
164 tx = (area->allocation.width / font_width);
165 ty = (area->allocation.height / font_height) + 1;
166 #endif
168 #if CONFOPT_GTK == 3
169 tx = (gtk_widget_get_allocated_width(area) / font_width);
170 ty = (gtk_widget_get_allocated_height(area) / font_height) + 1;
171 #endif
173 /* store the 'window' size */
174 v = mpdm_hget_s(mp, L"window");
175 mpdm_hset_s(v, L"tx", MPDM_I(tx));
176 mpdm_hset_s(v, L"ty", MPDM_I(ty));
178 /* rebuild the pixmap for the double buffer */
182 static void build_fonts(void)
183 /* builds the fonts */
185 char tmp[128];
186 int font_size = 12;
187 const char *font_face = "Mono";
188 mpdm_t c;
189 mpdm_t w = NULL;
191 if (font != NULL)
192 pango_font_description_free(font);
194 /* get current configuration */
195 if ((c = mpdm_hget_s(mp, L"config")) != NULL) {
196 mpdm_t v;
198 if ((v = mpdm_hget_s(c, L"font_size")) != NULL)
199 font_size = mpdm_ival(v);
200 else
201 mpdm_hset_s(c, L"font_size", MPDM_I(font_size));
203 if ((v = mpdm_hget_s(c, L"font_face")) != NULL) {
204 w = mpdm_ref(MPDM_2MBS(v->data));
205 font_face = w->data;
207 else
208 mpdm_hset_s(c, L"font_face", MPDM_MBS(font_face));
211 snprintf(tmp, sizeof(tmp) - 1, "%s %d", font_face, font_size);
212 tmp[sizeof(tmp) - 1] = '\0';
214 font = pango_font_description_from_string(tmp);
215 update_window_size();
217 mpdm_unref(w);
221 static void build_color(GdkColor * gdkcolor, int rgb)
222 /* builds a color */
224 gdkcolor->pixel = 0;
225 gdkcolor->blue = (rgb & 0x000000ff) << 8;
226 gdkcolor->green = (rgb & 0x0000ff00);
227 gdkcolor->red = (rgb & 0x00ff0000) >> 8;
229 #if CONFOPT_GTK == 2
230 gdk_colormap_alloc_color(gdk_colormap_get_system(), gdkcolor, FALSE,
231 TRUE);
232 #endif
236 static void build_colors(void)
237 /* builds the colors */
239 mpdm_t colors;
240 mpdm_t l;
241 mpdm_t c;
242 int n, s;
244 /* gets the color definitions and attribute names */
245 colors = mpdm_hget_s(mp, L"colors");
246 l = mpdm_ref(mpdm_keys(colors));
247 s = mpdm_size(l);
249 /* redim the structures */
250 inks = realloc(inks, sizeof(GdkColor) * s);
251 papers = realloc(papers, sizeof(GdkColor) * s);
252 underlines = realloc(underlines, sizeof(int) * s);
254 /* loop the colors */
255 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
256 mpdm_t d = mpdm_hget(colors, c);
257 mpdm_t v = mpdm_hget_s(d, L"gui");
259 /* store the 'normal' attribute */
260 if (wcscmp(mpdm_string(c), L"normal") == 0)
261 normal_attr = n;
263 /* store the attr */
264 mpdm_hset_s(d, L"attr", MPDM_I(n));
266 build_color(&inks[n], mpdm_ival(mpdm_aget(v, 0)));
267 build_color(&papers[n], mpdm_ival(mpdm_aget(v, 1)));
269 /* flags */
270 v = mpdm_hget_s(d, L"flags");
271 underlines[n] = mpdm_seek_s(v, L"underline", 1) != -1 ? 1 : 0;
273 if (mpdm_seek_s(v, L"reverse", 1) != -1) {
274 GdkColor t;
276 t = inks[n];
277 inks[n] = papers[n];
278 papers[n] = t;
282 mpdm_unref(l);
286 /** menu functions **/
288 static void redraw(void);
290 static void menu_item_callback(mpdm_t action)
291 /* menu click callback */
293 mp_process_action(action);
294 redraw();
296 if (mp_exit_requested)
297 gtk_main_quit();
301 static void build_submenu(GtkWidget * menu, mpdm_t labels)
302 /* build a submenu */
304 int n;
305 GtkWidget *menu_item;
307 mpdm_ref(labels);
309 for (n = 0; n < mpdm_size(labels); n++) {
310 /* get the action */
311 mpdm_t v = mpdm_aget(labels, n);
313 /* if the action is a separator... */
314 if (*((wchar_t *) v->data) == L'-')
315 menu_item = gtk_menu_item_new();
316 else {
317 char *ptr;
319 ptr = v_to_utf8(mp_menu_label(v));
320 menu_item = gtk_menu_item_new_with_label(ptr);
321 g_free(ptr);
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
331 g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
332 G_CALLBACK(menu_item_callback), v);
333 gtk_widget_show(menu_item);
336 mpdm_unref(labels);
340 static void build_menu(void)
341 /* builds the menu */
343 static mpdm_t prev_menu = NULL;
344 int n;
345 mpdm_t m;
347 /* gets the current menu */
348 if ((m = mpdm_hget_s(mp, L"menu")) == NULL)
349 return;
351 /* if it's the same, do nothing */
352 if (mpdm_cmp(m, prev_menu) == 0)
353 return;
355 /* create a new menu */
356 menu_bar = gtk_menu_bar_new();
358 for (n = 0; n < mpdm_size(m); n++) {
359 char *ptr;
360 mpdm_t mi;
361 mpdm_t v;
362 GtkWidget *menu;
363 GtkWidget *menu_item;
364 int i;
366 /* get the label and the items */
367 mi = mpdm_aget(m, n);
368 v = mpdm_aget(mi, 0);
370 if ((ptr = v_to_utf8(mpdm_gettext(v))) == NULL)
371 continue;
373 /* change the & by _ for the mnemonic */
374 for (i = 0; ptr[i]; i++)
375 if (ptr[i] == '&')
376 ptr[i] = '_';
378 /* add the menu and the label */
379 menu = gtk_menu_new();
380 menu_item = gtk_menu_item_new_with_mnemonic(ptr);
381 g_free(ptr);
383 gtk_widget_show(menu_item);
384 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
386 #if CONFOPT_GTK == 2
387 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_item);
388 #endif
389 #if CONFOPT_GTK == 3
390 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_item);
391 #endif
393 /* now loop the items */
394 build_submenu(menu, mpdm_aget(mi, 1));
399 /** main area drawing functions **/
401 static void switch_page(GtkNotebook * notebook, gpointer * page,
402 gint pg_num, gpointer data)
403 /* 'switch_page' handler (filetabs) */
405 /* sets the active one */
406 mpdm_hset_s(mp, L"active_i", MPDM_I(pg_num));
408 gtk_widget_grab_focus(area);
409 redraw();
413 static void draw_filetabs(void)
414 /* draws the filetabs */
416 static mpdm_t last = NULL;
417 mpdm_t names;
418 int n;
420 names = mpdm_ref(mp_get_doc_names());
422 /* disconnect redraw signal to avoid infinite loops */
423 g_signal_handlers_disconnect_by_func(G_OBJECT(file_tabs),
424 G_CALLBACK(switch_page), NULL);
426 /* is the list different from the previous one? */
427 if (mpdm_cmp(names, last) != 0) {
429 /* delete the current tabs */
430 for (n = 0; n < mpdm_size(last); n++)
431 gtk_notebook_remove_page(GTK_NOTEBOOK(file_tabs), 0);
433 /* create the new ones */
434 for (n = 0; n < mpdm_size(names); n++) {
435 GtkWidget *p;
436 GtkWidget *f;
437 char *ptr;
438 mpdm_t v = mpdm_aget(names, n);
440 if ((ptr = v_to_utf8(v)) != NULL) {
441 p = gtk_label_new(ptr);
442 gtk_widget_show(p);
444 f = gtk_frame_new(NULL);
445 gtk_widget_show(f);
447 gtk_notebook_append_page(GTK_NOTEBOOK(file_tabs), f, p);
449 g_free(ptr);
453 /* store for the next time */
454 mpdm_unref(last);
455 last = mpdm_ref(names);
458 mpdm_unref(names);
460 /* set the active one */
461 gtk_notebook_set_current_page(GTK_NOTEBOOK(file_tabs),
462 mpdm_ival(mpdm_hget_s(mp, L"active_i")));
464 /* reconnect signal */
465 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
466 G_CALLBACK(switch_page), NULL);
468 gtk_widget_grab_focus(area);
472 static void draw_status(void)
473 /* draws the status line */
475 char *ptr;
477 if ((ptr = v_to_utf8(mp_build_status_line())) != NULL) {
478 gtk_label_set_text(GTK_LABEL(status), ptr);
479 g_free(ptr);
484 static gint scroll_event(GtkWidget * widget, GdkEventScroll * event)
485 /* 'scroll_event' handler (mouse wheel) */
487 wchar_t *ptr = NULL;
489 switch (event->direction) {
490 case GDK_SCROLL_UP:
491 ptr = L"mouse-wheel-up";
492 break;
493 case GDK_SCROLL_DOWN:
494 ptr = L"mouse-wheel-down";
495 break;
496 case GDK_SCROLL_LEFT:
497 ptr = L"mouse-wheel-left";
498 break;
499 case GDK_SCROLL_RIGHT:
500 ptr = L"mouse-wheel-right";
501 break;
504 if (ptr != NULL)
505 mp_process_event(MPDM_S(ptr));
507 redraw();
509 return 0;
513 static void value_changed(GtkAdjustment * adj, gpointer * data)
514 /* 'value_changed' handler (scrollbar) */
516 int i = (int) gtk_adjustment_get_value(adj);
517 mpdm_t doc;
518 mpdm_t txt;
519 int y;
521 /* get current y position */
522 doc = mp_active();
523 txt = mpdm_hget_s(doc, L"txt");
524 y = mpdm_ival(mpdm_hget_s(txt, L"y"));
526 /* if it's different, set and redraw */
527 if (y != i) {
528 mp_set_y(doc, i);
529 mpdm_hset_s(txt, L"vy", MPDM_I(i));
530 redraw();
535 static void draw_scrollbar(void)
536 /* updates the scrollbar */
538 GtkAdjustment *adjustment;
539 mpdm_t d;
540 mpdm_t v;
541 int pos, size, max;
543 /* gets the active document */
544 if ((d = mp_active()) == NULL)
545 return;
547 /* get the coordinates */
548 v = mpdm_hget_s(d, L"txt");
549 pos = mpdm_ival(mpdm_hget_s(v, L"vy"));
550 max = mpdm_size(mpdm_hget_s(v, L"lines"));
552 v = mpdm_hget_s(mp, L"window");
553 size = mpdm_ival(mpdm_hget_s(v, L"ty"));
555 adjustment = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
557 /* disconnect to avoid infinite loops */
558 g_signal_handlers_disconnect_by_func(G_OBJECT(adjustment),
559 G_CALLBACK(value_changed), NULL);
561 gtk_adjustment_set_step_increment(adjustment, (gdouble) 1);
562 gtk_adjustment_set_upper(adjustment, (gdouble) max);
563 gtk_adjustment_set_page_size(adjustment, (gdouble) size);
564 gtk_adjustment_set_page_increment(adjustment, (gdouble) size);
565 gtk_adjustment_set_value(adjustment, (gdouble) pos);
567 gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment);
569 gtk_adjustment_changed(adjustment);
570 gtk_adjustment_value_changed(adjustment);
572 /* reattach again */
573 g_signal_connect(G_OBJECT
574 (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
575 "value_changed", G_CALLBACK(value_changed), NULL);
579 static void gtk_drv_paint(mpdm_t doc, int optimize)
580 /* GTK document draw function */
582 GdkRectangle gr;
583 cairo_t *cr;
584 mpdm_t d = NULL;
585 int n, m;
587 if (maximize)
588 gtk_window_maximize(GTK_WINDOW(window));
590 /* no gc? create it */
591 if (font == NULL)
592 build_fonts();
594 if ((d = mp_draw(doc, optimize)) == NULL)
595 return;
597 mpdm_ref(d);
599 gr.x = 0;
600 gr.y = 0;
602 #if CONFOPT_GTK == 2
603 gr.width = area->allocation.width;
604 #endif
605 #if CONFOPT_GTK == 3
606 gr.width = gtk_widget_get_allocated_width(area);
607 #endif
609 gr.height = font_height;
611 cr = gdk_cairo_create(gtk_widget_get_window(area));
612 for (n = 0; n < mpdm_size(d); n++) {
613 PangoLayout *pl;
614 PangoAttrList *pal;
615 mpdm_t l = mpdm_aget(d, n);
616 char *str = NULL;
617 int u, p;
619 if (l == NULL)
620 continue;
622 /* create the pango stuff */
623 pl = gtk_widget_create_pango_layout(area, NULL);
624 pango_layout_set_font_description(pl, font);
625 pal = pango_attr_list_new();
627 for (m = u = p = 0; m < mpdm_size(l); m++, u = p) {
628 PangoAttribute *pa;
629 int attr;
630 mpdm_t s;
631 char *ptr;
633 /* get the attribute and the string */
634 attr = mpdm_ival(mpdm_aget(l, m++));
635 s = mpdm_aget(l, m);
637 /* convert the string to utf8 */
638 ptr = v_to_utf8(s);
640 /* add to the full line */
641 str = mpdm_poke(str, &p, ptr, strlen(ptr), 1);
643 g_free(ptr);
645 /* create the background if it's
646 different from the default */
647 if (papers[attr].red != papers[normal_attr].red ||
648 papers[attr].green != papers[normal_attr].green ||
649 papers[attr].blue != papers[normal_attr].blue) {
650 pa = pango_attr_background_new(papers[attr].red,
651 papers[attr].green,
652 papers[attr].blue);
654 pa->start_index = u;
655 pa->end_index = p;
657 pango_attr_list_insert(pal, pa);
660 /* underline? */
661 if (underlines[attr]) {
662 pa = pango_attr_underline_new(TRUE);
664 pa->start_index = u;
665 pa->end_index = p;
667 pango_attr_list_insert(pal, pa);
670 /* foreground color */
671 pa = pango_attr_foreground_new(inks[attr].red,
672 inks[attr].green,
673 inks[attr].blue);
675 pa->start_index = u;
676 pa->end_index = p;
678 pango_attr_list_insert(pal, pa);
681 /* store the attributes */
682 pango_layout_set_attributes(pl, pal);
683 pango_attr_list_unref(pal);
685 /* store and free the text */
686 pango_layout_set_text(pl, str, p);
687 free(str);
689 /* draw the background */
690 gdk_cairo_set_source_color(cr, &papers[normal_attr]);
691 cairo_rectangle(cr, 0, n * font_height, gr.width, gr.height);
692 cairo_fill(cr);
694 /* draw the text */
695 cairo_move_to(cr, 2, n * font_height);
696 pango_cairo_show_layout(cr, pl);
698 g_object_unref(pl);
701 cairo_destroy(cr);
702 mpdm_unref(d);
704 draw_filetabs();
705 draw_scrollbar();
706 draw_status();
710 static void redraw(void)
712 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
713 gtk_drv_paint(mp_active(), 0);
717 static gint delete_event(GtkWidget * w, GdkEvent * e, gpointer data)
718 /* 'delete_event' handler */
720 mp_process_event(MPDM_LS(L"close-window"));
722 return mp_exit_requested ? FALSE : TRUE;
726 static void destroy(GtkWidget * w, gpointer data)
727 /* 'destroy' handler */
729 gtk_main_quit();
733 static gint key_release_event(GtkWidget * widget, GdkEventKey * event,
734 gpointer data)
735 /* 'key_release_event' handler */
737 if (mp_keypress_throttle(0))
738 gtk_drv_paint(mp_active(), 0);
740 return 0;
744 #if CONFOPT_GTK == 2
745 #define MP_KEY_Up GDK_Up
746 #define MP_KEY_Down GDK_Down
747 #define MP_KEY_Left GDK_Left
748 #define MP_KEY_Right GDK_Right
749 #define MP_KEY_Prior GDK_Prior
750 #define MP_KEY_Next GDK_Next
751 #define MP_KEY_Home GDK_Home
752 #define MP_KEY_End GDK_End
753 #define MP_KEY_space GDK_space
754 #define MP_KEY_KP_Add GDK_KP_Add
755 #define MP_KEY_KP_Subtract GDK_KP_Subtract
756 #define MP_KEY_KP_Multiply GDK_KP_Multiply
757 #define MP_KEY_KP_Divide GDK_KP_Divide
758 #define MP_KEY_F1 GDK_F1
759 #define MP_KEY_F2 GDK_F2
760 #define MP_KEY_F3 GDK_F3
761 #define MP_KEY_F4 GDK_F4
762 #define MP_KEY_F5 GDK_F5
763 #define MP_KEY_F6 GDK_F6
764 #define MP_KEY_F7 GDK_F7
765 #define MP_KEY_F8 GDK_F8
766 #define MP_KEY_F9 GDK_F9
767 #define MP_KEY_F10 GDK_F10
768 #define MP_KEY_F11 GDK_F11
769 #define MP_KEY_F12 GDK_F12
770 #define MP_KEY_KP_Enter GDK_KP_Enter
771 #define MP_KEY_Return GDK_Return
772 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
773 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
774 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
775 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
776 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
777 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
778 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
779 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
780 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
781 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
782 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
783 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
784 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
785 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
786 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
787 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
788 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
789 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
790 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
791 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
792 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
793 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
794 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
795 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
796 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
797 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
798 #define MP_KEY_Up GDK_Up
799 #define MP_KEY_Down GDK_Down
800 #define MP_KEY_Left GDK_Left
801 #define MP_KEY_Right GDK_Right
802 #define MP_KEY_Prior GDK_Prior
803 #define MP_KEY_Next GDK_Next
804 #define MP_KEY_Home GDK_Home
805 #define MP_KEY_End GDK_End
806 #define MP_KEY_space GDK_space
807 #define MP_KEY_KP_Add GDK_KP_Add
808 #define MP_KEY_KP_Subtract GDK_KP_Subtract
809 #define MP_KEY_KP_Multiply GDK_KP_Multiply
810 #define MP_KEY_KP_Divide GDK_KP_Divide
811 #define MP_KEY_F1 GDK_F1
812 #define MP_KEY_F2 GDK_F2
813 #define MP_KEY_F3 GDK_F3
814 #define MP_KEY_F4 GDK_F4
815 #define MP_KEY_F5 GDK_F5
816 #define MP_KEY_F6 GDK_F6
817 #define MP_KEY_F7 GDK_F7
818 #define MP_KEY_F8 GDK_F8
819 #define MP_KEY_F9 GDK_F9
820 #define MP_KEY_F10 GDK_F10
821 #define MP_KEY_F11 GDK_F11
822 #define MP_KEY_F12 GDK_F12
823 #define MP_KEY_KP_Enter GDK_KP_Enter
824 #define MP_KEY_Return GDK_Return
825 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
826 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
827 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
828 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
829 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
830 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
831 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
832 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
833 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
834 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
835 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
836 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
837 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
838 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
839 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
840 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
841 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
842 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
843 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
844 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
845 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
846 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
847 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
848 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
849 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
850 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
851 #define MP_KEY_Up GDK_Up
852 #define MP_KEY_Down GDK_Down
853 #define MP_KEY_Left GDK_Left
854 #define MP_KEY_Right GDK_Right
855 #define MP_KEY_Prior GDK_Prior
856 #define MP_KEY_Next GDK_Next
857 #define MP_KEY_Home GDK_Home
858 #define MP_KEY_End GDK_End
859 #define MP_KEY_space GDK_space
860 #define MP_KEY_KP_Add GDK_KP_Add
861 #define MP_KEY_KP_Subtract GDK_KP_Subtract
862 #define MP_KEY_KP_Multiply GDK_KP_Multiply
863 #define MP_KEY_KP_Divide GDK_KP_Divide
864 #define MP_KEY_F1 GDK_F1
865 #define MP_KEY_F2 GDK_F2
866 #define MP_KEY_F3 GDK_F3
867 #define MP_KEY_F4 GDK_F4
868 #define MP_KEY_F5 GDK_F5
869 #define MP_KEY_F6 GDK_F6
870 #define MP_KEY_F7 GDK_F7
871 #define MP_KEY_F8 GDK_F8
872 #define MP_KEY_F9 GDK_F9
873 #define MP_KEY_F10 GDK_F10
874 #define MP_KEY_F11 GDK_F11
875 #define MP_KEY_F12 GDK_F12
876 #define MP_KEY_Insert GDK_Insert
877 #define MP_KEY_BackSpace GDK_BackSpace
878 #define MP_KEY_Delete GDK_Delete
879 #define MP_KEY_KP_Enter GDK_KP_Enter
880 #define MP_KEY_Return GDK_Return
881 #define MP_KEY_Tab GDK_Tab
882 #define MP_KEY_ISO_Left_Tab GDK_ISO_Left_Tab
883 #define MP_KEY_Escape GDK_Escape
884 #endif /* CONFOPT_GTK == 2 */
886 #if CONFOPT_GTK == 3
887 #define MP_KEY_Up GDK_KEY_Up
888 #define MP_KEY_Down GDK_KEY_Down
889 #define MP_KEY_Left GDK_KEY_Left
890 #define MP_KEY_Right GDK_KEY_Right
891 #define MP_KEY_Prior GDK_KEY_Prior
892 #define MP_KEY_Next GDK_KEY_Next
893 #define MP_KEY_Home GDK_KEY_Home
894 #define MP_KEY_End GDK_KEY_End
895 #define MP_KEY_space GDK_KEY_space
896 #define MP_KEY_KP_Add GDK_KEY_KP_Add
897 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
898 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
899 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
900 #define MP_KEY_F1 GDK_KEY_F1
901 #define MP_KEY_F2 GDK_KEY_F2
902 #define MP_KEY_F3 GDK_KEY_F3
903 #define MP_KEY_F4 GDK_KEY_F4
904 #define MP_KEY_F5 GDK_KEY_F5
905 #define MP_KEY_F6 GDK_KEY_F6
906 #define MP_KEY_F7 GDK_KEY_F7
907 #define MP_KEY_F8 GDK_KEY_F8
908 #define MP_KEY_F9 GDK_KEY_F9
909 #define MP_KEY_F10 GDK_KEY_F10
910 #define MP_KEY_F11 GDK_KEY_F11
911 #define MP_KEY_F12 GDK_KEY_F12
912 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
913 #define MP_KEY_Return GDK_KEY_Return
914 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
915 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
916 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
917 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
918 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
919 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
920 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
921 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
922 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
923 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
924 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
925 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
926 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
927 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
928 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
929 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
930 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
931 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
932 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
933 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
934 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
935 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
936 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
937 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
938 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
939 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
940 #define MP_KEY_Up GDK_KEY_Up
941 #define MP_KEY_Down GDK_KEY_Down
942 #define MP_KEY_Left GDK_KEY_Left
943 #define MP_KEY_Right GDK_KEY_Right
944 #define MP_KEY_Prior GDK_KEY_Prior
945 #define MP_KEY_Next GDK_KEY_Next
946 #define MP_KEY_Home GDK_KEY_Home
947 #define MP_KEY_End GDK_KEY_End
948 #define MP_KEY_space GDK_KEY_space
949 #define MP_KEY_KP_Add GDK_KEY_KP_Add
950 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
951 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
952 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
953 #define MP_KEY_F1 GDK_KEY_F1
954 #define MP_KEY_F2 GDK_KEY_F2
955 #define MP_KEY_F3 GDK_KEY_F3
956 #define MP_KEY_F4 GDK_KEY_F4
957 #define MP_KEY_F5 GDK_KEY_F5
958 #define MP_KEY_F6 GDK_KEY_F6
959 #define MP_KEY_F7 GDK_KEY_F7
960 #define MP_KEY_F8 GDK_KEY_F8
961 #define MP_KEY_F9 GDK_KEY_F9
962 #define MP_KEY_F10 GDK_KEY_F10
963 #define MP_KEY_F11 GDK_KEY_F11
964 #define MP_KEY_F12 GDK_KEY_F12
965 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
966 #define MP_KEY_Return GDK_KEY_Return
967 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
968 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
969 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
970 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
971 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
972 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
973 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
974 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
975 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
976 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
977 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
978 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
979 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
980 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
981 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
982 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
983 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
984 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
985 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
986 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
987 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
988 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
989 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
990 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
991 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
992 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
993 #define MP_KEY_Up GDK_KEY_Up
994 #define MP_KEY_Down GDK_KEY_Down
995 #define MP_KEY_Left GDK_KEY_Left
996 #define MP_KEY_Right GDK_KEY_Right
997 #define MP_KEY_Prior GDK_KEY_Prior
998 #define MP_KEY_Next GDK_KEY_Next
999 #define MP_KEY_Home GDK_KEY_Home
1000 #define MP_KEY_End GDK_KEY_End
1001 #define MP_KEY_space GDK_KEY_space
1002 #define MP_KEY_KP_Add GDK_KEY_KP_Add
1003 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
1004 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
1005 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
1006 #define MP_KEY_F1 GDK_KEY_F1
1007 #define MP_KEY_F2 GDK_KEY_F2
1008 #define MP_KEY_F3 GDK_KEY_F3
1009 #define MP_KEY_F4 GDK_KEY_F4
1010 #define MP_KEY_F5 GDK_KEY_F5
1011 #define MP_KEY_F6 GDK_KEY_F6
1012 #define MP_KEY_F7 GDK_KEY_F7
1013 #define MP_KEY_F8 GDK_KEY_F8
1014 #define MP_KEY_F9 GDK_KEY_F9
1015 #define MP_KEY_F10 GDK_KEY_F10
1016 #define MP_KEY_F11 GDK_KEY_F11
1017 #define MP_KEY_F12 GDK_KEY_F12
1018 #define MP_KEY_Insert GDK_KEY_Insert
1019 #define MP_KEY_BackSpace GDK_KEY_BackSpace
1020 #define MP_KEY_Delete GDK_KEY_Delete
1021 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
1022 #define MP_KEY_Return GDK_KEY_Return
1023 #define MP_KEY_Tab GDK_KEY_Tab
1024 #define MP_KEY_ISO_Left_Tab GDK_KEY_ISO_Left_Tab
1025 #define MP_KEY_Escape GDK_KEY_Escape
1026 #endif /* CONFOPT_GTK == 3 */
1028 static gint key_press_event(GtkWidget * widget, GdkEventKey * event,
1029 gpointer data)
1030 /* 'key_press_event' handler */
1032 wchar_t *ptr = NULL;
1034 gtk_im_context_filter_keypress(im, event);
1036 /* set mp.shift_pressed */
1037 if (event->state & (GDK_SHIFT_MASK))
1038 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
1040 /* reserve alt for menu mnemonics */
1041 /* if (GDK_MOD1_MASK & event->state)
1042 return(0);*/
1044 if (event->state & (GDK_CONTROL_MASK)) {
1045 switch (event->keyval) {
1046 case MP_KEY_Up:
1047 ptr = L"ctrl-cursor-up";
1048 break;
1049 case MP_KEY_Down:
1050 ptr = L"ctrl-cursor-down";
1051 break;
1052 case MP_KEY_Left:
1053 ptr = L"ctrl-cursor-left";
1054 break;
1055 case MP_KEY_Right:
1056 ptr = L"ctrl-cursor-right";
1057 break;
1058 case MP_KEY_Prior:
1059 ptr = L"ctrl-page-up";
1060 break;
1061 case MP_KEY_Next:
1062 ptr = L"ctrl-page-down";
1063 break;
1064 case MP_KEY_Home:
1065 ptr = L"ctrl-home";
1066 break;
1067 case MP_KEY_End:
1068 ptr = L"ctrl-end";
1069 break;
1070 case MP_KEY_space:
1071 ptr = L"ctrl-space";
1072 break;
1073 case MP_KEY_KP_Add:
1074 ptr = L"ctrl-kp-plus";
1075 break;
1076 case MP_KEY_KP_Subtract:
1077 ptr = L"ctrl-kp-minus";
1078 break;
1079 case MP_KEY_KP_Multiply:
1080 ptr = L"ctrl-kp-multiply";
1081 break;
1082 case MP_KEY_KP_Divide:
1083 ptr = L"ctrl-kp-divide";
1084 break;
1085 case MP_KEY_F1:
1086 ptr = L"ctrl-f1";
1087 break;
1088 case MP_KEY_F2:
1089 ptr = L"ctrl-f2";
1090 break;
1091 case MP_KEY_F3:
1092 ptr = L"ctrl-f3";
1093 break;
1094 case MP_KEY_F4:
1095 ptr = L"ctrl-f4";
1096 break;
1097 case MP_KEY_F5:
1098 ptr = L"ctrl-f5";
1099 break;
1100 case MP_KEY_F6:
1101 ptr = L"ctrl-f6";
1102 break;
1103 case MP_KEY_F7:
1104 ptr = L"ctrl-f7";
1105 break;
1106 case MP_KEY_F8:
1107 ptr = L"ctrl-f8";
1108 break;
1109 case MP_KEY_F9:
1110 ptr = L"ctrl-f9";
1111 break;
1112 case MP_KEY_F10:
1113 ptr = L"ctrl-f10";
1114 break;
1115 case MP_KEY_F11:
1116 ptr = L"ctrl-f11";
1117 break;
1118 case MP_KEY_F12:
1119 ptr = L"ctrl-f12";
1120 break;
1121 case MP_KEY_KP_Enter:
1122 case MP_KEY_Return:
1123 ptr = L"ctrl-enter";
1124 break;
1125 case MP_KEY_Cyrillic_ve:
1126 ptr = L"ctrl-d";
1127 break;
1128 case MP_KEY_Cyrillic_a:
1129 ptr = L"ctrl-f";
1130 break;
1131 case MP_KEY_Cyrillic_tse:
1132 ptr = L"ctrl-w";
1133 break;
1134 case MP_KEY_Cyrillic_de:
1135 ptr = L"ctrl-l";
1136 break;
1137 case MP_KEY_Cyrillic_ie:
1138 ptr = L"ctrl-t";
1139 break;
1140 case MP_KEY_Cyrillic_ef:
1141 ptr = L"ctrl-a";
1142 break;
1143 case MP_KEY_Cyrillic_ghe:
1144 ptr = L"ctrl-u";
1145 break;
1146 case MP_KEY_Cyrillic_i:
1147 ptr = L"ctrl-b";
1148 break;
1149 case MP_KEY_Cyrillic_shorti:
1150 ptr = L"ctrl-q";
1151 break;
1152 case MP_KEY_Cyrillic_ka:
1153 ptr = L"ctrl-r";
1154 break;
1155 case MP_KEY_Cyrillic_el:
1156 ptr = L"ctrl-k";
1157 break;
1158 case MP_KEY_Cyrillic_em:
1159 ptr = L"ctrl-v";
1160 break;
1161 case MP_KEY_Cyrillic_en:
1162 ptr = L"ctrl-y";
1163 break;
1164 case MP_KEY_Cyrillic_o:
1165 ptr = L"ctrl-j";
1166 break;
1167 case MP_KEY_Cyrillic_pe:
1168 ptr = L"ctrl-g";
1169 break;
1170 case MP_KEY_Cyrillic_ya:
1171 ptr = L"ctrl-z";
1172 break;
1173 case MP_KEY_Cyrillic_er:
1174 ptr = L"ctrl-h";
1175 break;
1176 case MP_KEY_Cyrillic_es:
1177 ptr = L"ctrl-c";
1178 break;
1179 case MP_KEY_Cyrillic_te:
1180 ptr = L"ctrl-n";
1181 break;
1182 case MP_KEY_Cyrillic_softsign:
1183 ptr = L"ctrl-m";
1184 break;
1185 case MP_KEY_Cyrillic_yeru:
1186 ptr = L"ctrl-s";
1187 break;
1188 case MP_KEY_Cyrillic_ze:
1189 ptr = L"ctrl-p";
1190 break;
1191 case MP_KEY_Cyrillic_sha:
1192 ptr = L"ctrl-i";
1193 break;
1194 case MP_KEY_Cyrillic_e:
1195 ptr = L"ctrl-t";
1196 break;
1197 case MP_KEY_Cyrillic_shcha:
1198 ptr = L"ctrl-o";
1199 break;
1200 case MP_KEY_Cyrillic_che:
1201 ptr = L"ctrl-x";
1202 break;
1205 if (ptr == NULL) {
1206 char c = event->keyval & 0xdf;
1208 switch (c) {
1209 case 'A':
1210 ptr = L"ctrl-a";
1211 break;
1212 case 'B':
1213 ptr = L"ctrl-b";
1214 break;
1215 case 'C':
1216 ptr = L"ctrl-c";
1217 break;
1218 case 'D':
1219 ptr = L"ctrl-d";
1220 break;
1221 case 'E':
1222 ptr = L"ctrl-e";
1223 break;
1224 case 'F':
1225 ptr = L"ctrl-f";
1226 break;
1227 case 'G':
1228 ptr = L"ctrl-g";
1229 break;
1230 case 'H':
1231 ptr = L"ctrl-h";
1232 break;
1233 case 'I':
1234 ptr = L"ctrl-i";
1235 break;
1236 case 'J':
1237 ptr = L"ctrl-j";
1238 break;
1239 case 'K':
1240 ptr = L"ctrl-k";
1241 break;
1242 case 'L':
1243 ptr = L"ctrl-l";
1244 break;
1245 case 'M':
1246 ptr = L"ctrl-m";
1247 break;
1248 case 'N':
1249 ptr = L"ctrl-n";
1250 break;
1251 case 'O':
1252 ptr = L"ctrl-o";
1253 break;
1254 case 'P':
1255 ptr = L"ctrl-p";
1256 break;
1257 case 'Q':
1258 ptr = L"ctrl-q";
1259 break;
1260 case 'R':
1261 ptr = L"ctrl-r";
1262 break;
1263 case 'S':
1264 ptr = L"ctrl-s";
1265 break;
1266 case 'T':
1267 ptr = L"ctrl-t";
1268 break;
1269 case 'U':
1270 ptr = L"ctrl-u";
1271 break;
1272 case 'V':
1273 ptr = L"ctrl-v";
1274 break;
1275 case 'W':
1276 ptr = L"ctrl-w";
1277 break;
1278 case 'X':
1279 ptr = L"ctrl-x";
1280 break;
1281 case 'Y':
1282 ptr = L"ctrl-y";
1283 break;
1284 case 'Z':
1285 ptr = L"ctrl-z";
1286 break;
1290 else
1291 if (event->state & (GDK_MOD1_MASK)) {
1292 switch (event->keyval) {
1293 case MP_KEY_Up:
1294 ptr = L"alt-cursor-up";
1295 break;
1296 case MP_KEY_Down:
1297 ptr = L"alt-cursor-down";
1298 break;
1299 case MP_KEY_Left:
1300 ptr = L"alt-cursor-left";
1301 break;
1302 case MP_KEY_Right:
1303 ptr = L"alt-cursor-right";
1304 break;
1305 case MP_KEY_Prior:
1306 ptr = L"alt-page-up";
1307 break;
1308 case MP_KEY_Next:
1309 ptr = L"alt-page-down";
1310 break;
1311 case MP_KEY_Home:
1312 ptr = L"alt-home";
1313 break;
1314 case MP_KEY_End:
1315 ptr = L"alt-end";
1316 break;
1317 case MP_KEY_space:
1318 ptr = L"alt-space";
1319 break;
1320 case MP_KEY_KP_Add:
1321 ptr = L"alt-kp-plus";
1322 break;
1323 case MP_KEY_KP_Subtract:
1324 ptr = L"alt-kp-minus";
1325 break;
1326 case MP_KEY_KP_Multiply:
1327 ptr = L"alt-kp-multiply";
1328 break;
1329 case MP_KEY_KP_Divide:
1330 ptr = L"alt-kp-divide";
1331 break;
1332 case MP_KEY_F1:
1333 ptr = L"alt-f1";
1334 break;
1335 case MP_KEY_F2:
1336 ptr = L"alt-f2";
1337 break;
1338 case MP_KEY_F3:
1339 ptr = L"alt-f3";
1340 break;
1341 case MP_KEY_F4:
1342 ptr = L"alt-f4";
1343 break;
1344 case MP_KEY_F5:
1345 ptr = L"alt-f5";
1346 break;
1347 case MP_KEY_F6:
1348 ptr = L"alt-f6";
1349 break;
1350 case MP_KEY_F7:
1351 ptr = L"alt-f7";
1352 break;
1353 case MP_KEY_F8:
1354 ptr = L"alt-f8";
1355 break;
1356 case MP_KEY_F9:
1357 ptr = L"alt-f9";
1358 break;
1359 case MP_KEY_F10:
1360 ptr = L"alt-f10";
1361 break;
1362 case MP_KEY_F11:
1363 ptr = L"alt-f11";
1364 break;
1365 case MP_KEY_F12:
1366 ptr = L"alt-f12";
1367 break;
1368 case MP_KEY_KP_Enter:
1369 case MP_KEY_Return:
1370 ptr = L"alt-enter";
1371 break;
1372 case MP_KEY_Cyrillic_ve:
1373 ptr = L"alt-d";
1374 break;
1375 case MP_KEY_Cyrillic_a:
1376 ptr = L"alt-f";
1377 break;
1378 case MP_KEY_Cyrillic_tse:
1379 ptr = L"alt-w";
1380 break;
1381 case MP_KEY_Cyrillic_de:
1382 ptr = L"alt-l";
1383 break;
1384 case MP_KEY_Cyrillic_ie:
1385 ptr = L"alt-t";
1386 break;
1387 case MP_KEY_Cyrillic_ef:
1388 ptr = L"alt-a";
1389 break;
1390 case MP_KEY_Cyrillic_ghe:
1391 ptr = L"alt-u";
1392 break;
1393 case MP_KEY_Cyrillic_i:
1394 ptr = L"alt-b";
1395 break;
1396 case MP_KEY_Cyrillic_shorti:
1397 ptr = L"alt-q";
1398 break;
1399 case MP_KEY_Cyrillic_ka:
1400 ptr = L"alt-r";
1401 break;
1402 case MP_KEY_Cyrillic_el:
1403 ptr = L"alt-k";
1404 break;
1405 case MP_KEY_Cyrillic_em:
1406 ptr = L"alt-v";
1407 break;
1408 case MP_KEY_Cyrillic_en:
1409 ptr = L"alt-y";
1410 break;
1411 case MP_KEY_Cyrillic_o:
1412 ptr = L"alt-j";
1413 break;
1414 case MP_KEY_Cyrillic_pe:
1415 ptr = L"alt-g";
1416 break;
1417 case MP_KEY_Cyrillic_ya:
1418 ptr = L"alt-z";
1419 break;
1420 case MP_KEY_Cyrillic_er:
1421 ptr = L"alt-h";
1422 break;
1423 case MP_KEY_Cyrillic_es:
1424 ptr = L"alt-c";
1425 break;
1426 case MP_KEY_Cyrillic_te:
1427 ptr = L"alt-n";
1428 break;
1429 case MP_KEY_Cyrillic_softsign:
1430 ptr = L"alt-m";
1431 break;
1432 case MP_KEY_Cyrillic_yeru:
1433 ptr = L"alt-s";
1434 break;
1435 case MP_KEY_Cyrillic_ze:
1436 ptr = L"alt-p";
1437 break;
1438 case MP_KEY_Cyrillic_sha:
1439 ptr = L"alt-i";
1440 break;
1441 case MP_KEY_Cyrillic_e:
1442 ptr = L"alt-t";
1443 break;
1444 case MP_KEY_Cyrillic_shcha:
1445 ptr = L"alt-o";
1446 break;
1447 case MP_KEY_Cyrillic_che:
1448 ptr = L"alt-x";
1449 break;
1452 if (ptr == NULL) {
1453 char c = event->keyval & 0xdf;
1455 switch (c) {
1456 case 'A':
1457 ptr = L"alt-a";
1458 break;
1459 case 'B':
1460 ptr = L"alt-b";
1461 break;
1462 case 'C':
1463 ptr = L"alt-c";
1464 break;
1465 case 'D':
1466 ptr = L"alt-d";
1467 break;
1468 case 'E':
1469 ptr = L"alt-e";
1470 break;
1471 case 'F':
1472 ptr = L"alt-f";
1473 break;
1474 case 'G':
1475 ptr = L"alt-g";
1476 break;
1477 case 'H':
1478 ptr = L"alt-h";
1479 break;
1480 case 'I':
1481 ptr = L"alt-i";
1482 break;
1483 case 'J':
1484 ptr = L"alt-j";
1485 break;
1486 case 'K':
1487 ptr = L"alt-k";
1488 break;
1489 case 'L':
1490 ptr = L"alt-l";
1491 break;
1492 case 'M':
1493 ptr = L"alt-m";
1494 break;
1495 case 'N':
1496 ptr = L"alt-n";
1497 break;
1498 case 'O':
1499 ptr = L"alt-o";
1500 break;
1501 case 'P':
1502 ptr = L"alt-p";
1503 break;
1504 case 'Q':
1505 ptr = L"alt-q";
1506 break;
1507 case 'R':
1508 ptr = L"alt-r";
1509 break;
1510 case 'S':
1511 ptr = L"alt-s";
1512 break;
1513 case 'T':
1514 ptr = L"alt-t";
1515 break;
1516 case 'U':
1517 ptr = L"alt-u";
1518 break;
1519 case 'V':
1520 ptr = L"alt-v";
1521 break;
1522 case 'W':
1523 ptr = L"alt-w";
1524 break;
1525 case 'X':
1526 ptr = L"alt-x";
1527 break;
1528 case 'Y':
1529 ptr = L"alt-y";
1530 break;
1531 case 'Z':
1532 ptr = L"alt-z";
1533 break;
1537 else {
1538 switch (event->keyval) {
1539 case MP_KEY_Up:
1540 ptr = L"cursor-up";
1541 break;
1542 case MP_KEY_Down:
1543 ptr = L"cursor-down";
1544 break;
1545 case MP_KEY_Left:
1546 ptr = L"cursor-left";
1547 break;
1548 case MP_KEY_Right:
1549 ptr = L"cursor-right";
1550 break;
1551 case MP_KEY_Prior:
1552 ptr = L"page-up";
1553 break;
1554 case MP_KEY_Next:
1555 ptr = L"page-down";
1556 break;
1557 case MP_KEY_Home:
1558 ptr = L"home";
1559 break;
1560 case MP_KEY_End:
1561 ptr = L"end";
1562 break;
1563 case MP_KEY_space:
1564 ptr = L"space";
1565 break;
1566 case MP_KEY_KP_Add:
1567 ptr = L"kp-plus";
1568 break;
1569 case MP_KEY_KP_Subtract:
1570 ptr = L"kp-minus";
1571 break;
1572 case MP_KEY_KP_Multiply:
1573 ptr = L"kp-multiply";
1574 break;
1575 case MP_KEY_KP_Divide:
1576 ptr = L"kp-divide";
1577 break;
1578 case MP_KEY_F1:
1579 ptr = L"f1";
1580 break;
1581 case MP_KEY_F2:
1582 ptr = L"f2";
1583 break;
1584 case MP_KEY_F3:
1585 ptr = L"f3";
1586 break;
1587 case MP_KEY_F4:
1588 ptr = L"f4";
1589 break;
1590 case MP_KEY_F5:
1591 ptr = L"f5";
1592 break;
1593 case MP_KEY_F6:
1594 ptr = L"f6";
1595 break;
1596 case MP_KEY_F7:
1597 ptr = L"f7";
1598 break;
1599 case MP_KEY_F8:
1600 ptr = L"f8";
1601 break;
1602 case MP_KEY_F9:
1603 ptr = L"f9";
1604 break;
1605 case MP_KEY_F10:
1606 ptr = L"f10";
1607 break;
1608 case MP_KEY_F11:
1609 ptr = L"f11";
1610 break;
1611 case MP_KEY_F12:
1612 ptr = L"f12";
1613 break;
1614 case MP_KEY_Insert:
1615 ptr = L"insert";
1616 break;
1617 case MP_KEY_BackSpace:
1618 ptr = L"backspace";
1619 break;
1620 case MP_KEY_Delete:
1621 ptr = L"delete";
1622 break;
1623 case MP_KEY_KP_Enter:
1624 case MP_KEY_Return:
1625 ptr = L"enter";
1626 break;
1627 case MP_KEY_Tab:
1628 ptr = L"tab";
1629 break;
1630 case MP_KEY_ISO_Left_Tab:
1631 ptr = L"shift-tab";
1632 break;
1633 case MP_KEY_Escape:
1634 ptr = L"escape";
1635 break;
1639 /* if there is a pending char in im_char, use it */
1640 if (ptr == NULL && im_char[0] != L'\0')
1641 ptr = im_char;
1643 /* finally process */
1644 if (ptr != NULL)
1645 mp_process_event(MPDM_S(ptr));
1647 /* delete the pending char */
1648 im_char[0] = L'\0';
1650 if (mp_exit_requested)
1651 gtk_main_quit();
1653 if (mp_keypress_throttle(1))
1654 gtk_drv_paint(mp_active(), 1);
1656 return 0;
1660 static gint button_press_event(GtkWidget * widget, GdkEventButton * event,
1661 gpointer data)
1662 /* 'button_press_event' handler (mouse buttons) */
1664 int x, y;
1665 wchar_t *ptr = NULL;
1667 mouse_down = 1;
1669 /* mouse instant positioning */
1670 x = ((int) event->x) / font_width;
1671 y = ((int) event->y) / font_height;
1673 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
1674 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
1676 switch (event->button) {
1677 case 1:
1678 ptr = L"mouse-left-button";
1679 break;
1680 case 2:
1681 ptr = L"mouse-middle-button";
1682 break;
1683 case 3:
1684 ptr = L"mouse-right-button";
1685 break;
1686 case 4:
1687 ptr = L"mouse-wheel-up";
1688 break;
1689 case 5:
1690 ptr = L"mouse-wheel-down";
1691 break;
1694 if (ptr != NULL)
1695 mp_process_event(MPDM_S(ptr));
1697 redraw();
1699 return 0;
1703 static gint button_release_event(GtkWidget * widget,
1704 GdkEventButton * event, gpointer data)
1705 /* 'button_release_event' handle (mouse buttons) */
1707 mouse_down = 0;
1709 return TRUE;
1713 static gint motion_notify_event(GtkWidget * widget, GdkEventMotion * event,
1714 gpointer data)
1715 /* 'motion_notify_event' handler (mouse movement) */
1717 static int ox = 0;
1718 static int oy = 0;
1720 if (mouse_down) {
1721 int x, y;
1723 /* mouse dragging */
1724 x = ((int) event->x) / font_width;
1725 y = ((int) event->y) / font_height;
1727 if (ox != x && oy != y) {
1728 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
1729 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
1731 mp_process_event(MPDM_LS(L"mouse-drag"));
1732 gtk_drv_paint(mp_active(), 1);
1736 return TRUE;
1740 static void drag_data_received(GtkWidget * widget, GdkDragContext * dc,
1741 gint x, gint y, GtkSelectionData * data,
1742 guint info, guint time)
1743 /* 'drag_data_received' handler */
1745 printf("drag_data_received (unsupported)\n");
1749 /** clipboard functions **/
1751 static void commit(GtkIMContext * i, char *str, gpointer u)
1752 /* 'commit' handler */
1754 wchar_t *wstr;
1756 wstr = (wchar_t *) g_convert(str, -1,
1757 "WCHAR_T", "UTF-8", NULL, NULL, NULL);
1759 im_char[0] = *wstr;
1760 im_char[1] = L'\0';
1762 g_free(wstr);
1766 static void realize(GtkWidget * widget)
1767 /* 'realize' handler */
1769 im = gtk_im_multicontext_new();
1770 g_signal_connect(im, "commit", G_CALLBACK(commit), NULL);
1771 gtk_im_context_set_client_window(im, gtk_widget_get_window(widget));
1775 static gint expose_event(GtkWidget * widget, cairo_t *event)
1776 /* 'expose_event' handler */
1778 redraw();
1780 return FALSE;
1784 static gint configure_event(GtkWidget * widget, GdkEventConfigure * event)
1785 /* 'configure_event' handler */
1787 static GdkEventConfigure o;
1789 if (memcmp(&o, event, sizeof(o)) == 0)
1790 return TRUE;
1792 memcpy(&o, event, sizeof(o));
1794 update_window_size();
1795 redraw();
1797 return TRUE;
1801 static gint selection_clear_event(GtkWidget * widget,
1802 GdkEventSelection * event, gpointer data)
1803 /* 'selection_clear_event' handler */
1805 got_selection = 0;
1807 return TRUE;
1811 static void selection_get(GtkWidget * widget,
1812 GtkSelectionData * sel, guint info, guint tm)
1813 /* 'selection_get' handler */
1815 mpdm_t d;
1816 unsigned char *ptr;
1817 int s;
1819 if (!got_selection)
1820 return;
1822 /* gets the clipboard and joins */
1823 d = mpdm_hget_s(mp, L"clipboard");
1825 if (mpdm_size(d) == 0)
1826 return;
1828 d = mpdm_ref(mpdm_join_s(d, L"\n"));
1830 /* convert to current locale */
1831 ptr = (unsigned char *) mpdm_wcstombs(d->data, &s);
1833 /* pastes into primary selection */
1834 gtk_selection_data_set(sel, GDK_SELECTION_TYPE_STRING, 8, ptr,
1835 (gsize) s);
1837 free(ptr);
1839 mpdm_unref(d);
1843 static void selection_received(GtkWidget * widget,
1844 GtkSelectionData * sel, gpointer data)
1845 /* 'selection_received' handler */
1847 mpdm_t d;
1849 if (gtk_selection_data_get_data(sel) != NULL) {
1850 /* get selection */
1851 wchar_t *wptr = utf8_to_wcs((char *) gtk_selection_data_get_data(sel));
1852 d = MPDM_S(wptr);
1853 g_free(wptr);
1855 /* split and set as the clipboard */
1856 mpdm_hset_s(mp, L"clipboard", mpdm_split_s(d, L"\n"));
1857 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
1859 /* wait no more for the selection */
1860 wait_for_selection = 0;
1862 else
1863 wait_for_selection = -1;
1867 static mpdm_t gtk_drv_clip_to_sys(mpdm_t a, mpdm_t ctxt)
1868 /* driver-dependent mp to system clipboard */
1870 got_selection = gtk_selection_owner_set(area,
1871 GDK_SELECTION_PRIMARY,
1872 GDK_CURRENT_TIME);
1874 return NULL;
1878 static mpdm_t gtk_drv_sys_to_clip(mpdm_t a, mpdm_t ctxt)
1879 /* driver-dependent system to mp clipboard */
1881 if (!got_selection) {
1882 int n;
1883 char *formats[] = { "UTF8_STRING", "STRING", NULL };
1885 for (n = 0; formats[n] != NULL; n++) {
1887 /* triggers a selection capture */
1888 if (gtk_selection_convert(area, GDK_SELECTION_PRIMARY,
1889 gdk_atom_intern(formats[n], FALSE),
1890 GDK_CURRENT_TIME)) {
1892 /* processes the pending events
1893 (i.e., the 'selection_received' handler) */
1894 wait_for_selection = 1;
1896 while (wait_for_selection == 1)
1897 gtk_main_iteration();
1899 if (!wait_for_selection)
1900 break;
1905 return NULL;
1909 /** interface functions **/
1911 static void clicked_ok(GtkWidget * widget, gpointer data)
1912 /* 'clicked_on' signal handler (for gtk_drv_form) */
1914 int n;
1916 for (n = 0; n < mpdm_size(form_args); n++) {
1917 GtkWidget *widget = form_widgets[n];
1918 mpdm_t w = mpdm_aget(form_args, n);
1919 wchar_t *wptr = mpdm_string(mpdm_hget_s(w, L"type"));
1920 mpdm_t v = NULL;
1922 /* if there is already a value there, if was
1923 previously set from a callback */
1924 if (mpdm_aget(form_values, n) != NULL)
1925 continue;
1927 if (wcscmp(wptr, L"text") == 0 || wcscmp(wptr, L"password") == 0) {
1928 char *ptr;
1929 GtkWidget *gw = widget;
1930 mpdm_t h;
1932 if (wcscmp(wptr, L"text") == 0)
1933 #if CONFOPT_GTK == 2
1934 gw = GTK_COMBO(widget)->entry;
1935 #endif
1936 #if CONFOPT_GTK == 3
1937 gw = gtk_bin_get_child(GTK_BIN(widget));
1938 #endif
1940 if ((ptr =
1941 gtk_editable_get_chars(GTK_EDITABLE(gw), 0, -1)) != NULL
1942 && (wptr = utf8_to_wcs(ptr)) != NULL) {
1943 v = MPDM_S(wptr);
1944 g_free(wptr);
1945 g_free(ptr);
1948 mpdm_ref(v);
1950 /* if it has history, fill it */
1951 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1952 v != NULL && mpdm_cmp_s(v, L"") != 0) {
1953 h = mp_get_history(h);
1955 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1956 mpdm_push(h, v);
1959 mpdm_unrefnd(v);
1961 else
1962 if (wcscmp(wptr, L"checkbox") == 0) {
1963 v = MPDM_I(gtk_toggle_button_get_active
1964 (GTK_TOGGLE_BUTTON(widget)));
1966 else
1967 if (wcscmp(wptr, L"list") == 0) {
1968 GtkWidget *list = gtk_bin_get_child(GTK_BIN(widget));
1969 GtkTreeSelection *selection =
1970 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
1971 GList *selected =
1972 gtk_tree_selection_get_selected_rows(selection, NULL);
1973 GtkTreePath *path = selected->data;
1975 v = MPDM_I(gtk_tree_path_get_indices(path)[0]);
1976 gtk_tree_path_free(path);
1977 g_list_free(selected);
1981 mpdm_aset(form_values, v, n);
1986 static gint timer_callback(gpointer data)
1988 mpdm_void(mpdm_exec(timer_func, NULL, NULL));
1989 redraw();
1991 return TRUE;
1995 static void build_form_data(mpdm_t widget_list)
1996 /* builds the necessary information for a list of widgets */
1998 mpdm_unref(form_args);
1999 form_args = mpdm_ref(widget_list);
2001 mpdm_unref(form_values);
2002 form_values = widget_list == NULL ? NULL :
2003 mpdm_ref(MPDM_A(mpdm_size(form_args)));
2005 /* resize the widget array */
2006 form_widgets = (GtkWidget **) realloc(form_widgets,
2007 mpdm_size(form_args) *
2008 sizeof(GtkWidget *));
2012 /** dialog functions **/
2014 #define DIALOG_BUTTON(l,f) do { GtkWidget * btn; \
2015 ptr = localize(l); btn = gtk_button_new_with_label(ptr); \
2016 g_signal_connect_swapped(G_OBJECT(btn), "clicked", \
2017 G_CALLBACK(f), G_OBJECT(dlg)); \
2018 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), \
2019 btn, TRUE, TRUE, 0); \
2020 g_free(ptr); \
2021 } while (0);
2023 static mpdm_t gtk_drv_alert(mpdm_t a, mpdm_t ctxt)
2024 /* alert driver function */
2026 gchar *ptr;
2027 GtkWidget *dlg;
2029 build_form_data(NULL);
2031 /* 1# arg: prompt */
2032 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
2033 return NULL;
2035 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
2036 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "%s", ptr);
2037 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
2038 g_free(ptr);
2040 gtk_dialog_run(GTK_DIALOG(dlg));
2041 gtk_widget_destroy(dlg);
2043 return NULL;
2047 static mpdm_t gtk_drv_confirm(mpdm_t a, mpdm_t ctxt)
2048 /* confirm driver function */
2050 char *ptr;
2051 GtkWidget *dlg;
2052 gint response;
2054 build_form_data(NULL);
2056 /* 1# arg: prompt */
2057 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
2058 return NULL;
2060 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
2061 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
2062 "%s", ptr);
2063 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
2064 g_free(ptr);
2066 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_YES, 1);
2067 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_NO, 2);
2068 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_CANCEL, 0);
2070 response = gtk_dialog_run(GTK_DIALOG(dlg));
2071 gtk_widget_destroy(dlg);
2073 if (response == GTK_RESPONSE_DELETE_EVENT)
2074 response = 0;
2076 return MPDM_I(response);
2080 static mpdm_t gtk_drv_form(mpdm_t a, mpdm_t ctxt)
2081 /* 'form' driver function */
2083 GtkWidget *dlg;
2084 GtkWidget *table;
2085 GtkWidget *content_area;
2086 int n;
2087 mpdm_t ret = NULL;
2089 /* first argument: list of widgets */
2090 build_form_data(mpdm_aget(a, 0));
2092 dlg = gtk_dialog_new_with_buttons("mp " VERSION, GTK_WINDOW(window),
2093 GTK_DIALOG_MODAL,
2094 GTK_STOCK_CANCEL,
2095 GTK_RESPONSE_CANCEL, GTK_STOCK_OK,
2096 GTK_RESPONSE_OK, NULL);
2097 gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
2098 gtk_container_set_border_width(GTK_CONTAINER(dlg), 5);
2100 content_area = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
2101 gtk_box_set_spacing(GTK_BOX(content_area), 2);
2103 table = gtk_table_new(mpdm_size(a), 2, FALSE);
2104 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
2105 gtk_table_set_col_spacings(GTK_TABLE(table), 12);
2106 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
2108 for (n = 0; n < mpdm_size(form_args); n++) {
2109 mpdm_t w = mpdm_aget(form_args, n);
2110 GtkWidget *widget = NULL;
2111 wchar_t *type;
2112 char *ptr;
2113 mpdm_t t;
2114 int col = 0;
2116 type = mpdm_string(mpdm_hget_s(w, L"type"));
2118 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
2119 GtkWidget *label;
2121 if ((ptr = v_to_utf8(mpdm_gettext(t))) != NULL) {
2122 label = gtk_label_new(ptr);
2123 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
2125 gtk_table_attach_defaults(GTK_TABLE(table),
2126 label, 0, wcscmp(type,
2127 L"label") ==
2128 0 ? 2 : 1, n, n + 1);
2130 g_free(ptr);
2132 col++;
2136 t = mpdm_hget_s(w, L"value");
2138 if (wcscmp(type, L"text") == 0) {
2139 GList *combo_items = NULL;
2140 mpdm_t h;
2142 #if CONFOPT_GTK == 2
2143 widget = gtk_combo_new();
2144 gtk_combo_set_use_arrows_always(GTK_COMBO(widget), TRUE);
2145 gtk_combo_set_case_sensitive(GTK_COMBO(widget), TRUE);
2146 gtk_entry_set_activates_default(GTK_ENTRY
2147 (GTK_COMBO(widget)->entry),
2148 TRUE);
2149 #endif
2150 #if CONFOPT_GTK == 3
2151 widget = gtk_combo_box_text_new_with_entry();
2152 gtk_entry_set_activates_default(GTK_ENTRY
2153 (gtk_bin_get_child(GTK_BIN(widget))),
2154 TRUE);
2155 #endif
2157 gtk_widget_set_size_request(widget, 300, -1);
2159 if ((h = mpdm_hget_s(w, L"history")) != NULL) {
2160 int i;
2162 /* has history; fill it */
2163 h = mp_get_history(h);
2165 for (i = 0; i < mpdm_size(h); i++) {
2166 ptr = v_to_utf8(mpdm_aget(h, i));
2168 combo_items = g_list_prepend(combo_items, ptr);
2170 #if CONFOPT_GTK == 3
2171 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget), ptr);
2172 #endif
2176 if (t != NULL) {
2177 ptr = v_to_utf8(t);
2179 combo_items = g_list_prepend(combo_items, ptr);
2180 #if CONFOPT_GTK == 3
2181 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget), ptr);
2182 #endif
2185 #if CONFOPT_GTK == 2
2186 gtk_combo_set_popdown_strings(GTK_COMBO(widget), combo_items);
2187 #endif
2188 g_list_free(combo_items);
2190 else
2191 if (wcscmp(type, L"password") == 0) {
2192 widget = gtk_entry_new();
2193 gtk_widget_set_size_request(widget, 300, -1);
2194 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
2196 else
2197 if (wcscmp(type, L"checkbox") == 0) {
2198 widget = gtk_check_button_new();
2200 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
2201 mpdm_ival(t) ? TRUE : FALSE);
2203 else
2204 if (wcscmp(type, L"list") == 0) {
2205 GtkWidget *list;
2206 GtkListStore *list_store;
2207 GtkCellRenderer *renderer;
2208 GtkTreeViewColumn *column;
2209 GtkTreePath *path;
2210 mpdm_t l;
2211 gint i;
2213 if ((i = 450 / mpdm_size(form_args)) < 100)
2214 i = 100;
2216 widget = gtk_scrolled_window_new(NULL, NULL);
2217 gtk_widget_set_size_request(widget, 500, i);
2218 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
2219 GTK_POLICY_NEVER,
2220 GTK_POLICY_AUTOMATIC);
2221 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
2222 (widget), GTK_SHADOW_IN);
2224 list_store = gtk_list_store_new(1, G_TYPE_STRING);
2225 list =
2226 gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
2227 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2228 renderer = gtk_cell_renderer_text_new();
2229 column = gtk_tree_view_column_new_with_attributes("", renderer,
2230 "text", 0,
2231 NULL);
2232 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2233 gtk_container_add(GTK_CONTAINER(widget), list);
2235 l = mpdm_hget_s(w, L"list");
2237 for (i = 0; i < mpdm_size(l); i++) {
2238 if ((ptr = v_to_utf8(mpdm_aget(l, i))) != NULL) {
2239 GtkTreeIter iter;
2240 gtk_list_store_append(list_store, &iter);
2241 gtk_list_store_set(list_store, &iter, 0, ptr, -1);
2242 g_free(ptr);
2246 /* initial position */
2247 i = mpdm_ival(t);
2249 path = gtk_tree_path_new_from_indices(i, -1);
2250 gtk_tree_view_set_cursor(GTK_TREE_VIEW(list), path, NULL,
2251 FALSE);
2252 gtk_tree_path_free(path);
2254 g_signal_connect_swapped(G_OBJECT(list), "row-activated",
2255 G_CALLBACK
2256 (gtk_window_activate_default), dlg);
2259 if (widget != NULL) {
2260 form_widgets[n] = widget;
2261 gtk_table_attach_defaults(GTK_TABLE(table),
2262 widget, col, 2, n, n + 1);
2266 gtk_widget_show_all(table);
2268 gtk_box_pack_start(GTK_BOX(content_area), table, TRUE, TRUE, 0);
2270 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) {
2271 clicked_ok(NULL, NULL);
2272 ret = form_values;
2274 gtk_widget_destroy(dlg);
2276 return ret;
2280 static mpdm_t run_filechooser(mpdm_t a, gboolean save)
2281 /* openfile driver function */
2283 GtkWidget *dlg;
2284 char *ptr;
2285 mpdm_t ret = NULL;
2286 gint response;
2288 /* 1# arg: prompt */
2289 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
2290 return (NULL);
2292 if (!save) {
2293 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2294 GTK_FILE_CHOOSER_ACTION_OPEN,
2295 GTK_STOCK_CANCEL,
2296 GTK_RESPONSE_CANCEL,
2297 GTK_STOCK_OK, GTK_RESPONSE_OK,
2298 NULL);
2300 else {
2301 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2302 GTK_FILE_CHOOSER_ACTION_SAVE,
2303 GTK_STOCK_CANCEL,
2304 GTK_STOCK_CANCEL, GTK_STOCK_OK,
2305 GTK_RESPONSE_OK, NULL);
2306 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER
2307 (dlg), TRUE);
2309 g_free(ptr);
2311 build_form_data(NULL);
2313 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), TRUE);
2314 response = gtk_dialog_run(GTK_DIALOG(dlg));
2316 if (response == GTK_RESPONSE_OK) {
2317 gchar *filename;
2318 gchar *utf8name;
2319 wchar_t *wfilename;
2321 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
2322 utf8name = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
2323 g_free(filename);
2324 wfilename = utf8_to_wcs(utf8name);
2325 g_free(utf8name);
2326 ret = MPDM_S(wfilename);
2327 g_free(wfilename);
2329 gtk_widget_destroy(dlg);
2331 return ret;
2335 static mpdm_t gtk_drv_openfile(mpdm_t a, mpdm_t ctxt)
2336 /* openfile driver function */
2338 return run_filechooser(a, FALSE);
2342 static mpdm_t gtk_drv_savefile(mpdm_t a, mpdm_t ctxt)
2343 /* savefile driver function */
2345 return run_filechooser(a, TRUE);
2349 static mpdm_t gtk_drv_update_ui(mpdm_t a, mpdm_t ctxt)
2351 build_fonts();
2352 build_colors();
2353 build_menu();
2355 redraw();
2357 return NULL;
2361 static mpdm_t gtk_drv_timer(mpdm_t a, mpdm_t ctxt)
2363 static guint prev = 0;
2364 int msecs = mpdm_ival(mpdm_aget(a, 0));
2365 mpdm_t func = mpdm_aget(a, 1);
2367 /* previously defined one? remove */
2368 if (timer_func != NULL)
2369 g_source_remove(prev);
2371 /* if msecs and func are set, program timer */
2372 if (msecs > 0 && func != NULL)
2373 prev = g_timeout_add(msecs, timer_callback, NULL);
2375 mpdm_ref(func);
2376 mpdm_unref(timer_func);
2377 timer_func = func;
2379 return NULL;
2383 static mpdm_t gtk_drv_busy(mpdm_t a, mpdm_t ctxt)
2385 int onoff = mpdm_ival(mpdm_aget(a, 0));
2387 gdk_window_set_cursor(gtk_widget_get_window(window),
2388 gdk_cursor_new(onoff ? GDK_WATCH :
2389 GDK_LEFT_PTR));
2391 while (gtk_events_pending())
2392 gtk_main_iteration();
2394 return NULL;
2398 static mpdm_t gtk_drv_main_loop(mpdm_t a, mpdm_t ctxt)
2399 /* main loop */
2401 if (!mp_exit_requested) {
2402 gtk_drv_paint(mp_active(), 0);
2404 gtk_main();
2407 return NULL;
2411 static mpdm_t gtk_drv_shutdown(mpdm_t a, mpdm_t ctxt)
2412 /* shutdown */
2414 mpdm_t v;
2416 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
2417 mpdm_write_wcs(stdout, mpdm_string(v));
2418 printf("\n");
2421 return NULL;
2425 static void register_functions(void)
2427 mpdm_t drv;
2429 drv = mpdm_hget_s(mp, L"drv");
2430 mpdm_hset_s(drv, L"main_loop", MPDM_X(gtk_drv_main_loop));
2431 mpdm_hset_s(drv, L"shutdown", MPDM_X(gtk_drv_shutdown));
2432 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(gtk_drv_clip_to_sys));
2433 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(gtk_drv_sys_to_clip));
2434 mpdm_hset_s(drv, L"update_ui", MPDM_X(gtk_drv_update_ui));
2435 mpdm_hset_s(drv, L"timer", MPDM_X(gtk_drv_timer));
2436 mpdm_hset_s(drv, L"busy", MPDM_X(gtk_drv_busy));
2437 mpdm_hset_s(drv, L"alert", MPDM_X(gtk_drv_alert));
2438 mpdm_hset_s(drv, L"confirm", MPDM_X(gtk_drv_confirm));
2439 mpdm_hset_s(drv, L"openfile", MPDM_X(gtk_drv_openfile));
2440 mpdm_hset_s(drv, L"savefile", MPDM_X(gtk_drv_savefile));
2441 mpdm_hset_s(drv, L"form", MPDM_X(gtk_drv_form));
2445 static mpdm_t gtk_drv_startup(mpdm_t a, mpdm_t ctxt)
2446 /* driver initialization */
2448 GtkWidget *vbox;
2449 GtkWidget *hbox;
2450 #if CONFOPT_GTK == 2
2451 GdkPixmap *pixmap;
2452 GdkPixmap *mask;
2453 #endif
2454 #if CONFOPT_GTK == 3
2455 GdkPixbuf *pixmap;
2456 #endif
2457 GdkScreen *screen;
2458 mpdm_t v;
2459 int w, h;
2460 GtkTargetEntry targets[] = {
2461 {"text/plain", 0, 0},
2462 {"text/uri-list", 0, 1}
2465 register_functions();
2467 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2469 gtk_window_set_title(GTK_WINDOW(window), "mp " VERSION);
2471 /* get real screen and pick a usable size for the main area */
2472 screen = gtk_window_get_screen(GTK_WINDOW(window));
2473 if (gdk_screen_get_n_monitors(screen) > 1) {
2474 GdkRectangle monitor_one_size;
2475 gdk_screen_get_monitor_geometry(screen, 0, &monitor_one_size);
2477 w = (monitor_one_size.width * 3) / 4;
2478 h = (monitor_one_size.height * 2) / 3;
2479 } else {
2480 w = (gdk_screen_get_width(screen) * 3) / 4;
2481 h = (gdk_screen_get_height(screen) * 2) / 3;
2484 g_signal_connect(G_OBJECT(window), "delete_event",
2485 G_CALLBACK(delete_event), NULL);
2487 g_signal_connect(G_OBJECT(window), "destroy",
2488 G_CALLBACK(destroy), NULL);
2490 /* file tabs */
2491 file_tabs = gtk_notebook_new();
2492 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(file_tabs), GTK_POS_TOP);
2494 #if CONFOPT_GTK == 2
2495 GTK_WIDGET_UNSET_FLAGS(file_tabs, GTK_CAN_FOCUS);
2496 #endif
2497 #if CONFOPT_GTK == 3
2498 gtk_widget_set_can_focus(file_tabs, FALSE);
2499 #endif
2500 gtk_notebook_set_scrollable(GTK_NOTEBOOK(file_tabs), 1);
2502 vbox = gtk_vbox_new(FALSE, 2);
2503 gtk_container_add(GTK_CONTAINER(window), vbox);
2505 build_menu();
2507 hbox = gtk_hbox_new(FALSE, 0);
2508 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2509 gtk_box_pack_start(GTK_BOX(hbox), menu_bar, FALSE, FALSE, 0);
2510 gtk_box_pack_start(GTK_BOX(hbox), file_tabs, TRUE, TRUE, 0);
2512 gtk_notebook_popup_enable(GTK_NOTEBOOK(file_tabs));
2514 /* horizontal box holding the text and the scrollbar */
2515 hbox = gtk_hbox_new(FALSE, 2);
2516 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
2518 /* the Minimum Profit area */
2519 area = gtk_drawing_area_new();
2520 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
2521 gtk_widget_set_size_request(GTK_WIDGET(area), w, h);
2522 gtk_widget_set_events(GTK_WIDGET(area), GDK_BUTTON_PRESS_MASK |
2523 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
2524 | GDK_LEAVE_NOTIFY_MASK);
2526 gtk_widget_set_double_buffered(area, FALSE);
2528 g_signal_connect(G_OBJECT(area), "configure_event",
2529 G_CALLBACK(configure_event), NULL);
2531 #if CONFOPT_GTK == 2
2532 g_signal_connect(G_OBJECT(area), "expose_event",
2533 #endif
2534 #if CONFOPT_GTK == 3
2535 g_signal_connect(G_OBJECT(area), "draw",
2536 #endif
2537 G_CALLBACK(expose_event), NULL);
2539 g_signal_connect(G_OBJECT(area), "realize", G_CALLBACK(realize), NULL);
2541 g_signal_connect(G_OBJECT(window), "key_press_event",
2542 G_CALLBACK(key_press_event), NULL);
2544 g_signal_connect(G_OBJECT(window), "key_release_event",
2545 G_CALLBACK(key_release_event), NULL);
2547 g_signal_connect(G_OBJECT(area), "button_press_event",
2548 G_CALLBACK(button_press_event), NULL);
2550 g_signal_connect(G_OBJECT(area), "button_release_event",
2551 G_CALLBACK(button_release_event), NULL);
2553 g_signal_connect(G_OBJECT(area), "motion_notify_event",
2554 G_CALLBACK(motion_notify_event), NULL);
2556 g_signal_connect(G_OBJECT(area), "selection_clear_event",
2557 G_CALLBACK(selection_clear_event), NULL);
2559 g_signal_connect(G_OBJECT(area), "selection_get",
2560 G_CALLBACK(selection_get), NULL);
2562 g_signal_connect(G_OBJECT(area), "selection_received",
2563 G_CALLBACK(selection_received), NULL);
2565 g_signal_connect(G_OBJECT(area), "scroll_event",
2566 G_CALLBACK(scroll_event), NULL);
2568 gtk_drag_dest_set(area, GTK_DEST_DEFAULT_ALL, targets,
2569 sizeof(targets) / sizeof(GtkTargetEntry),
2570 GDK_ACTION_COPY);
2571 g_signal_connect(G_OBJECT(area), "drag_data_received",
2572 G_CALLBACK(drag_data_received), NULL);
2574 gtk_selection_add_target(area, GDK_SELECTION_PRIMARY,
2575 GDK_SELECTION_TYPE_STRING, 1);
2577 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
2578 G_CALLBACK(switch_page), NULL);
2580 /* the scrollbar */
2581 scrollbar = gtk_vscrollbar_new(NULL);
2582 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0);
2584 g_signal_connect(G_OBJECT
2585 (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
2586 "value_changed", G_CALLBACK(value_changed), NULL);
2588 /* the status bar */
2589 status = gtk_label_new("mp " VERSION);
2590 gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
2591 gtk_misc_set_alignment(GTK_MISC(status), 0, 0.5);
2592 gtk_label_set_justify(GTK_LABEL(status), GTK_JUSTIFY_LEFT);
2594 gtk_widget_show_all(window);
2596 /* set application icon */
2597 #if CONFOPT_GTK == 2
2598 pixmap = gdk_pixmap_create_from_xpm_d(window->window,
2599 &mask, NULL, mp_xpm);
2600 gdk_window_set_icon(window->window, NULL, pixmap, mask);
2601 #endif
2602 #if CONFOPT_GTK == 3
2603 pixmap = gdk_pixbuf_new_from_xpm_data((const char **)mp_xpm);
2604 gtk_window_set_icon(GTK_WINDOW(window), pixmap);
2605 #endif
2607 build_colors();
2609 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
2610 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
2611 maximize = 1;
2613 return NULL;
2616 int gtk_drv_detect(int *argc, char ***argv)
2618 mpdm_t drv;
2619 int n;
2621 for (n = 0; n < *argc; n++) {
2622 if (strcmp(argv[0][n], "-txt") == 0 ||
2623 strcmp(argv[0][n], "-h") == 0)
2624 return 0;
2627 if (!gtk_init_check(argc, argv))
2628 return 0;
2630 drv = mpdm_hget_s(mp, L"drv");
2632 #if CONFOPT_GTK == 3
2633 mpdm_hset_s(drv, L"id", MPDM_LS(L"gtk3"));
2634 #else
2635 mpdm_hset_s(drv, L"id", MPDM_LS(L"gtk2"));
2636 #endif
2638 mpdm_hset_s(drv, L"startup", MPDM_X(gtk_drv_startup));
2640 return 1;
2643 #endif /* CONFOPT_GTK */