Added a function detection for diff syntax.
[mp-5.x.git] / mpv_gtk.c
blobecbe2d36112a9d0c35fad937a05aa99726b59a61
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 tx = (area->allocation.width / font_width);
164 ty = (area->allocation.height / font_height) + 1;
166 /* store the 'window' size */
167 v = mpdm_hget_s(mp, L"window");
168 mpdm_hset_s(v, L"tx", MPDM_I(tx));
169 mpdm_hset_s(v, L"ty", MPDM_I(ty));
171 /* rebuild the pixmap for the double buffer */
175 static void build_fonts(void)
176 /* builds the fonts */
178 char tmp[128];
179 int font_size = 12;
180 const char *font_face = "Mono";
181 mpdm_t c;
182 mpdm_t w = NULL;
184 if (font != NULL)
185 pango_font_description_free(font);
187 /* get current configuration */
188 if ((c = mpdm_hget_s(mp, L"config")) != NULL) {
189 mpdm_t v;
191 if ((v = mpdm_hget_s(c, L"font_size")) != NULL)
192 font_size = mpdm_ival(v);
193 else
194 mpdm_hset_s(c, L"font_size", MPDM_I(font_size));
196 if ((v = mpdm_hget_s(c, L"font_face")) != NULL) {
197 w = mpdm_ref(MPDM_2MBS(v->data));
198 font_face = w->data;
200 else
201 mpdm_hset_s(c, L"font_face", MPDM_MBS(font_face));
204 snprintf(tmp, sizeof(tmp) - 1, "%s %d", font_face, font_size);
205 tmp[sizeof(tmp) - 1] = '\0';
207 font = pango_font_description_from_string(tmp);
208 update_window_size();
210 mpdm_unref(w);
214 static void build_color(GdkColor * gdkcolor, int rgb)
215 /* builds a color */
217 gdkcolor->pixel = 0;
218 gdkcolor->blue = (rgb & 0x000000ff) << 8;
219 gdkcolor->green = (rgb & 0x0000ff00);
220 gdkcolor->red = (rgb & 0x00ff0000) >> 8;
221 gdk_colormap_alloc_color(gdk_colormap_get_system(), gdkcolor, FALSE,
222 TRUE);
226 static void build_colors(void)
227 /* builds the colors */
229 mpdm_t colors;
230 mpdm_t l;
231 mpdm_t c;
232 int n, s;
234 /* gets the color definitions and attribute names */
235 colors = mpdm_hget_s(mp, L"colors");
236 l = mpdm_ref(mpdm_keys(colors));
237 s = mpdm_size(l);
239 /* redim the structures */
240 inks = realloc(inks, sizeof(GdkColor) * s);
241 papers = realloc(papers, sizeof(GdkColor) * s);
242 underlines = realloc(underlines, sizeof(int) * s);
244 /* loop the colors */
245 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
246 mpdm_t d = mpdm_hget(colors, c);
247 mpdm_t v = mpdm_hget_s(d, L"gui");
249 /* store the 'normal' attribute */
250 if (wcscmp(mpdm_string(c), L"normal") == 0)
251 normal_attr = n;
253 /* store the attr */
254 mpdm_hset_s(d, L"attr", MPDM_I(n));
256 build_color(&inks[n], mpdm_ival(mpdm_aget(v, 0)));
257 build_color(&papers[n], mpdm_ival(mpdm_aget(v, 1)));
259 /* flags */
260 v = mpdm_hget_s(d, L"flags");
261 underlines[n] = mpdm_seek_s(v, L"underline", 1) != -1 ? 1 : 0;
263 if (mpdm_seek_s(v, L"reverse", 1) != -1) {
264 GdkColor t;
266 t = inks[n];
267 inks[n] = papers[n];
268 papers[n] = t;
272 mpdm_unref(l);
276 /** menu functions **/
278 static void redraw(void);
280 static void menu_item_callback(mpdm_t action)
281 /* menu click callback */
283 mp_process_action(action);
284 redraw();
286 if (mp_exit_requested)
287 gtk_main_quit();
291 static void build_submenu(GtkWidget * menu, mpdm_t labels)
292 /* build a submenu */
294 int n;
295 GtkWidget *menu_item;
297 mpdm_ref(labels);
299 for (n = 0; n < mpdm_size(labels); n++) {
300 /* get the action */
301 mpdm_t v = mpdm_aget(labels, n);
303 /* if the action is a separator... */
304 if (*((wchar_t *) v->data) == L'-')
305 menu_item = gtk_menu_item_new();
306 else {
307 char *ptr;
309 ptr = v_to_utf8(mp_menu_label(v));
310 menu_item = gtk_menu_item_new_with_label(ptr);
311 g_free(ptr);
314 gtk_menu_append(GTK_MENU(menu), menu_item);
315 g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
316 G_CALLBACK(menu_item_callback), v);
317 gtk_widget_show(menu_item);
320 mpdm_unref(labels);
324 static void build_menu(void)
325 /* builds the menu */
327 static mpdm_t prev_menu = NULL;
328 int n;
329 mpdm_t m;
331 /* gets the current menu */
332 if ((m = mpdm_hget_s(mp, L"menu")) == NULL)
333 return;
335 /* if it's the same, do nothing */
336 if (mpdm_cmp(m, prev_menu) == 0)
337 return;
339 /* create a new menu */
340 menu_bar = gtk_menu_bar_new();
342 for (n = 0; n < mpdm_size(m); n++) {
343 char *ptr;
344 mpdm_t mi;
345 mpdm_t v;
346 GtkWidget *menu;
347 GtkWidget *menu_item;
348 int i;
350 /* get the label and the items */
351 mi = mpdm_aget(m, n);
352 v = mpdm_aget(mi, 0);
354 if ((ptr = v_to_utf8(mpdm_gettext(v))) == NULL)
355 continue;
357 /* change the & by _ for the mnemonic */
358 for (i = 0; ptr[i]; i++)
359 if (ptr[i] == '&')
360 ptr[i] = '_';
362 /* add the menu and the label */
363 menu = gtk_menu_new();
364 menu_item = gtk_menu_item_new_with_mnemonic(ptr);
365 g_free(ptr);
367 gtk_widget_show(menu_item);
368 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
369 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_item);
371 /* now loop the items */
372 build_submenu(menu, mpdm_aget(mi, 1));
377 /** main area drawing functions **/
379 static void switch_page(GtkNotebook * notebook, gpointer * page,
380 gint pg_num, gpointer data)
381 /* 'switch_page' handler (filetabs) */
383 /* sets the active one */
384 mpdm_hset_s(mp, L"active_i", MPDM_I(pg_num));
386 gtk_widget_grab_focus(area);
387 redraw();
391 static void draw_filetabs(void)
392 /* draws the filetabs */
394 static mpdm_t last = NULL;
395 mpdm_t names;
396 int n;
398 names = mpdm_ref(mp_get_doc_names());
400 /* disconnect redraw signal to avoid infinite loops */
401 g_signal_handlers_disconnect_by_func(G_OBJECT(file_tabs),
402 G_CALLBACK(switch_page), NULL);
404 /* is the list different from the previous one? */
405 if (mpdm_cmp(names, last) != 0) {
407 /* delete the current tabs */
408 for (n = 0; n < mpdm_size(last); n++)
409 gtk_notebook_remove_page(GTK_NOTEBOOK(file_tabs), 0);
411 /* create the new ones */
412 for (n = 0; n < mpdm_size(names); n++) {
413 GtkWidget *p;
414 GtkWidget *f;
415 char *ptr;
416 mpdm_t v = mpdm_aget(names, n);
418 if ((ptr = v_to_utf8(v)) != NULL) {
419 p = gtk_label_new(ptr);
420 gtk_widget_show(p);
422 f = gtk_frame_new(NULL);
423 gtk_widget_show(f);
425 gtk_notebook_append_page(GTK_NOTEBOOK(file_tabs), f, p);
427 g_free(ptr);
431 /* store for the next time */
432 mpdm_unref(last);
433 last = mpdm_ref(names);
436 mpdm_unref(names);
438 /* set the active one */
439 gtk_notebook_set_current_page(GTK_NOTEBOOK(file_tabs),
440 mpdm_ival(mpdm_hget_s(mp, L"active_i")));
442 /* reconnect signal */
443 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
444 G_CALLBACK(switch_page), NULL);
446 gtk_widget_grab_focus(area);
450 static void draw_status(void)
451 /* draws the status line */
453 char *ptr;
455 if ((ptr = v_to_utf8(mp_build_status_line())) != NULL) {
456 gtk_label_set_text(GTK_LABEL(status), ptr);
457 g_free(ptr);
462 static gint scroll_event(GtkWidget * widget, GdkEventScroll * event)
463 /* 'scroll_event' handler (mouse wheel) */
465 wchar_t *ptr = NULL;
467 switch (event->direction) {
468 case GDK_SCROLL_UP:
469 ptr = L"mouse-wheel-up";
470 break;
471 case GDK_SCROLL_DOWN:
472 ptr = L"mouse-wheel-down";
473 break;
474 case GDK_SCROLL_LEFT:
475 ptr = L"mouse-wheel-left";
476 break;
477 case GDK_SCROLL_RIGHT:
478 ptr = L"mouse-wheel-right";
479 break;
482 if (ptr != NULL)
483 mp_process_event(MPDM_S(ptr));
485 redraw();
487 return 0;
491 static void value_changed(GtkAdjustment * adj, gpointer * data)
492 /* 'value_changed' handler (scrollbar) */
494 int i = (int) gtk_adjustment_get_value(adj);
495 mpdm_t doc;
496 mpdm_t txt;
497 int y;
499 /* get current y position */
500 doc = mp_active();
501 txt = mpdm_hget_s(doc, L"txt");
502 y = mpdm_ival(mpdm_hget_s(txt, L"y"));
504 /* if it's different, set and redraw */
505 if (y != i) {
506 mp_set_y(doc, i);
507 mpdm_hset_s(txt, L"vy", MPDM_I(i));
508 redraw();
513 static void draw_scrollbar(void)
514 /* updates the scrollbar */
516 GtkAdjustment *adjustment;
517 mpdm_t d;
518 mpdm_t v;
519 int pos, size, max;
521 /* gets the active document */
522 if ((d = mp_active()) == NULL)
523 return;
525 /* get the coordinates */
526 v = mpdm_hget_s(d, L"txt");
527 pos = mpdm_ival(mpdm_hget_s(v, L"vy"));
528 max = mpdm_size(mpdm_hget_s(v, L"lines"));
530 v = mpdm_hget_s(mp, L"window");
531 size = mpdm_ival(mpdm_hget_s(v, L"ty"));
533 adjustment = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
535 /* disconnect to avoid infinite loops */
536 g_signal_handlers_disconnect_by_func(G_OBJECT(adjustment),
537 G_CALLBACK(value_changed), NULL);
539 gtk_adjustment_set_step_increment(adjustment, (gdouble) 1);
540 gtk_adjustment_set_upper(adjustment, (gdouble) max);
541 gtk_adjustment_set_page_size(adjustment, (gdouble) size);
542 gtk_adjustment_set_page_increment(adjustment, (gdouble) size);
543 gtk_adjustment_set_value(adjustment, (gdouble) pos);
545 gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment);
547 gtk_adjustment_changed(adjustment);
548 gtk_adjustment_value_changed(adjustment);
550 /* reattach again */
551 g_signal_connect(G_OBJECT
552 (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
553 "value_changed", G_CALLBACK(value_changed), NULL);
557 static void gtk_drv_paint(mpdm_t doc, int optimize)
558 /* GTK document draw function */
560 GdkRectangle gr;
561 cairo_t *cr;
562 mpdm_t d = NULL;
563 int n, m;
565 if (maximize)
566 gtk_window_maximize(GTK_WINDOW(window));
568 /* no gc? create it */
569 if (font == NULL)
570 build_fonts();
572 if ((d = mp_draw(doc, optimize)) == NULL)
573 return;
575 mpdm_ref(d);
577 gr.x = 0;
578 gr.y = 0;
580 #if CONFOPT_GTK == 2
581 gr.width = area->allocation.width;
582 #endif
583 #if CONFOPT_GTK == 3
584 gr.width = gtk_widget_get_allocated_width(area);
585 #endif
587 gr.height = font_height;
589 cr = gdk_cairo_create(gtk_widget_get_window(area));
590 for (n = 0; n < mpdm_size(d); n++) {
591 PangoLayout *pl;
592 PangoAttrList *pal;
593 mpdm_t l = mpdm_aget(d, n);
594 char *str = NULL;
595 int u, p;
597 if (l == NULL)
598 continue;
600 /* create the pango stuff */
601 pl = gtk_widget_create_pango_layout(area, NULL);
602 pango_layout_set_font_description(pl, font);
603 pal = pango_attr_list_new();
605 for (m = u = p = 0; m < mpdm_size(l); m++, u = p) {
606 PangoAttribute *pa;
607 int attr;
608 mpdm_t s;
609 char *ptr;
611 /* get the attribute and the string */
612 attr = mpdm_ival(mpdm_aget(l, m++));
613 s = mpdm_aget(l, m);
615 /* convert the string to utf8 */
616 ptr = v_to_utf8(s);
618 /* add to the full line */
619 str = mpdm_poke(str, &p, ptr, strlen(ptr), 1);
621 g_free(ptr);
623 /* create the background if it's
624 different from the default */
625 if (papers[attr].red != papers[normal_attr].red ||
626 papers[attr].green != papers[normal_attr].green ||
627 papers[attr].blue != papers[normal_attr].blue) {
628 pa = pango_attr_background_new(papers[attr].red,
629 papers[attr].green,
630 papers[attr].blue);
632 pa->start_index = u;
633 pa->end_index = p;
635 pango_attr_list_insert(pal, pa);
638 /* underline? */
639 if (underlines[attr]) {
640 pa = pango_attr_underline_new(TRUE);
642 pa->start_index = u;
643 pa->end_index = p;
645 pango_attr_list_insert(pal, pa);
648 /* foreground color */
649 pa = pango_attr_foreground_new(inks[attr].red,
650 inks[attr].green,
651 inks[attr].blue);
653 pa->start_index = u;
654 pa->end_index = p;
656 pango_attr_list_insert(pal, pa);
659 /* store the attributes */
660 pango_layout_set_attributes(pl, pal);
661 pango_attr_list_unref(pal);
663 /* store and free the text */
664 pango_layout_set_text(pl, str, p);
665 free(str);
667 /* draw the background */
668 gdk_cairo_set_source_color(cr, &papers[normal_attr]);
669 cairo_rectangle(cr, 0, n * font_height, gr.width, gr.height);
670 cairo_fill(cr);
672 /* draw the text */
673 cairo_move_to(cr, 2, n * font_height);
674 pango_cairo_show_layout(cr, pl);
676 g_object_unref(pl);
679 cairo_destroy(cr);
680 mpdm_unref(d);
682 draw_filetabs();
683 draw_scrollbar();
684 draw_status();
688 static void redraw(void)
690 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
691 gtk_drv_paint(mp_active(), 0);
695 static gint delete_event(GtkWidget * w, GdkEvent * e, gpointer data)
696 /* 'delete_event' handler */
698 mp_process_event(MPDM_LS(L"close-window"));
700 return mp_exit_requested ? FALSE : TRUE;
704 static void destroy(GtkWidget * w, gpointer data)
705 /* 'destroy' handler */
707 gtk_main_quit();
711 static gint key_release_event(GtkWidget * widget, GdkEventKey * event,
712 gpointer data)
713 /* 'key_release_event' handler */
715 if (mp_keypress_throttle(0))
716 gtk_drv_paint(mp_active(), 0);
718 return 0;
722 #if CONFOPT_GTK == 2
723 #define MP_KEY_Up GDK_Up
724 #define MP_KEY_Down GDK_Down
725 #define MP_KEY_Left GDK_Left
726 #define MP_KEY_Right GDK_Right
727 #define MP_KEY_Prior GDK_Prior
728 #define MP_KEY_Next GDK_Next
729 #define MP_KEY_Home GDK_Home
730 #define MP_KEY_End GDK_End
731 #define MP_KEY_space GDK_space
732 #define MP_KEY_KP_Add GDK_KP_Add
733 #define MP_KEY_KP_Subtract GDK_KP_Subtract
734 #define MP_KEY_KP_Multiply GDK_KP_Multiply
735 #define MP_KEY_KP_Divide GDK_KP_Divide
736 #define MP_KEY_F1 GDK_F1
737 #define MP_KEY_F2 GDK_F2
738 #define MP_KEY_F3 GDK_F3
739 #define MP_KEY_F4 GDK_F4
740 #define MP_KEY_F5 GDK_F5
741 #define MP_KEY_F6 GDK_F6
742 #define MP_KEY_F7 GDK_F7
743 #define MP_KEY_F8 GDK_F8
744 #define MP_KEY_F9 GDK_F9
745 #define MP_KEY_F10 GDK_F10
746 #define MP_KEY_F11 GDK_F11
747 #define MP_KEY_F12 GDK_F12
748 #define MP_KEY_KP_Enter GDK_KP_Enter
749 #define MP_KEY_Return GDK_Return
750 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
751 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
752 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
753 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
754 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
755 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
756 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
757 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
758 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
759 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
760 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
761 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
762 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
763 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
764 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
765 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
766 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
767 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
768 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
769 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
770 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
771 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
772 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
773 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
774 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
775 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
776 #define MP_KEY_Up GDK_Up
777 #define MP_KEY_Down GDK_Down
778 #define MP_KEY_Left GDK_Left
779 #define MP_KEY_Right GDK_Right
780 #define MP_KEY_Prior GDK_Prior
781 #define MP_KEY_Next GDK_Next
782 #define MP_KEY_Home GDK_Home
783 #define MP_KEY_End GDK_End
784 #define MP_KEY_space GDK_space
785 #define MP_KEY_KP_Add GDK_KP_Add
786 #define MP_KEY_KP_Subtract GDK_KP_Subtract
787 #define MP_KEY_KP_Multiply GDK_KP_Multiply
788 #define MP_KEY_KP_Divide GDK_KP_Divide
789 #define MP_KEY_F1 GDK_F1
790 #define MP_KEY_F2 GDK_F2
791 #define MP_KEY_F3 GDK_F3
792 #define MP_KEY_F4 GDK_F4
793 #define MP_KEY_F5 GDK_F5
794 #define MP_KEY_F6 GDK_F6
795 #define MP_KEY_F7 GDK_F7
796 #define MP_KEY_F8 GDK_F8
797 #define MP_KEY_F9 GDK_F9
798 #define MP_KEY_F10 GDK_F10
799 #define MP_KEY_F11 GDK_F11
800 #define MP_KEY_F12 GDK_F12
801 #define MP_KEY_KP_Enter GDK_KP_Enter
802 #define MP_KEY_Return GDK_Return
803 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
804 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
805 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
806 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
807 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
808 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
809 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
810 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
811 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
812 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
813 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
814 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
815 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
816 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
817 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
818 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
819 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
820 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
821 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
822 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
823 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
824 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
825 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
826 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
827 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
828 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
829 #define MP_KEY_Up GDK_Up
830 #define MP_KEY_Down GDK_Down
831 #define MP_KEY_Left GDK_Left
832 #define MP_KEY_Right GDK_Right
833 #define MP_KEY_Prior GDK_Prior
834 #define MP_KEY_Next GDK_Next
835 #define MP_KEY_Home GDK_Home
836 #define MP_KEY_End GDK_End
837 #define MP_KEY_space GDK_space
838 #define MP_KEY_KP_Add GDK_KP_Add
839 #define MP_KEY_KP_Subtract GDK_KP_Subtract
840 #define MP_KEY_KP_Multiply GDK_KP_Multiply
841 #define MP_KEY_KP_Divide GDK_KP_Divide
842 #define MP_KEY_F1 GDK_F1
843 #define MP_KEY_F2 GDK_F2
844 #define MP_KEY_F3 GDK_F3
845 #define MP_KEY_F4 GDK_F4
846 #define MP_KEY_F5 GDK_F5
847 #define MP_KEY_F6 GDK_F6
848 #define MP_KEY_F7 GDK_F7
849 #define MP_KEY_F8 GDK_F8
850 #define MP_KEY_F9 GDK_F9
851 #define MP_KEY_F10 GDK_F10
852 #define MP_KEY_F11 GDK_F11
853 #define MP_KEY_F12 GDK_F12
854 #define MP_KEY_Insert GDK_Insert
855 #define MP_KEY_BackSpace GDK_BackSpace
856 #define MP_KEY_Delete GDK_Delete
857 #define MP_KEY_KP_Enter GDK_KP_Enter
858 #define MP_KEY_Return GDK_Return
859 #define MP_KEY_Tab GDK_Tab
860 #define MP_KEY_ISO_Left_Tab GDK_ISO_Left_Tab
861 #define MP_KEY_Escape GDK_Escape
862 #endif /* CONFOPT_GTK == 2 */
864 #if CONFOPT_GTK == 3
865 #define MP_KEY_Up GDK_KEY_Up
866 #define MP_KEY_Down GDK_KEY_Down
867 #define MP_KEY_Left GDK_KEY_Left
868 #define MP_KEY_Right GDK_KEY_Right
869 #define MP_KEY_Prior GDK_KEY_Prior
870 #define MP_KEY_Next GDK_KEY_Next
871 #define MP_KEY_Home GDK_KEY_Home
872 #define MP_KEY_End GDK_KEY_End
873 #define MP_KEY_space GDK_KEY_space
874 #define MP_KEY_KP_Add GDK_KEY_KP_Add
875 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
876 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
877 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
878 #define MP_KEY_F1 GDK_KEY_F1
879 #define MP_KEY_F2 GDK_KEY_F2
880 #define MP_KEY_F3 GDK_KEY_F3
881 #define MP_KEY_F4 GDK_KEY_F4
882 #define MP_KEY_F5 GDK_KEY_F5
883 #define MP_KEY_F6 GDK_KEY_F6
884 #define MP_KEY_F7 GDK_KEY_F7
885 #define MP_KEY_F8 GDK_KEY_F8
886 #define MP_KEY_F9 GDK_KEY_F9
887 #define MP_KEY_F10 GDK_KEY_F10
888 #define MP_KEY_F11 GDK_KEY_F11
889 #define MP_KEY_F12 GDK_KEY_F12
890 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
891 #define MP_KEY_Return GDK_KEY_Return
892 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
893 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
894 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
895 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
896 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
897 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
898 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
899 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
900 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
901 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
902 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
903 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
904 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
905 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
906 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
907 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
908 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
909 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
910 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
911 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
912 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
913 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
914 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
915 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
916 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
917 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
918 #define MP_KEY_Up GDK_KEY_Up
919 #define MP_KEY_Down GDK_KEY_Down
920 #define MP_KEY_Left GDK_KEY_Left
921 #define MP_KEY_Right GDK_KEY_Right
922 #define MP_KEY_Prior GDK_KEY_Prior
923 #define MP_KEY_Next GDK_KEY_Next
924 #define MP_KEY_Home GDK_KEY_Home
925 #define MP_KEY_End GDK_KEY_End
926 #define MP_KEY_space GDK_KEY_space
927 #define MP_KEY_KP_Add GDK_KEY_KP_Add
928 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
929 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
930 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
931 #define MP_KEY_F1 GDK_KEY_F1
932 #define MP_KEY_F2 GDK_KEY_F2
933 #define MP_KEY_F3 GDK_KEY_F3
934 #define MP_KEY_F4 GDK_KEY_F4
935 #define MP_KEY_F5 GDK_KEY_F5
936 #define MP_KEY_F6 GDK_KEY_F6
937 #define MP_KEY_F7 GDK_KEY_F7
938 #define MP_KEY_F8 GDK_KEY_F8
939 #define MP_KEY_F9 GDK_KEY_F9
940 #define MP_KEY_F10 GDK_KEY_F10
941 #define MP_KEY_F11 GDK_KEY_F11
942 #define MP_KEY_F12 GDK_KEY_F12
943 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
944 #define MP_KEY_Return GDK_KEY_Return
945 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
946 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
947 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
948 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
949 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
950 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
951 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
952 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
953 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
954 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
955 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
956 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
957 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
958 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
959 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
960 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
961 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
962 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
963 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
964 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
965 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
966 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
967 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
968 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
969 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
970 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
971 #define MP_KEY_Up GDK_KEY_Up
972 #define MP_KEY_Down GDK_KEY_Down
973 #define MP_KEY_Left GDK_KEY_Left
974 #define MP_KEY_Right GDK_KEY_Right
975 #define MP_KEY_Prior GDK_KEY_Prior
976 #define MP_KEY_Next GDK_KEY_Next
977 #define MP_KEY_Home GDK_KEY_Home
978 #define MP_KEY_End GDK_KEY_End
979 #define MP_KEY_space GDK_KEY_space
980 #define MP_KEY_KP_Add GDK_KEY_KP_Add
981 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
982 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
983 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
984 #define MP_KEY_F1 GDK_KEY_F1
985 #define MP_KEY_F2 GDK_KEY_F2
986 #define MP_KEY_F3 GDK_KEY_F3
987 #define MP_KEY_F4 GDK_KEY_F4
988 #define MP_KEY_F5 GDK_KEY_F5
989 #define MP_KEY_F6 GDK_KEY_F6
990 #define MP_KEY_F7 GDK_KEY_F7
991 #define MP_KEY_F8 GDK_KEY_F8
992 #define MP_KEY_F9 GDK_KEY_F9
993 #define MP_KEY_F10 GDK_KEY_F10
994 #define MP_KEY_F11 GDK_KEY_F11
995 #define MP_KEY_F12 GDK_KEY_F12
996 #define MP_KEY_Insert GDK_KEY_Insert
997 #define MP_KEY_BackSpace GDK_KEY_BackSpace
998 #define MP_KEY_Delete GDK_KEY_Delete
999 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
1000 #define MP_KEY_Return GDK_KEY_Return
1001 #define MP_KEY_Tab GDK_KEY_Tab
1002 #define MP_KEY_ISO_Left_Tab GDK_KEY_ISO_Left_Tab
1003 #define MP_KEY_Escape GDK_KEY_Escape
1004 #endif /* CONFOPT_GTK == 3 */
1006 static gint key_press_event(GtkWidget * widget, GdkEventKey * event,
1007 gpointer data)
1008 /* 'key_press_event' handler */
1010 wchar_t *ptr = NULL;
1012 gtk_im_context_filter_keypress(im, event);
1014 /* set mp.shift_pressed */
1015 if (event->state & (GDK_SHIFT_MASK))
1016 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
1018 /* reserve alt for menu mnemonics */
1019 /* if (GDK_MOD1_MASK & event->state)
1020 return(0);*/
1022 if (event->state & (GDK_CONTROL_MASK)) {
1023 switch (event->keyval) {
1024 case MP_KEY_Up:
1025 ptr = L"ctrl-cursor-up";
1026 break;
1027 case MP_KEY_Down:
1028 ptr = L"ctrl-cursor-down";
1029 break;
1030 case MP_KEY_Left:
1031 ptr = L"ctrl-cursor-left";
1032 break;
1033 case MP_KEY_Right:
1034 ptr = L"ctrl-cursor-right";
1035 break;
1036 case MP_KEY_Prior:
1037 ptr = L"ctrl-page-up";
1038 break;
1039 case MP_KEY_Next:
1040 ptr = L"ctrl-page-down";
1041 break;
1042 case MP_KEY_Home:
1043 ptr = L"ctrl-home";
1044 break;
1045 case MP_KEY_End:
1046 ptr = L"ctrl-end";
1047 break;
1048 case MP_KEY_space:
1049 ptr = L"ctrl-space";
1050 break;
1051 case MP_KEY_KP_Add:
1052 ptr = L"ctrl-kp-plus";
1053 break;
1054 case MP_KEY_KP_Subtract:
1055 ptr = L"ctrl-kp-minus";
1056 break;
1057 case MP_KEY_KP_Multiply:
1058 ptr = L"ctrl-kp-multiply";
1059 break;
1060 case MP_KEY_KP_Divide:
1061 ptr = L"ctrl-kp-divide";
1062 break;
1063 case MP_KEY_F1:
1064 ptr = L"ctrl-f1";
1065 break;
1066 case MP_KEY_F2:
1067 ptr = L"ctrl-f2";
1068 break;
1069 case MP_KEY_F3:
1070 ptr = L"ctrl-f3";
1071 break;
1072 case MP_KEY_F4:
1073 ptr = L"ctrl-f4";
1074 break;
1075 case MP_KEY_F5:
1076 ptr = L"ctrl-f5";
1077 break;
1078 case MP_KEY_F6:
1079 ptr = L"ctrl-f6";
1080 break;
1081 case MP_KEY_F7:
1082 ptr = L"ctrl-f7";
1083 break;
1084 case MP_KEY_F8:
1085 ptr = L"ctrl-f8";
1086 break;
1087 case MP_KEY_F9:
1088 ptr = L"ctrl-f9";
1089 break;
1090 case MP_KEY_F10:
1091 ptr = L"ctrl-f10";
1092 break;
1093 case MP_KEY_F11:
1094 ptr = L"ctrl-f11";
1095 break;
1096 case MP_KEY_F12:
1097 ptr = L"ctrl-f12";
1098 break;
1099 case MP_KEY_KP_Enter:
1100 case MP_KEY_Return:
1101 ptr = L"ctrl-enter";
1102 break;
1103 case MP_KEY_Cyrillic_ve:
1104 ptr = L"ctrl-d";
1105 break;
1106 case MP_KEY_Cyrillic_a:
1107 ptr = L"ctrl-f";
1108 break;
1109 case MP_KEY_Cyrillic_tse:
1110 ptr = L"ctrl-w";
1111 break;
1112 case MP_KEY_Cyrillic_de:
1113 ptr = L"ctrl-l";
1114 break;
1115 case MP_KEY_Cyrillic_ie:
1116 ptr = L"ctrl-t";
1117 break;
1118 case MP_KEY_Cyrillic_ef:
1119 ptr = L"ctrl-a";
1120 break;
1121 case MP_KEY_Cyrillic_ghe:
1122 ptr = L"ctrl-u";
1123 break;
1124 case MP_KEY_Cyrillic_i:
1125 ptr = L"ctrl-b";
1126 break;
1127 case MP_KEY_Cyrillic_shorti:
1128 ptr = L"ctrl-q";
1129 break;
1130 case MP_KEY_Cyrillic_ka:
1131 ptr = L"ctrl-r";
1132 break;
1133 case MP_KEY_Cyrillic_el:
1134 ptr = L"ctrl-k";
1135 break;
1136 case MP_KEY_Cyrillic_em:
1137 ptr = L"ctrl-v";
1138 break;
1139 case MP_KEY_Cyrillic_en:
1140 ptr = L"ctrl-y";
1141 break;
1142 case MP_KEY_Cyrillic_o:
1143 ptr = L"ctrl-j";
1144 break;
1145 case MP_KEY_Cyrillic_pe:
1146 ptr = L"ctrl-g";
1147 break;
1148 case MP_KEY_Cyrillic_ya:
1149 ptr = L"ctrl-z";
1150 break;
1151 case MP_KEY_Cyrillic_er:
1152 ptr = L"ctrl-h";
1153 break;
1154 case MP_KEY_Cyrillic_es:
1155 ptr = L"ctrl-c";
1156 break;
1157 case MP_KEY_Cyrillic_te:
1158 ptr = L"ctrl-n";
1159 break;
1160 case MP_KEY_Cyrillic_softsign:
1161 ptr = L"ctrl-m";
1162 break;
1163 case MP_KEY_Cyrillic_yeru:
1164 ptr = L"ctrl-s";
1165 break;
1166 case MP_KEY_Cyrillic_ze:
1167 ptr = L"ctrl-p";
1168 break;
1169 case MP_KEY_Cyrillic_sha:
1170 ptr = L"ctrl-i";
1171 break;
1172 case MP_KEY_Cyrillic_e:
1173 ptr = L"ctrl-t";
1174 break;
1175 case MP_KEY_Cyrillic_shcha:
1176 ptr = L"ctrl-o";
1177 break;
1178 case MP_KEY_Cyrillic_che:
1179 ptr = L"ctrl-x";
1180 break;
1183 if (ptr == NULL) {
1184 char c = event->keyval & 0xdf;
1186 switch (c) {
1187 case 'A':
1188 ptr = L"ctrl-a";
1189 break;
1190 case 'B':
1191 ptr = L"ctrl-b";
1192 break;
1193 case 'C':
1194 ptr = L"ctrl-c";
1195 break;
1196 case 'D':
1197 ptr = L"ctrl-d";
1198 break;
1199 case 'E':
1200 ptr = L"ctrl-e";
1201 break;
1202 case 'F':
1203 ptr = L"ctrl-f";
1204 break;
1205 case 'G':
1206 ptr = L"ctrl-g";
1207 break;
1208 case 'H':
1209 ptr = L"ctrl-h";
1210 break;
1211 case 'I':
1212 ptr = L"ctrl-i";
1213 break;
1214 case 'J':
1215 ptr = L"ctrl-j";
1216 break;
1217 case 'K':
1218 ptr = L"ctrl-k";
1219 break;
1220 case 'L':
1221 ptr = L"ctrl-l";
1222 break;
1223 case 'M':
1224 ptr = L"ctrl-m";
1225 break;
1226 case 'N':
1227 ptr = L"ctrl-n";
1228 break;
1229 case 'O':
1230 ptr = L"ctrl-o";
1231 break;
1232 case 'P':
1233 ptr = L"ctrl-p";
1234 break;
1235 case 'Q':
1236 ptr = L"ctrl-q";
1237 break;
1238 case 'R':
1239 ptr = L"ctrl-r";
1240 break;
1241 case 'S':
1242 ptr = L"ctrl-s";
1243 break;
1244 case 'T':
1245 ptr = L"ctrl-t";
1246 break;
1247 case 'U':
1248 ptr = L"ctrl-u";
1249 break;
1250 case 'V':
1251 ptr = L"ctrl-v";
1252 break;
1253 case 'W':
1254 ptr = L"ctrl-w";
1255 break;
1256 case 'X':
1257 ptr = L"ctrl-x";
1258 break;
1259 case 'Y':
1260 ptr = L"ctrl-y";
1261 break;
1262 case 'Z':
1263 ptr = L"ctrl-z";
1264 break;
1268 else
1269 if (event->state & (GDK_MOD1_MASK)) {
1270 switch (event->keyval) {
1271 case MP_KEY_Up:
1272 ptr = L"alt-cursor-up";
1273 break;
1274 case MP_KEY_Down:
1275 ptr = L"alt-cursor-down";
1276 break;
1277 case MP_KEY_Left:
1278 ptr = L"alt-cursor-left";
1279 break;
1280 case MP_KEY_Right:
1281 ptr = L"alt-cursor-right";
1282 break;
1283 case MP_KEY_Prior:
1284 ptr = L"alt-page-up";
1285 break;
1286 case MP_KEY_Next:
1287 ptr = L"alt-page-down";
1288 break;
1289 case MP_KEY_Home:
1290 ptr = L"alt-home";
1291 break;
1292 case MP_KEY_End:
1293 ptr = L"alt-end";
1294 break;
1295 case MP_KEY_space:
1296 ptr = L"alt-space";
1297 break;
1298 case MP_KEY_KP_Add:
1299 ptr = L"alt-kp-plus";
1300 break;
1301 case MP_KEY_KP_Subtract:
1302 ptr = L"alt-kp-minus";
1303 break;
1304 case MP_KEY_KP_Multiply:
1305 ptr = L"alt-kp-multiply";
1306 break;
1307 case MP_KEY_KP_Divide:
1308 ptr = L"alt-kp-divide";
1309 break;
1310 case MP_KEY_F1:
1311 ptr = L"alt-f1";
1312 break;
1313 case MP_KEY_F2:
1314 ptr = L"alt-f2";
1315 break;
1316 case MP_KEY_F3:
1317 ptr = L"alt-f3";
1318 break;
1319 case MP_KEY_F4:
1320 ptr = L"alt-f4";
1321 break;
1322 case MP_KEY_F5:
1323 ptr = L"alt-f5";
1324 break;
1325 case MP_KEY_F6:
1326 ptr = L"alt-f6";
1327 break;
1328 case MP_KEY_F7:
1329 ptr = L"alt-f7";
1330 break;
1331 case MP_KEY_F8:
1332 ptr = L"alt-f8";
1333 break;
1334 case MP_KEY_F9:
1335 ptr = L"alt-f9";
1336 break;
1337 case MP_KEY_F10:
1338 ptr = L"alt-f10";
1339 break;
1340 case MP_KEY_F11:
1341 ptr = L"alt-f11";
1342 break;
1343 case MP_KEY_F12:
1344 ptr = L"alt-f12";
1345 break;
1346 case MP_KEY_KP_Enter:
1347 case MP_KEY_Return:
1348 ptr = L"alt-enter";
1349 break;
1350 case MP_KEY_Cyrillic_ve:
1351 ptr = L"alt-d";
1352 break;
1353 case MP_KEY_Cyrillic_a:
1354 ptr = L"alt-f";
1355 break;
1356 case MP_KEY_Cyrillic_tse:
1357 ptr = L"alt-w";
1358 break;
1359 case MP_KEY_Cyrillic_de:
1360 ptr = L"alt-l";
1361 break;
1362 case MP_KEY_Cyrillic_ie:
1363 ptr = L"alt-t";
1364 break;
1365 case MP_KEY_Cyrillic_ef:
1366 ptr = L"alt-a";
1367 break;
1368 case MP_KEY_Cyrillic_ghe:
1369 ptr = L"alt-u";
1370 break;
1371 case MP_KEY_Cyrillic_i:
1372 ptr = L"alt-b";
1373 break;
1374 case MP_KEY_Cyrillic_shorti:
1375 ptr = L"alt-q";
1376 break;
1377 case MP_KEY_Cyrillic_ka:
1378 ptr = L"alt-r";
1379 break;
1380 case MP_KEY_Cyrillic_el:
1381 ptr = L"alt-k";
1382 break;
1383 case MP_KEY_Cyrillic_em:
1384 ptr = L"alt-v";
1385 break;
1386 case MP_KEY_Cyrillic_en:
1387 ptr = L"alt-y";
1388 break;
1389 case MP_KEY_Cyrillic_o:
1390 ptr = L"alt-j";
1391 break;
1392 case MP_KEY_Cyrillic_pe:
1393 ptr = L"alt-g";
1394 break;
1395 case MP_KEY_Cyrillic_ya:
1396 ptr = L"alt-z";
1397 break;
1398 case MP_KEY_Cyrillic_er:
1399 ptr = L"alt-h";
1400 break;
1401 case MP_KEY_Cyrillic_es:
1402 ptr = L"alt-c";
1403 break;
1404 case MP_KEY_Cyrillic_te:
1405 ptr = L"alt-n";
1406 break;
1407 case MP_KEY_Cyrillic_softsign:
1408 ptr = L"alt-m";
1409 break;
1410 case MP_KEY_Cyrillic_yeru:
1411 ptr = L"alt-s";
1412 break;
1413 case MP_KEY_Cyrillic_ze:
1414 ptr = L"alt-p";
1415 break;
1416 case MP_KEY_Cyrillic_sha:
1417 ptr = L"alt-i";
1418 break;
1419 case MP_KEY_Cyrillic_e:
1420 ptr = L"alt-t";
1421 break;
1422 case MP_KEY_Cyrillic_shcha:
1423 ptr = L"alt-o";
1424 break;
1425 case MP_KEY_Cyrillic_che:
1426 ptr = L"alt-x";
1427 break;
1430 if (ptr == NULL) {
1431 char c = event->keyval & 0xdf;
1433 switch (c) {
1434 case 'A':
1435 ptr = L"alt-a";
1436 break;
1437 case 'B':
1438 ptr = L"alt-b";
1439 break;
1440 case 'C':
1441 ptr = L"alt-c";
1442 break;
1443 case 'D':
1444 ptr = L"alt-d";
1445 break;
1446 case 'E':
1447 ptr = L"alt-e";
1448 break;
1449 case 'F':
1450 ptr = L"alt-f";
1451 break;
1452 case 'G':
1453 ptr = L"alt-g";
1454 break;
1455 case 'H':
1456 ptr = L"alt-h";
1457 break;
1458 case 'I':
1459 ptr = L"alt-i";
1460 break;
1461 case 'J':
1462 ptr = L"alt-j";
1463 break;
1464 case 'K':
1465 ptr = L"alt-k";
1466 break;
1467 case 'L':
1468 ptr = L"alt-l";
1469 break;
1470 case 'M':
1471 ptr = L"alt-m";
1472 break;
1473 case 'N':
1474 ptr = L"alt-n";
1475 break;
1476 case 'O':
1477 ptr = L"alt-o";
1478 break;
1479 case 'P':
1480 ptr = L"alt-p";
1481 break;
1482 case 'Q':
1483 ptr = L"alt-q";
1484 break;
1485 case 'R':
1486 ptr = L"alt-r";
1487 break;
1488 case 'S':
1489 ptr = L"alt-s";
1490 break;
1491 case 'T':
1492 ptr = L"alt-t";
1493 break;
1494 case 'U':
1495 ptr = L"alt-u";
1496 break;
1497 case 'V':
1498 ptr = L"alt-v";
1499 break;
1500 case 'W':
1501 ptr = L"alt-w";
1502 break;
1503 case 'X':
1504 ptr = L"alt-x";
1505 break;
1506 case 'Y':
1507 ptr = L"alt-y";
1508 break;
1509 case 'Z':
1510 ptr = L"alt-z";
1511 break;
1515 else {
1516 switch (event->keyval) {
1517 case MP_KEY_Up:
1518 ptr = L"cursor-up";
1519 break;
1520 case MP_KEY_Down:
1521 ptr = L"cursor-down";
1522 break;
1523 case MP_KEY_Left:
1524 ptr = L"cursor-left";
1525 break;
1526 case MP_KEY_Right:
1527 ptr = L"cursor-right";
1528 break;
1529 case MP_KEY_Prior:
1530 ptr = L"page-up";
1531 break;
1532 case MP_KEY_Next:
1533 ptr = L"page-down";
1534 break;
1535 case MP_KEY_Home:
1536 ptr = L"home";
1537 break;
1538 case MP_KEY_End:
1539 ptr = L"end";
1540 break;
1541 case MP_KEY_space:
1542 ptr = L"space";
1543 break;
1544 case MP_KEY_KP_Add:
1545 ptr = L"kp-plus";
1546 break;
1547 case MP_KEY_KP_Subtract:
1548 ptr = L"kp-minus";
1549 break;
1550 case MP_KEY_KP_Multiply:
1551 ptr = L"kp-multiply";
1552 break;
1553 case MP_KEY_KP_Divide:
1554 ptr = L"kp-divide";
1555 break;
1556 case MP_KEY_F1:
1557 ptr = L"f1";
1558 break;
1559 case MP_KEY_F2:
1560 ptr = L"f2";
1561 break;
1562 case MP_KEY_F3:
1563 ptr = L"f3";
1564 break;
1565 case MP_KEY_F4:
1566 ptr = L"f4";
1567 break;
1568 case MP_KEY_F5:
1569 ptr = L"f5";
1570 break;
1571 case MP_KEY_F6:
1572 ptr = L"f6";
1573 break;
1574 case MP_KEY_F7:
1575 ptr = L"f7";
1576 break;
1577 case MP_KEY_F8:
1578 ptr = L"f8";
1579 break;
1580 case MP_KEY_F9:
1581 ptr = L"f9";
1582 break;
1583 case MP_KEY_F10:
1584 ptr = L"f10";
1585 break;
1586 case MP_KEY_F11:
1587 ptr = L"f11";
1588 break;
1589 case MP_KEY_F12:
1590 ptr = L"f12";
1591 break;
1592 case MP_KEY_Insert:
1593 ptr = L"insert";
1594 break;
1595 case MP_KEY_BackSpace:
1596 ptr = L"backspace";
1597 break;
1598 case MP_KEY_Delete:
1599 ptr = L"delete";
1600 break;
1601 case MP_KEY_KP_Enter:
1602 case MP_KEY_Return:
1603 ptr = L"enter";
1604 break;
1605 case MP_KEY_Tab:
1606 ptr = L"tab";
1607 break;
1608 case MP_KEY_ISO_Left_Tab:
1609 ptr = L"shift-tab";
1610 break;
1611 case MP_KEY_Escape:
1612 ptr = L"escape";
1613 break;
1617 /* if there is a pending char in im_char, use it */
1618 if (ptr == NULL && im_char[0] != L'\0')
1619 ptr = im_char;
1621 /* finally process */
1622 if (ptr != NULL)
1623 mp_process_event(MPDM_S(ptr));
1625 /* delete the pending char */
1626 im_char[0] = L'\0';
1628 if (mp_exit_requested)
1629 gtk_main_quit();
1631 if (mp_keypress_throttle(1))
1632 gtk_drv_paint(mp_active(), 1);
1634 return 0;
1638 static gint button_press_event(GtkWidget * widget, GdkEventButton * event,
1639 gpointer data)
1640 /* 'button_press_event' handler (mouse buttons) */
1642 int x, y;
1643 wchar_t *ptr = NULL;
1645 mouse_down = 1;
1647 /* mouse instant positioning */
1648 x = ((int) event->x) / font_width;
1649 y = ((int) event->y) / font_height;
1651 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
1652 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
1654 switch (event->button) {
1655 case 1:
1656 ptr = L"mouse-left-button";
1657 break;
1658 case 2:
1659 ptr = L"mouse-middle-button";
1660 break;
1661 case 3:
1662 ptr = L"mouse-right-button";
1663 break;
1664 case 4:
1665 ptr = L"mouse-wheel-up";
1666 break;
1667 case 5:
1668 ptr = L"mouse-wheel-down";
1669 break;
1672 if (ptr != NULL)
1673 mp_process_event(MPDM_S(ptr));
1675 redraw();
1677 return 0;
1681 static gint button_release_event(GtkWidget * widget,
1682 GdkEventButton * event, gpointer data)
1683 /* 'button_release_event' handle (mouse buttons) */
1685 mouse_down = 0;
1687 return TRUE;
1691 static gint motion_notify_event(GtkWidget * widget, GdkEventMotion * event,
1692 gpointer data)
1693 /* 'motion_notify_event' handler (mouse movement) */
1695 static int ox = 0;
1696 static int oy = 0;
1698 if (mouse_down) {
1699 int x, y;
1701 /* mouse dragging */
1702 x = ((int) event->x) / font_width;
1703 y = ((int) event->y) / font_height;
1705 if (ox != x && oy != y) {
1706 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
1707 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
1709 mp_process_event(MPDM_LS(L"mouse-drag"));
1710 gtk_drv_paint(mp_active(), 1);
1714 return TRUE;
1718 static void drag_data_received(GtkWidget * widget, GdkDragContext * dc,
1719 gint x, gint y, GtkSelectionData * data,
1720 guint info, guint time)
1721 /* 'drag_data_received' handler */
1723 printf("drag_data_received (unsupported)\n");
1727 /** clipboard functions **/
1729 static void commit(GtkIMContext * i, char *str, gpointer u)
1730 /* 'commit' handler */
1732 wchar_t *wstr;
1734 wstr = (wchar_t *) g_convert(str, -1,
1735 "WCHAR_T", "UTF-8", NULL, NULL, NULL);
1737 im_char[0] = *wstr;
1738 im_char[1] = L'\0';
1740 g_free(wstr);
1744 static void realize(GtkWidget * widget)
1745 /* 'realize' handler */
1747 im = gtk_im_multicontext_new();
1748 g_signal_connect(im, "commit", G_CALLBACK(commit), NULL);
1749 gtk_im_context_set_client_window(im, gtk_widget_get_window(widget));
1753 static gint expose_event(GtkWidget * widget, cairo_t *event)
1754 /* 'expose_event' handler */
1756 redraw();
1758 return FALSE;
1762 static gint configure_event(GtkWidget * widget, GdkEventConfigure * event)
1763 /* 'configure_event' handler */
1765 static GdkEventConfigure o;
1767 if (memcmp(&o, event, sizeof(o)) == 0)
1768 return TRUE;
1770 memcpy(&o, event, sizeof(o));
1772 update_window_size();
1773 redraw();
1775 return TRUE;
1779 static gint selection_clear_event(GtkWidget * widget,
1780 GdkEventSelection * event, gpointer data)
1781 /* 'selection_clear_event' handler */
1783 got_selection = 0;
1785 return TRUE;
1789 static void selection_get(GtkWidget * widget,
1790 GtkSelectionData * sel, guint info, guint tm)
1791 /* 'selection_get' handler */
1793 mpdm_t d;
1794 unsigned char *ptr;
1795 int s;
1797 if (!got_selection)
1798 return;
1800 /* gets the clipboard and joins */
1801 d = mpdm_hget_s(mp, L"clipboard");
1803 if (mpdm_size(d) == 0)
1804 return;
1806 d = mpdm_ref(mpdm_join_s(d, L"\n"));
1808 /* convert to current locale */
1809 ptr = (unsigned char *) mpdm_wcstombs(d->data, &s);
1811 /* pastes into primary selection */
1812 gtk_selection_data_set(sel, GDK_SELECTION_TYPE_STRING, 8, ptr,
1813 (gsize) s);
1815 free(ptr);
1817 mpdm_unref(d);
1821 static void selection_received(GtkWidget * widget,
1822 GtkSelectionData * sel, gpointer data)
1823 /* 'selection_received' handler */
1825 mpdm_t d;
1827 if (gtk_selection_data_get_data(sel) != NULL) {
1828 /* get selection */
1829 wchar_t *wptr = utf8_to_wcs((char *) gtk_selection_data_get_data(sel));
1830 d = MPDM_S(wptr);
1831 g_free(wptr);
1833 /* split and set as the clipboard */
1834 mpdm_hset_s(mp, L"clipboard", mpdm_split_s(d, L"\n"));
1835 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
1837 /* wait no more for the selection */
1838 wait_for_selection = 0;
1840 else
1841 wait_for_selection = -1;
1845 static mpdm_t gtk_drv_clip_to_sys(mpdm_t a, mpdm_t ctxt)
1846 /* driver-dependent mp to system clipboard */
1848 got_selection = gtk_selection_owner_set(area,
1849 GDK_SELECTION_PRIMARY,
1850 GDK_CURRENT_TIME);
1852 return NULL;
1856 static mpdm_t gtk_drv_sys_to_clip(mpdm_t a, mpdm_t ctxt)
1857 /* driver-dependent system to mp clipboard */
1859 if (!got_selection) {
1860 int n;
1861 char *formats[] = { "UTF8_STRING", "STRING", NULL };
1863 for (n = 0; formats[n] != NULL; n++) {
1865 /* triggers a selection capture */
1866 if (gtk_selection_convert(area, GDK_SELECTION_PRIMARY,
1867 gdk_atom_intern(formats[n], FALSE),
1868 GDK_CURRENT_TIME)) {
1870 /* processes the pending events
1871 (i.e., the 'selection_received' handler) */
1872 wait_for_selection = 1;
1874 while (wait_for_selection == 1)
1875 gtk_main_iteration();
1877 if (!wait_for_selection)
1878 break;
1883 return NULL;
1887 /** interface functions **/
1889 static void clicked_ok(GtkWidget * widget, gpointer data)
1890 /* 'clicked_on' signal handler (for gtk_drv_form) */
1892 int n;
1894 for (n = 0; n < mpdm_size(form_args); n++) {
1895 GtkWidget *widget = form_widgets[n];
1896 mpdm_t w = mpdm_aget(form_args, n);
1897 wchar_t *wptr = mpdm_string(mpdm_hget_s(w, L"type"));
1898 mpdm_t v = NULL;
1900 /* if there is already a value there, if was
1901 previously set from a callback */
1902 if (mpdm_aget(form_values, n) != NULL)
1903 continue;
1905 if (wcscmp(wptr, L"text") == 0 || wcscmp(wptr, L"password") == 0) {
1906 char *ptr;
1907 GtkWidget *gw = widget;
1908 mpdm_t h;
1910 if (wcscmp(wptr, L"text") == 0)
1911 gw = GTK_COMBO(widget)->entry;
1913 if ((ptr =
1914 gtk_editable_get_chars(GTK_EDITABLE(gw), 0, -1)) != NULL
1915 && (wptr = utf8_to_wcs(ptr)) != NULL) {
1916 v = MPDM_S(wptr);
1917 g_free(wptr);
1918 g_free(ptr);
1921 mpdm_ref(v);
1923 /* if it has history, fill it */
1924 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1925 v != NULL && mpdm_cmp_s(v, L"") != 0) {
1926 h = mp_get_history(h);
1928 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1929 mpdm_push(h, v);
1932 mpdm_unrefnd(v);
1934 else
1935 if (wcscmp(wptr, L"checkbox") == 0) {
1936 v = MPDM_I(gtk_toggle_button_get_active
1937 (GTK_TOGGLE_BUTTON(widget)));
1939 else
1940 if (wcscmp(wptr, L"list") == 0) {
1941 GtkWidget *list = gtk_bin_get_child(GTK_BIN(widget));
1942 GtkTreeSelection *selection =
1943 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
1944 GList *selected =
1945 gtk_tree_selection_get_selected_rows(selection, NULL);
1946 GtkTreePath *path = selected->data;
1948 v = MPDM_I(gtk_tree_path_get_indices(path)[0]);
1949 gtk_tree_path_free(path);
1950 g_list_free(selected);
1954 mpdm_aset(form_values, v, n);
1959 static gint timer_callback(gpointer data)
1961 mpdm_void(mpdm_exec(timer_func, NULL, NULL));
1962 redraw();
1964 return TRUE;
1968 static void build_form_data(mpdm_t widget_list)
1969 /* builds the necessary information for a list of widgets */
1971 mpdm_unref(form_args);
1972 form_args = mpdm_ref(widget_list);
1974 mpdm_unref(form_values);
1975 form_values = widget_list == NULL ? NULL :
1976 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1978 /* resize the widget array */
1979 form_widgets = (GtkWidget **) realloc(form_widgets,
1980 mpdm_size(form_args) *
1981 sizeof(GtkWidget *));
1985 /** dialog functions **/
1987 #define DIALOG_BUTTON(l,f) do { GtkWidget * btn; \
1988 ptr = localize(l); btn = gtk_button_new_with_label(ptr); \
1989 g_signal_connect_swapped(G_OBJECT(btn), "clicked", \
1990 G_CALLBACK(f), G_OBJECT(dlg)); \
1991 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), \
1992 btn, TRUE, TRUE, 0); \
1993 g_free(ptr); \
1994 } while (0);
1996 static mpdm_t gtk_drv_alert(mpdm_t a, mpdm_t ctxt)
1997 /* alert driver function */
1999 gchar *ptr;
2000 GtkWidget *dlg;
2002 build_form_data(NULL);
2004 /* 1# arg: prompt */
2005 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
2006 return NULL;
2008 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
2009 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "%s", ptr);
2010 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
2011 g_free(ptr);
2013 gtk_dialog_run(GTK_DIALOG(dlg));
2014 gtk_widget_destroy(dlg);
2016 return NULL;
2020 static mpdm_t gtk_drv_confirm(mpdm_t a, mpdm_t ctxt)
2021 /* confirm driver function */
2023 char *ptr;
2024 GtkWidget *dlg;
2025 gint response;
2027 build_form_data(NULL);
2029 /* 1# arg: prompt */
2030 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
2031 return NULL;
2033 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
2034 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
2035 "%s", ptr);
2036 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
2037 g_free(ptr);
2039 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_YES, 1);
2040 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_NO, 2);
2041 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_CANCEL, 0);
2043 response = gtk_dialog_run(GTK_DIALOG(dlg));
2044 gtk_widget_destroy(dlg);
2046 if (response == GTK_RESPONSE_DELETE_EVENT)
2047 response = 0;
2049 return MPDM_I(response);
2053 static mpdm_t gtk_drv_form(mpdm_t a, mpdm_t ctxt)
2054 /* 'form' driver function */
2056 GtkWidget *dlg;
2057 GtkWidget *table;
2058 GtkWidget *content_area;
2059 int n;
2060 mpdm_t ret = NULL;
2062 /* first argument: list of widgets */
2063 build_form_data(mpdm_aget(a, 0));
2065 dlg = gtk_dialog_new_with_buttons("mp " VERSION, GTK_WINDOW(window),
2066 GTK_DIALOG_MODAL,
2067 GTK_STOCK_CANCEL,
2068 GTK_RESPONSE_CANCEL, GTK_STOCK_OK,
2069 GTK_RESPONSE_OK, NULL);
2070 gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
2071 gtk_container_set_border_width(GTK_CONTAINER(dlg), 5);
2073 content_area = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
2074 gtk_box_set_spacing(GTK_BOX(content_area), 2);
2076 table = gtk_table_new(mpdm_size(a), 2, FALSE);
2077 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
2078 gtk_table_set_col_spacings(GTK_TABLE(table), 12);
2079 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
2081 for (n = 0; n < mpdm_size(form_args); n++) {
2082 mpdm_t w = mpdm_aget(form_args, n);
2083 GtkWidget *widget = NULL;
2084 wchar_t *type;
2085 char *ptr;
2086 mpdm_t t;
2087 int col = 0;
2089 type = mpdm_string(mpdm_hget_s(w, L"type"));
2091 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
2092 GtkWidget *label;
2094 if ((ptr = v_to_utf8(mpdm_gettext(t))) != NULL) {
2095 label = gtk_label_new(ptr);
2096 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
2098 gtk_table_attach_defaults(GTK_TABLE(table),
2099 label, 0, wcscmp(type,
2100 L"label") ==
2101 0 ? 2 : 1, n, n + 1);
2103 g_free(ptr);
2105 col++;
2109 t = mpdm_hget_s(w, L"value");
2111 if (wcscmp(type, L"text") == 0) {
2112 GList *combo_items = NULL;
2113 mpdm_t h;
2115 widget = gtk_combo_new();
2116 gtk_widget_set_size_request(widget, 300, -1);
2117 gtk_combo_set_use_arrows_always(GTK_COMBO(widget), TRUE);
2118 gtk_combo_set_case_sensitive(GTK_COMBO(widget), TRUE);
2119 gtk_entry_set_activates_default(GTK_ENTRY
2120 (GTK_COMBO(widget)->entry),
2121 TRUE);
2123 if ((h = mpdm_hget_s(w, L"history")) != NULL) {
2124 int i;
2126 /* has history; fill it */
2127 h = mp_get_history(h);
2129 for (i = 0; i < mpdm_size(h); i++) {
2130 ptr = v_to_utf8(mpdm_aget(h, i));
2132 combo_items = g_list_prepend(combo_items, ptr);
2136 if (t != NULL) {
2137 ptr = v_to_utf8(t);
2139 combo_items = g_list_prepend(combo_items, ptr);
2142 gtk_combo_set_popdown_strings(GTK_COMBO(widget), combo_items);
2143 g_list_free(combo_items);
2145 else
2146 if (wcscmp(type, L"password") == 0) {
2147 widget = gtk_entry_new();
2148 gtk_widget_set_size_request(widget, 300, -1);
2149 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
2151 else
2152 if (wcscmp(type, L"checkbox") == 0) {
2153 widget = gtk_check_button_new();
2155 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
2156 mpdm_ival(t) ? TRUE : FALSE);
2158 else
2159 if (wcscmp(type, L"list") == 0) {
2160 GtkWidget *list;
2161 GtkListStore *list_store;
2162 GtkCellRenderer *renderer;
2163 GtkTreeViewColumn *column;
2164 GtkTreePath *path;
2165 mpdm_t l;
2166 gint i;
2168 if ((i = 450 / mpdm_size(form_args)) < 100)
2169 i = 100;
2171 widget = gtk_scrolled_window_new(NULL, NULL);
2172 gtk_widget_set_size_request(widget, 500, i);
2173 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
2174 GTK_POLICY_NEVER,
2175 GTK_POLICY_AUTOMATIC);
2176 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
2177 (widget), GTK_SHADOW_IN);
2179 list_store = gtk_list_store_new(1, G_TYPE_STRING);
2180 list =
2181 gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
2182 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
2183 renderer = gtk_cell_renderer_text_new();
2184 column = gtk_tree_view_column_new_with_attributes("", renderer,
2185 "text", 0,
2186 NULL);
2187 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
2188 gtk_container_add(GTK_CONTAINER(widget), list);
2190 l = mpdm_hget_s(w, L"list");
2192 for (i = 0; i < mpdm_size(l); i++) {
2193 if ((ptr = v_to_utf8(mpdm_aget(l, i))) != NULL) {
2194 GtkTreeIter iter;
2195 gtk_list_store_append(list_store, &iter);
2196 gtk_list_store_set(list_store, &iter, 0, ptr, -1);
2197 g_free(ptr);
2201 /* initial position */
2202 i = mpdm_ival(t);
2204 path = gtk_tree_path_new_from_indices(i, -1);
2205 gtk_tree_view_set_cursor(GTK_TREE_VIEW(list), path, NULL,
2206 FALSE);
2207 gtk_tree_path_free(path);
2209 g_signal_connect_swapped(G_OBJECT(list), "row-activated",
2210 G_CALLBACK
2211 (gtk_window_activate_default), dlg);
2214 if (widget != NULL) {
2215 form_widgets[n] = widget;
2216 gtk_table_attach_defaults(GTK_TABLE(table),
2217 widget, col, 2, n, n + 1);
2221 gtk_widget_show_all(table);
2223 gtk_box_pack_start(GTK_BOX(content_area), table, TRUE, TRUE, 0);
2225 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) {
2226 clicked_ok(NULL, NULL);
2227 ret = form_values;
2229 gtk_widget_destroy(dlg);
2231 return ret;
2235 static mpdm_t run_filechooser(mpdm_t a, gboolean save)
2236 /* openfile driver function */
2238 GtkWidget *dlg;
2239 char *ptr;
2240 mpdm_t ret = NULL;
2241 gint response;
2243 /* 1# arg: prompt */
2244 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
2245 return (NULL);
2247 if (!save) {
2248 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2249 GTK_FILE_CHOOSER_ACTION_OPEN,
2250 GTK_STOCK_CANCEL,
2251 GTK_RESPONSE_CANCEL,
2252 GTK_STOCK_OK, GTK_RESPONSE_OK,
2253 NULL);
2255 else {
2256 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
2257 GTK_FILE_CHOOSER_ACTION_SAVE,
2258 GTK_STOCK_CANCEL,
2259 GTK_STOCK_CANCEL, GTK_STOCK_OK,
2260 GTK_RESPONSE_OK, NULL);
2261 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER
2262 (dlg), TRUE);
2264 g_free(ptr);
2266 build_form_data(NULL);
2268 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), TRUE);
2269 response = gtk_dialog_run(GTK_DIALOG(dlg));
2271 if (response == GTK_RESPONSE_OK) {
2272 gchar *filename;
2273 gchar *utf8name;
2274 wchar_t *wfilename;
2276 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
2277 utf8name = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
2278 g_free(filename);
2279 wfilename = utf8_to_wcs(utf8name);
2280 g_free(utf8name);
2281 ret = MPDM_S(wfilename);
2282 g_free(wfilename);
2284 gtk_widget_destroy(dlg);
2286 return ret;
2290 static mpdm_t gtk_drv_openfile(mpdm_t a, mpdm_t ctxt)
2291 /* openfile driver function */
2293 return run_filechooser(a, FALSE);
2297 static mpdm_t gtk_drv_savefile(mpdm_t a, mpdm_t ctxt)
2298 /* savefile driver function */
2300 return run_filechooser(a, TRUE);
2304 static mpdm_t gtk_drv_update_ui(mpdm_t a, mpdm_t ctxt)
2306 build_fonts();
2307 build_colors();
2308 build_menu();
2310 redraw();
2312 return NULL;
2316 static mpdm_t gtk_drv_timer(mpdm_t a, mpdm_t ctxt)
2318 static guint prev = 0;
2319 int msecs = mpdm_ival(mpdm_aget(a, 0));
2320 mpdm_t func = mpdm_aget(a, 1);
2322 /* previously defined one? remove */
2323 if (timer_func != NULL)
2324 g_source_remove(prev);
2326 /* if msecs and func are set, program timer */
2327 if (msecs > 0 && func != NULL)
2328 prev = g_timeout_add(msecs, timer_callback, NULL);
2330 mpdm_ref(func);
2331 mpdm_unref(timer_func);
2332 timer_func = func;
2334 return NULL;
2338 static mpdm_t gtk_drv_busy(mpdm_t a, mpdm_t ctxt)
2340 int onoff = mpdm_ival(mpdm_aget(a, 0));
2342 gdk_window_set_cursor(gtk_widget_get_window(window),
2343 gdk_cursor_new(onoff ? GDK_WATCH :
2344 GDK_LEFT_PTR));
2346 while (gtk_events_pending())
2347 gtk_main_iteration();
2349 return NULL;
2353 static mpdm_t gtk_drv_main_loop(mpdm_t a, mpdm_t ctxt)
2354 /* main loop */
2356 if (!mp_exit_requested) {
2357 gtk_drv_paint(mp_active(), 0);
2359 gtk_main();
2362 return NULL;
2366 static mpdm_t gtk_drv_shutdown(mpdm_t a, mpdm_t ctxt)
2367 /* shutdown */
2369 mpdm_t v;
2371 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
2372 mpdm_write_wcs(stdout, mpdm_string(v));
2373 printf("\n");
2376 return NULL;
2380 static void register_functions(void)
2382 mpdm_t drv;
2384 drv = mpdm_hget_s(mp, L"drv");
2385 mpdm_hset_s(drv, L"main_loop", MPDM_X(gtk_drv_main_loop));
2386 mpdm_hset_s(drv, L"shutdown", MPDM_X(gtk_drv_shutdown));
2387 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(gtk_drv_clip_to_sys));
2388 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(gtk_drv_sys_to_clip));
2389 mpdm_hset_s(drv, L"update_ui", MPDM_X(gtk_drv_update_ui));
2390 mpdm_hset_s(drv, L"timer", MPDM_X(gtk_drv_timer));
2391 mpdm_hset_s(drv, L"busy", MPDM_X(gtk_drv_busy));
2392 mpdm_hset_s(drv, L"alert", MPDM_X(gtk_drv_alert));
2393 mpdm_hset_s(drv, L"confirm", MPDM_X(gtk_drv_confirm));
2394 mpdm_hset_s(drv, L"openfile", MPDM_X(gtk_drv_openfile));
2395 mpdm_hset_s(drv, L"savefile", MPDM_X(gtk_drv_savefile));
2396 mpdm_hset_s(drv, L"form", MPDM_X(gtk_drv_form));
2400 static mpdm_t gtk_drv_startup(mpdm_t a, mpdm_t ctxt)
2401 /* driver initialization */
2403 GtkWidget *vbox;
2404 GtkWidget *hbox;
2405 GdkPixmap *pixmap;
2406 GdkPixmap *mask;
2407 GdkScreen *screen;
2408 mpdm_t v;
2409 int w, h;
2410 GtkTargetEntry targets[] = {
2411 {"text/plain", 0, 0},
2412 {"text/uri-list", 0, 1}
2415 register_functions();
2417 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2419 gtk_window_set_title(GTK_WINDOW(window), "mp " VERSION);
2421 /* get real screen and pick a usable size for the main area */
2422 screen = gtk_window_get_screen(GTK_WINDOW(window));
2423 if (gdk_screen_get_n_monitors(screen) > 1) {
2424 GdkRectangle monitor_one_size;
2425 gdk_screen_get_monitor_geometry(screen, 0, &monitor_one_size);
2427 w = (monitor_one_size.width * 3) / 4;
2428 h = (monitor_one_size.height * 2) / 3;
2429 } else {
2430 w = (gdk_screen_get_width(screen) * 3) / 4;
2431 h = (gdk_screen_get_height(screen) * 2) / 3;
2434 g_signal_connect(G_OBJECT(window), "delete_event",
2435 G_CALLBACK(delete_event), NULL);
2437 g_signal_connect(G_OBJECT(window), "destroy",
2438 G_CALLBACK(destroy), NULL);
2440 /* file tabs */
2441 file_tabs = gtk_notebook_new();
2442 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(file_tabs), GTK_POS_TOP);
2443 GTK_WIDGET_UNSET_FLAGS(file_tabs, GTK_CAN_FOCUS);
2444 gtk_notebook_set_scrollable(GTK_NOTEBOOK(file_tabs), 1);
2446 vbox = gtk_vbox_new(FALSE, 2);
2447 gtk_container_add(GTK_CONTAINER(window), vbox);
2449 build_menu();
2451 hbox = gtk_hbox_new(FALSE, 0);
2452 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2453 gtk_box_pack_start(GTK_BOX(hbox), menu_bar, FALSE, FALSE, 0);
2454 gtk_box_pack_start(GTK_BOX(hbox), file_tabs, TRUE, TRUE, 0);
2456 gtk_notebook_popup_enable(GTK_NOTEBOOK(file_tabs));
2458 /* horizontal box holding the text and the scrollbar */
2459 hbox = gtk_hbox_new(FALSE, 2);
2460 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
2462 /* the Minimum Profit area */
2463 area = gtk_drawing_area_new();
2464 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
2465 gtk_widget_set_size_request(GTK_WIDGET(area), w, h);
2466 gtk_widget_set_events(GTK_WIDGET(area), GDK_BUTTON_PRESS_MASK |
2467 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
2468 | GDK_LEAVE_NOTIFY_MASK);
2470 gtk_widget_set_double_buffered(area, FALSE);
2472 g_signal_connect(G_OBJECT(area), "configure_event",
2473 G_CALLBACK(configure_event), NULL);
2475 g_signal_connect(G_OBJECT(area), "expose_event",
2476 G_CALLBACK(expose_event), NULL);
2478 g_signal_connect(G_OBJECT(area), "realize", G_CALLBACK(realize), NULL);
2480 g_signal_connect(G_OBJECT(window), "key_press_event",
2481 G_CALLBACK(key_press_event), NULL);
2483 g_signal_connect(G_OBJECT(window), "key_release_event",
2484 G_CALLBACK(key_release_event), NULL);
2486 g_signal_connect(G_OBJECT(area), "button_press_event",
2487 G_CALLBACK(button_press_event), NULL);
2489 g_signal_connect(G_OBJECT(area), "button_release_event",
2490 G_CALLBACK(button_release_event), NULL);
2492 g_signal_connect(G_OBJECT(area), "motion_notify_event",
2493 G_CALLBACK(motion_notify_event), NULL);
2495 g_signal_connect(G_OBJECT(area), "selection_clear_event",
2496 G_CALLBACK(selection_clear_event), NULL);
2498 g_signal_connect(G_OBJECT(area), "selection_get",
2499 G_CALLBACK(selection_get), NULL);
2501 g_signal_connect(G_OBJECT(area), "selection_received",
2502 G_CALLBACK(selection_received), NULL);
2504 g_signal_connect(G_OBJECT(area), "scroll_event",
2505 G_CALLBACK(scroll_event), NULL);
2507 gtk_drag_dest_set(area, GTK_DEST_DEFAULT_ALL, targets,
2508 sizeof(targets) / sizeof(GtkTargetEntry),
2509 GDK_ACTION_COPY);
2510 g_signal_connect(G_OBJECT(area), "drag_data_received",
2511 G_CALLBACK(drag_data_received), NULL);
2513 gtk_selection_add_target(area, GDK_SELECTION_PRIMARY,
2514 GDK_SELECTION_TYPE_STRING, 1);
2516 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
2517 G_CALLBACK(switch_page), NULL);
2519 /* the scrollbar */
2520 scrollbar = gtk_vscrollbar_new(NULL);
2521 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0);
2523 g_signal_connect(G_OBJECT
2524 (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
2525 "value_changed", G_CALLBACK(value_changed), NULL);
2527 /* the status bar */
2528 status = gtk_label_new("mp " VERSION);
2529 gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
2530 gtk_misc_set_alignment(GTK_MISC(status), 0, 0.5);
2531 gtk_label_set_justify(GTK_LABEL(status), GTK_JUSTIFY_LEFT);
2533 gtk_widget_show_all(window);
2535 /* set application icon */
2536 pixmap = gdk_pixmap_create_from_xpm_d(window->window,
2537 &mask, NULL, mp_xpm);
2538 gdk_window_set_icon(window->window, NULL, pixmap, mask);
2540 build_colors();
2542 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
2543 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
2544 maximize = 1;
2546 return NULL;
2550 int gtk_drv_detect(int *argc, char ***argv)
2552 mpdm_t drv;
2553 int n;
2555 for (n = 0; n < *argc; n++) {
2556 if (strcmp(argv[0][n], "-txt") == 0 ||
2557 strcmp(argv[0][n], "-h") == 0)
2558 return 0;
2561 if (!gtk_init_check(argc, argv))
2562 return 0;
2564 drv = mpdm_hget_s(mp, L"drv");
2565 mpdm_hset_s(drv, L"id", MPDM_LS(L"gtk"));
2566 mpdm_hset_s(drv, L"startup", MPDM_X(gtk_drv_startup));
2568 return 1;
2571 #endif /* CONFOPT_GTK */