Use mpdm_cmp_s() wherever possible.
[mp-5.x.git] / mpv_gtk.c
blob0376fad5e4bef0cbd004231d6c3e2eea012f75fc
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 wchar_t * utf8_to_wcs(const char * ptr)
123 /* converts utf-8 to wcs */
125 wchar_t * wptr;
126 gsize i, o;
128 i = strlen(ptr);
130 /* do the conversion */
131 wptr = (wchar_t *) g_convert((gchar *) ptr, i + 1,
132 "WCHAR_T", "UTF-8", NULL, &o, NULL);
134 return wptr;
138 static char * localize(char * msg)
140 mpdm_t v;
142 v = mpdm_gettext(MPDM_MBS(msg));
143 return wcs_to_utf8(v->data);
147 static void update_window_size(void)
148 /* updates the viewport size in characters */
150 PangoLayout * pa;
151 int tx, ty;
152 mpdm_t v;
154 /* get font metrics */
155 pa = gtk_widget_create_pango_layout(area, "m");
156 pango_layout_set_font_description(pa, font);
157 pango_layout_get_pixel_size(pa, &font_width, &font_height);
158 g_object_unref(pa);
160 /* calculate the size in chars */
161 tx = (area->allocation.width / font_width);
162 ty = (area->allocation.height / font_height) + 1;
164 /* store the 'window' size */
165 v = mpdm_hget_s(mp, L"window");
166 mpdm_hset_s(v, L"tx", MPDM_I(tx));
167 mpdm_hset_s(v, L"ty", MPDM_I(ty));
169 /* rebuild the pixmap for the double buffer */
170 if (pixmap != NULL)
171 gdk_pixmap_unref(pixmap);
173 pixmap = gdk_pixmap_new(area->window,
174 area->allocation.width, font_height, -1);
178 static void build_fonts(void)
179 /* builds the fonts */
181 char tmp[128];
182 int font_size = 12;
183 const char * font_face = "Mono";
184 mpdm_t c;
186 if(font != NULL)
187 pango_font_description_free(font);
189 /* get current configuration */
190 if ((c = mpdm_hget_s(mp, L"config")) != NULL) {
191 mpdm_t v;
193 if ((v = mpdm_hget_s(c, L"font_size")) != NULL)
194 font_size = mpdm_ival(v);
195 else
196 mpdm_hset_s(c, L"font_size", MPDM_I(font_size));
198 if ((v = mpdm_hget_s(c, L"font_face")) != NULL) {
199 v = MPDM_2MBS(v->data);
200 font_face = v->data;
202 else
203 mpdm_hset_s(c, L"font_face", MPDM_MBS(font_face));
206 snprintf(tmp, sizeof(tmp) - 1, "%s %d", font_face, font_size);
207 tmp[sizeof(tmp) - 1] = '\0';
209 font = pango_font_description_from_string(tmp);
210 update_window_size();
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, TRUE);
225 static void build_colors(void)
226 /* builds the colors */
228 mpdm_t colors;
229 mpdm_t l;
230 mpdm_t c;
231 int n, s;
233 /* gets the color definitions and attribute names */
234 colors = mpdm_hget_s(mp, L"colors");
235 l = mpdm_keys(colors);
236 s = mpdm_size(l);
238 /* redim the structures */
239 inks = realloc(inks, sizeof(GdkColor) * s);
240 papers = realloc(papers, sizeof(GdkColor) * s);
241 underlines = realloc(underlines, sizeof(int) * s);
243 /* loop the colors */
244 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
245 mpdm_t d = mpdm_hget(colors, c);
246 mpdm_t v = mpdm_hget_s(d, L"gui");
248 /* store the 'normal' attribute */
249 if (wcscmp(mpdm_string(c), L"normal") == 0)
250 normal_attr = n;
252 /* store the attr */
253 mpdm_hset_s(d, L"attr", MPDM_I(n));
255 build_color(&inks[n], mpdm_ival(mpdm_aget(v, 0)));
256 build_color(&papers[n], mpdm_ival(mpdm_aget(v, 1)));
258 /* flags */
259 v = mpdm_hget_s(d, L"flags");
260 underlines[n] = mpdm_seek_s(v, L"underline", 1) != -1 ? 1 : 0;
262 if (mpdm_seek_s(v, L"reverse", 1) != -1) {
263 GdkColor t;
265 t = inks[n];
266 inks[n] = papers[n];
267 papers[n] = t;
273 /** menu functions **/
275 static void redraw(void);
277 static void menu_item_callback(mpdm_t action)
278 /* menu click callback */
280 mp_process_action(action);
281 redraw();
283 if (mp_exit_requested)
284 gtk_main_quit();
288 static void build_submenu(GtkWidget * menu, mpdm_t labels)
289 /* build a submenu */
291 int n;
292 GtkWidget * menu_item;
294 for (n = 0; n < mpdm_size(labels); n++) {
295 /* get the action */
296 mpdm_t v = mpdm_aget(labels, n);
298 /* if the action is a separator... */
299 if (*((wchar_t *)v->data) == L'-')
300 menu_item = gtk_menu_item_new();
301 else {
302 char * ptr;
303 mpdm_t d = mp_menu_label(v);
305 ptr = wcs_to_utf8(mpdm_string(d));
306 menu_item = gtk_menu_item_new_with_label(ptr);
307 g_free(ptr);
310 gtk_menu_append(GTK_MENU(menu), menu_item);
311 g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
312 G_CALLBACK(menu_item_callback), v);
313 gtk_widget_show(menu_item);
318 static void build_menu(void)
319 /* builds the menu */
321 static mpdm_t prev_menu = NULL;
322 int n;
323 mpdm_t m;
325 /* gets the current menu */
326 if ((m = mpdm_hget_s(mp, L"menu")) == NULL)
327 return;
329 /* if it's the same, do nothing */
330 if (mpdm_cmp(m, prev_menu) == 0)
331 return;
333 /* create a new menu */
334 menu_bar = gtk_menu_bar_new();
336 for (n = 0; n < mpdm_size(m); n++) {
337 char * ptr;
338 mpdm_t mi;
339 mpdm_t v;
340 GtkWidget * menu;
341 GtkWidget * menu_item;
342 int i;
344 /* get the label and the items */
345 mi = mpdm_aget(m, n);
346 v = mpdm_aget(mi, 0);
348 if ((ptr = wcs_to_utf8(mpdm_string(mpdm_gettext(v)))) == NULL)
349 continue;
351 /* change the & by _ for the mnemonic */
352 for (i = 0; ptr[i]; i++)
353 if (ptr[i] == '&')
354 ptr[i] = '_';
356 /* add the menu and the label */
357 menu = gtk_menu_new();
358 menu_item = gtk_menu_item_new_with_mnemonic(ptr);
359 g_free(ptr);
361 gtk_widget_show(menu_item);
362 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
363 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_item);
365 /* now loop the items */
366 build_submenu(menu, mpdm_aget(mi, 1));
371 /** main area drawing functions **/
373 static void switch_page(GtkNotebook * notebook, GtkNotebookPage * page,
374 gint pg_num, gpointer data)
375 /* 'switch_page' handler (filetabs) */
377 /* sets the active one */
378 mpdm_hset_s(mp, L"active_i", MPDM_I(pg_num));
380 gtk_widget_grab_focus(area);
381 redraw();
385 static void draw_filetabs(void)
386 /* draws the filetabs */
388 static mpdm_t last = NULL;
389 mpdm_t names;
390 int n;
392 names = mp_get_doc_names();
394 /* disconnect redraw signal to avoid infinite loops */
395 g_signal_handlers_disconnect_by_func(G_OBJECT(file_tabs),
396 G_CALLBACK(switch_page), NULL);
398 /* is the list different from the previous one? */
399 if (mpdm_cmp(names, last) != 0) {
401 /* delete the current tabs */
402 for (n = 0; n < mpdm_size(last); n++)
403 gtk_notebook_remove_page(
404 GTK_NOTEBOOK(file_tabs), 0);
406 /* create the new ones */
407 for (n = 0; n < mpdm_size(names); n++) {
408 GtkWidget * p;
409 GtkWidget * f;
410 char * ptr;
411 mpdm_t v = mpdm_aget(names, n);
413 if ((ptr = wcs_to_utf8(v->data)) != NULL) {
414 p = gtk_label_new(ptr);
415 gtk_widget_show(p);
417 f = gtk_frame_new(NULL);
418 gtk_widget_show(f);
420 gtk_notebook_append_page(
421 GTK_NOTEBOOK(file_tabs), f, p);
423 g_free(ptr);
427 /* store for the next time */
428 mpdm_unref(last);
429 last = mpdm_ref(names);
432 /* set the active one */
433 gtk_notebook_set_page(GTK_NOTEBOOK(file_tabs),
434 mpdm_ival(mpdm_hget_s(mp, L"active_i")));
436 /* reconnect signal */
437 g_signal_connect(G_OBJECT(file_tabs), "switch_page",
438 G_CALLBACK(switch_page), NULL);
440 gtk_widget_grab_focus(area);
444 static void draw_status(void)
445 /* draws the status line */
447 mpdm_t t;
448 char * ptr;
450 /* call mp.status_line() */
451 t = mp_build_status_line();
453 if (t != NULL && (ptr = wcs_to_utf8(t->data)) != NULL) {
454 gtk_label_set_text(GTK_LABEL(status), ptr);
455 g_free(ptr);
460 static gint scroll_event(GtkWidget * widget, GdkEventScroll * event)
461 /* 'scroll_event' handler (mouse wheel) */
463 wchar_t * ptr = NULL;
465 switch (event->direction) {
466 case GDK_SCROLL_UP: ptr = L"mouse-wheel-up"; break;
467 case GDK_SCROLL_DOWN: ptr = L"mouse-wheel-down"; break;
468 case GDK_SCROLL_LEFT: ptr = L"mouse-wheel-left"; break;
469 case GDK_SCROLL_RIGHT: ptr = L"mouse-wheel-right"; break;
472 if (ptr != NULL)
473 mp_process_event(MPDM_S(ptr));
475 redraw();
477 return 0;
481 static void value_changed(GtkAdjustment * adj, gpointer * data)
482 /* 'value_changed' handler (scrollbar) */
484 int i = (int) adj->value;
485 mpdm_t doc;
486 mpdm_t txt;
487 int y;
489 /* get current y position */
490 doc = mp_active();
491 txt = mpdm_hget_s(doc, L"txt");
492 y = mpdm_ival(mpdm_hget_s(txt, L"y"));
494 /* if it's different, set and redraw */
495 if (y != i) {
496 mp_set_y(doc, i);
497 mpdm_hset_s(txt, L"vy", MPDM_I(i));
498 redraw();
503 static void draw_scrollbar(void)
504 /* updates the scrollbar */
506 GtkAdjustment * adjustment;
507 mpdm_t d;
508 mpdm_t v;
509 int pos, size, max;
511 /* gets the active document */
512 if ((d = mp_active()) == NULL)
513 return;
515 /* get the coordinates */
516 v = mpdm_hget_s(d, L"txt");
517 pos = mpdm_ival(mpdm_hget_s(v, L"vy"));
518 max = mpdm_size(mpdm_hget_s(v, L"lines"));
520 v = mpdm_hget_s(mp, L"window");
521 size = mpdm_ival(mpdm_hget_s(v, L"ty"));
523 adjustment = gtk_range_get_adjustment(GTK_RANGE(scrollbar));
525 /* if((int)adjustment->upper==max &&
526 (int)adjustment->page_size==size &&
527 (int)adjustment->page_increment==size &&
528 (int)adjustment->value==pos)
529 return;
531 /* disconnect to avoid infinite loops */
532 g_signal_handlers_disconnect_by_func(G_OBJECT(adjustment),
533 G_CALLBACK(value_changed), NULL);
535 adjustment->step_increment = (gfloat)1;
536 adjustment->upper = (gfloat)max;
537 adjustment->page_size = (gfloat)size;
538 adjustment->page_increment = (gfloat)size;
539 adjustment->value = (gfloat)pos;
541 gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment);
543 gtk_adjustment_changed(adjustment);
544 gtk_adjustment_value_changed(adjustment);
546 /* reattach again */
547 g_signal_connect(
548 G_OBJECT(gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
549 "value_changed", G_CALLBACK(value_changed), NULL);
553 static void gtk_drv_paint(mpdm_t doc, int optimize)
554 /* GTK document draw function */
556 GdkRectangle gr;
557 mpdm_t d = NULL;
558 int n, m;
560 if (maximize)
561 gtk_window_maximize(GTK_WINDOW(window));
563 /* no gc? create it */
564 if (gc == NULL)
565 gc = gdk_gc_new(area->window);
567 if (font == NULL)
568 build_fonts();
570 if ((d = mp_draw(doc, optimize)) == NULL)
571 return;
573 gr.x = 0;
574 gr.y = 0;
575 gr.width = area->allocation.width;
576 gr.height = font_height;
578 for (n = 0; n < mpdm_size(d); n++) {
579 PangoLayout * pl;
580 PangoAttrList * pal;
581 mpdm_t l = mpdm_aget(d, n);
582 char * str = NULL;
583 int u, p;
585 if (l == NULL)
586 continue;
588 /* create the pango stuff */
589 pl = gtk_widget_create_pango_layout(area, NULL);
590 pango_layout_set_font_description(pl, font);
591 pal = pango_attr_list_new();
593 for (m = u = p = 0; m < mpdm_size(l); m++, u = p) {
594 PangoAttribute * pa;
595 int attr;
596 mpdm_t s;
597 char * ptr;
599 /* get the attribute and the string */
600 attr = mpdm_ival(mpdm_aget(l, m++));
601 s = mpdm_aget(l, m);
603 /* convert the string to utf8 */
604 ptr = wcs_to_utf8(s->data);
606 /* add to the full line */
607 str = mpdm_poke(str, &p, ptr, strlen(ptr), 1);
609 g_free(ptr);
611 /* create the background if it's
612 different from the default */
613 if (papers[attr].red != papers[normal_attr].red ||
614 papers[attr].green != papers[normal_attr].green ||
615 papers[attr].blue != papers[normal_attr].blue) {
616 pa = pango_attr_background_new(
617 papers[attr].red, papers[attr].green,
618 papers[attr].blue);
620 pa->start_index = u;
621 pa->end_index = p;
623 pango_attr_list_insert(pal, pa);
626 /* underline? */
627 if (underlines[attr]) {
628 pa = pango_attr_underline_new(TRUE);
630 pa->start_index = u;
631 pa->end_index = p;
633 pango_attr_list_insert(pal, pa);
636 /* foreground color */
637 pa = pango_attr_foreground_new(inks[attr].red,
638 inks[attr].green, inks[attr].blue);
640 pa->start_index = u;
641 pa->end_index = p;
643 pango_attr_list_insert(pal, pa);
646 /* store the attributes */
647 pango_layout_set_attributes(pl, pal);
648 pango_attr_list_unref(pal);
650 /* store and free the text */
651 pango_layout_set_text(pl, str, p);
652 free(str);
654 /* draw the background */
655 gdk_gc_set_foreground(gc, &papers[normal_attr]);
656 gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0,
657 gr.width, gr.height);
659 /* draw the text */
660 gtk_paint_layout(area->style, pixmap,
661 GTK_STATE_NORMAL, TRUE,
662 &gr, area, "", 2, 0, pl);
664 /* dump the pixmap */
665 gdk_draw_pixmap(area->window, gc, pixmap,
666 0, 0, 0, n * font_height, gr.width, gr.height);
668 g_object_unref(pl);
671 draw_filetabs();
672 draw_scrollbar();
673 draw_status();
677 static void redraw(void)
679 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
680 gtk_drv_paint(mp_active(), 0);
684 static gint delete_event(GtkWidget * w, GdkEvent * e, gpointer data)
685 /* 'delete_event' handler */
687 mp_process_event(MPDM_LS(L"close-window"));
689 return mp_exit_requested ? FALSE : TRUE;
693 static void destroy(GtkWidget * w, gpointer data)
694 /* 'destroy' handler */
696 gtk_main_quit();
700 static gint key_release_event(GtkWidget * widget, GdkEventKey * event, gpointer data)
701 /* 'key_release_event' handler */
703 if (mp_keypress_throttle(0))
704 gtk_drv_paint(mp_active(), 0);
706 return 0;
710 static gint key_press_event(GtkWidget * widget, GdkEventKey * event, gpointer data)
711 /* 'key_press_event' handler */
713 wchar_t * ptr = NULL;
715 gtk_im_context_filter_keypress(im, event);
717 /* set mp.shift_pressed */
718 if (event->state & (GDK_SHIFT_MASK))
719 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
721 /* reserve alt for menu mnemonics */
722 /* if (GDK_MOD1_MASK & event->state)
723 return(0);*/
725 if (event->state & (GDK_CONTROL_MASK)) {
726 switch (event->keyval) {
727 case GDK_Up: ptr = L"ctrl-cursor-up"; break;
728 case GDK_Down: ptr = L"ctrl-cursor-down"; break;
729 case GDK_Left: ptr = L"ctrl-cursor-left"; break;
730 case GDK_Right: ptr = L"ctrl-cursor-right"; break;
731 case GDK_Prior: ptr = L"ctrl-page-up"; break;
732 case GDK_Next: ptr = L"ctrl-page-down"; break;
733 case GDK_Home: ptr = L"ctrl-home"; break;
734 case GDK_End: ptr = L"ctrl-end"; break;
735 case GDK_space: ptr = L"ctrl-space"; break;
736 case GDK_KP_Add: ptr = L"ctrl-kp-plus"; break;
737 case GDK_KP_Subtract: ptr = L"ctrl-kp-minus"; break;
738 case GDK_KP_Multiply: ptr = L"ctrl-kp-multiply"; break;
739 case GDK_KP_Divide: ptr = L"ctrl-kp-divide"; break;
740 case GDK_F1: ptr = L"ctrl-f1"; break;
741 case GDK_F2: ptr = L"ctrl-f2"; break;
742 case GDK_F3: ptr = L"ctrl-f3"; break;
743 case GDK_F4: ptr = L"ctrl-f4"; break;
744 case GDK_F5: ptr = L"ctrl-f5"; break;
745 case GDK_F6: ptr = L"ctrl-f6"; break;
746 case GDK_F7: ptr = L"ctrl-f7"; break;
747 case GDK_F8: ptr = L"ctrl-f8"; break;
748 case GDK_F9: ptr = L"ctrl-f9"; break;
749 case GDK_F10: ptr = L"ctrl-f10"; break;
750 case GDK_F11: ptr = L"ctrl-f11"; break;
751 case GDK_F12: ptr = L"ctrl-f12"; break;
752 case GDK_KP_Enter:
753 case GDK_Return: ptr = L"ctrl-enter"; break;
754 case GDK_Cyrillic_ve: ptr = L"ctrl-d"; break;
755 case GDK_Cyrillic_a: ptr = L"ctrl-f"; break;
756 case GDK_Cyrillic_tse: ptr = L"ctrl-w"; break;
757 case GDK_Cyrillic_de: ptr = L"ctrl-l"; break;
758 case GDK_Cyrillic_ie: ptr = L"ctrl-t"; break;
759 case GDK_Cyrillic_ef: ptr = L"ctrl-a"; break;
760 case GDK_Cyrillic_ghe: ptr = L"ctrl-u"; break;
761 case GDK_Cyrillic_i: ptr = L"ctrl-b"; break;
762 case GDK_Cyrillic_shorti: ptr = L"ctrl-q"; break;
763 case GDK_Cyrillic_ka: ptr = L"ctrl-r"; break;
764 case GDK_Cyrillic_el: ptr = L"ctrl-k"; break;
765 case GDK_Cyrillic_em: ptr = L"ctrl-v"; break;
766 case GDK_Cyrillic_en: ptr = L"ctrl-y"; break;
767 case GDK_Cyrillic_o: ptr = L"ctrl-j"; break;
768 case GDK_Cyrillic_pe: ptr = L"ctrl-g"; break;
769 case GDK_Cyrillic_ya: ptr = L"ctrl-z"; break;
770 case GDK_Cyrillic_er: ptr = L"ctrl-h"; break;
771 case GDK_Cyrillic_es: ptr = L"ctrl-c"; break;
772 case GDK_Cyrillic_te: ptr = L"ctrl-n"; break;
773 case GDK_Cyrillic_softsign: ptr = L"ctrl-m"; break;
774 case GDK_Cyrillic_yeru: ptr = L"ctrl-s"; break;
775 case GDK_Cyrillic_ze: ptr = L"ctrl-p"; break;
776 case GDK_Cyrillic_sha: ptr = L"ctrl-i"; break;
777 case GDK_Cyrillic_e: ptr = L"ctrl-t"; break;
778 case GDK_Cyrillic_shcha: ptr = L"ctrl-o"; break;
779 case GDK_Cyrillic_che: ptr = L"ctrl-x"; break;
782 if (ptr == NULL) {
783 char c = event->keyval & 0xdf;
785 switch (c) {
786 case 'A': ptr = L"ctrl-a"; break;
787 case 'B': ptr = L"ctrl-b"; break;
788 case 'C': ptr = L"ctrl-c"; break;
789 case 'D': ptr = L"ctrl-d"; break;
790 case 'E': ptr = L"ctrl-e"; break;
791 case 'F': ptr = L"ctrl-f"; break;
792 case 'G': ptr = L"ctrl-g"; break;
793 case 'H': ptr = L"ctrl-h"; break;
794 case 'I': ptr = L"ctrl-i"; break;
795 case 'J': ptr = L"ctrl-j"; break;
796 case 'K': ptr = L"ctrl-k"; break;
797 case 'L': ptr = L"ctrl-l"; break;
798 case 'M': ptr = L"ctrl-m"; break;
799 case 'N': ptr = L"ctrl-n"; break;
800 case 'O': ptr = L"ctrl-o"; break;
801 case 'P': ptr = L"ctrl-p"; break;
802 case 'Q': ptr = L"ctrl-q"; break;
803 case 'R': ptr = L"ctrl-r"; break;
804 case 'S': ptr = L"ctrl-s"; break;
805 case 'T': ptr = L"ctrl-t"; break;
806 case 'U': ptr = L"ctrl-u"; break;
807 case 'V': ptr = L"ctrl-v"; break;
808 case 'W': ptr = L"ctrl-w"; break;
809 case 'X': ptr = L"ctrl-x"; break;
810 case 'Y': ptr = L"ctrl-y"; break;
811 case 'Z': ptr = L"ctrl-z"; break;
815 else
816 if (event->state & (GDK_MOD1_MASK)) {
817 switch (event->keyval) {
818 case GDK_Up: ptr = L"alt-cursor-up"; break;
819 case GDK_Down: ptr = L"alt-cursor-down"; break;
820 case GDK_Left: ptr = L"alt-cursor-left"; break;
821 case GDK_Right: ptr = L"alt-cursor-right"; break;
822 case GDK_Prior: ptr = L"alt-page-up"; break;
823 case GDK_Next: ptr = L"alt-page-down"; break;
824 case GDK_Home: ptr = L"alt-home"; break;
825 case GDK_End: ptr = L"alt-end"; break;
826 case GDK_space: ptr = L"alt-space"; break;
827 case GDK_KP_Add: ptr = L"alt-kp-plus"; break;
828 case GDK_KP_Subtract: ptr = L"alt-kp-minus"; break;
829 case GDK_KP_Multiply: ptr = L"alt-kp-multiply"; break;
830 case GDK_KP_Divide: ptr = L"alt-kp-divide"; break;
831 case GDK_F1: ptr = L"alt-f1"; break;
832 case GDK_F2: ptr = L"alt-f2"; break;
833 case GDK_F3: ptr = L"alt-f3"; break;
834 case GDK_F4: ptr = L"alt-f4"; break;
835 case GDK_F5: ptr = L"alt-f5"; break;
836 case GDK_F6: ptr = L"alt-f6"; break;
837 case GDK_F7: ptr = L"alt-f7"; break;
838 case GDK_F8: ptr = L"alt-f8"; break;
839 case GDK_F9: ptr = L"alt-f9"; break;
840 case GDK_F10: ptr = L"alt-f10"; break;
841 case GDK_F11: ptr = L"alt-f11"; break;
842 case GDK_F12: ptr = L"alt-f12"; break;
843 case GDK_KP_Enter:
844 case GDK_Return: ptr = L"alt-enter"; break;
845 case GDK_Cyrillic_ve: ptr = L"alt-d"; break;
846 case GDK_Cyrillic_a: ptr = L"alt-f"; break;
847 case GDK_Cyrillic_tse: ptr = L"alt-w"; break;
848 case GDK_Cyrillic_de: ptr = L"alt-l"; break;
849 case GDK_Cyrillic_ie: ptr = L"alt-t"; break;
850 case GDK_Cyrillic_ef: ptr = L"alt-a"; break;
851 case GDK_Cyrillic_ghe: ptr = L"alt-u"; break;
852 case GDK_Cyrillic_i: ptr = L"alt-b"; break;
853 case GDK_Cyrillic_shorti: ptr = L"alt-q"; break;
854 case GDK_Cyrillic_ka: ptr = L"alt-r"; break;
855 case GDK_Cyrillic_el: ptr = L"alt-k"; break;
856 case GDK_Cyrillic_em: ptr = L"alt-v"; break;
857 case GDK_Cyrillic_en: ptr = L"alt-y"; break;
858 case GDK_Cyrillic_o: ptr = L"alt-j"; break;
859 case GDK_Cyrillic_pe: ptr = L"alt-g"; break;
860 case GDK_Cyrillic_ya: ptr = L"alt-z"; break;
861 case GDK_Cyrillic_er: ptr = L"alt-h"; break;
862 case GDK_Cyrillic_es: ptr = L"alt-c"; break;
863 case GDK_Cyrillic_te: ptr = L"alt-n"; break;
864 case GDK_Cyrillic_softsign: ptr = L"alt-m"; break;
865 case GDK_Cyrillic_yeru: ptr = L"alt-s"; break;
866 case GDK_Cyrillic_ze: ptr = L"alt-p"; break;
867 case GDK_Cyrillic_sha: ptr = L"alt-i"; break;
868 case GDK_Cyrillic_e: ptr = L"alt-t"; break;
869 case GDK_Cyrillic_shcha: ptr = L"alt-o"; break;
870 case GDK_Cyrillic_che: ptr = L"alt-x"; break;
873 if (ptr == NULL) {
874 char c = event->keyval & 0xdf;
876 switch (c) {
877 case 'A': ptr = L"alt-a"; break;
878 case 'B': ptr = L"alt-b"; break;
879 case 'C': ptr = L"alt-c"; break;
880 case 'D': ptr = L"alt-d"; break;
881 case 'E': ptr = L"alt-e"; break;
882 case 'F': ptr = L"alt-f"; break;
883 case 'G': ptr = L"alt-g"; break;
884 case 'H': ptr = L"alt-h"; break;
885 case 'I': ptr = L"alt-i"; break;
886 case 'J': ptr = L"alt-j"; break;
887 case 'K': ptr = L"alt-k"; break;
888 case 'L': ptr = L"alt-l"; break;
889 case 'M': ptr = L"alt-m"; break;
890 case 'N': ptr = L"alt-n"; break;
891 case 'O': ptr = L"alt-o"; break;
892 case 'P': ptr = L"alt-p"; break;
893 case 'Q': ptr = L"alt-q"; break;
894 case 'R': ptr = L"alt-r"; break;
895 case 'S': ptr = L"alt-s"; break;
896 case 'T': ptr = L"alt-t"; break;
897 case 'U': ptr = L"alt-u"; break;
898 case 'V': ptr = L"alt-v"; break;
899 case 'W': ptr = L"alt-w"; break;
900 case 'X': ptr = L"alt-x"; break;
901 case 'Y': ptr = L"alt-y"; break;
902 case 'Z': ptr = L"alt-z"; break;
906 else {
907 switch (event->keyval) {
908 case GDK_Up: ptr = L"cursor-up"; break;
909 case GDK_Down: ptr = L"cursor-down"; break;
910 case GDK_Left: ptr = L"cursor-left"; break;
911 case GDK_Right: ptr = L"cursor-right"; break;
912 case GDK_Prior: ptr = L"page-up"; break;
913 case GDK_Next: ptr = L"page-down"; break;
914 case GDK_Home: ptr = L"home"; break;
915 case GDK_End: ptr = L"end"; break;
916 case GDK_space: ptr = L"space"; break;
917 case GDK_KP_Add: ptr = L"kp-plus"; break;
918 case GDK_KP_Subtract: ptr = L"kp-minus"; break;
919 case GDK_KP_Multiply: ptr = L"kp-multiply"; break;
920 case GDK_KP_Divide: ptr = L"kp-divide"; break;
921 case GDK_F1: ptr = L"f1"; break;
922 case GDK_F2: ptr = L"f2"; break;
923 case GDK_F3: ptr = L"f3"; break;
924 case GDK_F4: ptr = L"f4"; break;
925 case GDK_F5: ptr = L"f5"; break;
926 case GDK_F6: ptr = L"f6"; break;
927 case GDK_F7: ptr = L"f7"; break;
928 case GDK_F8: ptr = L"f8"; break;
929 case GDK_F9: ptr = L"f9"; break;
930 case GDK_F10: ptr = L"f10"; break;
931 case GDK_F11: ptr = L"f11"; break;
932 case GDK_F12: ptr = L"f12"; break;
933 case GDK_Insert: ptr = L"insert"; break;
934 case GDK_BackSpace: ptr = L"backspace"; break;
935 case GDK_Delete: ptr = L"delete"; break;
936 case GDK_KP_Enter:
937 case GDK_Return: ptr = L"enter"; break;
938 case GDK_Tab: ptr = L"tab"; break;
939 case GDK_Escape: ptr = L"escape"; break;
943 /* if there is a pending char in im_char, use it */
944 if (ptr == NULL && im_char[0] != L'\0')
945 ptr = im_char;
947 /* finally process */
948 if (ptr != NULL)
949 mp_process_event(MPDM_S(ptr));
951 /* delete the pending char */
952 im_char[0] = L'\0';
954 if (mp_exit_requested)
955 gtk_main_quit();
957 if (mp_keypress_throttle(1))
958 gtk_drv_paint(mp_active(), 1);
960 return 0;
964 static gint button_press_event(GtkWidget * widget, GdkEventButton * event, gpointer data)
965 /* 'button_press_event' handler (mouse buttons) */
967 int x, y;
968 wchar_t * ptr = NULL;
970 mouse_down = 1;
972 /* mouse instant positioning */
973 x = ((int)event->x) / font_width;
974 y = ((int)event->y) / font_height;
976 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
977 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
979 switch (event->button) {
980 case 1: ptr = L"mouse-left-button"; break;
981 case 2: ptr = L"mouse-middle-button"; break;
982 case 3: ptr = L"mouse-right-button"; break;
983 case 4: ptr = L"mouse-wheel-up"; break;
984 case 5: ptr = L"mouse-wheel-down"; break;
987 if (ptr != NULL)
988 mp_process_event(MPDM_S(ptr));
990 redraw();
992 return 0;
996 static gint button_release_event(GtkWidget * widget, GdkEventButton * event, gpointer data)
997 /* 'button_release_event' handle (mouse buttons) */
999 mouse_down = 0;
1001 return TRUE;
1005 static gint motion_notify_event(GtkWidget *widget, GdkEventMotion * event, gpointer data)
1006 /* 'motion_notify_event' handler (mouse movement) */
1008 static int ox = 0;
1009 static int oy = 0;
1011 if (mouse_down) {
1012 int x, y;
1014 /* mouse dragging */
1015 x = ((int)event->x) / font_width;
1016 y = ((int)event->y) / font_height;
1018 if (ox != x && oy != y) {
1019 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
1020 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
1022 mp_process_event(MPDM_LS(L"mouse-drag"));
1023 gtk_drv_paint(mp_active(), 1);
1027 return TRUE;
1031 static void drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
1032 GtkSelectionData *data, guint info, guint time)
1033 /* 'drag_data_received' handler */
1035 printf("drag_data_received (unsupported)\n");
1039 /** clipboard functions **/
1041 static void commit(GtkIMContext * i, char * str, gpointer u)
1042 /* 'commit' handler */
1044 wchar_t * wstr;
1046 wstr = (wchar_t *) g_convert(str, -1,
1047 "WCHAR_T", "UTF-8", NULL, NULL, NULL);
1049 im_char[0] = *wstr;
1050 im_char[1] = L'\0';
1052 g_free(wstr);
1056 static void realize(GtkWidget * widget)
1057 /* 'realize' handler */
1059 im = gtk_im_multicontext_new();
1060 g_signal_connect(im, "commit", G_CALLBACK(commit), NULL);
1061 gtk_im_context_set_client_window(im, widget->window);
1065 static gint expose_event(GtkWidget * widget, GdkEventExpose * event)
1066 /* 'expose_event' handler */
1068 redraw();
1070 return FALSE;
1074 static gint configure_event(GtkWidget * widget, GdkEventConfigure * event)
1075 /* 'configure_event' handler */
1077 static GdkEventConfigure o;
1079 if (memcmp(&o, event, sizeof(o)) == 0)
1080 return TRUE;
1082 memcpy(&o, event, sizeof(o));
1084 update_window_size();
1085 redraw();
1087 return TRUE;
1091 static gint selection_clear_event(GtkWidget * widget,
1092 GdkEventSelection * event, gpointer data)
1093 /* 'selection_clear_event' handler */
1095 got_selection = 0;
1097 return TRUE;
1101 static void selection_get(GtkWidget * widget,
1102 GtkSelectionData * sel, guint info, guint tm)
1103 /* 'selection_get' handler */
1105 mpdm_t d;
1106 unsigned char * ptr;
1107 int s;
1109 if (!got_selection)
1110 return;
1112 /* gets the clipboard and joins */
1113 d = mpdm_hget_s(mp, L"clipboard");
1115 if (mpdm_size(d) == 0)
1116 return;
1118 d = mpdm_join(MPDM_LS(L"\n"), d);
1120 /* convert to current locale */
1121 ptr = (unsigned char *) mpdm_wcstombs(d->data, &s);
1123 /* pastes into primary selection */
1124 gtk_selection_data_set(sel, GDK_SELECTION_TYPE_STRING, 8, ptr, (gsize) s);
1126 free(ptr);
1130 static void selection_received(GtkWidget * widget,
1131 GtkSelectionData * sel, gpointer data)
1132 /* 'selection_received' handler */
1134 mpdm_t d;
1136 if (sel->data != NULL) {
1137 /* get selection */
1138 wchar_t * wptr = utf8_to_wcs((char *)sel->data);
1139 d = MPDM_S(wptr);
1140 g_free(wptr);
1142 /* split and set as the clipboard */
1143 mpdm_hset_s(mp, L"clipboard", mpdm_split(MPDM_LS(L"\n"), d));
1144 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
1146 /* wait no more for the selection */
1147 wait_for_selection = 0;
1149 else
1150 wait_for_selection = -1;
1154 static mpdm_t gtk_drv_clip_to_sys(mpdm_t a)
1155 /* driver-dependent mp to system clipboard */
1157 got_selection = gtk_selection_owner_set(area,
1158 GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME);
1160 return NULL;
1164 static mpdm_t gtk_drv_sys_to_clip(mpdm_t a)
1165 /* driver-dependent system to mp clipboard */
1167 if (!got_selection) {
1168 int n;
1169 char * formats[] = { "UTF8_STRING", "STRING", NULL };
1171 for(n = 0; formats[n] != NULL; n++) {
1173 /* triggers a selection capture */
1174 if (gtk_selection_convert(area, GDK_SELECTION_PRIMARY,
1175 gdk_atom_intern(formats[n], FALSE),
1176 GDK_CURRENT_TIME)) {
1178 /* processes the pending events
1179 (i.e., the 'selection_received' handler) */
1180 wait_for_selection = 1;
1182 while(wait_for_selection == 1)
1183 gtk_main_iteration();
1185 if (!wait_for_selection)
1186 break;
1191 return NULL;
1195 /** interface functions **/
1197 static void clicked_ok(GtkWidget * widget, gpointer data)
1198 /* 'clicked_on' signal handler (for gtk_drv_form) */
1200 int n;
1202 for (n = 0; n < mpdm_size(form_args); n++) {
1203 GtkWidget * widget = form_widgets[n];
1204 mpdm_t w = mpdm_aget(form_args, n);
1205 wchar_t * wptr = mpdm_string(mpdm_hget_s(w, L"type"));
1206 mpdm_t v = NULL;
1208 /* if there is already a value there, if was
1209 previously set from a callback */
1210 if (mpdm_aget(form_values, n) != NULL)
1211 continue;
1213 if (wcscmp(wptr, L"text") == 0 ||
1214 wcscmp(wptr, L"password") == 0) {
1215 char * ptr;
1216 GtkWidget * gw = widget;
1217 mpdm_t h;
1219 if (wcscmp(wptr, L"text") == 0)
1220 gw = GTK_COMBO(widget)->entry;
1222 if ((ptr = gtk_editable_get_chars(
1223 GTK_EDITABLE(gw), 0, -1)) != NULL &&
1224 (wptr = utf8_to_wcs(ptr)) != NULL) {
1225 v = MPDM_S(wptr);
1226 g_free(wptr);
1227 g_free(ptr);
1230 /* if it has history, fill it */
1231 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1232 v != NULL && mpdm_cmp_s(v, L"") != 0) {
1233 h = mp_get_history(h);
1235 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1236 mpdm_push(h, v);
1239 else
1240 if (wcscmp(wptr, L"checkbox") == 0) {
1241 v = MPDM_I(gtk_toggle_button_get_active(
1242 GTK_TOGGLE_BUTTON(widget)));
1244 else
1245 if (wcscmp(wptr, L"list") == 0) {
1246 GtkWidget * list = gtk_bin_get_child(GTK_BIN(widget));
1247 GtkTreeSelection * selection =
1248 gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
1249 GList * selected = gtk_tree_selection_get_selected_rows(selection, NULL);
1250 GtkTreePath * path = selected->data;
1252 v = MPDM_I(gtk_tree_path_get_indices(path)[0]);
1253 gtk_tree_path_free(path);
1254 g_list_free(selected);
1258 mpdm_aset(form_values, v, n);
1263 static gint timer_callback(gpointer data)
1265 mpdm_exec(timer_func, NULL);
1266 redraw();
1268 return TRUE;
1272 static void build_form_data(mpdm_t widget_list)
1273 /* builds the necessary information for a list of widgets */
1275 mpdm_unref(form_args);
1276 form_args = mpdm_ref(widget_list);
1278 mpdm_unref(form_values);
1279 form_values = widget_list == NULL ? NULL :
1280 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1282 /* resize the widget array */
1283 form_widgets = (GtkWidget **) realloc(form_widgets,
1284 mpdm_size(form_args) * sizeof(GtkWidget *));
1288 /** dialog functions **/
1290 #define DIALOG_BUTTON(l,f) do { GtkWidget * btn; \
1291 ptr = localize(l); btn = gtk_button_new_with_label(ptr); \
1292 g_signal_connect_swapped(G_OBJECT(btn), "clicked", \
1293 G_CALLBACK(f), G_OBJECT(dlg)); \
1294 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), \
1295 btn, TRUE, TRUE, 0); \
1296 g_free(ptr); \
1297 } while (0);
1299 static mpdm_t gtk_drv_alert(mpdm_t a)
1300 /* alert driver function */
1302 wchar_t * wptr;
1303 gchar * ptr;
1304 GtkWidget * dlg;
1306 build_form_data(NULL);
1308 /* 1# arg: prompt */
1309 wptr = mpdm_string(mpdm_aget(a, 0));
1311 if ((ptr = wcs_to_utf8(wptr)) == NULL)
1312 return NULL;
1314 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
1315 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, ptr);
1316 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
1317 g_free(ptr);
1319 gtk_dialog_run(GTK_DIALOG(dlg));
1320 gtk_widget_destroy(dlg);
1322 return NULL;
1326 static mpdm_t gtk_drv_confirm(mpdm_t a)
1327 /* confirm driver function */
1329 wchar_t * wptr;
1330 char * ptr;
1331 GtkWidget * dlg;
1332 gint response;
1334 build_form_data(NULL);
1336 /* 1# arg: prompt */
1337 wptr = mpdm_string(mpdm_aget(a, 0));
1339 if ((ptr = wcs_to_utf8(wptr)) == NULL)
1340 return NULL;
1342 dlg = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL,
1343 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, ptr);
1344 gtk_window_set_title(GTK_WINDOW(dlg), "mp " VERSION);
1345 g_free(ptr);
1347 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_YES, 1);
1348 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_NO, 2);
1349 gtk_dialog_add_button(GTK_DIALOG(dlg), GTK_STOCK_CANCEL, 0);
1351 response = gtk_dialog_run(GTK_DIALOG(dlg));
1352 gtk_widget_destroy(dlg);
1354 if (response == GTK_RESPONSE_DELETE_EVENT)
1355 response = 0;
1357 return MPDM_I(response);
1361 static mpdm_t gtk_drv_form(mpdm_t a)
1362 /* 'form' driver function */
1364 GtkWidget * dlg;
1365 GtkWidget * table;
1366 GtkWidget * content_area;
1367 int n;
1368 mpdm_t ret = NULL;
1370 /* first argument: list of widgets */
1371 build_form_data(mpdm_aget(a, 0));
1373 dlg = gtk_dialog_new_with_buttons("mp " VERSION, GTK_WINDOW(window),
1374 GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
1375 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1376 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1377 gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
1378 gtk_container_set_border_width(GTK_CONTAINER(dlg), 5);
1380 content_area = GTK_DIALOG(dlg)->vbox;
1381 gtk_box_set_spacing(GTK_BOX(content_area), 2);
1383 table = gtk_table_new(mpdm_size(a), 2, FALSE);
1384 gtk_container_set_border_width(GTK_CONTAINER(table), 5);
1385 gtk_table_set_col_spacings(GTK_TABLE(table), 12);
1386 gtk_table_set_row_spacings(GTK_TABLE(table), 6);
1388 for (n = 0; n < mpdm_size(form_args); n++) {
1389 mpdm_t w = mpdm_aget(form_args, n);
1390 GtkWidget * widget = NULL;
1391 wchar_t * type;
1392 char * ptr;
1393 mpdm_t t;
1394 int col = 0;
1396 type = mpdm_string(mpdm_hget_s(w, L"type"));
1398 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
1399 GtkWidget * label;
1400 wchar_t * wptr;
1402 if ((wptr = mpdm_string(mpdm_gettext(t))) != NULL &&
1403 (ptr = wcs_to_utf8(wptr)) != NULL) {
1404 label = gtk_label_new(ptr);
1405 gtk_misc_set_alignment(GTK_MISC(label), 0, .5);
1407 gtk_table_attach_defaults(GTK_TABLE(table),
1408 label, 0, wcscmp(type, L"label") == 0 ? 2 : 1,
1409 n, n + 1);
1411 g_free(ptr);
1413 col++;
1417 t = mpdm_hget_s(w, L"value");
1419 if (wcscmp(type, L"text") == 0) {
1420 GList * combo_items = NULL;
1421 mpdm_t h;
1422 wchar_t * wptr;
1424 widget = gtk_combo_new();
1425 gtk_widget_set_size_request(widget, 300, -1);
1426 gtk_combo_set_use_arrows_always(GTK_COMBO(widget), TRUE);
1427 gtk_combo_set_case_sensitive(GTK_COMBO(widget), TRUE);
1428 gtk_entry_set_activates_default(GTK_ENTRY(GTK_COMBO(widget)->entry), TRUE);
1430 if ((h = mpdm_hget_s(w, L"history")) != NULL) {
1431 int i;
1433 /* has history; fill it */
1434 h = mp_get_history(h);
1436 for (i = 0; i < mpdm_size(h); i++) {
1437 wptr = mpdm_string(mpdm_aget(h, i));
1438 ptr = wcs_to_utf8(wptr);
1440 combo_items = g_list_prepend(combo_items, ptr);
1444 if (t != NULL) {
1445 wptr = mpdm_string(t);
1446 ptr = wcs_to_utf8(wptr);
1448 combo_items = g_list_prepend(combo_items, ptr);
1451 gtk_combo_set_popdown_strings(GTK_COMBO(widget), combo_items);
1452 g_list_free(combo_items);
1454 else
1455 if (wcscmp(type, L"password") == 0) {
1456 widget = gtk_entry_new();
1457 gtk_widget_set_size_request(widget, 300, -1);
1458 gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE);
1460 else
1461 if (wcscmp(type, L"checkbox") == 0) {
1462 widget = gtk_check_button_new();
1464 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
1465 mpdm_ival(t) ? TRUE : FALSE);
1467 else
1468 if (wcscmp(type, L"list") == 0) {
1469 GtkWidget * list;
1470 GtkListStore * list_store;
1471 GtkCellRenderer * renderer;
1472 GtkTreeViewColumn * column;
1473 GtkTreePath * path;
1474 mpdm_t l;
1475 gint i;
1477 if ((i = 450 / mpdm_size(form_args)) < 100)
1478 i = 100;
1480 widget = gtk_scrolled_window_new(NULL, NULL);
1481 gtk_widget_set_size_request(widget, 500, i);
1482 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
1483 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1484 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget),
1485 GTK_SHADOW_IN);
1487 list_store = gtk_list_store_new(1, G_TYPE_STRING);
1488 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
1489 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
1490 renderer = gtk_cell_renderer_text_new();
1491 column = gtk_tree_view_column_new_with_attributes("", renderer,
1492 "text", 0, NULL);
1493 gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
1494 gtk_container_add(GTK_CONTAINER(widget), list);
1496 l = mpdm_hget_s(w, L"list");
1498 for (i = 0; i < mpdm_size(l); i++) {
1499 wchar_t * wptr = mpdm_string(mpdm_aget(l, i));
1501 if ((ptr = wcs_to_utf8(wptr)) != NULL) {
1502 GtkTreeIter iter;
1503 gtk_list_store_append(list_store, &iter);
1504 gtk_list_store_set(list_store, &iter, 0, ptr, -1);
1505 g_free(ptr);
1509 /* initial position */
1510 i = mpdm_ival(t);
1512 path = gtk_tree_path_new_from_indices(i, -1);
1513 gtk_tree_view_set_cursor(GTK_TREE_VIEW(list), path, NULL, FALSE);
1514 gtk_tree_path_free(path);
1516 g_signal_connect_swapped(G_OBJECT(list), "row-activated",
1517 G_CALLBACK(gtk_window_activate_default), dlg);
1520 if (widget != NULL) {
1521 form_widgets[n] = widget;
1522 gtk_table_attach_defaults(GTK_TABLE(table),
1523 widget, col, 2, n, n + 1);
1527 gtk_widget_show_all(table);
1529 gtk_box_pack_start(GTK_BOX(content_area), table, TRUE, TRUE, 0);
1531 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_OK) {
1532 clicked_ok(NULL, NULL);
1533 ret = form_values;
1535 gtk_widget_destroy(dlg);
1537 return ret;
1541 static mpdm_t run_filechooser(mpdm_t a, gboolean save)
1542 /* openfile driver function */
1544 GtkWidget * dlg;
1545 wchar_t * wptr;
1546 char * ptr;
1547 mpdm_t ret = NULL;
1548 gint response;
1550 /* 1# arg: prompt */
1551 wptr = mpdm_string(mpdm_aget(a, 0));
1553 if ((ptr = wcs_to_utf8(wptr)) == NULL)
1554 return(NULL);
1556 if (!save) {
1557 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
1558 GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1559 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1561 else {
1562 dlg = gtk_file_chooser_dialog_new(ptr, GTK_WINDOW(window),
1563 GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_CANCEL,
1564 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1565 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dlg), TRUE);
1567 g_free(ptr);
1569 build_form_data(NULL);
1571 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg), TRUE);
1572 response = gtk_dialog_run(GTK_DIALOG(dlg));
1574 if (response == GTK_RESPONSE_OK) {
1575 gchar * filename;
1576 gchar * utf8name;
1577 wchar_t *wfilename;
1579 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg));
1580 utf8name = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
1581 g_free(filename);
1582 wfilename = utf8_to_wcs(utf8name);
1583 g_free(utf8name);
1584 ret = MPDM_S(wfilename);
1585 g_free(wfilename);
1587 gtk_widget_destroy(dlg);
1589 return ret;
1593 static mpdm_t gtk_drv_openfile(mpdm_t a)
1594 /* openfile driver function */
1596 return run_filechooser(a, FALSE);
1600 static mpdm_t gtk_drv_savefile(mpdm_t a)
1601 /* savefile driver function */
1603 return run_filechooser(a, TRUE);
1607 static mpdm_t gtk_drv_update_ui(mpdm_t a)
1609 build_fonts();
1610 build_colors();
1611 build_menu();
1613 redraw();
1615 return NULL;
1619 static mpdm_t gtk_drv_timer(mpdm_t a)
1621 static guint prev = 0;
1622 int msecs = mpdm_ival(mpdm_aget(a, 0));
1623 mpdm_t func = mpdm_aget(a, 1);
1624 mpdm_t r;
1626 /* previously defined one? remove */
1627 if (timer_func != NULL)
1628 gtk_timeout_remove(prev);
1630 /* if msecs and func are set, program timer */
1631 if (msecs > 0 && func != NULL)
1632 prev = gtk_timeout_add(msecs, timer_callback, NULL);
1634 r = mpdm_unref(timer_func);
1635 timer_func = mpdm_ref(func);
1637 return r;
1641 static mpdm_t gtk_drv_busy(mpdm_t a)
1643 int onoff = mpdm_ival(mpdm_aget(a, 0));
1645 gdk_window_set_cursor(window->window,
1646 gdk_cursor_new(onoff ? GDK_WATCH : GDK_LEFT_PTR));
1648 while (gtk_events_pending())
1649 gtk_main_iteration();
1651 return NULL;
1655 static mpdm_t gtk_drv_main_loop(mpdm_t a)
1656 /* main loop */
1658 if (!mp_exit_requested) {
1659 gtk_drv_paint(mp_active(), 0);
1661 gtk_main();
1664 return NULL;
1668 static mpdm_t gtk_drv_shutdown(mpdm_t a)
1669 /* shutdown */
1671 mpdm_t v;
1673 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
1674 mpdm_write_wcs(stdout, mpdm_string(v));
1675 printf("\n");
1678 return NULL;
1682 static void register_functions(void)
1684 mpdm_t drv;
1686 drv = mpdm_hget_s(mp, L"drv");
1687 mpdm_hset_s(drv, L"main_loop", MPDM_X(gtk_drv_main_loop));
1688 mpdm_hset_s(drv, L"shutdown", MPDM_X(gtk_drv_shutdown));
1690 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(gtk_drv_clip_to_sys));
1691 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(gtk_drv_sys_to_clip));
1692 mpdm_hset_s(drv, L"update_ui", MPDM_X(gtk_drv_update_ui));
1693 mpdm_hset_s(drv, L"timer", MPDM_X(gtk_drv_timer));
1694 mpdm_hset_s(drv, L"busy", MPDM_X(gtk_drv_busy));
1696 mpdm_hset_s(drv, L"alert", MPDM_X(gtk_drv_alert));
1697 mpdm_hset_s(drv, L"confirm", MPDM_X(gtk_drv_confirm));
1698 mpdm_hset_s(drv, L"openfile", MPDM_X(gtk_drv_openfile));
1699 mpdm_hset_s(drv, L"savefile", MPDM_X(gtk_drv_savefile));
1700 mpdm_hset_s(drv, L"form", MPDM_X(gtk_drv_form));
1704 static mpdm_t gtk_drv_startup(mpdm_t a)
1705 /* driver initialization */
1707 GtkWidget * vbox;
1708 GtkWidget * hbox;
1709 GdkPixmap * pixmap;
1710 GdkPixmap * mask;
1711 GdkScreen *screen;
1712 mpdm_t v;
1713 int w, h;
1714 GtkTargetEntry targets[] = {
1715 { "text/plain", 0, 0 },
1716 { "text/uri-list", 0, 1 }
1719 register_functions();
1721 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1723 /* get real screen and pick a usable size for the main area */
1724 screen = gtk_window_get_screen(GTK_WINDOW(window));
1725 w = (gdk_screen_get_width(screen) * 3) / 4;
1726 h = (gdk_screen_get_height(screen) * 2) / 3;
1728 g_signal_connect(G_OBJECT(window), "delete_event",
1729 G_CALLBACK(delete_event), NULL);
1731 g_signal_connect(G_OBJECT(window), "destroy",
1732 G_CALLBACK(destroy), NULL);
1734 /* file tabs */
1735 file_tabs = gtk_notebook_new();
1736 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(file_tabs), GTK_POS_TOP);
1737 GTK_WIDGET_UNSET_FLAGS(file_tabs,GTK_CAN_FOCUS);
1738 gtk_notebook_set_scrollable(GTK_NOTEBOOK(file_tabs), 1);
1740 vbox = gtk_vbox_new(FALSE, 2);
1741 gtk_container_add(GTK_CONTAINER(window), vbox);
1743 build_menu();
1744 gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0);
1746 gtk_box_pack_start(GTK_BOX(vbox), file_tabs, FALSE, FALSE, 0);
1748 /* horizontal box holding the text and the scrollbar */
1749 hbox = gtk_hbox_new(FALSE, 2);
1750 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
1752 /* the Minimum Profit area */
1753 area = gtk_drawing_area_new();
1754 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
1755 gtk_widget_set_size_request(GTK_WIDGET(area), w, h);
1756 gtk_widget_set_events(GTK_WIDGET(area), GDK_BUTTON_PRESS_MASK |
1757 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
1758 GDK_LEAVE_NOTIFY_MASK);
1760 gtk_widget_set_double_buffered(area, FALSE);
1762 g_signal_connect(G_OBJECT(area),"configure_event",
1763 G_CALLBACK(configure_event), NULL);
1765 g_signal_connect(G_OBJECT(area),"expose_event",
1766 G_CALLBACK(expose_event), NULL);
1768 g_signal_connect(G_OBJECT(area), "realize",
1769 G_CALLBACK(realize), NULL);
1771 g_signal_connect(G_OBJECT(window),"key_press_event",
1772 G_CALLBACK(key_press_event), NULL);
1774 g_signal_connect(G_OBJECT(window),"key_release_event",
1775 G_CALLBACK(key_release_event), NULL);
1777 g_signal_connect(G_OBJECT(area),"button_press_event",
1778 G_CALLBACK(button_press_event), NULL);
1780 g_signal_connect(G_OBJECT(area),"button_release_event",
1781 G_CALLBACK(button_release_event), NULL);
1783 g_signal_connect(G_OBJECT(area),"motion_notify_event",
1784 G_CALLBACK(motion_notify_event), NULL);
1786 g_signal_connect(G_OBJECT(area), "selection_clear_event",
1787 G_CALLBACK(selection_clear_event), NULL);
1789 g_signal_connect(G_OBJECT(area), "selection_get",
1790 G_CALLBACK(selection_get), NULL);
1792 g_signal_connect(G_OBJECT(area), "selection_received",
1793 G_CALLBACK(selection_received), NULL);
1795 g_signal_connect(G_OBJECT(area), "scroll_event",
1796 G_CALLBACK(scroll_event), NULL);
1798 gtk_drag_dest_set(area, GTK_DEST_DEFAULT_ALL, targets,
1799 sizeof(targets) / sizeof(GtkTargetEntry),
1800 GDK_ACTION_COPY);
1801 g_signal_connect(G_OBJECT(area), "drag_data_received",
1802 G_CALLBACK(drag_data_received), NULL);
1804 gtk_selection_add_target(area, GDK_SELECTION_PRIMARY,
1805 GDK_SELECTION_TYPE_STRING, 1);
1807 g_signal_connect(G_OBJECT(file_tabs),"switch_page",
1808 G_CALLBACK(switch_page), NULL);
1810 /* the scrollbar */
1811 scrollbar = gtk_vscrollbar_new(NULL);
1812 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, FALSE, 0);
1814 g_signal_connect(
1815 G_OBJECT(gtk_range_get_adjustment(GTK_RANGE(scrollbar))),
1816 "value_changed", G_CALLBACK(value_changed), NULL);
1818 /* the status bar */
1819 status = gtk_label_new("mp " VERSION);
1820 gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
1821 gtk_misc_set_alignment(GTK_MISC(status), 0, 0.5);
1822 gtk_label_set_justify(GTK_LABEL(status), GTK_JUSTIFY_LEFT);
1824 gtk_widget_show_all(window);
1826 /* _mpv_font_size--;
1827 mpv_zoom(1);
1829 if (mpv_gtk_maximize)
1830 gtk_window_maximize(GTK_WINDOW(window));
1833 /* set size */
1834 /* if(!mpv_gtk_maximize)
1836 if(_mpv_gtk_xpos >= 0 && _mpv_gtk_ypos >= 0)
1837 gdk_window_move(GTK_WIDGET(window)->window,
1838 _mpv_gtk_xpos, _mpv_gtk_ypos);
1840 if(_mpv_gtk_width > 0 && _mpv_gtk_height > 0)
1842 if(_mpv_gtk_width < 150) _mpv_gtk_width=150;
1843 if(_mpv_gtk_height < 150) _mpv_gtk_height=150;
1845 gtk_window_set_default_size(GTK_WINDOW(window),
1846 _mpv_gtk_width, _mpv_gtk_height);
1848 gdk_window_resize(GTK_WIDGET(window)->window,
1849 _mpv_gtk_width, _mpv_gtk_height);
1853 /* colors */
1854 /* _mpv_create_colors();
1856 fclose(stderr);
1858 mp_log("X11 geometry set to %dx%d+%d+%d\n", _mpv_gtk_width, _mpv_gtk_height,
1859 _mpv_gtk_xpos, _mpv_gtk_ypos);
1862 /* set application icon */
1863 pixmap = gdk_pixmap_create_from_xpm_d(window->window,
1864 &mask, NULL, mp_xpm);
1865 gdk_window_set_icon(window->window, NULL, pixmap, mask);
1867 build_colors();
1869 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
1870 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
1871 maximize = 1;
1873 return NULL;
1877 int gtk_drv_detect(int * argc, char *** argv)
1879 mpdm_t drv;
1880 int n;
1882 for (n = 0; n < *argc; n++) {
1883 if (strcmp(argv[0][n], "-txt") == 0 ||
1884 strcmp(argv[0][n], "-h") == 0)
1885 return 0;
1888 if (!gtk_init_check(argc, argv))
1889 return 0;
1891 drv = mpdm_hget_s(mp, L"drv");
1892 mpdm_hset_s(drv, L"id", MPDM_LS(L"gtk"));
1893 mpdm_hset_s(drv, L"startup", MPDM_X(gtk_drv_startup));
1895 return 1;
1898 #endif /* CONFOPT_GTK */