3 Minimum Profit - Programmer Text Editor
7 Copyright (C) 1991-2011 Angel Ortega <angel@triptico.com>
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 http://www.triptico.com
39 #include <gdk/gdkkeysyms.h>
52 static GtkWidget
*window
= NULL
;
53 static GtkWidget
*file_tabs
= NULL
;
54 static GtkWidget
*area
= NULL
;
55 static GtkWidget
*scrollbar
= NULL
;
56 static GtkWidget
*status
= NULL
;
57 static GtkWidget
*menu_bar
= NULL
;
58 static GtkIMContext
*im
= NULL
;
60 /* character read from the keyboard */
61 static wchar_t im_char
[2];
63 /* font information */
64 static int font_width
= 0;
65 static int font_height
= 0;
66 static PangoFontDescription
*font
= NULL
;
69 static GdkColor
*inks
= NULL
;
70 static GdkColor
*papers
= NULL
;
71 static int *underlines
= NULL
;
73 /* true if the selection is ours */
74 static int got_selection
= 0;
76 /* hack for active waiting for the selection */
77 static int wait_for_selection
= 0;
79 /* global modal status */
80 /* code for the 'normal' attribute */
81 static int normal_attr
= 0;
83 /* mp.drv.form() controls */
85 static GtkWidget
**form_widgets
= NULL
;
86 static mpdm_t form_args
= NULL
;
87 static mpdm_t form_values
= NULL
;
90 static int mouse_down
= 0;
93 static mpdm_t timer_func
= NULL
;
95 /* maximize wanted? */
96 static int maximize
= 0;
100 /** support functions **/
104 static char *wcs_to_utf8(const wchar_t * wptr
)
105 /* converts a wcs to utf-8 */
112 /* do the conversion */
113 ptr
= g_convert((const gchar
*) wptr
, (i
+ 1) * sizeof(wchar_t),
114 "UTF-8", "WCHAR_T", NULL
, &o
, NULL
);
120 static char *v_to_utf8(mpdm_t v
)
126 ptr
= wcs_to_utf8(mpdm_string(v
));
133 static wchar_t *utf8_to_wcs(const char *ptr
)
134 /* converts utf-8 to wcs */
141 /* do the conversion */
142 wptr
= (wchar_t *) g_convert((gchar
*) ptr
, i
+ 1,
143 "WCHAR_T", "UTF-8", NULL
, &o
, NULL
);
149 static void update_window_size(void)
150 /* updates the viewport size in characters */
156 /* get font metrics */
157 pa
= gtk_widget_create_pango_layout(area
, "m");
158 pango_layout_set_font_description(pa
, font
);
159 pango_layout_get_pixel_size(pa
, &font_width
, &font_height
);
162 /* calculate the size in chars */
164 tx
= (area
->allocation
.width
/ font_width
);
165 ty
= (area
->allocation
.height
/ font_height
) + 1;
169 tx
= (gtk_widget_get_allocated_width(area
) / font_width
);
170 ty
= (gtk_widget_get_allocated_height(area
) / font_height
) + 1;
173 /* store the 'window' size */
174 v
= mpdm_hget_s(mp
, L
"window");
175 mpdm_hset_s(v
, L
"tx", MPDM_I(tx
));
176 mpdm_hset_s(v
, L
"ty", MPDM_I(ty
));
178 /* rebuild the pixmap for the double buffer */
182 static void build_fonts(void)
183 /* builds the fonts */
187 const char *font_face
= "Mono";
192 pango_font_description_free(font
);
194 /* get current configuration */
195 if ((c
= mpdm_hget_s(mp
, L
"config")) != NULL
) {
198 if ((v
= mpdm_hget_s(c
, L
"font_size")) != NULL
)
199 font_size
= mpdm_ival(v
);
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
));
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();
221 static void build_color(GdkColor
* gdkcolor
, int rgb
)
225 gdkcolor
->blue
= (rgb
& 0x000000ff) << 8;
226 gdkcolor
->green
= (rgb
& 0x0000ff00);
227 gdkcolor
->red
= (rgb
& 0x00ff0000) >> 8;
230 gdk_colormap_alloc_color(gdk_colormap_get_system(), gdkcolor
, FALSE
,
236 static void build_colors(void)
237 /* builds the colors */
244 /* gets the color definitions and attribute names */
245 colors
= mpdm_hget_s(mp
, L
"colors");
246 l
= mpdm_ref(mpdm_keys(colors
));
249 /* redim the structures */
250 inks
= realloc(inks
, sizeof(GdkColor
) * s
);
251 papers
= realloc(papers
, sizeof(GdkColor
) * s
);
252 underlines
= realloc(underlines
, sizeof(int) * s
);
254 /* loop the colors */
255 for (n
= 0; n
< s
&& (c
= mpdm_aget(l
, n
)) != NULL
; n
++) {
256 mpdm_t d
= mpdm_hget(colors
, c
);
257 mpdm_t v
= mpdm_hget_s(d
, L
"gui");
259 /* store the 'normal' attribute */
260 if (wcscmp(mpdm_string(c
), L
"normal") == 0)
264 mpdm_hset_s(d
, L
"attr", MPDM_I(n
));
266 build_color(&inks
[n
], mpdm_ival(mpdm_aget(v
, 0)));
267 build_color(&papers
[n
], mpdm_ival(mpdm_aget(v
, 1)));
270 v
= mpdm_hget_s(d
, L
"flags");
271 underlines
[n
] = mpdm_seek_s(v
, L
"underline", 1) != -1 ? 1 : 0;
273 if (mpdm_seek_s(v
, L
"reverse", 1) != -1) {
286 /** menu functions **/
288 static void redraw(void);
290 static void menu_item_callback(mpdm_t action
)
291 /* menu click callback */
293 mp_process_action(action
);
296 if (mp_exit_requested
)
301 static void build_submenu(GtkWidget
* menu
, mpdm_t labels
)
302 /* build a submenu */
305 GtkWidget
*menu_item
;
309 for (n
= 0; n
< mpdm_size(labels
); n
++) {
311 mpdm_t v
= mpdm_aget(labels
, n
);
313 /* if the action is a separator... */
314 if (*((wchar_t *) v
->data
) == L
'-')
315 menu_item
= gtk_menu_item_new();
319 ptr
= v_to_utf8(mp_menu_label(v
));
320 menu_item
= gtk_menu_item_new_with_label(ptr
);
325 gtk_menu_append(GTK_MENU(menu
), menu_item
);
328 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_item
);
331 g_signal_connect_swapped(G_OBJECT(menu_item
), "activate",
332 G_CALLBACK(menu_item_callback
), v
);
333 gtk_widget_show(menu_item
);
340 static void build_menu(void)
341 /* builds the menu */
343 static mpdm_t prev_menu
= NULL
;
347 /* gets the current menu */
348 if ((m
= mpdm_hget_s(mp
, L
"menu")) == NULL
)
351 /* if it's the same, do nothing */
352 if (mpdm_cmp(m
, prev_menu
) == 0)
355 /* create a new menu */
356 menu_bar
= gtk_menu_bar_new();
358 for (n
= 0; n
< mpdm_size(m
); n
++) {
363 GtkWidget
*menu_item
;
366 /* get the label and the items */
367 mi
= mpdm_aget(m
, n
);
368 v
= mpdm_aget(mi
, 0);
370 if ((ptr
= v_to_utf8(mpdm_gettext(v
))) == NULL
)
373 /* change the & by _ for the mnemonic */
374 for (i
= 0; ptr
[i
]; i
++)
378 /* add the menu and the label */
379 menu
= gtk_menu_new();
380 menu_item
= gtk_menu_item_new_with_mnemonic(ptr
);
383 gtk_widget_show(menu_item
);
384 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item
), menu
);
387 gtk_menu_bar_append(GTK_MENU_BAR(menu_bar
), menu_item
);
390 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar
), menu_item
);
393 /* now loop the items */
394 build_submenu(menu
, mpdm_aget(mi
, 1));
399 /** main area drawing functions **/
401 static void switch_page(GtkNotebook
* notebook
, gpointer
* page
,
402 gint pg_num
, gpointer data
)
403 /* 'switch_page' handler (filetabs) */
405 /* sets the active one */
406 mpdm_hset_s(mp
, L
"active_i", MPDM_I(pg_num
));
408 gtk_widget_grab_focus(area
);
413 static void draw_filetabs(void)
414 /* draws the filetabs */
416 static mpdm_t last
= NULL
;
420 names
= mpdm_ref(mp_get_doc_names());
422 /* disconnect redraw signal to avoid infinite loops */
423 g_signal_handlers_disconnect_by_func(G_OBJECT(file_tabs
),
424 G_CALLBACK(switch_page
), NULL
);
426 /* is the list different from the previous one? */
427 if (mpdm_cmp(names
, last
) != 0) {
429 /* delete the current tabs */
430 for (n
= 0; n
< mpdm_size(last
); n
++)
431 gtk_notebook_remove_page(GTK_NOTEBOOK(file_tabs
), 0);
433 /* create the new ones */
434 for (n
= 0; n
< mpdm_size(names
); n
++) {
438 mpdm_t v
= mpdm_aget(names
, n
);
440 if ((ptr
= v_to_utf8(v
)) != NULL
) {
441 p
= gtk_label_new(ptr
);
444 f
= gtk_frame_new(NULL
);
447 gtk_notebook_append_page(GTK_NOTEBOOK(file_tabs
), f
, p
);
453 /* store for the next time */
455 last
= mpdm_ref(names
);
460 /* set the active one */
461 gtk_notebook_set_current_page(GTK_NOTEBOOK(file_tabs
),
462 mpdm_ival(mpdm_hget_s(mp
, L
"active_i")));
464 /* reconnect signal */
465 g_signal_connect(G_OBJECT(file_tabs
), "switch_page",
466 G_CALLBACK(switch_page
), NULL
);
468 gtk_widget_grab_focus(area
);
472 static void draw_status(void)
473 /* draws the status line */
477 if ((ptr
= v_to_utf8(mp_build_status_line())) != NULL
) {
478 gtk_label_set_text(GTK_LABEL(status
), ptr
);
484 static gint
scroll_event(GtkWidget
* widget
, GdkEventScroll
* event
)
485 /* 'scroll_event' handler (mouse wheel) */
489 switch (event
->direction
) {
491 ptr
= L
"mouse-wheel-up";
493 case GDK_SCROLL_DOWN
:
494 ptr
= L
"mouse-wheel-down";
496 case GDK_SCROLL_LEFT
:
497 ptr
= L
"mouse-wheel-left";
499 case GDK_SCROLL_RIGHT
:
500 ptr
= L
"mouse-wheel-right";
505 mp_process_event(MPDM_S(ptr
));
513 static void value_changed(GtkAdjustment
* adj
, gpointer
* data
)
514 /* 'value_changed' handler (scrollbar) */
516 int i
= (int) gtk_adjustment_get_value(adj
);
521 /* get current y position */
523 txt
= mpdm_hget_s(doc
, L
"txt");
524 y
= mpdm_ival(mpdm_hget_s(txt
, L
"y"));
526 /* if it's different, set and redraw */
529 mpdm_hset_s(txt
, L
"vy", MPDM_I(i
));
535 static void draw_scrollbar(void)
536 /* updates the scrollbar */
538 GtkAdjustment
*adjustment
;
543 /* gets the active document */
544 if ((d
= mp_active()) == NULL
)
547 /* get the coordinates */
548 v
= mpdm_hget_s(d
, L
"txt");
549 pos
= mpdm_ival(mpdm_hget_s(v
, L
"vy"));
550 max
= mpdm_size(mpdm_hget_s(v
, L
"lines"));
552 v
= mpdm_hget_s(mp
, L
"window");
553 size
= mpdm_ival(mpdm_hget_s(v
, L
"ty"));
555 adjustment
= gtk_range_get_adjustment(GTK_RANGE(scrollbar
));
557 /* disconnect to avoid infinite loops */
558 g_signal_handlers_disconnect_by_func(G_OBJECT(adjustment
),
559 G_CALLBACK(value_changed
), NULL
);
561 gtk_adjustment_set_step_increment(adjustment
, (gdouble
) 1);
562 gtk_adjustment_set_upper(adjustment
, (gdouble
) max
);
563 gtk_adjustment_set_page_size(adjustment
, (gdouble
) size
);
564 gtk_adjustment_set_page_increment(adjustment
, (gdouble
) size
);
565 gtk_adjustment_set_value(adjustment
, (gdouble
) pos
);
567 gtk_range_set_adjustment(GTK_RANGE(scrollbar
), adjustment
);
569 gtk_adjustment_changed(adjustment
);
570 gtk_adjustment_value_changed(adjustment
);
573 g_signal_connect(G_OBJECT
574 (gtk_range_get_adjustment(GTK_RANGE(scrollbar
))),
575 "value_changed", G_CALLBACK(value_changed
), NULL
);
579 static void gtk_drv_paint(mpdm_t doc
, int optimize
)
580 /* GTK document draw function */
588 gtk_window_maximize(GTK_WINDOW(window
));
590 /* no gc? create it */
594 if ((d
= mp_draw(doc
, optimize
)) == NULL
)
603 gr
.width
= area
->allocation
.width
;
606 gr
.width
= gtk_widget_get_allocated_width(area
);
609 gr
.height
= font_height
;
611 cr
= gdk_cairo_create(gtk_widget_get_window(area
));
612 for (n
= 0; n
< mpdm_size(d
); n
++) {
615 mpdm_t l
= mpdm_aget(d
, n
);
622 /* create the pango stuff */
623 pl
= gtk_widget_create_pango_layout(area
, NULL
);
624 pango_layout_set_font_description(pl
, font
);
625 pal
= pango_attr_list_new();
627 for (m
= u
= p
= 0; m
< mpdm_size(l
); m
++, u
= p
) {
633 /* get the attribute and the string */
634 attr
= mpdm_ival(mpdm_aget(l
, m
++));
637 /* convert the string to utf8 */
640 /* add to the full line */
641 str
= mpdm_poke(str
, &p
, ptr
, strlen(ptr
), 1);
645 /* create the background if it's
646 different from the default */
647 if (papers
[attr
].red
!= papers
[normal_attr
].red
||
648 papers
[attr
].green
!= papers
[normal_attr
].green
||
649 papers
[attr
].blue
!= papers
[normal_attr
].blue
) {
650 pa
= pango_attr_background_new(papers
[attr
].red
,
657 pango_attr_list_insert(pal
, pa
);
661 if (underlines
[attr
]) {
662 pa
= pango_attr_underline_new(TRUE
);
667 pango_attr_list_insert(pal
, pa
);
670 /* foreground color */
671 pa
= pango_attr_foreground_new(inks
[attr
].red
,
678 pango_attr_list_insert(pal
, pa
);
681 /* store the attributes */
682 pango_layout_set_attributes(pl
, pal
);
683 pango_attr_list_unref(pal
);
685 /* store and free the text */
686 pango_layout_set_text(pl
, str
, p
);
689 /* draw the background */
690 gdk_cairo_set_source_color(cr
, &papers
[normal_attr
]);
691 cairo_rectangle(cr
, 0, n
* font_height
, gr
.width
, gr
.height
);
695 cairo_move_to(cr
, 2, n
* font_height
);
696 pango_cairo_show_layout(cr
, pl
);
710 static void redraw(void)
712 if (mpdm_size(mpdm_hget_s(mp
, L
"docs")))
713 gtk_drv_paint(mp_active(), 0);
717 static gint
delete_event(GtkWidget
* w
, GdkEvent
* e
, gpointer data
)
718 /* 'delete_event' handler */
720 mp_process_event(MPDM_LS(L
"close-window"));
722 return mp_exit_requested
? FALSE
: TRUE
;
726 static void destroy(GtkWidget
* w
, gpointer data
)
727 /* 'destroy' handler */
733 static gint
key_release_event(GtkWidget
* widget
, GdkEventKey
* event
,
735 /* 'key_release_event' handler */
737 if (mp_keypress_throttle(0))
738 gtk_drv_paint(mp_active(), 0);
745 #define MP_KEY_Up GDK_Up
746 #define MP_KEY_Down GDK_Down
747 #define MP_KEY_Left GDK_Left
748 #define MP_KEY_Right GDK_Right
749 #define MP_KEY_Prior GDK_Prior
750 #define MP_KEY_Next GDK_Next
751 #define MP_KEY_Home GDK_Home
752 #define MP_KEY_End GDK_End
753 #define MP_KEY_space GDK_space
754 #define MP_KEY_KP_Add GDK_KP_Add
755 #define MP_KEY_KP_Subtract GDK_KP_Subtract
756 #define MP_KEY_KP_Multiply GDK_KP_Multiply
757 #define MP_KEY_KP_Divide GDK_KP_Divide
758 #define MP_KEY_F1 GDK_F1
759 #define MP_KEY_F2 GDK_F2
760 #define MP_KEY_F3 GDK_F3
761 #define MP_KEY_F4 GDK_F4
762 #define MP_KEY_F5 GDK_F5
763 #define MP_KEY_F6 GDK_F6
764 #define MP_KEY_F7 GDK_F7
765 #define MP_KEY_F8 GDK_F8
766 #define MP_KEY_F9 GDK_F9
767 #define MP_KEY_F10 GDK_F10
768 #define MP_KEY_F11 GDK_F11
769 #define MP_KEY_F12 GDK_F12
770 #define MP_KEY_KP_Enter GDK_KP_Enter
771 #define MP_KEY_Return GDK_Return
772 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
773 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
774 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
775 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
776 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
777 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
778 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
779 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
780 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
781 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
782 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
783 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
784 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
785 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
786 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
787 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
788 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
789 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
790 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
791 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
792 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
793 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
794 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
795 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
796 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
797 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
798 #define MP_KEY_Up GDK_Up
799 #define MP_KEY_Down GDK_Down
800 #define MP_KEY_Left GDK_Left
801 #define MP_KEY_Right GDK_Right
802 #define MP_KEY_Prior GDK_Prior
803 #define MP_KEY_Next GDK_Next
804 #define MP_KEY_Home GDK_Home
805 #define MP_KEY_End GDK_End
806 #define MP_KEY_space GDK_space
807 #define MP_KEY_KP_Add GDK_KP_Add
808 #define MP_KEY_KP_Subtract GDK_KP_Subtract
809 #define MP_KEY_KP_Multiply GDK_KP_Multiply
810 #define MP_KEY_KP_Divide GDK_KP_Divide
811 #define MP_KEY_F1 GDK_F1
812 #define MP_KEY_F2 GDK_F2
813 #define MP_KEY_F3 GDK_F3
814 #define MP_KEY_F4 GDK_F4
815 #define MP_KEY_F5 GDK_F5
816 #define MP_KEY_F6 GDK_F6
817 #define MP_KEY_F7 GDK_F7
818 #define MP_KEY_F8 GDK_F8
819 #define MP_KEY_F9 GDK_F9
820 #define MP_KEY_F10 GDK_F10
821 #define MP_KEY_F11 GDK_F11
822 #define MP_KEY_F12 GDK_F12
823 #define MP_KEY_KP_Enter GDK_KP_Enter
824 #define MP_KEY_Return GDK_Return
825 #define MP_KEY_Cyrillic_ve GDK_Cyrillic_ve
826 #define MP_KEY_Cyrillic_a GDK_Cyrillic_a
827 #define MP_KEY_Cyrillic_tse GDK_Cyrillic_tse
828 #define MP_KEY_Cyrillic_de GDK_Cyrillic_de
829 #define MP_KEY_Cyrillic_ie GDK_Cyrillic_ie
830 #define MP_KEY_Cyrillic_ef GDK_Cyrillic_ef
831 #define MP_KEY_Cyrillic_ghe GDK_Cyrillic_ghe
832 #define MP_KEY_Cyrillic_i GDK_Cyrillic_i
833 #define MP_KEY_Cyrillic_shorti GDK_Cyrillic_shorti
834 #define MP_KEY_Cyrillic_ka GDK_Cyrillic_ka
835 #define MP_KEY_Cyrillic_el GDK_Cyrillic_el
836 #define MP_KEY_Cyrillic_em GDK_Cyrillic_em
837 #define MP_KEY_Cyrillic_en GDK_Cyrillic_en
838 #define MP_KEY_Cyrillic_o GDK_Cyrillic_o
839 #define MP_KEY_Cyrillic_pe GDK_Cyrillic_pe
840 #define MP_KEY_Cyrillic_ya GDK_Cyrillic_ya
841 #define MP_KEY_Cyrillic_er GDK_Cyrillic_er
842 #define MP_KEY_Cyrillic_es GDK_Cyrillic_es
843 #define MP_KEY_Cyrillic_te GDK_Cyrillic_te
844 #define MP_KEY_Cyrillic_softsign GDK_Cyrillic_softsign
845 #define MP_KEY_Cyrillic_yeru GDK_Cyrillic_yeru
846 #define MP_KEY_Cyrillic_ze GDK_Cyrillic_ze
847 #define MP_KEY_Cyrillic_sha GDK_Cyrillic_sha
848 #define MP_KEY_Cyrillic_e GDK_Cyrillic_e
849 #define MP_KEY_Cyrillic_shcha GDK_Cyrillic_shcha
850 #define MP_KEY_Cyrillic_che GDK_Cyrillic_che
851 #define MP_KEY_Up GDK_Up
852 #define MP_KEY_Down GDK_Down
853 #define MP_KEY_Left GDK_Left
854 #define MP_KEY_Right GDK_Right
855 #define MP_KEY_Prior GDK_Prior
856 #define MP_KEY_Next GDK_Next
857 #define MP_KEY_Home GDK_Home
858 #define MP_KEY_End GDK_End
859 #define MP_KEY_space GDK_space
860 #define MP_KEY_KP_Add GDK_KP_Add
861 #define MP_KEY_KP_Subtract GDK_KP_Subtract
862 #define MP_KEY_KP_Multiply GDK_KP_Multiply
863 #define MP_KEY_KP_Divide GDK_KP_Divide
864 #define MP_KEY_F1 GDK_F1
865 #define MP_KEY_F2 GDK_F2
866 #define MP_KEY_F3 GDK_F3
867 #define MP_KEY_F4 GDK_F4
868 #define MP_KEY_F5 GDK_F5
869 #define MP_KEY_F6 GDK_F6
870 #define MP_KEY_F7 GDK_F7
871 #define MP_KEY_F8 GDK_F8
872 #define MP_KEY_F9 GDK_F9
873 #define MP_KEY_F10 GDK_F10
874 #define MP_KEY_F11 GDK_F11
875 #define MP_KEY_F12 GDK_F12
876 #define MP_KEY_Insert GDK_Insert
877 #define MP_KEY_BackSpace GDK_BackSpace
878 #define MP_KEY_Delete GDK_Delete
879 #define MP_KEY_KP_Enter GDK_KP_Enter
880 #define MP_KEY_Return GDK_Return
881 #define MP_KEY_Tab GDK_Tab
882 #define MP_KEY_ISO_Left_Tab GDK_ISO_Left_Tab
883 #define MP_KEY_Escape GDK_Escape
884 #endif /* CONFOPT_GTK == 2 */
887 #define MP_KEY_Up GDK_KEY_Up
888 #define MP_KEY_Down GDK_KEY_Down
889 #define MP_KEY_Left GDK_KEY_Left
890 #define MP_KEY_Right GDK_KEY_Right
891 #define MP_KEY_Prior GDK_KEY_Prior
892 #define MP_KEY_Next GDK_KEY_Next
893 #define MP_KEY_Home GDK_KEY_Home
894 #define MP_KEY_End GDK_KEY_End
895 #define MP_KEY_space GDK_KEY_space
896 #define MP_KEY_KP_Add GDK_KEY_KP_Add
897 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
898 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
899 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
900 #define MP_KEY_F1 GDK_KEY_F1
901 #define MP_KEY_F2 GDK_KEY_F2
902 #define MP_KEY_F3 GDK_KEY_F3
903 #define MP_KEY_F4 GDK_KEY_F4
904 #define MP_KEY_F5 GDK_KEY_F5
905 #define MP_KEY_F6 GDK_KEY_F6
906 #define MP_KEY_F7 GDK_KEY_F7
907 #define MP_KEY_F8 GDK_KEY_F8
908 #define MP_KEY_F9 GDK_KEY_F9
909 #define MP_KEY_F10 GDK_KEY_F10
910 #define MP_KEY_F11 GDK_KEY_F11
911 #define MP_KEY_F12 GDK_KEY_F12
912 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
913 #define MP_KEY_Return GDK_KEY_Return
914 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
915 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
916 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
917 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
918 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
919 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
920 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
921 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
922 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
923 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
924 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
925 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
926 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
927 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
928 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
929 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
930 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
931 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
932 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
933 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
934 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
935 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
936 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
937 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
938 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
939 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
940 #define MP_KEY_Up GDK_KEY_Up
941 #define MP_KEY_Down GDK_KEY_Down
942 #define MP_KEY_Left GDK_KEY_Left
943 #define MP_KEY_Right GDK_KEY_Right
944 #define MP_KEY_Prior GDK_KEY_Prior
945 #define MP_KEY_Next GDK_KEY_Next
946 #define MP_KEY_Home GDK_KEY_Home
947 #define MP_KEY_End GDK_KEY_End
948 #define MP_KEY_space GDK_KEY_space
949 #define MP_KEY_KP_Add GDK_KEY_KP_Add
950 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
951 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
952 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
953 #define MP_KEY_F1 GDK_KEY_F1
954 #define MP_KEY_F2 GDK_KEY_F2
955 #define MP_KEY_F3 GDK_KEY_F3
956 #define MP_KEY_F4 GDK_KEY_F4
957 #define MP_KEY_F5 GDK_KEY_F5
958 #define MP_KEY_F6 GDK_KEY_F6
959 #define MP_KEY_F7 GDK_KEY_F7
960 #define MP_KEY_F8 GDK_KEY_F8
961 #define MP_KEY_F9 GDK_KEY_F9
962 #define MP_KEY_F10 GDK_KEY_F10
963 #define MP_KEY_F11 GDK_KEY_F11
964 #define MP_KEY_F12 GDK_KEY_F12
965 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
966 #define MP_KEY_Return GDK_KEY_Return
967 #define MP_KEY_Cyrillic_ve GDK_KEY_Cyrillic_ve
968 #define MP_KEY_Cyrillic_a GDK_KEY_Cyrillic_a
969 #define MP_KEY_Cyrillic_tse GDK_KEY_Cyrillic_tse
970 #define MP_KEY_Cyrillic_de GDK_KEY_Cyrillic_de
971 #define MP_KEY_Cyrillic_ie GDK_KEY_Cyrillic_ie
972 #define MP_KEY_Cyrillic_ef GDK_KEY_Cyrillic_ef
973 #define MP_KEY_Cyrillic_ghe GDK_KEY_Cyrillic_ghe
974 #define MP_KEY_Cyrillic_i GDK_KEY_Cyrillic_i
975 #define MP_KEY_Cyrillic_shorti GDK_KEY_Cyrillic_shorti
976 #define MP_KEY_Cyrillic_ka GDK_KEY_Cyrillic_ka
977 #define MP_KEY_Cyrillic_el GDK_KEY_Cyrillic_el
978 #define MP_KEY_Cyrillic_em GDK_KEY_Cyrillic_em
979 #define MP_KEY_Cyrillic_en GDK_KEY_Cyrillic_en
980 #define MP_KEY_Cyrillic_o GDK_KEY_Cyrillic_o
981 #define MP_KEY_Cyrillic_pe GDK_KEY_Cyrillic_pe
982 #define MP_KEY_Cyrillic_ya GDK_KEY_Cyrillic_ya
983 #define MP_KEY_Cyrillic_er GDK_KEY_Cyrillic_er
984 #define MP_KEY_Cyrillic_es GDK_KEY_Cyrillic_es
985 #define MP_KEY_Cyrillic_te GDK_KEY_Cyrillic_te
986 #define MP_KEY_Cyrillic_softsign GDK_KEY_Cyrillic_softsign
987 #define MP_KEY_Cyrillic_yeru GDK_KEY_Cyrillic_yeru
988 #define MP_KEY_Cyrillic_ze GDK_KEY_Cyrillic_ze
989 #define MP_KEY_Cyrillic_sha GDK_KEY_Cyrillic_sha
990 #define MP_KEY_Cyrillic_e GDK_KEY_Cyrillic_e
991 #define MP_KEY_Cyrillic_shcha GDK_KEY_Cyrillic_shcha
992 #define MP_KEY_Cyrillic_che GDK_KEY_Cyrillic_che
993 #define MP_KEY_Up GDK_KEY_Up
994 #define MP_KEY_Down GDK_KEY_Down
995 #define MP_KEY_Left GDK_KEY_Left
996 #define MP_KEY_Right GDK_KEY_Right
997 #define MP_KEY_Prior GDK_KEY_Prior
998 #define MP_KEY_Next GDK_KEY_Next
999 #define MP_KEY_Home GDK_KEY_Home
1000 #define MP_KEY_End GDK_KEY_End
1001 #define MP_KEY_space GDK_KEY_space
1002 #define MP_KEY_KP_Add GDK_KEY_KP_Add
1003 #define MP_KEY_KP_Subtract GDK_KEY_KP_Subtract
1004 #define MP_KEY_KP_Multiply GDK_KEY_KP_Multiply
1005 #define MP_KEY_KP_Divide GDK_KEY_KP_Divide
1006 #define MP_KEY_F1 GDK_KEY_F1
1007 #define MP_KEY_F2 GDK_KEY_F2
1008 #define MP_KEY_F3 GDK_KEY_F3
1009 #define MP_KEY_F4 GDK_KEY_F4
1010 #define MP_KEY_F5 GDK_KEY_F5
1011 #define MP_KEY_F6 GDK_KEY_F6
1012 #define MP_KEY_F7 GDK_KEY_F7
1013 #define MP_KEY_F8 GDK_KEY_F8
1014 #define MP_KEY_F9 GDK_KEY_F9
1015 #define MP_KEY_F10 GDK_KEY_F10
1016 #define MP_KEY_F11 GDK_KEY_F11
1017 #define MP_KEY_F12 GDK_KEY_F12
1018 #define MP_KEY_Insert GDK_KEY_Insert
1019 #define MP_KEY_BackSpace GDK_KEY_BackSpace
1020 #define MP_KEY_Delete GDK_KEY_Delete
1021 #define MP_KEY_KP_Enter GDK_KEY_KP_Enter
1022 #define MP_KEY_Return GDK_KEY_Return
1023 #define MP_KEY_Tab GDK_KEY_Tab
1024 #define MP_KEY_ISO_Left_Tab GDK_KEY_ISO_Left_Tab
1025 #define MP_KEY_Escape GDK_KEY_Escape
1026 #endif /* CONFOPT_GTK == 3 */
1028 static gint
key_press_event(GtkWidget
* widget
, GdkEventKey
* event
,
1030 /* 'key_press_event' handler */
1032 wchar_t *ptr
= NULL
;
1034 gtk_im_context_filter_keypress(im
, event
);
1036 /* set mp.shift_pressed */
1037 if (event
->state
& (GDK_SHIFT_MASK
))
1038 mpdm_hset_s(mp
, L
"shift_pressed", MPDM_I(1));
1040 /* reserve alt for menu mnemonics */
1041 /* if (GDK_MOD1_MASK & event->state)
1044 if (event
->state
& (GDK_CONTROL_MASK
)) {
1045 switch (event
->keyval
) {
1047 ptr
= L
"ctrl-cursor-up";
1050 ptr
= L
"ctrl-cursor-down";
1053 ptr
= L
"ctrl-cursor-left";
1056 ptr
= L
"ctrl-cursor-right";
1059 ptr
= L
"ctrl-page-up";
1062 ptr
= L
"ctrl-page-down";
1071 ptr
= L
"ctrl-space";
1074 ptr
= L
"ctrl-kp-plus";
1076 case MP_KEY_KP_Subtract
:
1077 ptr
= L
"ctrl-kp-minus";
1079 case MP_KEY_KP_Multiply
:
1080 ptr
= L
"ctrl-kp-multiply";
1082 case MP_KEY_KP_Divide
:
1083 ptr
= L
"ctrl-kp-divide";
1121 case MP_KEY_KP_Enter
:
1123 ptr
= L
"ctrl-enter";
1125 case MP_KEY_Cyrillic_ve
:
1128 case MP_KEY_Cyrillic_a
:
1131 case MP_KEY_Cyrillic_tse
:
1134 case MP_KEY_Cyrillic_de
:
1137 case MP_KEY_Cyrillic_ie
:
1140 case MP_KEY_Cyrillic_ef
:
1143 case MP_KEY_Cyrillic_ghe
:
1146 case MP_KEY_Cyrillic_i
:
1149 case MP_KEY_Cyrillic_shorti
:
1152 case MP_KEY_Cyrillic_ka
:
1155 case MP_KEY_Cyrillic_el
:
1158 case MP_KEY_Cyrillic_em
:
1161 case MP_KEY_Cyrillic_en
:
1164 case MP_KEY_Cyrillic_o
:
1167 case MP_KEY_Cyrillic_pe
:
1170 case MP_KEY_Cyrillic_ya
:
1173 case MP_KEY_Cyrillic_er
:
1176 case MP_KEY_Cyrillic_es
:
1179 case MP_KEY_Cyrillic_te
:
1182 case MP_KEY_Cyrillic_softsign
:
1185 case MP_KEY_Cyrillic_yeru
:
1188 case MP_KEY_Cyrillic_ze
:
1191 case MP_KEY_Cyrillic_sha
:
1194 case MP_KEY_Cyrillic_e
:
1197 case MP_KEY_Cyrillic_shcha
:
1200 case MP_KEY_Cyrillic_che
:
1206 char c
= event
->keyval
& 0xdf;
1291 if (event
->state
& (GDK_MOD1_MASK
)) {
1292 switch (event
->keyval
) {
1294 ptr
= L
"alt-cursor-up";
1297 ptr
= L
"alt-cursor-down";
1300 ptr
= L
"alt-cursor-left";
1303 ptr
= L
"alt-cursor-right";
1306 ptr
= L
"alt-page-up";
1309 ptr
= L
"alt-page-down";
1321 ptr
= L
"alt-kp-plus";
1323 case MP_KEY_KP_Subtract
:
1324 ptr
= L
"alt-kp-minus";
1326 case MP_KEY_KP_Multiply
:
1327 ptr
= L
"alt-kp-multiply";
1329 case MP_KEY_KP_Divide
:
1330 ptr
= L
"alt-kp-divide";
1368 case MP_KEY_KP_Enter
:
1372 case MP_KEY_Cyrillic_ve
:
1375 case MP_KEY_Cyrillic_a
:
1378 case MP_KEY_Cyrillic_tse
:
1381 case MP_KEY_Cyrillic_de
:
1384 case MP_KEY_Cyrillic_ie
:
1387 case MP_KEY_Cyrillic_ef
:
1390 case MP_KEY_Cyrillic_ghe
:
1393 case MP_KEY_Cyrillic_i
:
1396 case MP_KEY_Cyrillic_shorti
:
1399 case MP_KEY_Cyrillic_ka
:
1402 case MP_KEY_Cyrillic_el
:
1405 case MP_KEY_Cyrillic_em
:
1408 case MP_KEY_Cyrillic_en
:
1411 case MP_KEY_Cyrillic_o
:
1414 case MP_KEY_Cyrillic_pe
:
1417 case MP_KEY_Cyrillic_ya
:
1420 case MP_KEY_Cyrillic_er
:
1423 case MP_KEY_Cyrillic_es
:
1426 case MP_KEY_Cyrillic_te
:
1429 case MP_KEY_Cyrillic_softsign
:
1432 case MP_KEY_Cyrillic_yeru
:
1435 case MP_KEY_Cyrillic_ze
:
1438 case MP_KEY_Cyrillic_sha
:
1441 case MP_KEY_Cyrillic_e
:
1444 case MP_KEY_Cyrillic_shcha
:
1447 case MP_KEY_Cyrillic_che
:
1453 char c
= event
->keyval
& 0xdf;
1538 switch (event
->keyval
) {
1543 ptr
= L
"cursor-down";
1546 ptr
= L
"cursor-left";
1549 ptr
= L
"cursor-right";
1569 case MP_KEY_KP_Subtract
:
1572 case MP_KEY_KP_Multiply
:
1573 ptr
= L
"kp-multiply";
1575 case MP_KEY_KP_Divide
:
1617 case MP_KEY_BackSpace
:
1623 case MP_KEY_KP_Enter
:
1630 case MP_KEY_ISO_Left_Tab
:
1639 /* if there is a pending char in im_char, use it */
1640 if (ptr
== NULL
&& im_char
[0] != L
'\0')
1643 /* finally process */
1645 mp_process_event(MPDM_S(ptr
));
1647 /* delete the pending char */
1650 if (mp_exit_requested
)
1653 if (mp_keypress_throttle(1))
1654 gtk_drv_paint(mp_active(), 1);
1660 static gint
button_press_event(GtkWidget
* widget
, GdkEventButton
* event
,
1662 /* 'button_press_event' handler (mouse buttons) */
1665 wchar_t *ptr
= NULL
;
1669 /* mouse instant positioning */
1670 x
= ((int) event
->x
) / font_width
;
1671 y
= ((int) event
->y
) / font_height
;
1673 mpdm_hset_s(mp
, L
"mouse_x", MPDM_I(x
));
1674 mpdm_hset_s(mp
, L
"mouse_y", MPDM_I(y
));
1676 switch (event
->button
) {
1678 ptr
= L
"mouse-left-button";
1681 ptr
= L
"mouse-middle-button";
1684 ptr
= L
"mouse-right-button";
1687 ptr
= L
"mouse-wheel-up";
1690 ptr
= L
"mouse-wheel-down";
1695 mp_process_event(MPDM_S(ptr
));
1703 static gint
button_release_event(GtkWidget
* widget
,
1704 GdkEventButton
* event
, gpointer data
)
1705 /* 'button_release_event' handle (mouse buttons) */
1713 static gint
motion_notify_event(GtkWidget
* widget
, GdkEventMotion
* event
,
1715 /* 'motion_notify_event' handler (mouse movement) */
1723 /* mouse dragging */
1724 x
= ((int) event
->x
) / font_width
;
1725 y
= ((int) event
->y
) / font_height
;
1727 if (ox
!= x
&& oy
!= y
) {
1728 mpdm_hset_s(mp
, L
"mouse_to_x", MPDM_I(x
));
1729 mpdm_hset_s(mp
, L
"mouse_to_y", MPDM_I(y
));
1731 mp_process_event(MPDM_LS(L
"mouse-drag"));
1732 gtk_drv_paint(mp_active(), 1);
1740 static void drag_data_received(GtkWidget
* widget
, GdkDragContext
* dc
,
1741 gint x
, gint y
, GtkSelectionData
* data
,
1742 guint info
, guint time
)
1743 /* 'drag_data_received' handler */
1745 printf("drag_data_received (unsupported)\n");
1749 /** clipboard functions **/
1751 static void commit(GtkIMContext
* i
, char *str
, gpointer u
)
1752 /* 'commit' handler */
1756 wstr
= (wchar_t *) g_convert(str
, -1,
1757 "WCHAR_T", "UTF-8", NULL
, NULL
, NULL
);
1766 static void realize(GtkWidget
* widget
)
1767 /* 'realize' handler */
1769 im
= gtk_im_multicontext_new();
1770 g_signal_connect(im
, "commit", G_CALLBACK(commit
), NULL
);
1771 gtk_im_context_set_client_window(im
, gtk_widget_get_window(widget
));
1775 static gint
expose_event(GtkWidget
* widget
, cairo_t
*event
)
1776 /* 'expose_event' handler */
1784 static gint
configure_event(GtkWidget
* widget
, GdkEventConfigure
* event
)
1785 /* 'configure_event' handler */
1787 static GdkEventConfigure o
;
1789 if (memcmp(&o
, event
, sizeof(o
)) == 0)
1792 memcpy(&o
, event
, sizeof(o
));
1794 update_window_size();
1801 static gint
selection_clear_event(GtkWidget
* widget
,
1802 GdkEventSelection
* event
, gpointer data
)
1803 /* 'selection_clear_event' handler */
1811 static void selection_get(GtkWidget
* widget
,
1812 GtkSelectionData
* sel
, guint info
, guint tm
)
1813 /* 'selection_get' handler */
1822 /* gets the clipboard and joins */
1823 d
= mpdm_hget_s(mp
, L
"clipboard");
1825 if (mpdm_size(d
) == 0)
1828 d
= mpdm_ref(mpdm_join_s(d
, L
"\n"));
1830 /* convert to current locale */
1831 ptr
= (unsigned char *) mpdm_wcstombs(d
->data
, &s
);
1833 /* pastes into primary selection */
1834 gtk_selection_data_set(sel
, GDK_SELECTION_TYPE_STRING
, 8, ptr
,
1843 static void selection_received(GtkWidget
* widget
,
1844 GtkSelectionData
* sel
, gpointer data
)
1845 /* 'selection_received' handler */
1849 if (gtk_selection_data_get_data(sel
) != NULL
) {
1851 wchar_t *wptr
= utf8_to_wcs((char *) gtk_selection_data_get_data(sel
));
1855 /* split and set as the clipboard */
1856 mpdm_hset_s(mp
, L
"clipboard", mpdm_split_s(d
, L
"\n"));
1857 mpdm_hset_s(mp
, L
"clipboard_vertical", MPDM_I(0));
1859 /* wait no more for the selection */
1860 wait_for_selection
= 0;
1863 wait_for_selection
= -1;
1867 static mpdm_t
gtk_drv_clip_to_sys(mpdm_t a
, mpdm_t ctxt
)
1868 /* driver-dependent mp to system clipboard */
1870 got_selection
= gtk_selection_owner_set(area
,
1871 GDK_SELECTION_PRIMARY
,
1878 static mpdm_t
gtk_drv_sys_to_clip(mpdm_t a
, mpdm_t ctxt
)
1879 /* driver-dependent system to mp clipboard */
1881 if (!got_selection
) {
1883 char *formats
[] = { "UTF8_STRING", "STRING", NULL
};
1885 for (n
= 0; formats
[n
] != NULL
; n
++) {
1887 /* triggers a selection capture */
1888 if (gtk_selection_convert(area
, GDK_SELECTION_PRIMARY
,
1889 gdk_atom_intern(formats
[n
], FALSE
),
1890 GDK_CURRENT_TIME
)) {
1892 /* processes the pending events
1893 (i.e., the 'selection_received' handler) */
1894 wait_for_selection
= 1;
1896 while (wait_for_selection
== 1)
1897 gtk_main_iteration();
1899 if (!wait_for_selection
)
1909 /** interface functions **/
1911 static void clicked_ok(GtkWidget
* widget
, gpointer data
)
1912 /* 'clicked_on' signal handler (for gtk_drv_form) */
1916 for (n
= 0; n
< mpdm_size(form_args
); n
++) {
1917 GtkWidget
*widget
= form_widgets
[n
];
1918 mpdm_t w
= mpdm_aget(form_args
, n
);
1919 wchar_t *wptr
= mpdm_string(mpdm_hget_s(w
, L
"type"));
1922 /* if there is already a value there, if was
1923 previously set from a callback */
1924 if (mpdm_aget(form_values
, n
) != NULL
)
1927 if (wcscmp(wptr
, L
"text") == 0 || wcscmp(wptr
, L
"password") == 0) {
1929 GtkWidget
*gw
= widget
;
1932 if (wcscmp(wptr
, L
"text") == 0)
1933 #if CONFOPT_GTK == 2
1934 gw
= GTK_COMBO(widget
)->entry
;
1936 #if CONFOPT_GTK == 3
1937 gw
= gtk_bin_get_child(GTK_BIN(widget
));
1941 gtk_editable_get_chars(GTK_EDITABLE(gw
), 0, -1)) != NULL
1942 && (wptr
= utf8_to_wcs(ptr
)) != NULL
) {
1950 /* if it has history, fill it */
1951 if ((h
= mpdm_hget_s(w
, L
"history")) != NULL
&&
1952 v
!= NULL
&& mpdm_cmp_s(v
, L
"") != 0) {
1953 h
= mp_get_history(h
);
1955 if (mpdm_cmp(v
, mpdm_aget(h
, -1)) != 0)
1962 if (wcscmp(wptr
, L
"checkbox") == 0) {
1963 v
= MPDM_I(gtk_toggle_button_get_active
1964 (GTK_TOGGLE_BUTTON(widget
)));
1967 if (wcscmp(wptr
, L
"list") == 0) {
1968 GtkWidget
*list
= gtk_bin_get_child(GTK_BIN(widget
));
1969 GtkTreeSelection
*selection
=
1970 gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
1972 gtk_tree_selection_get_selected_rows(selection
, NULL
);
1973 GtkTreePath
*path
= selected
->data
;
1975 v
= MPDM_I(gtk_tree_path_get_indices(path
)[0]);
1976 gtk_tree_path_free(path
);
1977 g_list_free(selected
);
1981 mpdm_aset(form_values
, v
, n
);
1986 static gint
timer_callback(gpointer data
)
1988 mpdm_void(mpdm_exec(timer_func
, NULL
, NULL
));
1995 static void build_form_data(mpdm_t widget_list
)
1996 /* builds the necessary information for a list of widgets */
1998 mpdm_unref(form_args
);
1999 form_args
= mpdm_ref(widget_list
);
2001 mpdm_unref(form_values
);
2002 form_values
= widget_list
== NULL
? NULL
:
2003 mpdm_ref(MPDM_A(mpdm_size(form_args
)));
2005 /* resize the widget array */
2006 form_widgets
= (GtkWidget
**) realloc(form_widgets
,
2007 mpdm_size(form_args
) *
2008 sizeof(GtkWidget
*));
2012 /** dialog functions **/
2014 #define DIALOG_BUTTON(l,f) do { GtkWidget * btn; \
2015 ptr = localize(l); btn = gtk_button_new_with_label(ptr); \
2016 g_signal_connect_swapped(G_OBJECT(btn), "clicked", \
2017 G_CALLBACK(f), G_OBJECT(dlg)); \
2018 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), \
2019 btn, TRUE, TRUE, 0); \
2023 static mpdm_t
gtk_drv_alert(mpdm_t a
, mpdm_t ctxt
)
2024 /* alert driver function */
2029 build_form_data(NULL
);
2031 /* 1# arg: prompt */
2032 if ((ptr
= v_to_utf8(mpdm_aget(a
, 0))) == NULL
)
2035 dlg
= gtk_message_dialog_new(GTK_WINDOW(window
), GTK_DIALOG_MODAL
,
2036 GTK_MESSAGE_WARNING
, GTK_BUTTONS_OK
, "%s", ptr
);
2037 gtk_window_set_title(GTK_WINDOW(dlg
), "mp " VERSION
);
2040 gtk_dialog_run(GTK_DIALOG(dlg
));
2041 gtk_widget_destroy(dlg
);
2047 static mpdm_t
gtk_drv_confirm(mpdm_t a
, mpdm_t ctxt
)
2048 /* confirm driver function */
2054 build_form_data(NULL
);
2056 /* 1# arg: prompt */
2057 if ((ptr
= v_to_utf8(mpdm_aget(a
, 0))) == NULL
)
2060 dlg
= gtk_message_dialog_new(GTK_WINDOW(window
), GTK_DIALOG_MODAL
,
2061 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_NONE
,
2063 gtk_window_set_title(GTK_WINDOW(dlg
), "mp " VERSION
);
2066 gtk_dialog_add_button(GTK_DIALOG(dlg
), GTK_STOCK_YES
, 1);
2067 gtk_dialog_add_button(GTK_DIALOG(dlg
), GTK_STOCK_NO
, 2);
2068 gtk_dialog_add_button(GTK_DIALOG(dlg
), GTK_STOCK_CANCEL
, 0);
2070 response
= gtk_dialog_run(GTK_DIALOG(dlg
));
2071 gtk_widget_destroy(dlg
);
2073 if (response
== GTK_RESPONSE_DELETE_EVENT
)
2076 return MPDM_I(response
);
2080 static mpdm_t
gtk_drv_form(mpdm_t a
, mpdm_t ctxt
)
2081 /* 'form' driver function */
2085 GtkWidget
*content_area
;
2089 /* first argument: list of widgets */
2090 build_form_data(mpdm_aget(a
, 0));
2092 dlg
= gtk_dialog_new_with_buttons("mp " VERSION
, GTK_WINDOW(window
),
2095 GTK_RESPONSE_CANCEL
, GTK_STOCK_OK
,
2096 GTK_RESPONSE_OK
, NULL
);
2097 gtk_dialog_set_default_response(GTK_DIALOG(dlg
), GTK_RESPONSE_OK
);
2098 gtk_container_set_border_width(GTK_CONTAINER(dlg
), 5);
2100 content_area
= gtk_dialog_get_content_area(GTK_DIALOG(dlg
));
2101 gtk_box_set_spacing(GTK_BOX(content_area
), 2);
2103 table
= gtk_table_new(mpdm_size(a
), 2, FALSE
);
2104 gtk_container_set_border_width(GTK_CONTAINER(table
), 5);
2105 gtk_table_set_col_spacings(GTK_TABLE(table
), 12);
2106 gtk_table_set_row_spacings(GTK_TABLE(table
), 6);
2108 for (n
= 0; n
< mpdm_size(form_args
); n
++) {
2109 mpdm_t w
= mpdm_aget(form_args
, n
);
2110 GtkWidget
*widget
= NULL
;
2116 type
= mpdm_string(mpdm_hget_s(w
, L
"type"));
2118 if ((t
= mpdm_hget_s(w
, L
"label")) != NULL
) {
2121 if ((ptr
= v_to_utf8(mpdm_gettext(t
))) != NULL
) {
2122 label
= gtk_label_new(ptr
);
2123 gtk_misc_set_alignment(GTK_MISC(label
), 0, .5);
2125 gtk_table_attach_defaults(GTK_TABLE(table
),
2126 label
, 0, wcscmp(type
,
2128 0 ? 2 : 1, n
, n
+ 1);
2136 t
= mpdm_hget_s(w
, L
"value");
2138 if (wcscmp(type
, L
"text") == 0) {
2139 GList
*combo_items
= NULL
;
2142 #if CONFOPT_GTK == 2
2143 widget
= gtk_combo_new();
2144 gtk_combo_set_use_arrows_always(GTK_COMBO(widget
), TRUE
);
2145 gtk_combo_set_case_sensitive(GTK_COMBO(widget
), TRUE
);
2146 gtk_entry_set_activates_default(GTK_ENTRY
2147 (GTK_COMBO(widget
)->entry
),
2150 #if CONFOPT_GTK == 3
2151 widget
= gtk_combo_box_text_new_with_entry();
2152 gtk_entry_set_activates_default(GTK_ENTRY
2153 (gtk_bin_get_child(GTK_BIN(widget
))),
2157 gtk_widget_set_size_request(widget
, 300, -1);
2159 if ((h
= mpdm_hget_s(w
, L
"history")) != NULL
) {
2162 /* has history; fill it */
2163 h
= mp_get_history(h
);
2165 for (i
= 0; i
< mpdm_size(h
); i
++) {
2166 ptr
= v_to_utf8(mpdm_aget(h
, i
));
2168 combo_items
= g_list_prepend(combo_items
, ptr
);
2170 #if CONFOPT_GTK == 3
2171 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget
), ptr
);
2179 combo_items
= g_list_prepend(combo_items
, ptr
);
2180 #if CONFOPT_GTK == 3
2181 gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(widget
), ptr
);
2185 #if CONFOPT_GTK == 2
2186 gtk_combo_set_popdown_strings(GTK_COMBO(widget
), combo_items
);
2188 g_list_free(combo_items
);
2191 if (wcscmp(type
, L
"password") == 0) {
2192 widget
= gtk_entry_new();
2193 gtk_widget_set_size_request(widget
, 300, -1);
2194 gtk_entry_set_visibility(GTK_ENTRY(widget
), FALSE
);
2197 if (wcscmp(type
, L
"checkbox") == 0) {
2198 widget
= gtk_check_button_new();
2200 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget
),
2201 mpdm_ival(t
) ? TRUE
: FALSE
);
2204 if (wcscmp(type
, L
"list") == 0) {
2206 GtkListStore
*list_store
;
2207 GtkCellRenderer
*renderer
;
2208 GtkTreeViewColumn
*column
;
2213 if ((i
= 450 / mpdm_size(form_args
)) < 100)
2216 widget
= gtk_scrolled_window_new(NULL
, NULL
);
2217 gtk_widget_set_size_request(widget
, 500, i
);
2218 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget
),
2220 GTK_POLICY_AUTOMATIC
);
2221 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW
2222 (widget
), GTK_SHADOW_IN
);
2224 list_store
= gtk_list_store_new(1, G_TYPE_STRING
);
2226 gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store
));
2227 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list
), FALSE
);
2228 renderer
= gtk_cell_renderer_text_new();
2229 column
= gtk_tree_view_column_new_with_attributes("", renderer
,
2232 gtk_tree_view_append_column(GTK_TREE_VIEW(list
), column
);
2233 gtk_container_add(GTK_CONTAINER(widget
), list
);
2235 l
= mpdm_hget_s(w
, L
"list");
2237 for (i
= 0; i
< mpdm_size(l
); i
++) {
2238 if ((ptr
= v_to_utf8(mpdm_aget(l
, i
))) != NULL
) {
2240 gtk_list_store_append(list_store
, &iter
);
2241 gtk_list_store_set(list_store
, &iter
, 0, ptr
, -1);
2246 /* initial position */
2249 path
= gtk_tree_path_new_from_indices(i
, -1);
2250 gtk_tree_view_set_cursor(GTK_TREE_VIEW(list
), path
, NULL
,
2252 gtk_tree_path_free(path
);
2254 g_signal_connect_swapped(G_OBJECT(list
), "row-activated",
2256 (gtk_window_activate_default
), dlg
);
2259 if (widget
!= NULL
) {
2260 form_widgets
[n
] = widget
;
2261 gtk_table_attach_defaults(GTK_TABLE(table
),
2262 widget
, col
, 2, n
, n
+ 1);
2266 gtk_widget_show_all(table
);
2268 gtk_box_pack_start(GTK_BOX(content_area
), table
, TRUE
, TRUE
, 0);
2270 if (gtk_dialog_run(GTK_DIALOG(dlg
)) == GTK_RESPONSE_OK
) {
2271 clicked_ok(NULL
, NULL
);
2274 gtk_widget_destroy(dlg
);
2280 static mpdm_t
run_filechooser(mpdm_t a
, gboolean save
)
2281 /* openfile driver function */
2288 /* 1# arg: prompt */
2289 if ((ptr
= v_to_utf8(mpdm_aget(a
, 0))) == NULL
)
2293 dlg
= gtk_file_chooser_dialog_new(ptr
, GTK_WINDOW(window
),
2294 GTK_FILE_CHOOSER_ACTION_OPEN
,
2296 GTK_RESPONSE_CANCEL
,
2297 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
2301 dlg
= gtk_file_chooser_dialog_new(ptr
, GTK_WINDOW(window
),
2302 GTK_FILE_CHOOSER_ACTION_SAVE
,
2304 GTK_STOCK_CANCEL
, GTK_STOCK_OK
,
2305 GTK_RESPONSE_OK
, NULL
);
2306 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER
2311 build_form_data(NULL
);
2313 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dlg
), TRUE
);
2314 response
= gtk_dialog_run(GTK_DIALOG(dlg
));
2316 if (response
== GTK_RESPONSE_OK
) {
2321 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg
));
2322 utf8name
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
2324 wfilename
= utf8_to_wcs(utf8name
);
2326 ret
= MPDM_S(wfilename
);
2329 gtk_widget_destroy(dlg
);
2335 static mpdm_t
gtk_drv_openfile(mpdm_t a
, mpdm_t ctxt
)
2336 /* openfile driver function */
2338 return run_filechooser(a
, FALSE
);
2342 static mpdm_t
gtk_drv_savefile(mpdm_t a
, mpdm_t ctxt
)
2343 /* savefile driver function */
2345 return run_filechooser(a
, TRUE
);
2349 static mpdm_t
gtk_drv_update_ui(mpdm_t a
, mpdm_t ctxt
)
2361 static mpdm_t
gtk_drv_timer(mpdm_t a
, mpdm_t ctxt
)
2363 static guint prev
= 0;
2364 int msecs
= mpdm_ival(mpdm_aget(a
, 0));
2365 mpdm_t func
= mpdm_aget(a
, 1);
2367 /* previously defined one? remove */
2368 if (timer_func
!= NULL
)
2369 g_source_remove(prev
);
2371 /* if msecs and func are set, program timer */
2372 if (msecs
> 0 && func
!= NULL
)
2373 prev
= g_timeout_add(msecs
, timer_callback
, NULL
);
2376 mpdm_unref(timer_func
);
2383 static mpdm_t
gtk_drv_busy(mpdm_t a
, mpdm_t ctxt
)
2385 int onoff
= mpdm_ival(mpdm_aget(a
, 0));
2387 gdk_window_set_cursor(gtk_widget_get_window(window
),
2388 gdk_cursor_new(onoff
? GDK_WATCH
:
2391 while (gtk_events_pending())
2392 gtk_main_iteration();
2398 static mpdm_t
gtk_drv_main_loop(mpdm_t a
, mpdm_t ctxt
)
2401 if (!mp_exit_requested
) {
2402 gtk_drv_paint(mp_active(), 0);
2411 static mpdm_t
gtk_drv_shutdown(mpdm_t a
, mpdm_t ctxt
)
2416 if ((v
= mpdm_hget_s(mp
, L
"exit_message")) != NULL
) {
2417 mpdm_write_wcs(stdout
, mpdm_string(v
));
2425 static void register_functions(void)
2429 drv
= mpdm_hget_s(mp
, L
"drv");
2430 mpdm_hset_s(drv
, L
"main_loop", MPDM_X(gtk_drv_main_loop
));
2431 mpdm_hset_s(drv
, L
"shutdown", MPDM_X(gtk_drv_shutdown
));
2432 mpdm_hset_s(drv
, L
"clip_to_sys", MPDM_X(gtk_drv_clip_to_sys
));
2433 mpdm_hset_s(drv
, L
"sys_to_clip", MPDM_X(gtk_drv_sys_to_clip
));
2434 mpdm_hset_s(drv
, L
"update_ui", MPDM_X(gtk_drv_update_ui
));
2435 mpdm_hset_s(drv
, L
"timer", MPDM_X(gtk_drv_timer
));
2436 mpdm_hset_s(drv
, L
"busy", MPDM_X(gtk_drv_busy
));
2437 mpdm_hset_s(drv
, L
"alert", MPDM_X(gtk_drv_alert
));
2438 mpdm_hset_s(drv
, L
"confirm", MPDM_X(gtk_drv_confirm
));
2439 mpdm_hset_s(drv
, L
"openfile", MPDM_X(gtk_drv_openfile
));
2440 mpdm_hset_s(drv
, L
"savefile", MPDM_X(gtk_drv_savefile
));
2441 mpdm_hset_s(drv
, L
"form", MPDM_X(gtk_drv_form
));
2445 static mpdm_t
gtk_drv_startup(mpdm_t a
, mpdm_t ctxt
)
2446 /* driver initialization */
2450 #if CONFOPT_GTK == 2
2454 #if CONFOPT_GTK == 3
2460 GtkTargetEntry targets
[] = {
2461 {"text/plain", 0, 0},
2462 {"text/uri-list", 0, 1}
2465 register_functions();
2467 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
2469 gtk_window_set_title(GTK_WINDOW(window
), "mp " VERSION
);
2471 /* get real screen and pick a usable size for the main area */
2472 screen
= gtk_window_get_screen(GTK_WINDOW(window
));
2473 if (gdk_screen_get_n_monitors(screen
) > 1) {
2474 GdkRectangle monitor_one_size
;
2475 gdk_screen_get_monitor_geometry(screen
, 0, &monitor_one_size
);
2477 w
= (monitor_one_size
.width
* 3) / 4;
2478 h
= (monitor_one_size
.height
* 2) / 3;
2480 w
= (gdk_screen_get_width(screen
) * 3) / 4;
2481 h
= (gdk_screen_get_height(screen
) * 2) / 3;
2484 g_signal_connect(G_OBJECT(window
), "delete_event",
2485 G_CALLBACK(delete_event
), NULL
);
2487 g_signal_connect(G_OBJECT(window
), "destroy",
2488 G_CALLBACK(destroy
), NULL
);
2491 file_tabs
= gtk_notebook_new();
2492 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(file_tabs
), GTK_POS_TOP
);
2494 #if CONFOPT_GTK == 2
2495 GTK_WIDGET_UNSET_FLAGS(file_tabs
, GTK_CAN_FOCUS
);
2497 #if CONFOPT_GTK == 3
2498 gtk_widget_set_can_focus(file_tabs
, FALSE
);
2500 gtk_notebook_set_scrollable(GTK_NOTEBOOK(file_tabs
), 1);
2502 vbox
= gtk_vbox_new(FALSE
, 2);
2503 gtk_container_add(GTK_CONTAINER(window
), vbox
);
2507 hbox
= gtk_hbox_new(FALSE
, 0);
2508 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
2509 gtk_box_pack_start(GTK_BOX(hbox
), menu_bar
, FALSE
, FALSE
, 0);
2510 gtk_box_pack_start(GTK_BOX(hbox
), file_tabs
, TRUE
, TRUE
, 0);
2512 gtk_notebook_popup_enable(GTK_NOTEBOOK(file_tabs
));
2514 /* horizontal box holding the text and the scrollbar */
2515 hbox
= gtk_hbox_new(FALSE
, 2);
2516 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, TRUE
, TRUE
, 0);
2518 /* the Minimum Profit area */
2519 area
= gtk_drawing_area_new();
2520 gtk_box_pack_start(GTK_BOX(hbox
), area
, TRUE
, TRUE
, 0);
2521 gtk_widget_set_size_request(GTK_WIDGET(area
), w
, h
);
2522 gtk_widget_set_events(GTK_WIDGET(area
), GDK_BUTTON_PRESS_MASK
|
2523 GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
2524 | GDK_LEAVE_NOTIFY_MASK
);
2526 gtk_widget_set_double_buffered(area
, FALSE
);
2528 g_signal_connect(G_OBJECT(area
), "configure_event",
2529 G_CALLBACK(configure_event
), NULL
);
2531 #if CONFOPT_GTK == 2
2532 g_signal_connect(G_OBJECT(area
), "expose_event",
2534 #if CONFOPT_GTK == 3
2535 g_signal_connect(G_OBJECT(area
), "draw",
2537 G_CALLBACK(expose_event
), NULL
);
2539 g_signal_connect(G_OBJECT(area
), "realize", G_CALLBACK(realize
), NULL
);
2541 g_signal_connect(G_OBJECT(window
), "key_press_event",
2542 G_CALLBACK(key_press_event
), NULL
);
2544 g_signal_connect(G_OBJECT(window
), "key_release_event",
2545 G_CALLBACK(key_release_event
), NULL
);
2547 g_signal_connect(G_OBJECT(area
), "button_press_event",
2548 G_CALLBACK(button_press_event
), NULL
);
2550 g_signal_connect(G_OBJECT(area
), "button_release_event",
2551 G_CALLBACK(button_release_event
), NULL
);
2553 g_signal_connect(G_OBJECT(area
), "motion_notify_event",
2554 G_CALLBACK(motion_notify_event
), NULL
);
2556 g_signal_connect(G_OBJECT(area
), "selection_clear_event",
2557 G_CALLBACK(selection_clear_event
), NULL
);
2559 g_signal_connect(G_OBJECT(area
), "selection_get",
2560 G_CALLBACK(selection_get
), NULL
);
2562 g_signal_connect(G_OBJECT(area
), "selection_received",
2563 G_CALLBACK(selection_received
), NULL
);
2565 g_signal_connect(G_OBJECT(area
), "scroll_event",
2566 G_CALLBACK(scroll_event
), NULL
);
2568 gtk_drag_dest_set(area
, GTK_DEST_DEFAULT_ALL
, targets
,
2569 sizeof(targets
) / sizeof(GtkTargetEntry
),
2571 g_signal_connect(G_OBJECT(area
), "drag_data_received",
2572 G_CALLBACK(drag_data_received
), NULL
);
2574 gtk_selection_add_target(area
, GDK_SELECTION_PRIMARY
,
2575 GDK_SELECTION_TYPE_STRING
, 1);
2577 g_signal_connect(G_OBJECT(file_tabs
), "switch_page",
2578 G_CALLBACK(switch_page
), NULL
);
2581 scrollbar
= gtk_vscrollbar_new(NULL
);
2582 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, FALSE
, 0);
2584 g_signal_connect(G_OBJECT
2585 (gtk_range_get_adjustment(GTK_RANGE(scrollbar
))),
2586 "value_changed", G_CALLBACK(value_changed
), NULL
);
2588 /* the status bar */
2589 status
= gtk_label_new("mp " VERSION
);
2590 gtk_box_pack_start(GTK_BOX(vbox
), status
, FALSE
, FALSE
, 0);
2591 gtk_misc_set_alignment(GTK_MISC(status
), 0, 0.5);
2592 gtk_label_set_justify(GTK_LABEL(status
), GTK_JUSTIFY_LEFT
);
2594 gtk_widget_show_all(window
);
2596 /* set application icon */
2597 #if CONFOPT_GTK == 2
2598 pixmap
= gdk_pixmap_create_from_xpm_d(window
->window
,
2599 &mask
, NULL
, mp_xpm
);
2600 gdk_window_set_icon(window
->window
, NULL
, pixmap
, mask
);
2602 #if CONFOPT_GTK == 3
2603 pixmap
= gdk_pixbuf_new_from_xpm_data((const char **)mp_xpm
);
2604 gtk_window_set_icon(GTK_WINDOW(window
), pixmap
);
2609 if ((v
= mpdm_hget_s(mp
, L
"config")) != NULL
&&
2610 mpdm_ival(mpdm_hget_s(v
, L
"maximize")) > 0)
2616 int gtk_drv_detect(int *argc
, char ***argv
)
2621 for (n
= 0; n
< *argc
; n
++) {
2622 if (strcmp(argv
[0][n
], "-txt") == 0 ||
2623 strcmp(argv
[0][n
], "-h") == 0)
2627 if (!gtk_init_check(argc
, argv
))
2630 drv
= mpdm_hget_s(mp
, L
"drv");
2632 #if CONFOPT_GTK == 3
2633 mpdm_hset_s(drv
, L
"id", MPDM_LS(L
"gtk3"));
2635 mpdm_hset_s(drv
, L
"id", MPDM_LS(L
"gtk2"));
2638 mpdm_hset_s(drv
, L
"startup", MPDM_X(gtk_drv_startup
));
2643 #endif /* CONFOPT_GTK */