3 Minimum Profit - Programmer Text Editor
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
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 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
;
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
;
92 static int mouse_down
= 0;
95 static mpdm_t timer_func
= NULL
;
97 /* maximize wanted? */
98 static int maximize
= 0;
102 /** support functions **/
106 static char * wcs_to_utf8(const wchar_t * wptr
)
107 /* converts a wcs to utf-8 */
114 /* do the conversion */
115 ptr
= g_convert((const gchar
*) wptr
, (i
+ 1) * sizeof(wchar_t),
116 "UTF-8", "WCHAR_T", NULL
, &o
, NULL
);
122 static wchar_t * utf8_to_wcs(const char * ptr
)
123 /* converts utf-8 to wcs */
130 /* do the conversion */
131 wptr
= (wchar_t *) g_convert((gchar
*) ptr
, i
+ 1,
132 "WCHAR_T", "UTF-8", NULL
, &o
, NULL
);
138 static char * localize(char * msg
)
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 */
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
);
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 */
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 */
183 const char * font_face
= "Mono";
187 pango_font_description_free(font
);
189 /* get current configuration */
190 if ((c
= mpdm_hget_s(mp
, L
"config")) != NULL
) {
193 if ((v
= mpdm_hget_s(c
, L
"font_size")) != NULL
)
194 font_size
= mpdm_ival(v
);
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
);
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
)
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 */
233 /* gets the color definitions and attribute names */
234 colors
= mpdm_hget_s(mp
, L
"colors");
235 l
= mpdm_keys(colors
);
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)
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)));
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) {
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
);
283 if (mp_exit_requested
)
288 static void build_submenu(GtkWidget
* menu
, mpdm_t labels
)
289 /* build a submenu */
292 GtkWidget
* menu_item
;
294 for (n
= 0; n
< mpdm_size(labels
); n
++) {
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();
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
);
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
;
325 /* gets the current menu */
326 if ((m
= mpdm_hget_s(mp
, L
"menu")) == NULL
)
329 /* if it's the same, do nothing */
330 if (mpdm_cmp(m
, prev_menu
) == 0)
333 /* create a new menu */
334 menu_bar
= gtk_menu_bar_new();
336 for (n
= 0; n
< mpdm_size(m
); n
++) {
341 GtkWidget
* menu_item
;
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
)
351 /* change the & by _ for the mnemonic */
352 for (i
= 0; ptr
[i
]; i
++)
356 /* add the menu and the label */
357 menu
= gtk_menu_new();
358 menu_item
= gtk_menu_item_new_with_mnemonic(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
);
385 static void draw_filetabs(void)
386 /* draws the filetabs */
388 static mpdm_t last
= NULL
;
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
++) {
411 mpdm_t v
= mpdm_aget(names
, n
);
413 if ((ptr
= wcs_to_utf8(v
->data
)) != NULL
) {
414 p
= gtk_label_new(ptr
);
417 f
= gtk_frame_new(NULL
);
420 gtk_notebook_append_page(
421 GTK_NOTEBOOK(file_tabs
), f
, p
);
427 /* store for the next time */
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 */
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
);
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;
473 mp_process_event(MPDM_S(ptr
));
481 static void value_changed(GtkAdjustment
* adj
, gpointer
* data
)
482 /* 'value_changed' handler (scrollbar) */
484 int i
= (int) adj
->value
;
489 /* get current y position */
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 */
497 mpdm_hset_s(txt
, L
"vy", MPDM_I(i
));
503 static void draw_scrollbar(void)
504 /* updates the scrollbar */
506 GtkAdjustment
* adjustment
;
511 /* gets the active document */
512 if ((d
= mp_active()) == NULL
)
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)
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
);
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 */
561 gtk_window_maximize(GTK_WINDOW(window
));
563 /* no gc? create it */
565 gc
= gdk_gc_new(area
->window
);
570 if ((d
= mp_draw(doc
, optimize
)) == NULL
)
575 gr
.width
= area
->allocation
.width
;
576 gr
.height
= font_height
;
578 for (n
= 0; n
< mpdm_size(d
); n
++) {
581 mpdm_t l
= mpdm_aget(d
, n
);
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
) {
599 /* get the attribute and the string */
600 attr
= mpdm_ival(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);
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
,
623 pango_attr_list_insert(pal
, pa
);
627 if (underlines
[attr
]) {
628 pa
= pango_attr_underline_new(TRUE
);
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
);
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
);
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
);
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
);
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 */
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);
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)
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;
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;
783 char c
= event
->keyval
& 0xdf;
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;
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;
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;
874 char c
= event
->keyval
& 0xdf;
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;
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;
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')
947 /* finally process */
949 mp_process_event(MPDM_S(ptr
));
951 /* delete the pending char */
954 if (mp_exit_requested
)
957 if (mp_keypress_throttle(1))
958 gtk_drv_paint(mp_active(), 1);
964 static gint
button_press_event(GtkWidget
* widget
, GdkEventButton
* event
, gpointer data
)
965 /* 'button_press_event' handler (mouse buttons) */
968 wchar_t * ptr
= NULL
;
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;
988 mp_process_event(MPDM_S(ptr
));
996 static gint
button_release_event(GtkWidget
* widget
, GdkEventButton
* event
, gpointer data
)
997 /* 'button_release_event' handle (mouse buttons) */
1005 static gint
motion_notify_event(GtkWidget
*widget
, GdkEventMotion
* event
, gpointer data
)
1006 /* 'motion_notify_event' handler (mouse movement) */
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);
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 */
1046 wstr
= (wchar_t *) g_convert(str
, -1,
1047 "WCHAR_T", "UTF-8", NULL
, NULL
, NULL
);
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 */
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)
1082 memcpy(&o
, event
, sizeof(o
));
1084 update_window_size();
1091 static gint
selection_clear_event(GtkWidget
* widget
,
1092 GdkEventSelection
* event
, gpointer data
)
1093 /* 'selection_clear_event' handler */
1101 static void selection_get(GtkWidget
* widget
,
1102 GtkSelectionData
* sel
, guint info
, guint tm
)
1103 /* 'selection_get' handler */
1106 unsigned char * ptr
;
1112 /* gets the clipboard and joins */
1113 d
= mpdm_hget_s(mp
, L
"clipboard");
1115 if (mpdm_size(d
) == 0)
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
);
1130 static void selection_received(GtkWidget
* widget
,
1131 GtkSelectionData
* sel
, gpointer data
)
1132 /* 'selection_received' handler */
1136 if (sel
->data
!= NULL
) {
1138 wchar_t * wptr
= utf8_to_wcs((char *)sel
->data
);
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;
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
);
1164 static mpdm_t
gtk_drv_sys_to_clip(mpdm_t a
)
1165 /* driver-dependent system to mp clipboard */
1167 if (!got_selection
) {
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
)
1195 /** interface functions **/
1197 static void clicked_ok(GtkWidget
* widget
, gpointer data
)
1198 /* 'clicked_on' signal handler (for gtk_drv_form) */
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"));
1208 /* if there is already a value there, if was
1209 previously set from a callback */
1210 if (mpdm_aget(form_values
, n
) != NULL
)
1213 if (wcscmp(wptr
, L
"text") == 0 ||
1214 wcscmp(wptr
, L
"password") == 0) {
1216 GtkWidget
* gw
= widget
;
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
) {
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)
1240 if (wcscmp(wptr
, L
"checkbox") == 0) {
1241 v
= MPDM_I(gtk_toggle_button_get_active(
1242 GTK_TOGGLE_BUTTON(widget
)));
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
);
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); \
1299 static mpdm_t
gtk_drv_alert(mpdm_t a
)
1300 /* alert driver function */
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
)
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
);
1319 gtk_dialog_run(GTK_DIALOG(dlg
));
1320 gtk_widget_destroy(dlg
);
1326 static mpdm_t
gtk_drv_confirm(mpdm_t a
)
1327 /* confirm driver function */
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
)
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
);
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
)
1357 return MPDM_I(response
);
1361 static mpdm_t
gtk_drv_form(mpdm_t a
)
1362 /* 'form' driver function */
1366 GtkWidget
* content_area
;
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
;
1396 type
= mpdm_string(mpdm_hget_s(w
, L
"type"));
1398 if ((t
= mpdm_hget_s(w
, L
"label")) != NULL
) {
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,
1417 t
= mpdm_hget_s(w
, L
"value");
1419 if (wcscmp(type
, L
"text") == 0) {
1420 GList
* combo_items
= NULL
;
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
) {
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
);
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
);
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
);
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
);
1468 if (wcscmp(type
, L
"list") == 0) {
1470 GtkListStore
* list_store
;
1471 GtkCellRenderer
* renderer
;
1472 GtkTreeViewColumn
* column
;
1477 if ((i
= 450 / mpdm_size(form_args
)) < 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
),
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
,
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
) {
1503 gtk_list_store_append(list_store
, &iter
);
1504 gtk_list_store_set(list_store
, &iter
, 0, ptr
, -1);
1509 /* initial position */
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
);
1535 gtk_widget_destroy(dlg
);
1541 static mpdm_t
run_filechooser(mpdm_t a
, gboolean save
)
1542 /* openfile driver function */
1550 /* 1# arg: prompt */
1551 wptr
= mpdm_string(mpdm_aget(a
, 0));
1553 if ((ptr
= wcs_to_utf8(wptr
)) == NULL
)
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
);
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
);
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
) {
1579 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlg
));
1580 utf8name
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
1582 wfilename
= utf8_to_wcs(utf8name
);
1584 ret
= MPDM_S(wfilename
);
1587 gtk_widget_destroy(dlg
);
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
)
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);
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
);
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();
1655 static mpdm_t
gtk_drv_main_loop(mpdm_t a
)
1658 if (!mp_exit_requested
) {
1659 gtk_drv_paint(mp_active(), 0);
1668 static mpdm_t
gtk_drv_shutdown(mpdm_t a
)
1673 if ((v
= mpdm_hget_s(mp
, L
"exit_message")) != NULL
) {
1674 mpdm_write_wcs(stdout
, mpdm_string(v
));
1682 static void register_functions(void)
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 */
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
);
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
);
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
),
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
);
1811 scrollbar
= gtk_vscrollbar_new(NULL
);
1812 gtk_box_pack_start(GTK_BOX(hbox
), scrollbar
, FALSE
, FALSE
, 0);
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--;
1829 if (mpv_gtk_maximize)
1830 gtk_window_maximize(GTK_WINDOW(window));
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);
1854 /* _mpv_create_colors();
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
);
1869 if ((v
= mpdm_hget_s(mp
, L
"config")) != NULL
&&
1870 mpdm_ival(mpdm_hget_s(v
, L
"maximize")) > 0)
1877 int gtk_drv_detect(int * argc
, char *** argv
)
1882 for (n
= 0; n
< *argc
; n
++) {
1883 if (strcmp(argv
[0][n
], "-txt") == 0 ||
1884 strcmp(argv
[0][n
], "-h") == 0)
1888 if (!gtk_init_check(argc
, argv
))
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
));
1898 #endif /* CONFOPT_GTK */