New action 'mark_all'.
[mp-5.x.git] / mpv_gtk.c
blobba13367798185cbb19b992bde7f0f6bfb09cc81d
1 /*
3 Minimum Profit - Programmer Text Editor
5 GTK driver.
7 Copyright (C) 1991-2010 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 GdkGC *gc = NULL;
59 static GtkIMContext *im = NULL;
60 static GdkPixmap *pixmap = NULL;
62 /* character read from the keyboard */
63 static wchar_t im_char[2];
65 /* font information */
66 static int font_width = 0;
67 static int font_height = 0;
68 static PangoFontDescription *font = NULL;
70 /* the attributes */
71 static GdkColor *inks = NULL;
72 static GdkColor *papers = NULL;
73 static int *underlines = NULL;
75 /* true if the selection is ours */
76 static int got_selection = 0;
78 /* hack for active waiting for the selection */
79 static int wait_for_selection = 0;
81 /* global modal status */
82 /* code for the 'normal' attribute */
83 static int normal_attr = 0;
85 /* mp.drv.form() controls */
87 static GtkWidget **form_widgets = NULL;
88 static mpdm_t form_args = NULL;
89 static mpdm_t form_values = NULL;
91 /* mouse down flag */
92 static int mouse_down = 0;
94 /* timer function */
95 static mpdm_t timer_func = NULL;
97 /* maximize wanted? */
98 static int maximize = 0;
100 /** code **/
102 /** support functions **/
104 #define LL(m) (m)
106 static char *wcs_to_utf8(const wchar_t * wptr)
107 /* converts a wcs to utf-8 */
109 char *ptr;
110 gsize i, o;
112 i = wcslen(wptr);
114 /* do the conversion */
115 ptr = g_convert((const gchar *) wptr, (i + 1) * sizeof(wchar_t),
116 "UTF-8", "WCHAR_T", NULL, &o, NULL);
118 return ptr;
122 static char *v_to_utf8(mpdm_t v)
124 char *ptr = NULL;
126 if (v != NULL) {
127 mpdm_ref(v);
128 ptr = wcs_to_utf8(mpdm_string(v));
129 mpdm_unref(v);
132 return ptr;
135 static wchar_t *utf8_to_wcs(const char *ptr)
136 /* converts utf-8 to wcs */
138 wchar_t *wptr;
139 gsize i, o;
141 i = strlen(ptr);
143 /* do the conversion */
144 wptr = (wchar_t *) g_convert((gchar *) ptr, i + 1,
145 "WCHAR_T", "UTF-8", NULL, &o, NULL);
147 return wptr;
151 static void update_window_size(void)
152 /* updates the viewport size in characters */
154 PangoLayout *pa;
155 int tx, ty;
156 mpdm_t v;
158 /* get font metrics */
159 pa = gtk_widget_create_pango_layout(area, "m");
160 pango_layout_set_font_description(pa, font);
161 pango_layout_get_pixel_size(pa, &font_width, &font_height);
162 g_object_unref(pa);
164 /* calculate the size in chars */
165 tx = (area->allocation.width / font_width);
166 ty = (area->allocation.height / font_height) + 1;
168 /* store the 'window' size */
169 v = mpdm_hget_s(mp, L"window");
170 mpdm_hset_s(v, L"tx", MPDM_I(tx));
171 mpdm_hset_s(v, L"ty", MPDM_I(ty));
173 /* rebuild the pixmap for the double buffer */
174 if (pixmap != NULL)
175 gdk_pixmap_unref(pixmap);
177 pixmap = gdk_pixmap_new(area->window,
178 area->allocation.width, font_height, -1);
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;
228 gdk_colormap_alloc_color(gdk_colormap_get_system(), gdkcolor, FALSE,
229 TRUE);
233 static void build_colors(void)
234 /* builds the colors */
236 mpdm_t colors;
237 mpdm_t l;
238 mpdm_t c;
239 int n, s;
241 /* gets the color definitions and attribute names */
242 colors = mpdm_hget_s(mp, L"colors");
243 l = mpdm_ref(mpdm_keys(colors));
244 s = mpdm_size(l);
246 /* redim the structures */
247 inks = realloc(inks, sizeof(GdkColor) * s);
248 papers = realloc(papers, sizeof(GdkColor) * s);
249 underlines = realloc(underlines, sizeof(int) * s);
251 /* loop the colors */
252 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
253 mpdm_t d = mpdm_hget(colors, c);
254 mpdm_t v = mpdm_hget_s(d, L"gui");
256 /* store the 'normal' attribute */
257 if (wcscmp(mpdm_string(c), L"normal") == 0)
258 normal_attr = n;
260 /* store the attr */
261 mpdm_hset_s(d, L"attr", MPDM_I(n));
263 build_color(&inks[n], mpdm_ival(mpdm_aget(v, 0)));
264 build_color(&papers[n], mpdm_ival(mpdm_aget(v, 1)));
266 /* flags */
267 v = mpdm_hget_s(d, L"flags");
268 underlines[n] = mpdm_seek_s(v, L"underline", 1) != -1 ? 1 : 0;
270 if (mpdm_seek_s(v, L"reverse", 1) != -1) {
271 GdkColor t;
273 t = inks[n];
274 inks[n] = papers[n];
275 papers[n] = t;
279 mpdm_unref(l);
283 /** menu functions **/
285 static void redraw(void);
287 static void menu_item_callback(mpdm_t action)
288 /* menu click callback */
290 mp_process_action(action);
291 redraw();
293 if (mp_exit_requested)
294 gtk_main_quit();
298 static void build_submenu(GtkWidget * menu, mpdm_t labels)
299 /* build a submenu */
301 int n;
302 GtkWidget *menu_item;
304 mpdm_ref(labels);
306 for (n = 0; n < mpdm_size(labels); n++) {
307 /* get the action */
308 mpdm_t v = mpdm_aget(labels, n);
310 /* if the action is a separator... */
311 if (*((wchar_t *) v->data) == L'-')
312 menu_item = gtk_menu_item_new();
313 else {
314 char *ptr;
316 ptr = v_to_utf8(mp_menu_label(v));
317 menu_item = gtk_menu_item_new_with_label(ptr);
318 g_free(ptr);
321 gtk_menu_append(GTK_MENU(menu), menu_item);
322 g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
323 G_CALLBACK(menu_item_callback), v);
324 gtk_widget_show(menu_item);
327 mpdm_unref(labels);
331 static void build_menu(void)
332 /* builds the menu */
334 static mpdm_t prev_menu = NULL;
335 int n;
336 mpdm_t m;
338 /* gets the current menu */
339 if ((m = mpdm_hget_s(mp, L"menu")) == NULL)
340 return;
342 /* if it's the same, do nothing */
343 if (mpdm_cmp(m, prev_menu) == 0)
344 return;
346 /* create a new menu */
347 menu_bar = gtk_menu_bar_new();
349 for (n = 0; n < mpdm_size(m); n++) {
350 char *ptr;
351 mpdm_t mi;
352 mpdm_t v;
353 GtkWidget *menu;
354 GtkWidget *menu_item;
355 int i;
357 /* get the label and the items */
358 mi = mpdm_aget(m, n);
359 v = mpdm_aget(mi, 0);
361 if ((ptr = v_to_utf8(mpdm_gettext(v))) == NULL)
362 continue;
364 /* change the & by _ for the mnemonic */
365 for (i = 0; ptr[i]; i++)
366 if (ptr[i] == '&')
367 ptr[i] = '_';
369 /* add the menu and the label */
370 menu = gtk_menu_new();
371 menu_item = gtk_menu_item_new_with_mnemonic(ptr);
372 g_free(ptr);
374 gtk_widget_show(menu_item);
375 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
376 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_item);
378 /* now loop the items */
379 build_submenu(menu, mpdm_aget(mi, 1));
384 /** main area drawing functions **/
386 static void switch_page(GtkNotebook * notebook, GtkNotebookPage * page,
387 gint pg_num, gpointer data)
388 /* 'switch_page' handler (filetabs) */
390 /* sets the active one */
391 mpdm_hset_s(mp, L"active_i", MPDM_I(pg_num));
393 gtk_widget_grab_focus(area);
394 redraw();
398 static void draw_filetabs(void)
399 /* draws the filetabs */
401 static mpdm_t last = NULL;
402 mpdm_t names;
403 int n;
405 names = mpdm_ref(mp_get_doc_names());
407 /* disconnect redraw signal to avoid infinite loops */
408 g_signal_handlers_disconnect_by_func(G_OBJECT(file_tabs),
409 G_CALLBACK(switch_page), NULL);
411 /* is the list different from the previous one? */
412 if (mpdm_cmp(names, last) != 0) {
414 /* delete the current tabs */
415 for (n = 0; n < mpdm_size(last); n++)
416 gtk_notebook_remove_page(GTK_NOTEBOOK(file_tabs), 0);
418 /* create the new ones */
419 for (n = 0; n < mpdm_size(names); n++) {
420 GtkWidget *p;
421 GtkWidget *f;
422 char *ptr;
423 mpdm_t v = mpdm_aget(names, n);
425 if ((ptr = v_to_utf8(v)) != NULL) {
426 p = gtk_label_new(ptr);
427 gtk_widget_show(p);
429 f = gtk_frame_new(NULL);
430 gtk_widget_show(f);
432 gtk_notebook_append_page(GTK_NOTEBOOK(file_tabs), f, p);
434 g_free(ptr);
438 /* store for the next time */
439 mpdm_unref(last);
440 last = mpdm_ref(names);
443 mpdm_unref(names);
445 /* set the active one */
446 gtk_notebook_set_page(GTK_NOTEBOOK(file_tabs),
447 mpdm_ival(mpdm_hget_s(mp, L"active_i")));
449 /* reconnect signal */
450 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
451 G_CALLBACK(switch_page), NULL);
453 gtk_widget_grab_focus(area);
457 static void draw_status(void)
458 /* draws the status line */
460 char *ptr;
462 if ((ptr = v_to_utf8(mp_build_status_line())) != NULL) {
463 gtk_label_set_text(GTK_LABEL(status), ptr);
464 g_free(ptr);
469 static gint scroll_event(GtkWidget * widget, GdkEventScroll * event)
470 /* 'scroll_event' handler (mouse wheel) */
472 wchar_t *ptr = NULL;
474 switch (event->direction) {
475 case GDK_SCROLL_UP:
476 ptr = L"mouse-wheel-up";
477 break;
478 case GDK_SCROLL_DOWN:
479 ptr = L"mouse-wheel-down";
480 break;
481 case GDK_SCROLL_LEFT:
482 ptr = L"mouse-wheel-left";
483 break;
484 case GDK_SCROLL_RIGHT:
485 ptr = L"mouse-wheel-right";
486 break;
489 if (ptr != NULL)
490 mp_process_event(MPDM_S(ptr));
492 redraw();
494 return 0;
498 static void value_changed(GtkAdjustment * adj, gpointer * data)
499 /* 'value_changed' handler (scrollbar) */
501 int i = (int) adj->value;
502 mpdm_t doc;
503 mpdm_t txt;
504 int y;
506 /* get current y position */
507 doc = mp_active();
508 txt = mpdm_hget_s(doc, L"txt");
509 y = mpdm_ival(mpdm_hget_s(txt, L"y"));
511 /* if it's different, set and redraw */
512 if (y != i) {
513 mp_set_y(doc, i);
514 mpdm_hset_s(txt, L"vy", MPDM_I(i));
515 redraw();
520 static void draw_scrollbar(void)
521 /* updates the scrollbar */
523 GtkAdjustment *adjustment;
524 mpdm_t d;
525 mpdm_t v;
526 int pos, size, max;
528 /* gets the active document */
529 if ((d = mp_active()) == NULL)
530 return;
532 /* get the coordinates */
533 v = mpdm_hget_s(d, L"txt");
534 pos = mpdm_ival(mpdm_hget_s(v, L"vy"));
535 max = mpdm_size(mpdm_hget_s(v, L"lines"));
537 v = mpdm_hget_s(mp, L"window");
538 size = mpdm_ival(mpdm_hget_s(v, L"ty"));
540 adjustment = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
542 /* disconnect to avoid infinite loops */
543 g_signal_handlers_disconnect_by_func(G_OBJECT(adjustment),
544 G_CALLBACK(value_changed), NULL);
546 adjustment->step_increment = (gfloat) 1;
547 adjustment->upper = (gfloat) max;
548 adjustment->page_size = (gfloat) size;
549 adjustment->page_increment = (gfloat) size;
550 adjustment->value = (gfloat) pos;
552 gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment);
554 gtk_adjustment_changed(adjustment);
555 gtk_adjustment_value_changed(adjustment);
557 /* reattach again */
558 g_signal_connect(G_OBJECT
559 (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
560 "value_changed", G_CALLBACK(value_changed), NULL);
564 static void gtk_drv_paint(mpdm_t doc, int optimize)
565 /* GTK document draw function */
567 GdkRectangle gr;
568 mpdm_t d = NULL;
569 int n, m;
571 if (maximize)
572 gtk_window_maximize(GTK_WINDOW(window));
574 /* no gc? create it */
575 if (gc == NULL)
576 gc = gdk_gc_new(area->window);
578 if (font == NULL)
579 build_fonts();
581 if ((d = mp_draw(doc, optimize)) == NULL)
582 return;
584 mpdm_ref(d);
586 gr.x = 0;
587 gr.y = 0;
588 gr.width = area->allocation.width;
589 gr.height = font_height;
591 for (n = 0; n < mpdm_size(d); n++) {
592 PangoLayout *pl;
593 PangoAttrList *pal;
594 mpdm_t l = mpdm_aget(d, n);
595 char *str = NULL;
596 int u, p;
598 if (l == NULL)
599 continue;
601 /* create the pango stuff */
602 pl = gtk_widget_create_pango_layout(area, NULL);
603 pango_layout_set_font_description(pl, font);
604 pal = pango_attr_list_new();
606 for (m = u = p = 0; m < mpdm_size(l); m++, u = p) {
607 PangoAttribute *pa;
608 int attr;
609 mpdm_t s;
610 char *ptr;
612 /* get the attribute and the string */
613 attr = mpdm_ival(mpdm_aget(l, m++));
614 s = mpdm_aget(l, m);
616 /* convert the string to utf8 */
617 ptr = v_to_utf8(s);
619 /* add to the full line */
620 str = mpdm_poke(str, &p, ptr, strlen(ptr), 1);
622 g_free(ptr);
624 /* create the background if it's
625 different from the default */
626 if (papers[attr].red != papers[normal_attr].red ||
627 papers[attr].green != papers[normal_attr].green ||
628 papers[attr].blue != papers[normal_attr].blue) {
629 pa = pango_attr_background_new(papers[attr].red,
630 papers[attr].green,
631 papers[attr].blue);
633 pa->start_index = u;
634 pa->end_index = p;
636 pango_attr_list_insert(pal, pa);
639 /* underline? */
640 if (underlines[attr]) {
641 pa = pango_attr_underline_new(TRUE);
643 pa->start_index = u;
644 pa->end_index = p;
646 pango_attr_list_insert(pal, pa);
649 /* foreground color */
650 pa = pango_attr_foreground_new(inks[attr].red,
651 inks[attr].green,
652 inks[attr].blue);
654 pa->start_index = u;
655 pa->end_index = p;
657 pango_attr_list_insert(pal, pa);
660 /* store the attributes */
661 pango_layout_set_attributes(pl, pal);
662 pango_attr_list_unref(pal);
664 /* store and free the text */
665 pango_layout_set_text(pl, str, p);
666 free(str);
668 /* draw the background */
669 gdk_gc_set_foreground(gc, &papers[normal_attr]);
670 gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, gr.width, gr.height);
672 /* draw the text */
673 gtk_paint_layout(area->style, pixmap,
674 GTK_STATE_NORMAL, TRUE, &gr, area, "", 2, 0, pl);
676 /* dump the pixmap */
677 gdk_draw_pixmap(area->window, gc, pixmap,
678 0, 0, 0, n * font_height, gr.width, gr.height);
680 g_object_unref(pl);
683 mpdm_unref(d);
685 draw_filetabs();
686 draw_scrollbar();
687 draw_status();
691 static void redraw(void)
693 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
694 gtk_drv_paint(mp_active(), 0);
698 static gint delete_event(GtkWidget * w, GdkEvent * e, gpointer data)
699 /* 'delete_event' handler */
701 mp_process_event(MPDM_LS(L"close-window"));
703 return mp_exit_requested ? FALSE : TRUE;
707 static void destroy(GtkWidget * w, gpointer data)
708 /* 'destroy' handler */
710 gtk_main_quit();
714 static gint key_release_event(GtkWidget * widget, GdkEventKey * event,
715 gpointer data)
716 /* 'key_release_event' handler */
718 if (mp_keypress_throttle(0))
719 gtk_drv_paint(mp_active(), 0);
721 return 0;
725 static gint key_press_event(GtkWidget * widget, GdkEventKey * event,
726 gpointer data)
727 /* 'key_press_event' handler */
729 wchar_t *ptr = NULL;
731 gtk_im_context_filter_keypress(im, event);
733 /* set mp.shift_pressed */
734 if (event->state & (GDK_SHIFT_MASK))
735 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
737 /* reserve alt for menu mnemonics */
738 /* if (GDK_MOD1_MASK & event->state)
739 return(0);*/
741 if (event->state & (GDK_CONTROL_MASK)) {
742 switch (event->keyval) {
743 case GDK_Up:
744 ptr = L"ctrl-cursor-up";
745 break;
746 case GDK_Down:
747 ptr = L"ctrl-cursor-down";
748 break;
749 case GDK_Left:
750 ptr = L"ctrl-cursor-left";
751 break;
752 case GDK_Right:
753 ptr = L"ctrl-cursor-right";
754 break;
755 case GDK_Prior:
756 ptr = L"ctrl-page-up";
757 break;
758 case GDK_Next:
759 ptr = L"ctrl-page-down";
760 break;
761 case GDK_Home:
762 ptr = L"ctrl-home";
763 break;
764 case GDK_End:
765 ptr = L"ctrl-end";
766 break;
767 case GDK_space:
768 ptr = L"ctrl-space";
769 break;
770 case GDK_KP_Add:
771 ptr = L"ctrl-kp-plus";
772 break;
773 case GDK_KP_Subtract:
774 ptr = L"ctrl-kp-minus";
775 break;
776 case GDK_KP_Multiply:
777 ptr = L"ctrl-kp-multiply";
778 break;
779 case GDK_KP_Divide:
780 ptr = L"ctrl-kp-divide";
781 break;
782 case GDK_F1:
783 ptr = L"ctrl-f1";
784 break;
785 case GDK_F2:
786 ptr = L"ctrl-f2";
787 break;
788 case GDK_F3:
789 ptr = L"ctrl-f3";
790 break;
791 case GDK_F4:
792 ptr = L"ctrl-f4";
793 break;
794 case GDK_F5:
795 ptr = L"ctrl-f5";
796 break;
797 case GDK_F6:
798 ptr = L"ctrl-f6";
799 break;
800 case GDK_F7:
801 ptr = L"ctrl-f7";
802 break;
803 case GDK_F8:
804 ptr = L"ctrl-f8";
805 break;
806 case GDK_F9:
807 ptr = L"ctrl-f9";
808 break;
809 case GDK_F10:
810 ptr = L"ctrl-f10";
811 break;
812 case GDK_F11:
813 ptr = L"ctrl-f11";
814 break;
815 case GDK_F12:
816 ptr = L"ctrl-f12";
817 break;
818 case GDK_KP_Enter:
819 case GDK_Return:
820 ptr = L"ctrl-enter";
821 break;
822 case GDK_Cyrillic_ve:
823 ptr = L"ctrl-d";
824 break;
825 case GDK_Cyrillic_a:
826 ptr = L"ctrl-f";
827 break;
828 case GDK_Cyrillic_tse:
829 ptr = L"ctrl-w";
830 break;
831 case GDK_Cyrillic_de:
832 ptr = L"ctrl-l";
833 break;
834 case GDK_Cyrillic_ie:
835 ptr = L"ctrl-t";
836 break;
837 case GDK_Cyrillic_ef:
838 ptr = L"ctrl-a";
839 break;
840 case GDK_Cyrillic_ghe:
841 ptr = L"ctrl-u";
842 break;
843 case GDK_Cyrillic_i:
844 ptr = L"ctrl-b";
845 break;
846 case GDK_Cyrillic_shorti:
847 ptr = L"ctrl-q";
848 break;
849 case GDK_Cyrillic_ka:
850 ptr = L"ctrl-r";
851 break;
852 case GDK_Cyrillic_el:
853 ptr = L"ctrl-k";
854 break;
855 case GDK_Cyrillic_em:
856 ptr = L"ctrl-v";
857 break;
858 case GDK_Cyrillic_en:
859 ptr = L"ctrl-y";
860 break;
861 case GDK_Cyrillic_o:
862 ptr = L"ctrl-j";
863 break;
864 case GDK_Cyrillic_pe:
865 ptr = L"ctrl-g";
866 break;
867 case GDK_Cyrillic_ya:
868 ptr = L"ctrl-z";
869 break;
870 case GDK_Cyrillic_er:
871 ptr = L"ctrl-h";
872 break;
873 case GDK_Cyrillic_es:
874 ptr = L"ctrl-c";
875 break;
876 case GDK_Cyrillic_te:
877 ptr = L"ctrl-n";
878 break;
879 case GDK_Cyrillic_softsign:
880 ptr = L"ctrl-m";
881 break;
882 case GDK_Cyrillic_yeru:
883 ptr = L"ctrl-s";
884 break;
885 case GDK_Cyrillic_ze:
886 ptr = L"ctrl-p";
887 break;
888 case GDK_Cyrillic_sha:
889 ptr = L"ctrl-i";
890 break;
891 case GDK_Cyrillic_e:
892 ptr = L"ctrl-t";
893 break;
894 case GDK_Cyrillic_shcha:
895 ptr = L"ctrl-o";
896 break;
897 case GDK_Cyrillic_che:
898 ptr = L"ctrl-x";
899 break;
902 if (ptr == NULL) {
903 char c = event->keyval & 0xdf;
905 switch (c) {
906 case 'A':
907 ptr = L"ctrl-a";
908 break;
909 case 'B':
910 ptr = L"ctrl-b";
911 break;
912 case 'C':
913 ptr = L"ctrl-c";
914 break;
915 case 'D':
916 ptr = L"ctrl-d";
917 break;
918 case 'E':
919 ptr = L"ctrl-e";
920 break;
921 case 'F':
922 ptr = L"ctrl-f";
923 break;
924 case 'G':
925 ptr = L"ctrl-g";
926 break;
927 case 'H':
928 ptr = L"ctrl-h";
929 break;
930 case 'I':
931 ptr = L"ctrl-i";
932 break;
933 case 'J':
934 ptr = L"ctrl-j";
935 break;
936 case 'K':
937 ptr = L"ctrl-k";
938 break;
939 case 'L':
940 ptr = L"ctrl-l";
941 break;
942 case 'M':
943 ptr = L"ctrl-m";
944 break;
945 case 'N':
946 ptr = L"ctrl-n";
947 break;
948 case 'O':
949 ptr = L"ctrl-o";
950 break;
951 case 'P':
952 ptr = L"ctrl-p";
953 break;
954 case 'Q':
955 ptr = L"ctrl-q";
956 break;
957 case 'R':
958 ptr = L"ctrl-r";
959 break;
960 case 'S':
961 ptr = L"ctrl-s";
962 break;
963 case 'T':
964 ptr = L"ctrl-t";
965 break;
966 case 'U':
967 ptr = L"ctrl-u";
968 break;
969 case 'V':
970 ptr = L"ctrl-v";
971 break;
972 case 'W':
973 ptr = L"ctrl-w";
974 break;
975 case 'X':
976 ptr = L"ctrl-x";
977 break;
978 case 'Y':
979 ptr = L"ctrl-y";
980 break;
981 case 'Z':
982 ptr = L"ctrl-z";
983 break;
987 else
988 if (event->state & (GDK_MOD1_MASK)) {
989 switch (event->keyval) {
990 case GDK_Up:
991 ptr = L"alt-cursor-up";
992 break;
993 case GDK_Down:
994 ptr = L"alt-cursor-down";
995 break;
996 case GDK_Left:
997 ptr = L"alt-cursor-left";
998 break;
999 case GDK_Right:
1000 ptr = L"alt-cursor-right";
1001 break;
1002 case GDK_Prior:
1003 ptr = L"alt-page-up";
1004 break;
1005 case GDK_Next:
1006 ptr = L"alt-page-down";
1007 break;
1008 case GDK_Home:
1009 ptr = L"alt-home";
1010 break;
1011 case GDK_End:
1012 ptr = L"alt-end";
1013 break;
1014 case GDK_space:
1015 ptr = L"alt-space";
1016 break;
1017 case GDK_KP_Add:
1018 ptr = L"alt-kp-plus";
1019 break;
1020 case GDK_KP_Subtract:
1021 ptr = L"alt-kp-minus";
1022 break;
1023 case GDK_KP_Multiply:
1024 ptr = L"alt-kp-multiply";
1025 break;
1026 case GDK_KP_Divide:
1027 ptr = L"alt-kp-divide";
1028 break;
1029 case GDK_F1:
1030 ptr = L"alt-f1";
1031 break;
1032 case GDK_F2:
1033 ptr = L"alt-f2";
1034 break;
1035 case GDK_F3:
1036 ptr = L"alt-f3";
1037 break;
1038 case GDK_F4:
1039 ptr = L"alt-f4";
1040 break;
1041 case GDK_F5:
1042 ptr = L"alt-f5";
1043 break;
1044 case GDK_F6:
1045 ptr = L"alt-f6";
1046 break;
1047 case GDK_F7:
1048 ptr = L"alt-f7";
1049 break;
1050 case GDK_F8:
1051 ptr = L"alt-f8";
1052 break;
1053 case GDK_F9:
1054 ptr = L"alt-f9";
1055 break;
1056 case GDK_F10:
1057 ptr = L"alt-f10";
1058 break;
1059 case GDK_F11:
1060 ptr = L"alt-f11";
1061 break;
1062 case GDK_F12:
1063 ptr = L"alt-f12";
1064 break;
1065 case GDK_KP_Enter:
1066 case GDK_Return:
1067 ptr = L"alt-enter";
1068 break;
1069 case GDK_Cyrillic_ve:
1070 ptr = L"alt-d";
1071 break;
1072 case GDK_Cyrillic_a:
1073 ptr = L"alt-f";
1074 break;
1075 case GDK_Cyrillic_tse:
1076 ptr = L"alt-w";
1077 break;
1078 case GDK_Cyrillic_de:
1079 ptr = L"alt-l";
1080 break;
1081 case GDK_Cyrillic_ie:
1082 ptr = L"alt-t";
1083 break;
1084 case GDK_Cyrillic_ef:
1085 ptr = L"alt-a";
1086 break;
1087 case GDK_Cyrillic_ghe:
1088 ptr = L"alt-u";
1089 break;
1090 case GDK_Cyrillic_i:
1091 ptr = L"alt-b";
1092 break;
1093 case GDK_Cyrillic_shorti:
1094 ptr = L"alt-q";
1095 break;
1096 case GDK_Cyrillic_ka:
1097 ptr = L"alt-r";
1098 break;
1099 case GDK_Cyrillic_el:
1100 ptr = L"alt-k";
1101 break;
1102 case GDK_Cyrillic_em:
1103 ptr = L"alt-v";
1104 break;
1105 case GDK_Cyrillic_en:
1106 ptr = L"alt-y";
1107 break;
1108 case GDK_Cyrillic_o:
1109 ptr = L"alt-j";
1110 break;
1111 case GDK_Cyrillic_pe:
1112 ptr = L"alt-g";
1113 break;
1114 case GDK_Cyrillic_ya:
1115 ptr = L"alt-z";
1116 break;
1117 case GDK_Cyrillic_er:
1118 ptr = L"alt-h";
1119 break;
1120 case GDK_Cyrillic_es:
1121 ptr = L"alt-c";
1122 break;
1123 case GDK_Cyrillic_te:
1124 ptr = L"alt-n";
1125 break;
1126 case GDK_Cyrillic_softsign:
1127 ptr = L"alt-m";
1128 break;
1129 case GDK_Cyrillic_yeru:
1130 ptr = L"alt-s";
1131 break;
1132 case GDK_Cyrillic_ze:
1133 ptr = L"alt-p";
1134 break;
1135 case GDK_Cyrillic_sha:
1136 ptr = L"alt-i";
1137 break;
1138 case GDK_Cyrillic_e:
1139 ptr = L"alt-t";
1140 break;
1141 case GDK_Cyrillic_shcha:
1142 ptr = L"alt-o";
1143 break;
1144 case GDK_Cyrillic_che:
1145 ptr = L"alt-x";
1146 break;
1149 if (ptr == NULL) {
1150 char c = event->keyval & 0xdf;
1152 switch (c) {
1153 case 'A':
1154 ptr = L"alt-a";
1155 break;
1156 case 'B':
1157 ptr = L"alt-b";
1158 break;
1159 case 'C':
1160 ptr = L"alt-c";
1161 break;
1162 case 'D':
1163 ptr = L"alt-d";
1164 break;
1165 case 'E':
1166 ptr = L"alt-e";
1167 break;
1168 case 'F':
1169 ptr = L"alt-f";
1170 break;
1171 case 'G':
1172 ptr = L"alt-g";
1173 break;
1174 case 'H':
1175 ptr = L"alt-h";
1176 break;
1177 case 'I':
1178 ptr = L"alt-i";
1179 break;
1180 case 'J':
1181 ptr = L"alt-j";
1182 break;
1183 case 'K':
1184 ptr = L"alt-k";
1185 break;
1186 case 'L':
1187 ptr = L"alt-l";
1188 break;
1189 case 'M':
1190 ptr = L"alt-m";
1191 break;
1192 case 'N':
1193 ptr = L"alt-n";
1194 break;
1195 case 'O':
1196 ptr = L"alt-o";
1197 break;
1198 case 'P':
1199 ptr = L"alt-p";
1200 break;
1201 case 'Q':
1202 ptr = L"alt-q";
1203 break;
1204 case 'R':
1205 ptr = L"alt-r";
1206 break;
1207 case 'S':
1208 ptr = L"alt-s";
1209 break;
1210 case 'T':
1211 ptr = L"alt-t";
1212 break;
1213 case 'U':
1214 ptr = L"alt-u";
1215 break;
1216 case 'V':
1217 ptr = L"alt-v";
1218 break;
1219 case 'W':
1220 ptr = L"alt-w";
1221 break;
1222 case 'X':
1223 ptr = L"alt-x";
1224 break;
1225 case 'Y':
1226 ptr = L"alt-y";
1227 break;
1228 case 'Z':
1229 ptr = L"alt-z";
1230 break;
1234 else {
1235 switch (event->keyval) {
1236 case GDK_Up:
1237 ptr = L"cursor-up";
1238 break;
1239 case GDK_Down:
1240 ptr = L"cursor-down";
1241 break;
1242 case GDK_Left:
1243 ptr = L"cursor-left";
1244 break;
1245 case GDK_Right:
1246 ptr = L"cursor-right";
1247 break;
1248 case GDK_Prior:
1249 ptr = L"page-up";
1250 break;
1251 case GDK_Next:
1252 ptr = L"page-down";
1253 break;
1254 case GDK_Home:
1255 ptr = L"home";
1256 break;
1257 case GDK_End:
1258 ptr = L"end";
1259 break;
1260 case GDK_space:
1261 ptr = L"space";
1262 break;
1263 case GDK_KP_Add:
1264 ptr = L"kp-plus";
1265 break;
1266 case GDK_KP_Subtract:
1267 ptr = L"kp-minus";
1268 break;
1269 case GDK_KP_Multiply:
1270 ptr = L"kp-multiply";
1271 break;
1272 case GDK_KP_Divide:
1273 ptr = L"kp-divide";
1274 break;
1275 case GDK_F1:
1276 ptr = L"f1";
1277 break;
1278 case GDK_F2:
1279 ptr = L"f2";
1280 break;
1281 case GDK_F3:
1282 ptr = L"f3";
1283 break;
1284 case GDK_F4:
1285 ptr = L"f4";
1286 break;
1287 case GDK_F5:
1288 ptr = L"f5";
1289 break;
1290 case GDK_F6:
1291 ptr = L"f6";
1292 break;
1293 case GDK_F7:
1294 ptr = L"f7";
1295 break;
1296 case GDK_F8:
1297 ptr = L"f8";
1298 break;
1299 case GDK_F9:
1300 ptr = L"f9";
1301 break;
1302 case GDK_F10:
1303 ptr = L"f10";
1304 break;
1305 case GDK_F11:
1306 ptr = L"f11";
1307 break;
1308 case GDK_F12:
1309 ptr = L"f12";
1310 break;
1311 case GDK_Insert:
1312 ptr = L"insert";
1313 break;
1314 case GDK_BackSpace:
1315 ptr = L"backspace";
1316 break;
1317 case GDK_Delete:
1318 ptr = L"delete";
1319 break;
1320 case GDK_KP_Enter:
1321 case GDK_Return:
1322 ptr = L"enter";
1323 break;
1324 case GDK_Tab:
1325 ptr = L"tab";
1326 break;
1327 case GDK_Escape:
1328 ptr = L"escape";
1329 break;
1333 /* if there is a pending char in im_char, use it */
1334 if (ptr == NULL && im_char[0] != L'\0')
1335 ptr = im_char;
1337 /* finally process */
1338 if (ptr != NULL)
1339 mp_process_event(MPDM_S(ptr));
1341 /* delete the pending char */
1342 im_char[0] = L'\0';
1344 if (mp_exit_requested)
1345 gtk_main_quit();
1347 if (mp_keypress_throttle(1))
1348 gtk_drv_paint(mp_active(), 1);
1350 return 0;
1354 static gint button_press_event(GtkWidget * widget, GdkEventButton * event,
1355 gpointer data)
1356 /* 'button_press_event' handler (mouse buttons) */
1358 int x, y;
1359 wchar_t *ptr = NULL;
1361 mouse_down = 1;
1363 /* mouse instant positioning */
1364 x = ((int) event->x) / font_width;
1365 y = ((int) event->y) / font_height;
1367 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
1368 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
1370 switch (event->button) {
1371 case 1:
1372 ptr = L"mouse-left-button";
1373 break;
1374 case 2:
1375 ptr = L"mouse-middle-button";
1376 break;
1377 case 3:
1378 ptr = L"mouse-right-button";
1379 break;
1380 case 4:
1381 ptr = L"mouse-wheel-up";
1382 break;
1383 case 5:
1384 ptr = L"mouse-wheel-down";
1385 break;
1388 if (ptr != NULL)
1389 mp_process_event(MPDM_S(ptr));
1391 redraw();
1393 return 0;
1397 static gint button_release_event(GtkWidget * widget,
1398 GdkEventButton * event, gpointer data)
1399 /* 'button_release_event' handle (mouse buttons) */
1401 mouse_down = 0;
1403 return TRUE;
1407 static gint motion_notify_event(GtkWidget * widget, GdkEventMotion * event,
1408 gpointer data)
1409 /* 'motion_notify_event' handler (mouse movement) */
1411 static int ox = 0;
1412 static int oy = 0;
1414 if (mouse_down) {
1415 int x, y;
1417 /* mouse dragging */
1418 x = ((int) event->x) / font_width;
1419 y = ((int) event->y) / font_height;
1421 if (ox != x && oy != y) {
1422 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
1423 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
1425 mp_process_event(MPDM_LS(L"mouse-drag"));
1426 gtk_drv_paint(mp_active(), 1);
1430 return TRUE;
1434 static void drag_data_received(GtkWidget * widget, GdkDragContext * dc,
1435 gint x, gint y, GtkSelectionData * data,
1436 guint info, guint time)
1437 /* 'drag_data_received' handler */
1439 printf("drag_data_received (unsupported)\n");
1443 /** clipboard functions **/
1445 static void commit(GtkIMContext * i, char *str, gpointer u)
1446 /* 'commit' handler */
1448 wchar_t *wstr;
1450 wstr = (wchar_t *) g_convert(str, -1,
1451 "WCHAR_T", "UTF-8", NULL, NULL, NULL);
1453 im_char[0] = *wstr;
1454 im_char[1] = L'\0';
1456 g_free(wstr);
1460 static void realize(GtkWidget * widget)
1461 /* 'realize' handler */
1463 im = gtk_im_multicontext_new();
1464 g_signal_connect(im, "commit", G_CALLBACK(commit), NULL);
1465 gtk_im_context_set_client_window(im, widget->window);
1469 static gint expose_event(GtkWidget * widget, GdkEventExpose * event)
1470 /* 'expose_event' handler */
1472 redraw();
1474 return FALSE;
1478 static gint configure_event(GtkWidget * widget, GdkEventConfigure * event)
1479 /* 'configure_event' handler */
1481 static GdkEventConfigure o;
1483 if (memcmp(&o, event, sizeof(o)) == 0)
1484 return TRUE;
1486 memcpy(&o, event, sizeof(o));
1488 update_window_size();
1489 redraw();
1491 return TRUE;
1495 static gint selection_clear_event(GtkWidget * widget,
1496 GdkEventSelection * event, gpointer data)
1497 /* 'selection_clear_event' handler */
1499 got_selection = 0;
1501 return TRUE;
1505 static void selection_get(GtkWidget * widget,
1506 GtkSelectionData * sel, guint info, guint tm)
1507 /* 'selection_get' handler */
1509 mpdm_t d;
1510 unsigned char *ptr;
1511 int s;
1513 if (!got_selection)
1514 return;
1516 /* gets the clipboard and joins */
1517 d = mpdm_hget_s(mp, L"clipboard");
1519 if (mpdm_size(d) == 0)
1520 return;
1522 d = mpdm_ref(mpdm_join_s(L"\n", d));
1524 /* convert to current locale */
1525 ptr = (unsigned char *) mpdm_wcstombs(d->data, &s);
1527 /* pastes into primary selection */
1528 gtk_selection_data_set(sel, GDK_SELECTION_TYPE_STRING, 8, ptr,
1529 (gsize) s);
1531 free(ptr);
1533 mpdm_unref(d);
1537 static void selection_received(GtkWidget * widget,
1538 GtkSelectionData * sel, gpointer data)
1539 /* 'selection_received' handler */
1541 mpdm_t d;
1543 if (sel->data != NULL) {
1544 /* get selection */
1545 wchar_t *wptr = utf8_to_wcs((char *) sel->data);
1546 d = MPDM_S(wptr);
1547 g_free(wptr);
1549 /* split and set as the clipboard */
1550 mpdm_hset_s(mp, L"clipboard", mpdm_split_s(L"\n", d));
1551 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
1553 /* wait no more for the selection */
1554 wait_for_selection = 0;
1556 else
1557 wait_for_selection = -1;
1561 static mpdm_t gtk_drv_clip_to_sys(mpdm_t a, mpdm_t ctxt)
1562 /* driver-dependent mp to system clipboard */
1564 got_selection = gtk_selection_owner_set(area,
1565 GDK_SELECTION_PRIMARY,
1566 GDK_CURRENT_TIME);
1568 return NULL;
1572 static mpdm_t gtk_drv_sys_to_clip(mpdm_t a, mpdm_t ctxt)
1573 /* driver-dependent system to mp clipboard */
1575 if (!got_selection) {
1576 int n;
1577 char *formats[] = { "UTF8_STRING", "STRING", NULL };
1579 for (n = 0; formats[n] != NULL; n++) {
1581 /* triggers a selection capture */
1582 if (gtk_selection_convert(area, GDK_SELECTION_PRIMARY,
1583 gdk_atom_intern(formats[n], FALSE),
1584 GDK_CURRENT_TIME)) {
1586 /* processes the pending events
1587 (i.e., the 'selection_received' handler) */
1588 wait_for_selection = 1;
1590 while (wait_for_selection == 1)
1591 gtk_main_iteration();
1593 if (!wait_for_selection)
1594 break;
1599 return NULL;
1603 /** interface functions **/
1605 static void clicked_ok(GtkWidget * widget, gpointer data)
1606 /* 'clicked_on' signal handler (for gtk_drv_form) */
1608 int n;
1610 for (n = 0; n < mpdm_size(form_args); n++) {
1611 GtkWidget *widget = form_widgets[n];
1612 mpdm_t w = mpdm_aget(form_args, n);
1613 wchar_t *wptr = mpdm_string(mpdm_hget_s(w, L"type"));
1614 mpdm_t v = NULL;
1616 /* if there is already a value there, if was
1617 previously set from a callback */
1618 if (mpdm_aget(form_values, n) != NULL)
1619 continue;
1621 if (wcscmp(wptr, L"text") == 0 || wcscmp(wptr, L"password") == 0) {
1622 char *ptr;
1623 GtkWidget *gw = widget;
1624 mpdm_t h;
1626 if (wcscmp(wptr, L"text") == 0)
1627 gw = GTK_COMBO(widget)->entry;
1629 if ((ptr =
1630 gtk_editable_get_chars(GTK_EDITABLE(gw), 0, -1)) != NULL
1631 && (wptr = utf8_to_wcs(ptr)) != NULL) {
1632 v = MPDM_S(wptr);
1633 g_free(wptr);
1634 g_free(ptr);
1637 mpdm_ref(v);
1639 /* if it has history, fill it */
1640 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1641 v != NULL && mpdm_cmp_s(v, L"") != 0) {
1642 h = mp_get_history(h);
1644 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1645 mpdm_push(h, v);
1648 mpdm_unrefnd(v);
1650 else
1651 if (wcscmp(wptr, L"checkbox") == 0) {
1652 v = MPDM_I(gtk_toggle_button_get_active
1653 (GTK_TOGGLE_BUTTON(widget)));
1655 else
1656 if (wcscmp(wptr, L"list") == 0) {
1657 GtkWidget *list = gtk_bin_get_child(GTK_BIN(widget));
1658 GtkTreeSelection *selection =
1659 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
1660 GList *selected =
1661 gtk_tree_selection_get_selected_rows(selection, NULL);
1662 GtkTreePath *path = selected->data;
1664 v = MPDM_I(gtk_tree_path_get_indices(path)[0]);
1665 gtk_tree_path_free(path);
1666 g_list_free(selected);
1670 mpdm_aset(form_values, v, n);
1675 static gint timer_callback(gpointer data)
1677 mpdm_void(mpdm_exec(timer_func, NULL, NULL));
1678 redraw();
1680 return TRUE;
1684 static void build_form_data(mpdm_t widget_list)
1685 /* builds the necessary information for a list of widgets */
1687 mpdm_unref(form_args);
1688 form_args = mpdm_ref(widget_list);
1690 mpdm_unref(form_values);
1691 form_values = widget_list == NULL ? NULL :
1692 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1694 /* resize the widget array */
1695 form_widgets = (GtkWidget **) realloc(form_widgets,
1696 mpdm_size(form_args) *
1697 sizeof(GtkWidget *));
1701 /** dialog functions **/
1703 #define DIALOG_BUTTON(l,f) do { GtkWidget * btn; \
1704 ptr = localize(l); btn = gtk_button_new_with_label(ptr); \
1705 g_signal_connect_swapped(G_OBJECT(btn), "clicked", \
1706 G_CALLBACK(f), G_OBJECT(dlg)); \
1707 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), \
1708 btn, TRUE, TRUE, 0); \
1709 g_free(ptr); \
1710 } while (0);
1712 static mpdm_t gtk_drv_alert(mpdm_t a, mpdm_t ctxt)
1713 /* alert driver function */
1715 gchar *ptr;
1716 GtkWidget *dlg;
1718 build_form_data(NULL);
1720 /* 1# arg: prompt */
1721 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
1722 return NULL;
1724 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
1725 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, ptr);
1726 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
1727 g_free(ptr);
1729 gtk_dialog_run(GTK_DIALOG(dlg));
1730 gtk_widget_destroy(dlg);
1732 return NULL;
1736 static mpdm_t gtk_drv_confirm(mpdm_t a, mpdm_t ctxt)
1737 /* confirm driver function */
1739 char *ptr;
1740 GtkWidget *dlg;
1741 gint response;
1743 build_form_data(NULL);
1745 /* 1# arg: prompt */
1746 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
1747 return NULL;
1749 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
1750 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1751 ptr);
1752 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
1753 g_free(ptr);
1755 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_YES, 1);
1756 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_NO, 2);
1757 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_CANCEL, 0);
1759 response = gtk_dialog_run(GTK_DIALOG(dlg));
1760 gtk_widget_destroy(dlg);
1762 if (response == GTK_RESPONSE_DELETE_EVENT)
1763 response = 0;
1765 return MPDM_I(response);
1769 static mpdm_t gtk_drv_form(mpdm_t a, mpdm_t ctxt)
1770 /* 'form' driver function */
1772 GtkWidget *dlg;
1773 GtkWidget *table;
1774 GtkWidget *content_area;
1775 int n;
1776 mpdm_t ret = NULL;
1778 /* first argument: list of widgets */
1779 build_form_data(mpdm_aget(a, 0));
1781 dlg = gtk_dialog_new_with_buttons("mp " VERSION, GTK_WINDOW(window),
1782 GTK_DIALOG_MODAL |
1783 GTK_DIALOG_NO_SEPARATOR,
1784 GTK_STOCK_CANCEL,
1785 GTK_RESPONSE_CANCEL, GTK_STOCK_OK,
1786 GTK_RESPONSE_OK, NULL);
1787 gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
1788 gtk_container_set_border_width(GTK_CONTAINER(dlg), 5);
1790 content_area = GTK_DIALOG(dlg)->vbox;
1791 gtk_box_set_spacing(GTK_BOX(content_area), 2);
1793 table = gtk_table_new(mpdm_size(a), 2, FALSE);
1794 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
1795 gtk_table_set_col_spacings(GTK_TABLE(table), 12);
1796 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
1798 for (n = 0; n < mpdm_size(form_args); n++) {
1799 mpdm_t w = mpdm_aget(form_args, n);
1800 GtkWidget *widget = NULL;
1801 wchar_t *type;
1802 char *ptr;
1803 mpdm_t t;
1804 int col = 0;
1806 type = mpdm_string(mpdm_hget_s(w, L"type"));
1808 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
1809 GtkWidget *label;
1811 if ((ptr = v_to_utf8(mpdm_gettext(t))) != NULL) {
1812 label = gtk_label_new(ptr);
1813 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
1815 gtk_table_attach_defaults(GTK_TABLE(table),
1816 label, 0, wcscmp(type,
1817 L"label") ==
1818 0 ? 2 : 1, n, n + 1);
1820 g_free(ptr);
1822 col++;
1826 t = mpdm_hget_s(w, L"value");
1828 if (wcscmp(type, L"text") == 0) {
1829 GList *combo_items = NULL;
1830 mpdm_t h;
1832 widget = gtk_combo_new();
1833 gtk_widget_set_size_request(widget, 300, -1);
1834 gtk_combo_set_use_arrows_always(GTK_COMBO(widget), TRUE);
1835 gtk_combo_set_case_sensitive(GTK_COMBO(widget), TRUE);
1836 gtk_entry_set_activates_default(GTK_ENTRY
1837 (GTK_COMBO(widget)->entry),
1838 TRUE);
1840 if ((h = mpdm_hget_s(w, L"history")) != NULL) {
1841 int i;
1843 /* has history; fill it */
1844 h = mp_get_history(h);
1846 for (i = 0; i < mpdm_size(h); i++) {
1847 ptr = v_to_utf8(mpdm_aget(h, i));
1849 combo_items = g_list_prepend(combo_items, ptr);
1853 if (t != NULL) {
1854 ptr = v_to_utf8(t);
1856 combo_items = g_list_prepend(combo_items, ptr);
1859 gtk_combo_set_popdown_strings(GTK_COMBO(widget), combo_items);
1860 g_list_free(combo_items);
1862 else
1863 if (wcscmp(type, L"password") == 0) {
1864 widget = gtk_entry_new();
1865 gtk_widget_set_size_request(widget, 300, -1);
1866 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
1868 else
1869 if (wcscmp(type, L"checkbox") == 0) {
1870 widget = gtk_check_button_new();
1872 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1873 mpdm_ival(t) ? TRUE : FALSE);
1875 else
1876 if (wcscmp(type, L"list") == 0) {
1877 GtkWidget *list;
1878 GtkListStore *list_store;
1879 GtkCellRenderer *renderer;
1880 GtkTreeViewColumn *column;
1881 GtkTreePath *path;
1882 mpdm_t l;
1883 gint i;
1885 if ((i = 450 / mpdm_size(form_args)) < 100)
1886 i = 100;
1888 widget = gtk_scrolled_window_new(NULL, NULL);
1889 gtk_widget_set_size_request(widget, 500, i);
1890 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
1891 GTK_POLICY_NEVER,
1892 GTK_POLICY_AUTOMATIC);
1893 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
1894 (widget), GTK_SHADOW_IN);
1896 list_store = gtk_list_store_new(1, G_TYPE_STRING);
1897 list =
1898 gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
1899 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
1900 renderer = gtk_cell_renderer_text_new();
1901 column = gtk_tree_view_column_new_with_attributes("", renderer,
1902 "text", 0,
1903 NULL);
1904 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
1905 gtk_container_add(GTK_CONTAINER(widget), list);
1907 l = mpdm_hget_s(w, L"list");
1909 for (i = 0; i < mpdm_size(l); i++) {
1910 if ((ptr = v_to_utf8(mpdm_aget(l, i))) != NULL) {
1911 GtkTreeIter iter;
1912 gtk_list_store_append(list_store, &iter);
1913 gtk_list_store_set(list_store, &iter, 0, ptr, -1);
1914 g_free(ptr);
1918 /* initial position */
1919 i = mpdm_ival(t);
1921 path = gtk_tree_path_new_from_indices(i, -1);
1922 gtk_tree_view_set_cursor(GTK_TREE_VIEW(list), path, NULL,
1923 FALSE);
1924 gtk_tree_path_free(path);
1926 g_signal_connect_swapped(G_OBJECT(list), "row-activated",
1927 G_CALLBACK
1928 (gtk_window_activate_default), dlg);
1931 if (widget != NULL) {
1932 form_widgets[n] = widget;
1933 gtk_table_attach_defaults(GTK_TABLE(table),
1934 widget, col, 2, n, n + 1);
1938 gtk_widget_show_all(table);
1940 gtk_box_pack_start(GTK_BOX(content_area), table, TRUE, TRUE, 0);
1942 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) {
1943 clicked_ok(NULL, NULL);
1944 ret = form_values;
1946 gtk_widget_destroy(dlg);
1948 return ret;
1952 static mpdm_t run_filechooser(mpdm_t a, gboolean save)
1953 /* openfile driver function */
1955 GtkWidget *dlg;
1956 char *ptr;
1957 mpdm_t ret = NULL;
1958 gint response;
1960 /* 1# arg: prompt */
1961 if ((ptr = v_to_utf8(mpdm_aget(a, 0))) == NULL)
1962 return (NULL);
1964 if (!save) {
1965 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
1966 GTK_FILE_CHOOSER_ACTION_OPEN,
1967 GTK_STOCK_CANCEL,
1968 GTK_RESPONSE_CANCEL,
1969 GTK_STOCK_OK, GTK_RESPONSE_OK,
1970 NULL);
1972 else {
1973 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
1974 GTK_FILE_CHOOSER_ACTION_SAVE,
1975 GTK_STOCK_CANCEL,
1976 GTK_STOCK_CANCEL, GTK_STOCK_OK,
1977 GTK_RESPONSE_OK, NULL);
1978 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER
1979 (dlg), TRUE);
1981 g_free(ptr);
1983 build_form_data(NULL);
1985 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), TRUE);
1986 response = gtk_dialog_run(GTK_DIALOG(dlg));
1988 if (response == GTK_RESPONSE_OK) {
1989 gchar *filename;
1990 gchar *utf8name;
1991 wchar_t *wfilename;
1993 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
1994 utf8name = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
1995 g_free(filename);
1996 wfilename = utf8_to_wcs(utf8name);
1997 g_free(utf8name);
1998 ret = MPDM_S(wfilename);
1999 g_free(wfilename);
2001 gtk_widget_destroy(dlg);
2003 return ret;
2007 static mpdm_t gtk_drv_openfile(mpdm_t a, mpdm_t ctxt)
2008 /* openfile driver function */
2010 return run_filechooser(a, FALSE);
2014 static mpdm_t gtk_drv_savefile(mpdm_t a, mpdm_t ctxt)
2015 /* savefile driver function */
2017 return run_filechooser(a, TRUE);
2021 static mpdm_t gtk_drv_update_ui(mpdm_t a, mpdm_t ctxt)
2023 build_fonts();
2024 build_colors();
2025 build_menu();
2027 redraw();
2029 return NULL;
2033 static mpdm_t gtk_drv_timer(mpdm_t a, mpdm_t ctxt)
2035 static guint prev = 0;
2036 int msecs = mpdm_ival(mpdm_aget(a, 0));
2037 mpdm_t func = mpdm_aget(a, 1);
2039 /* previously defined one? remove */
2040 if (timer_func != NULL)
2041 gtk_timeout_remove(prev);
2043 /* if msecs and func are set, program timer */
2044 if (msecs > 0 && func != NULL)
2045 prev = gtk_timeout_add(msecs, timer_callback, NULL);
2047 mpdm_ref(func);
2048 mpdm_unref(timer_func);
2049 timer_func = func;
2051 return NULL;
2055 static mpdm_t gtk_drv_busy(mpdm_t a, mpdm_t ctxt)
2057 int onoff = mpdm_ival(mpdm_aget(a, 0));
2059 gdk_window_set_cursor(window->window,
2060 gdk_cursor_new(onoff ? GDK_WATCH :
2061 GDK_LEFT_PTR));
2063 while (gtk_events_pending())
2064 gtk_main_iteration();
2066 return NULL;
2070 static mpdm_t gtk_drv_main_loop(mpdm_t a, mpdm_t ctxt)
2071 /* main loop */
2073 if (!mp_exit_requested) {
2074 gtk_drv_paint(mp_active(), 0);
2076 gtk_main();
2079 return NULL;
2083 static mpdm_t gtk_drv_shutdown(mpdm_t a, mpdm_t ctxt)
2084 /* shutdown */
2086 mpdm_t v;
2088 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
2089 mpdm_write_wcs(stdout, mpdm_string(v));
2090 printf("\n");
2093 return NULL;
2097 static void register_functions(void)
2099 mpdm_t drv;
2101 drv = mpdm_hget_s(mp, L"drv");
2102 mpdm_hset_s(drv, L"main_loop", MPDM_X(gtk_drv_main_loop));
2103 mpdm_hset_s(drv, L"shutdown", MPDM_X(gtk_drv_shutdown));
2104 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(gtk_drv_clip_to_sys));
2105 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(gtk_drv_sys_to_clip));
2106 mpdm_hset_s(drv, L"update_ui", MPDM_X(gtk_drv_update_ui));
2107 mpdm_hset_s(drv, L"timer", MPDM_X(gtk_drv_timer));
2108 mpdm_hset_s(drv, L"busy", MPDM_X(gtk_drv_busy));
2109 mpdm_hset_s(drv, L"alert", MPDM_X(gtk_drv_alert));
2110 mpdm_hset_s(drv, L"confirm", MPDM_X(gtk_drv_confirm));
2111 mpdm_hset_s(drv, L"openfile", MPDM_X(gtk_drv_openfile));
2112 mpdm_hset_s(drv, L"savefile", MPDM_X(gtk_drv_savefile));
2113 mpdm_hset_s(drv, L"form", MPDM_X(gtk_drv_form));
2117 static mpdm_t gtk_drv_startup(mpdm_t a, mpdm_t ctxt)
2118 /* driver initialization */
2120 GtkWidget *vbox;
2121 GtkWidget *hbox;
2122 GdkPixmap *pixmap;
2123 GdkPixmap *mask;
2124 GdkScreen *screen;
2125 mpdm_t v;
2126 int w, h;
2127 GtkTargetEntry targets[] = {
2128 {"text/plain", 0, 0},
2129 {"text/uri-list", 0, 1}
2132 register_functions();
2134 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2136 gtk_window_set_title(GTK_WINDOW(window), "mp " VERSION);
2138 /* get real screen and pick a usable size for the main area */
2139 screen = gtk_window_get_screen(GTK_WINDOW(window));
2140 w = (gdk_screen_get_width(screen) * 3) / 4;
2141 h = (gdk_screen_get_height(screen) * 2) / 3;
2143 g_signal_connect(G_OBJECT(window), "delete_event",
2144 G_CALLBACK(delete_event), NULL);
2146 g_signal_connect(G_OBJECT(window), "destroy",
2147 G_CALLBACK(destroy), NULL);
2149 /* file tabs */
2150 file_tabs = gtk_notebook_new();
2151 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(file_tabs), GTK_POS_TOP);
2152 GTK_WIDGET_UNSET_FLAGS(file_tabs, GTK_CAN_FOCUS);
2153 gtk_notebook_set_scrollable(GTK_NOTEBOOK(file_tabs), 1);
2155 vbox = gtk_vbox_new(FALSE, 2);
2156 gtk_container_add(GTK_CONTAINER(window), vbox);
2158 build_menu();
2160 hbox = gtk_hbox_new(FALSE, 0);
2161 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2162 gtk_box_pack_start(GTK_BOX(hbox), menu_bar, FALSE, FALSE, 0);
2163 gtk_box_pack_start(GTK_BOX(hbox), file_tabs, TRUE, TRUE, 0);
2165 gtk_notebook_popup_enable(GTK_NOTEBOOK(file_tabs));
2167 /* horizontal box holding the text and the scrollbar */
2168 hbox = gtk_hbox_new(FALSE, 2);
2169 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
2171 /* the Minimum Profit area */
2172 area = gtk_drawing_area_new();
2173 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
2174 gtk_widget_set_size_request(GTK_WIDGET(area), w, h);
2175 gtk_widget_set_events(GTK_WIDGET(area), GDK_BUTTON_PRESS_MASK |
2176 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
2177 | GDK_LEAVE_NOTIFY_MASK);
2179 gtk_widget_set_double_buffered(area, FALSE);
2181 g_signal_connect(G_OBJECT(area), "configure_event",
2182 G_CALLBACK(configure_event), NULL);
2184 g_signal_connect(G_OBJECT(area), "expose_event",
2185 G_CALLBACK(expose_event), NULL);
2187 g_signal_connect(G_OBJECT(area), "realize", G_CALLBACK(realize), NULL);
2189 g_signal_connect(G_OBJECT(window), "key_press_event",
2190 G_CALLBACK(key_press_event), NULL);
2192 g_signal_connect(G_OBJECT(window), "key_release_event",
2193 G_CALLBACK(key_release_event), NULL);
2195 g_signal_connect(G_OBJECT(area), "button_press_event",
2196 G_CALLBACK(button_press_event), NULL);
2198 g_signal_connect(G_OBJECT(area), "button_release_event",
2199 G_CALLBACK(button_release_event), NULL);
2201 g_signal_connect(G_OBJECT(area), "motion_notify_event",
2202 G_CALLBACK(motion_notify_event), NULL);
2204 g_signal_connect(G_OBJECT(area), "selection_clear_event",
2205 G_CALLBACK(selection_clear_event), NULL);
2207 g_signal_connect(G_OBJECT(area), "selection_get",
2208 G_CALLBACK(selection_get), NULL);
2210 g_signal_connect(G_OBJECT(area), "selection_received",
2211 G_CALLBACK(selection_received), NULL);
2213 g_signal_connect(G_OBJECT(area), "scroll_event",
2214 G_CALLBACK(scroll_event), NULL);
2216 gtk_drag_dest_set(area, GTK_DEST_DEFAULT_ALL, targets,
2217 sizeof(targets) / sizeof(GtkTargetEntry),
2218 GDK_ACTION_COPY);
2219 g_signal_connect(G_OBJECT(area), "drag_data_received",
2220 G_CALLBACK(drag_data_received), NULL);
2222 gtk_selection_add_target(area, GDK_SELECTION_PRIMARY,
2223 GDK_SELECTION_TYPE_STRING, 1);
2225 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
2226 G_CALLBACK(switch_page), NULL);
2228 /* the scrollbar */
2229 scrollbar = gtk_vscrollbar_new(NULL);
2230 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0);
2232 g_signal_connect(G_OBJECT
2233 (gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
2234 "value_changed", G_CALLBACK(value_changed), NULL);
2236 /* the status bar */
2237 status = gtk_label_new("mp " VERSION);
2238 gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
2239 gtk_misc_set_alignment(GTK_MISC(status), 0, 0.5);
2240 gtk_label_set_justify(GTK_LABEL(status), GTK_JUSTIFY_LEFT);
2242 gtk_widget_show_all(window);
2244 /* set application icon */
2245 pixmap = gdk_pixmap_create_from_xpm_d(window->window,
2246 &mask, NULL, mp_xpm);
2247 gdk_window_set_icon(window->window, NULL, pixmap, mask);
2249 build_colors();
2251 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
2252 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
2253 maximize = 1;
2255 return NULL;
2259 int gtk_drv_detect(int *argc, char ***argv)
2261 mpdm_t drv;
2262 int n;
2264 for (n = 0; n < *argc; n++) {
2265 if (strcmp(argv[0][n], "-txt") == 0 ||
2266 strcmp(argv[0][n], "-h") == 0)
2267 return 0;
2270 if (!gtk_init_check(argc, argv))
2271 return 0;
2273 drv = mpdm_hget_s(mp, L"drv");
2274 mpdm_hset_s(drv, L"id", MPDM_LS(L"gtk"));
2275 mpdm_hset_s(drv, L"startup", MPDM_X(gtk_drv_startup));
2277 return 1;
2280 #endif /* CONFOPT_GTK */