xref: /vim-8.2.3635/src/gui_photon.c (revision ed37d9b3)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *			Photon GUI support by Julian Kinraid
5  *
6  * Do ":help uganda"  in Vim to read copying and usage conditions.
7  * Do ":help credits" in Vim to see a list of people who contributed.
8  *
9  *
10  * Clipboard support is in os_qnx.c
11  * PhAttach() is called in os_qnx.c:qnx_init()
12  */
13 
14 #include "vim.h"
15 
16 // cproto fails on missing include files
17 #ifndef PROTO
18 # ifdef FEAT_TOOLBAR
19 #  include <photon/PxImage.h>
20 # endif
21 #endif
22 
23 #if !defined(__QNX__)
24 // Used when generating prototypes.
25 # define PgColor_t	int
26 # define PhEvent_t	int
27 # define PhPoint_t	int
28 # define PtWidget_t	int
29 # define Pg_BLACK	0
30 # define PtCallbackF_t	int
31 # define PtCallbackInfo_t int
32 # define PhTile_t	int
33 # define PtWidget_t	int
34 # define PhImage_t	int
35 #endif
36 
37 #define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a[0]))
38 #define RGB(r, g, b) PgRGB(r, g, b)
39 
40 #define EVENT_BUFFER_SIZE sizeof(PhEvent_t) + 1000
41 
42 // Some defines for gui_mch_mousehide()
43 #define MOUSE_HIDE		TRUE
44 #define MOUSE_SHOW		FALSE
45 
46 // Optional support for using a PtPanelGroup widget, needs work
47 #undef USE_PANEL_GROUP
48 
49 #ifdef USE_PANEL_GROUP
50 static char	*empty_title = "    ";
51 static char	**panel_titles = NULL;
52 static ushort_t	num_panels = 0;
53 static short pg_margin_left, pg_margin_right, pg_margin_top, pg_margin_bottom;
54 #endif
55 
56 #define GUI_PH_MARGIN		4	// Size of the bevel
57 
58 #define GUI_PH_MOUSE_TYPE		Ph_CURSOR_INSERT
59 static PgColor_t gui_ph_mouse_color =	Pg_BLACK;
60 
61 static PhPoint_t    gui_ph_raw_offset;
62 static PtWidget_t   *gui_ph_timer_cursor;   // handle cursor blinking
63 static PtWidget_t   *gui_ph_timer_timeout;  // used in gui_mch_wait_for_chars
64 static short	    is_timeout;		    // Has the timeout occurred?
65 
66 /*
67  * This is set inside the mouse callback for a right mouse
68  * button click, and used for the popup menus
69  */
70 static PhPoint_t    abs_mouse;
71 
72 // Try and avoid redraws while a resize is in progress
73 static int is_ignore_draw = FALSE;
74 
75 // Used for converting to/from utf-8 and other charsets
76 static struct PxTransCtrl *charset_translate;
77 
78 /*
79  * Cursor blink functions.
80  *
81  * This is a simple state machine:
82  * BLINK_NONE	not blinking at all
83  * BLINK_OFF	blinking, cursor is not shown
84  * BLINK_ON	blinking, cursor is shown
85  */
86 static enum {
87     BLINK_NONE,
88     BLINK_OFF,
89     BLINK_ON
90 } blink_state = BLINK_NONE;
91 
92 static long_u	blink_waittime	= 700;
93 static long_u	blink_ontime	= 400;
94 static long_u	blink_offtime	= 250;
95 
96 static struct
97 {
98     int	    key_sym;
99     char_u  vim_code0;
100     char_u  vim_code1;
101 } special_keys[] =
102 {
103     {Pk_Up,	    'k', 'u'},
104     {Pk_Down,	    'k', 'd'},
105     {Pk_Left,	    'k', 'l'},
106     {Pk_Right,	    'k', 'r'},
107 
108     {Pk_F1,	    'k', '1'},
109     {Pk_F2,	    'k', '2'},
110     {Pk_F3,	    'k', '3'},
111     {Pk_F4,	    'k', '4'},
112     {Pk_F5,	    'k', '5'},
113     {Pk_F6,	    'k', '6'},
114     {Pk_F7,	    'k', '7'},
115     {Pk_F8,	    'k', '8'},
116     {Pk_F9,	    'k', '9'},
117     {Pk_F10,	    'k', ';'},
118 
119     {Pk_F11,	    'F', '1'},
120     {Pk_F12,	    'F', '2'},
121     {Pk_F13,	    'F', '3'},
122     {Pk_F14,	    'F', '4'},
123     {Pk_F15,	    'F', '5'},
124     {Pk_F16,	    'F', '6'},
125     {Pk_F17,	    'F', '7'},
126     {Pk_F18,	    'F', '8'},
127     {Pk_F19,	    'F', '9'},
128     {Pk_F20,	    'F', 'A'},
129 
130     {Pk_F21,	    'F', 'B'},
131     {Pk_F22,	    'F', 'C'},
132     {Pk_F23,	    'F', 'D'},
133     {Pk_F24,	    'F', 'E'},
134     {Pk_F25,	    'F', 'F'},
135     {Pk_F26,	    'F', 'G'},
136     {Pk_F27,	    'F', 'H'},
137     {Pk_F28,	    'F', 'I'},
138     {Pk_F29,	    'F', 'J'},
139 
140     {Pk_F30,	    'F', 'K'},
141     {Pk_F31,	    'F', 'L'},
142     {Pk_F32,	    'F', 'M'},
143     {Pk_F33,	    'F', 'N'},
144     {Pk_F34,	    'F', 'O'},
145     {Pk_F35,	    'F', 'P'},
146 
147     {Pk_Help,	    '%', '1'},
148     {Pk_BackSpace,  'k', 'b'},
149     {Pk_Insert,	    'k', 'I'},
150     {Pk_Delete,	    'k', 'D'},
151     {Pk_Home,	    'k', 'h'},
152     {Pk_End,	    '@', '7'},
153     {Pk_Prior,	    'k', 'P'},
154     {Pk_Next,	    'k', 'N'},
155     {Pk_Print,	    '%', '9'},
156 
157     {Pk_KP_Add,	    'K', '6'},
158     {Pk_KP_Subtract,'K', '7'},
159     {Pk_KP_Divide,  'K', '8'},
160     {Pk_KP_Multiply,'K', '9'},
161     {Pk_KP_Enter,   'K', 'A'},
162 
163     {Pk_KP_0,	    KS_EXTRA, KE_KINS}, // Insert
164     {Pk_KP_Decimal, KS_EXTRA, KE_KDEL}, // Delete
165 
166     {Pk_KP_4,	    'k', 'l'}, // Left
167     {Pk_KP_6,	    'k', 'r'}, // Right
168     {Pk_KP_8,	    'k', 'u'}, // Up
169     {Pk_KP_2,	    'k', 'd'}, // Down
170 
171     {Pk_KP_7,	    'K', '1'}, // Home
172     {Pk_KP_1,	    'K', '4'}, // End
173 
174     {Pk_KP_9,	    'K', '3'}, // Page Up
175     {Pk_KP_3,	    'K', '5'}, // Page Down
176 
177     {Pk_KP_5,	    '&', '8'}, // Undo
178 
179     // Keys that we want to be able to use any modifier with:
180     {Pk_Return,	    CAR,  NUL},
181     {Pk_space,	    ' ', NUL},
182     {Pk_Tab,	    TAB, NUL},
183     {Pk_Escape,	    ESC, NUL},
184     {NL,	    NL,	 NUL},
185     {CAR,	    CAR,  NUL},
186 
187     // End of list marker:
188     {0,		0, 0}
189 };
190 
191 
192 ////////////////////////////////////////////////////////////////////////////
193 
194 static PtCallbackF_t gui_ph_handle_timer_cursor;
195 static PtCallbackF_t gui_ph_handle_timer_timeout;
196 
197 static PtCallbackF_t gui_ph_handle_window_cb;
198 
199 static PtCallbackF_t gui_ph_handle_scrollbar;
200 static PtCallbackF_t gui_ph_handle_keyboard;
201 static PtCallbackF_t gui_ph_handle_mouse;
202 static PtCallbackF_t gui_ph_handle_pulldown_menu;
203 static PtCallbackF_t gui_ph_handle_menu;
204 static PtCallbackF_t gui_ph_handle_focus;	// focus change of text area
205 
206 static PtCallbackF_t gui_ph_handle_menu_resize;
207 
208 // When a menu is unrealized, give focus back to vimTextArea
209 static PtCallbackF_t gui_ph_handle_menu_unrealized;
210 
211 #ifdef USE_PANEL_GROUP
212 static void gui_ph_get_panelgroup_margins(short*, short*, short*, short*);
213 #endif
214 
215 static void gui_ph_draw_start(void);
216 static void gui_ph_draw_end(void);
217 
218 // Set the text for the balloon
219 static PtWidget_t * gui_ph_show_tooltip(PtWidget_t *window,
220 			     PtWidget_t *widget,
221 			     int position,
222 			     char *text,
223 			     char *font,
224 			     PgColor_t fill_color,
225 			     PgColor_t text_color);
226 
227 ////////////////////////////////////////////////////////////////////////////
228 
229 static PtWidget_t * gui_ph_show_tooltip(PtWidget_t *window,
230 			     PtWidget_t *widget,
231 			     int position,
232 			     char *text,
233 			     char *font,
234 			     PgColor_t fill_color,
235 			     PgColor_t text_color)
236 {
237     PtArg_t arg;
238     vimmenu_T *menu;
239     char_u  *tooltip;
240 
241     PtSetArg(&arg, Pt_ARG_POINTER, &menu, 0);
242     PtGetResources(widget, 1, &arg);
243 
244     // Override the text and position
245 
246     tooltip = text;
247     if (menu != NULL)
248     {
249 	int index = MENU_INDEX_TIP;
250 	if (menu->strings[ index ] != NULL)
251 	    tooltip = menu->strings[ index ];
252     }
253 
254     return PtInflateBalloon(
255 	    window,
256 	    widget,
257 	    // Don't put the balloon at the bottom,
258 	    // it gets drawn over by gfx done in the PtRaw
259 	    Pt_BALLOON_TOP,
260 	    tooltip,
261 	    font,
262 	    fill_color,
263 	    text_color);
264 }
265 
266     static void
267 gui_ph_resize_container(void)
268 {
269     PhArea_t area;
270 
271     PtWidgetArea(gui.vimWindow, &area);
272     PtWidgetPos (gui.vimContainer, &area.pos);
273 
274     PtSetResource(gui.vimContainer, Pt_ARG_AREA, &area, 0);
275 }
276 
277     static int
278 gui_ph_handle_menu_resize(
279 	PtWidget_t *widget,
280 	void *other,
281 	PtCallbackInfo_t *info)
282 {
283     PtContainerCallback_t *sizes = info->cbdata;
284     PtWidget_t		*container;
285     PhPoint_t		below_menu;
286     int_u		height;
287 
288     height = sizes->new_dim.h;
289 
290     // Because vim treats the toolbar and menubar separately,
291     // and here they're lumped together into a PtToolbarGroup,
292     // we only need either menu_height or toolbar_height set at once
293     if (gui.menu_is_active)
294     {
295 	gui.menu_height = height;
296 	gui.toolbar_height = 0;
297     }
298 #ifdef FEAT_TOOLBAR
299     else
300 	gui.toolbar_height = height;
301 #endif
302 
303     below_menu.x = 0;
304     below_menu.y = height;
305 
306 #ifdef USE_PANEL_GROUP
307     container = gui.vimPanelGroup;
308 #else
309     container = gui.vimContainer;
310 #endif
311 
312     PtSetResource(container, Pt_ARG_POS, &below_menu, 0);
313 
314     gui_ph_resize_container();
315 
316 #ifdef USE_PANEL_GROUP
317     gui_ph_get_panelgroup_margins(
318 	    &pg_margin_top, &pg_margin_bottom,
319 	    &pg_margin_left, &pg_margin_right);
320 #endif
321     return Pt_CONTINUE;
322 }
323 
324 /*
325  * Pt_ARG_TIMER_REPEAT isn't used because the on & off times
326  * are different
327  */
328     static int
329 gui_ph_handle_timer_cursor(
330 	PtWidget_t *widget,
331 	void *data,
332 	PtCallbackInfo_t *info)
333 {
334     if (blink_state == BLINK_ON)
335     {
336 	gui_undraw_cursor();
337 	blink_state = BLINK_OFF;
338 	PtSetResource(gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL,
339 		blink_offtime, 0);
340     }
341     else
342     {
343 	gui_update_cursor(TRUE, FALSE);
344 	blink_state = BLINK_ON;
345 	PtSetResource(gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL,
346 		blink_ontime, 0);
347     }
348     return Pt_CONTINUE;
349 }
350 
351     static int
352 gui_ph_handle_timer_timeout(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
353 {
354     is_timeout = TRUE;
355 
356     return Pt_CONTINUE;
357 }
358 
359     static int
360 gui_ph_handle_window_cb(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
361 {
362     PhWindowEvent_t *we = info->cbdata;
363     ushort_t *width, *height;
364 
365     switch (we->event_f) {
366 	case Ph_WM_CLOSE:
367 	    gui_shell_closed();
368 	    break;
369 
370 	case Ph_WM_FOCUS:
371 	    // Just in case it's hidden and needs to be shown
372 	    gui_mch_mousehide(MOUSE_SHOW);
373 
374 	    if (we->event_state == Ph_WM_EVSTATE_FOCUS)
375 	    {
376 		gui_focus_change(TRUE);
377 		gui_mch_start_blink();
378 	    }
379 	    else
380 	    {
381 		gui_focus_change(FALSE);
382 		gui_mch_stop_blink(TRUE);
383 	    }
384 	    break;
385 
386 	case Ph_WM_RESIZE:
387 	    PtGetResource(gui.vimWindow, Pt_ARG_WIDTH, &width, 0);
388 	    PtGetResource(gui.vimWindow, Pt_ARG_HEIGHT, &height, 0);
389 #ifdef USE_PANEL_GROUP
390 	    width  -= (pg_margin_left + pg_margin_right);
391 	    height -= (pg_margin_top + pg_margin_bottom);
392 #endif
393 	    gui_resize_shell(*width, *height);
394 	    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
395 	    is_ignore_draw = FALSE;
396 	    PtEndFlux(gui.vimContainer);
397 	    PtContainerRelease(gui.vimContainer);
398 	    break;
399 
400 	default:
401 	    break;
402     }
403 
404     return Pt_CONTINUE;
405 }
406 
407     static int
408 gui_ph_handle_scrollbar(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
409 {
410     PtScrollbarCallback_t *scroll;
411     scrollbar_T *sb;
412     int	    value, dragging = FALSE;
413 
414     scroll = info->cbdata;
415 
416     sb = (scrollbar_T *) data;
417     if (sb != NULL)
418     {
419 	value = scroll->position;
420 	switch (scroll->action)
421 	{
422 	    case Pt_SCROLL_DRAGGED:
423 		dragging = TRUE;
424 		break;
425 
426 	    case Pt_SCROLL_SET:
427 		// FIXME: return straight away here?
428 		return Pt_CONTINUE;
429 		break;
430 	}
431 
432 	gui_drag_scrollbar(sb, value, dragging);
433     }
434     return Pt_CONTINUE;
435 }
436 
437     static int
438 gui_ph_handle_keyboard(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
439 {
440     PhKeyEvent_t    *key;
441     unsigned char   string[6];
442     int		    len, i;
443     int		    ch, modifiers;
444 
445     key = PhGetData(info->event);
446 
447     ch = modifiers = len = 0;
448 
449     if (p_mh)
450 	gui_mch_mousehide(MOUSE_HIDE);
451 
452     // We're a good lil photon program, aren't we? yes we are, yeess wee arrr
453     if (key->key_flags & Pk_KF_Compose)
454 	return Pt_CONTINUE;
455 
456     if ((key->key_flags & Pk_KF_Cap_Valid) &&
457 	    PkIsKeyDown(key->key_flags))
458     {
459 #ifdef FEAT_MENU
460 	/*
461 	 * Only show the menu if the Alt key is down, and the Shift & Ctrl
462 	 * keys aren't down, as well as the other conditions
463 	 */
464 	if (((key->key_mods & Pk_KM_Alt) &&
465 		    !(key->key_mods & Pk_KM_Shift) &&
466 		    !(key->key_mods & Pk_KM_Ctrl)) &&
467 	    gui.menu_is_active &&
468 	    (*p_wak == 'y' ||
469 	      (*p_wak == 'm' &&
470 		gui_is_menu_shortcut(key->key_cap))))
471 	{
472 	    // Fallthrough and let photon look for the hotkey
473 	    return Pt_CONTINUE;
474 	}
475 #endif
476 
477 	for (i = 0; special_keys[i].key_sym != 0; i++)
478 	{
479 	    if (special_keys[i].key_sym == key->key_cap)
480 	    {
481 		len = 0;
482 		if (special_keys[i].vim_code1 == NUL)
483 		    ch = special_keys[i].vim_code0;
484 		else
485 		{
486 		    // Detect if a keypad number key has been pressed
487 		    // and change the key if Num Lock is on
488 		    if (key->key_cap >= Pk_KP_Enter && key->key_cap <= Pk_KP_9
489 			    && (key->key_mods & Pk_KM_Num_Lock))
490 		    {
491 			// FIXME: For now, just map the key to a ascii value
492 			// (see <photon/PkKeyDef.h>)
493 			ch = key->key_cap - 0xf080;
494 		    }
495 		    else
496 			ch = TO_SPECIAL(special_keys[i].vim_code0,
497 				special_keys[i].vim_code1);
498 		}
499 		break;
500 	    }
501 	}
502 
503 	if (key->key_mods & Pk_KM_Ctrl)
504 	    modifiers |= MOD_MASK_CTRL;
505 	if (key->key_mods & Pk_KM_Alt)
506 	    modifiers |= MOD_MASK_ALT;
507 	if (key->key_mods & Pk_KM_Shift)
508 	    modifiers |= MOD_MASK_SHIFT;
509 
510 	// Is this not a special key?
511 	if (special_keys[i].key_sym == 0)
512 	{
513 	    ch = PhTo8859_1(key);
514 	    if (ch == -1 || (enc_utf8 && ch > 127))
515 	    {
516 		len = PhKeyToMb(string, key);
517 		if (len > 0)
518 		{
519 		    static char buf[6];
520 		    int src_taken, dst_made;
521 		    if (enc_utf8 != TRUE)
522 		    {
523 			PxTranslateFromUTF(
524 				charset_translate,
525 				string,
526 				len,
527 				&src_taken,
528 				buf,
529 				6,
530 				&dst_made);
531 
532 			add_to_input_buf(buf, dst_made);
533 		    }
534 		    else
535 		    {
536 			add_to_input_buf(string, len);
537 		    }
538 
539 		    return Pt_CONSUME;
540 		}
541 		len = 0;
542 		ch = key->key_cap;
543 		if (ch < 0xff)
544 		{
545 		    // FIXME: is this the right thing to do?
546 		    if (modifiers & MOD_MASK_CTRL)
547 		    {
548 			modifiers &= ~MOD_MASK_CTRL;
549 
550 			if ((ch >= 'a' && ch <= 'z') ||
551 				ch == '[' ||
552 				ch == ']' ||
553 				ch == '\\')
554 			    ch = Ctrl_chr(ch);
555 			else if (ch == '2')
556 			    ch = NUL;
557 			else if (ch == '6')
558 			    ch = 0x1e;
559 			else if (ch == '-')
560 			    ch = 0x1f;
561 			else
562 			    modifiers |= MOD_MASK_CTRL;
563 		    }
564 
565 		    if (modifiers & MOD_MASK_ALT)
566 		    {
567 			ch = Meta(ch);
568 			modifiers &= ~MOD_MASK_ALT;
569 		    }
570 		}
571 		else
572 		{
573 		    return Pt_CONTINUE;
574 		}
575 	    }
576 	    else
577 		modifiers &= ~MOD_MASK_SHIFT;
578 	}
579 
580 	ch = simplify_key(ch, &modifiers);
581 	if (modifiers)
582 	{
583 	    string[ len++ ] = CSI;
584 	    string[ len++ ] = KS_MODIFIER;
585 	    string[ len++ ] = modifiers;
586 	}
587 
588 	if (IS_SPECIAL(ch))
589 	{
590 	    string[ len++ ] = CSI;
591 	    string[ len++ ] = K_SECOND(ch);
592 	    string[ len++ ] = K_THIRD(ch);
593 	}
594 	else
595 	{
596 	    string[ len++ ] = ch;
597 	}
598 
599 	if (len == 1 && ((ch == Ctrl_C && ctrl_c_interrupts)
600 							  || ch == intr_char))
601 	{
602 	    trash_input_buf();
603 	    got_int = TRUE;
604 	}
605 
606 	if (len == 1 && string[0] == CSI)
607 	{
608 	    // Turn CSI into K_CSI.
609 	    string[ len++ ] = KS_EXTRA;
610 	    string[ len++ ] = KE_CSI;
611 	}
612 
613 	if (len > 0)
614 	{
615 	    add_to_input_buf(string, len);
616 	    return Pt_CONSUME;
617 	}
618     }
619 
620     return Pt_CONTINUE;
621 }
622 
623     static int
624 gui_ph_handle_mouse(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
625 {
626     PhPointerEvent_t *pointer;
627     PhRect_t	     *pos;
628     int		     button = 0, repeated_click, modifiers = 0x0;
629     short	     mouse_x, mouse_y;
630 
631     pointer = PhGetData(info->event);
632     pos = PhGetRects(info->event);
633 
634     gui_mch_mousehide(MOUSE_SHOW);
635 
636     /*
637      * Coordinates need to be relative to the base window,
638      * not relative to the vimTextArea widget
639      */
640     mouse_x = pos->ul.x + gui.border_width;
641     mouse_y = pos->ul.y + gui.border_width;
642 
643     if (info->event->type == Ph_EV_PTR_MOTION_NOBUTTON)
644     {
645 	gui_mouse_moved(mouse_x, mouse_y);
646 	return Pt_CONTINUE;
647     }
648 
649     if (pointer->key_mods & Pk_KM_Shift)
650 	modifiers |= MOUSE_SHIFT;
651     if (pointer->key_mods & Pk_KM_Ctrl)
652 	modifiers |= MOUSE_CTRL;
653     if (pointer->key_mods & Pk_KM_Alt)
654 	modifiers |= MOUSE_ALT;
655 
656     /*
657      * FIXME More than one button may be involved, but for
658      * now just deal with one
659      */
660     if (pointer->buttons & Ph_BUTTON_SELECT)
661 	button = MOUSE_LEFT;
662 
663     if (pointer->buttons & Ph_BUTTON_MENU)
664     {
665 	button = MOUSE_RIGHT;
666 	// Need the absolute coordinates for the popup menu
667 	abs_mouse.x = pointer->pos.x;
668 	abs_mouse.y = pointer->pos.y;
669     }
670 
671     if (pointer->buttons & Ph_BUTTON_ADJUST)
672 	button = MOUSE_MIDDLE;
673 
674     // Catch a real release (not phantom or other releases
675     if (info->event->type == Ph_EV_BUT_RELEASE)
676 	button = MOUSE_RELEASE;
677 
678     if (info->event->type & Ph_EV_PTR_MOTION_BUTTON)
679 	button = MOUSE_DRAG;
680 
681 #if 0
682     // Vim doesn't use button repeats
683     if (info->event->type & Ph_EV_BUT_REPEAT)
684 	button = MOUSE_DRAG;
685 #endif
686 
687     // Don't do anything if it is one of the phantom mouse release events
688     if ((button != MOUSE_RELEASE) ||
689 	    (info->event->subtype == Ph_EV_RELEASE_REAL))
690     {
691 	repeated_click = (pointer->click_count >= 2) ? TRUE : FALSE;
692 
693 	gui_send_mouse_event(button , mouse_x, mouse_y, repeated_click, modifiers);
694     }
695 
696     return Pt_CONTINUE;
697 }
698 
699 /*
700  * Handle a focus change of the PtRaw widget
701  */
702     static int
703 gui_ph_handle_focus(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
704 {
705     if (info->reason == Pt_CB_LOST_FOCUS)
706     {
707 	PtRemoveEventHandler(gui.vimTextArea, Ph_EV_PTR_MOTION_NOBUTTON,
708 		gui_ph_handle_mouse, NULL);
709 
710 	gui_mch_mousehide(MOUSE_SHOW);
711     }
712     else
713     {
714 	PtAddEventHandler(gui.vimTextArea, Ph_EV_PTR_MOTION_NOBUTTON,
715 		gui_ph_handle_mouse, NULL);
716     }
717     return Pt_CONTINUE;
718 }
719 
720     static void
721 gui_ph_handle_raw_draw(PtWidget_t *widget, PhTile_t *damage)
722 {
723     PhRect_t	*r;
724     PhPoint_t	offset;
725     PhPoint_t	translation;
726 
727     if (is_ignore_draw == TRUE)
728 	return;
729 
730     PtSuperClassDraw(PtBasic, widget, damage);
731     PgGetTranslation(&translation);
732     PgClearTranslation();
733 
734 #if 0
735     /*
736      * This causes some weird problems, with drawing being done from
737      * within this raw drawing function (rather than just simple clearing
738      * and text drawing done by gui_redraw)
739      *
740      * The main problem is when PhBlit is used, and the cursor appearing
741      * in places where it shouldn't
742      */
743     out_flush();
744 #endif
745 
746     PtWidgetOffset(widget, &offset);
747     PhTranslatePoint(&offset, PtWidgetPos(gui.vimTextArea, NULL));
748 
749 #if 1
750     // Redraw individual damage regions
751     if (damage->next != NULL)
752 	damage = damage->next;
753 
754     while (damage != NULL)
755     {
756 	r = &damage->rect;
757 	gui_redraw(
758 		r->ul.x - offset.x, r->ul.y - offset.y,
759 		r->lr.x - r->ul.x + 1,
760 		r->lr.y - r->ul.y + 1);
761 	damage = damage->next;
762     }
763 #else
764     // Redraw the rectangle that covers all the damaged regions
765     r = &damage->rect;
766     gui_redraw(
767 	    r->ul.x - offset.x, r->ul.y - offset.y,
768 	    r->lr.x - r->ul.x + 1,
769 	    r->lr.y - r->ul.y + 1);
770 #endif
771 
772     PgSetTranslation(&translation, 0);
773 }
774 
775     static int
776 gui_ph_handle_pulldown_menu(
777 	PtWidget_t *widget,
778 	void *data,
779 	PtCallbackInfo_t *info)
780 {
781     if (data != NULL)
782     {
783 	vimmenu_T *menu = (vimmenu_T *) data;
784 
785 	PtPositionMenu(menu->submenu_id, NULL);
786 	PtRealizeWidget(menu->submenu_id);
787     }
788 
789     return Pt_CONTINUE;
790 }
791 
792 /*
793  * This is used for pulldown/popup menus and also toolbar buttons
794  */
795     static int
796 gui_ph_handle_menu(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
797 {
798     if (data != NULL)
799     {
800 	vimmenu_T *menu = (vimmenu_T *) data;
801 	gui_menu_cb(menu);
802     }
803     return Pt_CONTINUE;
804 }
805 
806 /*
807  * Stop focus from disappearing into the menubar...
808  */
809     static int
810 gui_ph_handle_menu_unrealized(
811 	PtWidget_t *widget,
812 	void *data,
813 	PtCallbackInfo_t *info)
814 {
815     PtGiveFocus(gui.vimTextArea, NULL);
816     return Pt_CONTINUE;
817 }
818 
819     static int
820 gui_ph_handle_window_open(
821 	PtWidget_t *widget,
822 	void *data,
823 	PtCallbackInfo_t *info)
824 {
825     gui_set_shellsize(FALSE, TRUE, RESIZE_BOTH);
826     return Pt_CONTINUE;
827 }
828 
829 ////////////////////////////////////////////////////////////////////////////
830 
831 #define DRAW_START  gui_ph_draw_start()
832 #define DRAW_END    gui_ph_draw_end()
833 
834 /*
835  * TODO: Set a clipping rect?
836  */
837     static void
838 gui_ph_draw_start(void)
839 {
840     PhGC_t *gc;
841 
842     gc = PgGetGC();
843     PgSetRegion(PtWidgetRid(PtFindDisjoint(gui.vimTextArea)));
844     PgClearClippingsCx(gc);
845     PgClearTranslationCx(gc);
846 
847     PtWidgetOffset(gui.vimTextArea, &gui_ph_raw_offset);
848     PhTranslatePoint(&gui_ph_raw_offset, PtWidgetPos(gui.vimTextArea, NULL));
849 
850     PgSetTranslation(&gui_ph_raw_offset, Pg_RELATIVE);
851 }
852 
853     static void
854 gui_ph_draw_end(void)
855 {
856     gui_ph_raw_offset.x = -gui_ph_raw_offset.x;
857     gui_ph_raw_offset.y = -gui_ph_raw_offset.y;
858     PgSetTranslation(&gui_ph_raw_offset, Pg_RELATIVE);
859 }
860 
861 #ifdef USE_PANEL_GROUP
862     static vimmenu_T *
863 gui_ph_find_buffer_item(char_u *name)
864 {
865     vimmenu_T *top_level = root_menu;
866     vimmenu_T *items = NULL;
867 
868     while (top_level != NULL &&
869 	    (STRCMP(top_level->dname, "Buffers") != 0))
870 	top_level = top_level->next;
871 
872     if (top_level != NULL)
873     {
874 	items = top_level->children;
875 
876 	while (items != NULL &&
877 		(STRCMP(items->dname, name) != 0))
878 	    items = items->next;
879     }
880     return items;
881 }
882 
883     static void
884 gui_ph_pg_set_buffer_num(int_u buf_num)
885 {
886     int i;
887     char search[16];
888     char *mark;
889 
890     if (gui.vimTextArea == NULL || buf_num == 0)
891 	return;
892 
893     search[0] = '(';
894     ultoa(buf_num, &search[1], 10);
895     STRCAT(search, ")");
896 
897     for (i = 0; i < num_panels; i++)
898     {
899 	// find the last "(" in the panel title and see if the buffer
900 	// number in the title matches the one we're looking for
901 	mark = STRRCHR(panel_titles[ i ], '(');
902 	if (mark != NULL && STRCMP(mark, search) == 0)
903 	{
904 	    PtSetResource(gui.vimPanelGroup, Pt_ARG_PG_CURRENT_INDEX,
905 		    i, 0);
906 	}
907     }
908 }
909 
910     static int
911 gui_ph_handle_pg_change(
912 	PtWidget_t *widget,
913 	void *data,
914 	PtCallbackInfo_t *info)
915 {
916     vimmenu_T *menu;
917     PtPanelGroupCallback_t *panel;
918 
919     if (info->event != NULL)
920     {
921 	panel = info->cbdata;
922 	if (panel->new_panel != NULL)
923 	{
924 	    menu = gui_ph_find_buffer_item(panel->new_panel);
925 	    if (menu)
926 		gui_menu_cb(menu);
927 	}
928     }
929     return Pt_CONTINUE;
930 }
931 
932     static void
933 gui_ph_get_panelgroup_margins(
934 	short *top,
935 	short *bottom,
936 	short *left,
937 	short *right)
938 {
939     unsigned short abs_raw_x, abs_raw_y, abs_panel_x, abs_panel_y;
940     const unsigned short *margin_top, *margin_bottom;
941     const unsigned short *margin_left, *margin_right;
942 
943     PtGetAbsPosition(gui.vimTextArea, &abs_raw_x, &abs_raw_y);
944     PtGetAbsPosition(gui.vimPanelGroup, &abs_panel_x, &abs_panel_y);
945 
946     PtGetResource(gui.vimPanelGroup, Pt_ARG_MARGIN_RIGHT, &margin_right, 0);
947     PtGetResource(gui.vimPanelGroup, Pt_ARG_MARGIN_BOTTOM, &margin_bottom, 0);
948 
949     abs_raw_x -= abs_panel_x;
950     abs_raw_y -= abs_panel_y;
951 
952     *top    = abs_raw_y;
953     *bottom = *margin_bottom;
954 
955     *left  = abs_raw_x;
956     *right = *margin_right;
957 }
958 
959 /*
960  * Used for the tabs for PtPanelGroup
961  */
962     static int
963 gui_ph_is_buffer_item(vimmenu_T *menu, vimmenu_T *parent)
964 {
965     char *mark;
966 
967     if (STRCMP(parent->dname, "Buffers") == 0)
968     {
969 	// Look for '(' digits ')'
970 	mark = vim_strchr(menu->dname, '(');
971 	if (mark != NULL)
972 	{
973 	    mark++;
974 	    while (isdigit(*mark))
975 		mark++;
976 
977 	    if (*mark == ')')
978 		return TRUE;
979 	}
980     }
981     return FALSE;
982 }
983 
984     static void
985 gui_ph_pg_add_buffer(char *name)
986 {
987     char **new_titles = NULL;
988 
989     new_titles = ALLOC_MULT(char *, (num_panels + 1));
990     if (new_titles != NULL)
991     {
992 	if (num_panels > 0)
993 	    memcpy(new_titles, panel_titles, num_panels * sizeof(char **));
994 
995 	new_titles[ num_panels++ ] = name;
996 
997 	PtSetResource(gui.vimPanelGroup, Pt_ARG_PG_PANEL_TITLES, new_titles,
998 		num_panels);
999 
1000 	vim_free(panel_titles);
1001 	panel_titles = new_titles;
1002     }
1003 }
1004 
1005     static void
1006 gui_ph_pg_remove_buffer(char *name)
1007 {
1008     int i;
1009     char **new_titles = NULL;
1010 
1011     // If there is only 1 panel, we just use the temporary place holder
1012     if (num_panels > 1)
1013     {
1014 	new_titles = ALLOC_MULT(char *, num_panels - 1);
1015 	if (new_titles != NULL)
1016 	{
1017 	    char **s = new_titles;
1018 	    // Copy all the titles except the one we're removing
1019 	    for (i = 0; i < num_panels; i++)
1020 	    {
1021 		if (STRCMP(panel_titles[ i ], name) != 0)
1022 		    *s++ = panel_titles[ i ];
1023 	    }
1024 	    num_panels--;
1025 
1026 	    PtSetResource(gui.vimPanelGroup, Pt_ARG_PG_PANEL_TITLES, new_titles,
1027 		    num_panels);
1028 
1029 	    vim_free(panel_titles);
1030 	    panel_titles = new_titles;
1031 	}
1032     }
1033     else
1034     {
1035 	num_panels--;
1036 	PtSetResource(gui.vimPanelGroup, Pt_ARG_PG_PANEL_TITLES, &empty_title,
1037 		1);
1038 
1039 	VIM_CLEAR(panel_titles);
1040     }
1041 }
1042 
1043 /*
1044  * When a buffer item is deleted from the buffer menu
1045  */
1046     static int
1047 gui_ph_handle_buffer_remove(
1048 	PtWidget_t *widget,
1049 	void *data,
1050 	PtCallbackInfo_t *info)
1051 {
1052     vimmenu_T *menu;
1053 
1054     if (data != NULL)
1055     {
1056 	menu = (vimmenu_T *) data;
1057 	gui_ph_pg_remove_buffer(menu->dname);
1058     }
1059 
1060     return Pt_CONTINUE;
1061 }
1062 #endif
1063 
1064     static int
1065 gui_ph_pane_resize(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
1066 {
1067     if (PtWidgetIsRealized(widget))
1068     {
1069 	is_ignore_draw = TRUE;
1070 	PtStartFlux(gui.vimContainer);
1071 	PtContainerHold(gui.vimContainer);
1072     }
1073 
1074     return Pt_CONTINUE;
1075 }
1076 
1077 ////////////////////////////////////////////////////////////////////////////
1078 
1079     void
1080 gui_ph_encoding_changed(int new_encoding)
1081 {
1082     // Default encoding is latin1
1083     char *charset = "latin1";
1084     int i;
1085 
1086     struct {
1087 	int encoding;
1088 	char *name;
1089     } charsets[] = {
1090 	{ DBCS_JPN, "SHIFT_JIS" },
1091 	{ DBCS_KOR, "csEUCKR" },
1092 	{ DBCS_CHT, "big5" },
1093 	{ DBCS_CHS, "gb" }
1094     };
1095 
1096     for (i = 0; i < ARRAY_LENGTH(charsets); i++)
1097     {
1098 	if (new_encoding == charsets[ i ].encoding)
1099 	    charset = charsets[ i ].name;
1100     }
1101 
1102     charset_translate = PxTranslateSet(charset_translate, charset);
1103 }
1104 
1105 ////////////////////////////////////////////////////////////////////////////
1106 
1107     void
1108 gui_mch_prepare(int *argc, char **argv)
1109 {
1110     PtInit(NULL);
1111 }
1112 
1113     int
1114 gui_mch_init(void)
1115 {
1116     PtArg_t args[10];
1117     int	    flags = 0, n = 0;
1118 
1119     PhDim_t	window_size = {100, 100}; // Arbitrary values
1120     PhPoint_t	pos = {0, 0};
1121 
1122     gui.event_buffer = alloc(EVENT_BUFFER_SIZE);
1123     if (gui.event_buffer == NULL)
1124 	return FAIL;
1125 
1126     // Get a translation so we can convert from ISO Latin-1 to UTF
1127     charset_translate = PxTranslateSet(NULL, "latin1");
1128 
1129     // The +2 is for the 1 pixel dark line on each side
1130     gui.border_offset = gui.border_width = GUI_PH_MARGIN + 2;
1131 
1132     // Handle close events ourselves
1133     PtSetArg(&args[ n++ ], Pt_ARG_WINDOW_MANAGED_FLAGS, Pt_FALSE, Ph_WM_CLOSE);
1134     PtSetArg(&args[ n++ ], Pt_ARG_WINDOW_NOTIFY_FLAGS, Pt_TRUE,
1135 	    Ph_WM_CLOSE | Ph_WM_RESIZE | Ph_WM_FOCUS);
1136     PtSetArg(&args[ n++ ], Pt_ARG_DIM, &window_size, 0);
1137     gui.vimWindow = PtCreateWidget(PtWindow, NULL, n, args);
1138     if (gui.vimWindow == NULL)
1139 	return FAIL;
1140 
1141     PtAddCallback(gui.vimWindow, Pt_CB_WINDOW, gui_ph_handle_window_cb, NULL);
1142     PtAddCallback(gui.vimWindow, Pt_CB_WINDOW_OPENING,
1143 	    gui_ph_handle_window_open, NULL);
1144 
1145     n = 0;
1146     PtSetArg(&args[ n++ ], Pt_ARG_ANCHOR_FLAGS, Pt_ANCHOR_ALL, Pt_IS_ANCHORED);
1147     PtSetArg(&args[ n++ ], Pt_ARG_DIM, &window_size, 0);
1148     PtSetArg(&args[ n++ ], Pt_ARG_POS, &pos, 0);
1149 
1150 #ifdef USE_PANEL_GROUP
1151     // Put in a temporary place holder title
1152     PtSetArg(&args[ n++ ], Pt_ARG_PG_PANEL_TITLES, &empty_title, 1);
1153 
1154     gui.vimPanelGroup = PtCreateWidget(PtPanelGroup, gui.vimWindow, n, args);
1155     if (gui.vimPanelGroup == NULL)
1156 	return FAIL;
1157 
1158     PtAddCallback(gui.vimPanelGroup, Pt_CB_PG_PANEL_SWITCHING,
1159 	    gui_ph_handle_pg_change, NULL);
1160 #else
1161     // Turn off all edge decorations
1162     PtSetArg(&args[ n++ ], Pt_ARG_BASIC_FLAGS, Pt_FALSE, Pt_ALL);
1163     PtSetArg(&args[ n++ ], Pt_ARG_BEVEL_WIDTH, 0, 0);
1164     PtSetArg(&args[ n++ ], Pt_ARG_MARGIN_WIDTH, 0, 0);
1165     PtSetArg(&args[ n++ ], Pt_ARG_MARGIN_HEIGHT, 0, 0);
1166     PtSetArg(&args[ n++ ], Pt_ARG_CONTAINER_FLAGS, Pt_TRUE, Pt_AUTO_EXTENT);
1167 
1168     gui.vimContainer = PtCreateWidget(PtPane, gui.vimWindow, n, args);
1169     if (gui.vimContainer == NULL)
1170 	return FAIL;
1171 
1172     PtAddCallback(gui.vimContainer, Pt_CB_RESIZE, gui_ph_pane_resize, NULL);
1173 #endif
1174 
1175     // Size for the text area is set in gui_mch_set_text_area_pos
1176     n = 0;
1177 
1178     PtSetArg(&args[ n++ ], Pt_ARG_RAW_DRAW_F, gui_ph_handle_raw_draw, 1);
1179     PtSetArg(&args[ n++ ], Pt_ARG_BEVEL_WIDTH, GUI_PH_MARGIN, 0);
1180     /*
1181      * Using focus render also causes the whole widget to be redrawn
1182      * whenever it changes focus, which is very annoying :p
1183      */
1184     PtSetArg(&args[ n++ ], Pt_ARG_FLAGS, Pt_TRUE,
1185 	    Pt_GETS_FOCUS | Pt_HIGHLIGHTED);
1186 #ifndef FEAT_MOUSESHAPE
1187     PtSetArg(&args[ n++ ], Pt_ARG_CURSOR_TYPE, GUI_PH_MOUSE_TYPE, 0);
1188     PtSetArg(&args[ n++ ], Pt_ARG_CURSOR_COLOR, gui_ph_mouse_color, 0);
1189 #endif
1190 
1191     gui.vimTextArea = PtCreateWidget(PtRaw, Pt_DFLT_PARENT, n, args);
1192     if (gui.vimTextArea == NULL)
1193 	return FAIL;
1194 
1195     // TODO: use PtAddEventHandlers instead?
1196     // Not using Ph_EV_BUT_REPEAT because vim wouldn't use it anyway
1197     PtAddEventHandler(gui.vimTextArea,
1198 	    Ph_EV_BUT_PRESS | Ph_EV_BUT_RELEASE | Ph_EV_PTR_MOTION_BUTTON,
1199 	    gui_ph_handle_mouse, NULL);
1200     PtAddEventHandler(gui.vimTextArea, Ph_EV_KEY,
1201 	    gui_ph_handle_keyboard, NULL);
1202     PtAddCallback(gui.vimTextArea, Pt_CB_GOT_FOCUS,
1203 	    gui_ph_handle_focus, NULL);
1204     PtAddCallback(gui.vimTextArea, Pt_CB_LOST_FOCUS,
1205 	    gui_ph_handle_focus, NULL);
1206 
1207     /*
1208      * Now that the text area widget has been created, set up the colours,
1209      * which will call PtSetResource from gui_mch_new_colors
1210      */
1211 
1212     /*
1213      * Create the two timers, not as accurate as using the kernel timer
1214      * functions, but good enough
1215      */
1216     gui_ph_timer_cursor  = PtCreateWidget(PtTimer, gui.vimWindow, 0, NULL);
1217     if (gui_ph_timer_cursor == NULL)
1218 	return FAIL;
1219 
1220     gui_ph_timer_timeout = PtCreateWidget(PtTimer, gui.vimWindow, 0, NULL);
1221     if (gui_ph_timer_timeout == NULL)
1222 	return FAIL;
1223 
1224     PtAddCallback(gui_ph_timer_cursor,  Pt_CB_TIMER_ACTIVATE,
1225 	    gui_ph_handle_timer_cursor, NULL);
1226     PtAddCallback(gui_ph_timer_timeout, Pt_CB_TIMER_ACTIVATE,
1227 	    gui_ph_handle_timer_timeout, NULL);
1228 
1229 #ifdef FEAT_MENU
1230     n = 0;
1231     PtSetArg(&args[ n++ ], Pt_ARG_WIDTH, window_size.w, 0);
1232     PtSetArg(&args[ n++ ], Pt_ARG_ANCHOR_FLAGS, Pt_ANCHOR_LEFT_RIGHT,
1233 	    Pt_IS_ANCHORED);
1234     gui.vimToolBarGroup = PtCreateWidget(PtToolbarGroup, gui.vimWindow,
1235 	    n, args);
1236     if (gui.vimToolBarGroup == NULL)
1237 	return FAIL;
1238 
1239     PtAddCallback(gui.vimToolBarGroup, Pt_CB_RESIZE,
1240 	    gui_ph_handle_menu_resize, NULL);
1241 
1242     n = 0;
1243     flags = 0;
1244     PtSetArg(&args[ n++ ], Pt_ARG_WIDTH, window_size.w, 0);
1245     if (! vim_strchr(p_go, GO_MENUS))
1246     {
1247 	flags |= Pt_DELAY_REALIZE;
1248 	PtSetArg(&args[ n++ ], Pt_ARG_FLAGS, Pt_TRUE, flags);
1249     }
1250     gui.vimMenuBar = PtCreateWidget(PtMenuBar, gui.vimToolBarGroup, n, args);
1251     if (gui.vimMenuBar == NULL)
1252 	return FAIL;
1253 
1254 # ifdef FEAT_TOOLBAR
1255     n = 0;
1256 
1257     PtSetArg(&args[ n++ ], Pt_ARG_ANCHOR_FLAGS,
1258 	    Pt_ANCHOR_LEFT_RIGHT |Pt_TOP_ANCHORED_TOP, Pt_IS_ANCHORED);
1259     PtSetArg(&args[ n++ ], Pt_ARG_RESIZE_FLAGS, Pt_TRUE,
1260 	    Pt_RESIZE_Y_AS_REQUIRED);
1261     PtSetArg(&args[ n++ ], Pt_ARG_WIDTH, window_size.w, 0);
1262 
1263     flags = Pt_GETS_FOCUS;
1264     if (! vim_strchr(p_go, GO_TOOLBAR))
1265 	flags |= Pt_DELAY_REALIZE;
1266 
1267     PtSetArg(&args[ n++ ], Pt_ARG_FLAGS, Pt_DELAY_REALIZE, flags);
1268 
1269     gui.vimToolBar = PtCreateWidget(PtToolbar, gui.vimToolBarGroup, n, args);
1270     if (gui.vimToolBar == NULL)
1271 	return FAIL;
1272 
1273     /*
1274      * Size for the toolbar is fetched in gui_mch_show_toolbar, after
1275      * the buttons have been added and the toolbar has resized it's height
1276      * for the buttons to fit
1277      */
1278 # endif
1279 
1280 #endif
1281 
1282     return OK;
1283 }
1284 
1285     int
1286 gui_mch_init_check(void)
1287 {
1288     return (is_photon_available == TRUE) ? OK : FAIL;
1289 }
1290 
1291     int
1292 gui_mch_open(void)
1293 {
1294     gui.norm_pixel =  Pg_BLACK;
1295     gui.back_pixel =  Pg_WHITE;
1296 
1297     set_normal_colors();
1298 
1299     gui_check_colors();
1300     gui.def_norm_pixel = gui.norm_pixel;
1301     gui.def_back_pixel = gui.back_pixel;
1302 
1303     highlight_gui_started();
1304 
1305     if (gui_win_x != -1 && gui_win_y != -1)
1306 	gui_mch_set_winpos(gui_win_x, gui_win_y);
1307 
1308     return (PtRealizeWidget(gui.vimWindow) == 0) ? OK : FAIL;
1309 }
1310 
1311     void
1312 gui_mch_exit(int rc)
1313 {
1314     PtDestroyWidget(gui.vimWindow);
1315 
1316     PxTranslateSet(charset_translate, NULL);
1317 
1318     vim_free(gui.event_buffer);
1319 
1320 #ifdef USE_PANEL_GROUPS
1321     vim_free(panel_titles);
1322 #endif
1323 }
1324 
1325 ////////////////////////////////////////////////////////////////////////////
1326 // events
1327 
1328 /*
1329  * When no events are available, photon will call this function, working is
1330  * set to FALSE, and the gui_mch_update loop will exit.
1331  */
1332     static int
1333 exit_gui_mch_update(void *data)
1334 {
1335     *(int *)data = FALSE;
1336     return Pt_END;
1337 }
1338 
1339     void
1340 gui_mch_update(void)
1341 {
1342     int working = TRUE;
1343 
1344     PtAppAddWorkProc(NULL, exit_gui_mch_update, &working);
1345     while ((working == TRUE) && !vim_is_input_buf_full())
1346 	PtProcessEvent();
1347 }
1348 
1349     int
1350 gui_mch_wait_for_chars(int wtime)
1351 {
1352     is_timeout = FALSE;
1353 
1354     if (wtime >= 0)
1355 	PtSetResource(gui_ph_timer_timeout, Pt_ARG_TIMER_INITIAL,
1356 						    wtime == 0 ? 1 : wtime, 0);
1357 
1358     while (1)
1359     {
1360 	PtProcessEvent();
1361 	if (input_available())
1362 	{
1363 	    PtSetResource(gui_ph_timer_timeout, Pt_ARG_TIMER_INITIAL, 0, 0);
1364 	    return OK;
1365 	}
1366 	else if (is_timeout == TRUE)
1367 	    return FAIL;
1368     }
1369 }
1370 
1371 #if defined(FEAT_BROWSE) || defined(PROTO)
1372 /*
1373  * Put up a file requester.
1374  * Returns the selected name in allocated memory, or NULL for Cancel.
1375  * saving,	    select file to write
1376  * title	    title for the window
1377  * default_name	    default name (well duh!)
1378  * ext		    not used (extension added)
1379  * initdir	    initial directory, NULL for current dir
1380  * filter	    not used (file name filter)
1381  */
1382     char_u *
1383 gui_mch_browse(
1384 	int saving,
1385 	char_u *title,
1386 	char_u *default_name,
1387 	char_u *ext,
1388 	char_u *initdir,
1389 	char_u *filter)
1390 {
1391     PtFileSelectionInfo_t file;
1392     int	    flags;
1393     char_u  *default_path;
1394     char_u  *open_text = NULL;
1395 
1396     flags = 0;
1397     memset(&file, 0, sizeof(file));
1398 
1399     default_path = alloc(MAXPATHL + 1 + NAME_MAX + 1);
1400     if (default_path != NULL)
1401     {
1402 	if (saving == TRUE)
1403 	{
1404 	    // Don't need Pt_FSR_CONFIRM_EXISTING, vim will ask anyway
1405 	    flags |= Pt_FSR_NO_FCHECK;
1406 	    open_text = "&Save";
1407 	}
1408 
1409 	// combine the directory and filename into a single path
1410 	if (initdir == NULL || *initdir == NUL)
1411 	{
1412 	    mch_dirname(default_path, MAXPATHL);
1413 	    initdir = default_path;
1414 	}
1415 	else
1416 	{
1417 	    STRCPY(default_path, initdir);
1418 	    initdir = default_path;
1419 	}
1420 
1421 	if (default_name != NULL)
1422 	{
1423 	    if (default_path[ STRLEN(default_path) - 1 ] != '/')
1424 		STRCAT(default_path, "/");
1425 
1426 	    STRCAT(default_path, default_name);
1427 	}
1428 
1429 	// TODO: add a filter?
1430 	PtFileSelection(
1431 		gui.vimWindow,
1432 		NULL,
1433 		title,
1434 		default_path,
1435 		NULL,
1436 		open_text,
1437 		NULL,
1438 		NULL,
1439 		&file,
1440 		flags);
1441 
1442 	vim_free(default_path);
1443 
1444 	if (file.ret == Pt_FSDIALOG_BTN1)
1445 	    return vim_strsave(file.path);
1446     }
1447     return NULL;
1448 }
1449 #endif
1450 
1451 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
1452 static PtWidget_t *gui_ph_dialog_text = NULL;
1453 
1454     static int
1455 gui_ph_dialog_close(int button, void *data)
1456 {
1457     PtModalCtrl_t *modal_ctrl = data;
1458     char_u *dialog_text, *vim_text;
1459 
1460     if (gui_ph_dialog_text != NULL)
1461     {
1462 	PtGetResource(gui_ph_dialog_text, Pt_ARG_TEXT_STRING, &dialog_text, 0);
1463 	PtGetResource(gui_ph_dialog_text, Pt_ARG_POINTER, &vim_text, 0);
1464 	STRNCPY(vim_text, dialog_text, IOSIZE - 1);
1465     }
1466 
1467     PtModalUnblock(modal_ctrl, (void *) button);
1468 
1469     return Pt_TRUE;
1470 }
1471 
1472     static int
1473 gui_ph_dialog_text_enter(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
1474 {
1475     if (info->reason_subtype == Pt_EDIT_ACTIVATE)
1476 	gui_ph_dialog_close(1, data);
1477     return Pt_CONTINUE;
1478 }
1479 
1480     static int
1481 gui_ph_dialog_esc(PtWidget_t *widget, void *data, PtCallbackInfo_t *info)
1482 {
1483     PhKeyEvent_t *key;
1484 
1485     key = PhGetData(info->event);
1486     if ((key->key_flags & Pk_KF_Cap_Valid) && (key->key_cap == Pk_Escape))
1487     {
1488 	gui_ph_dialog_close(0, data);
1489 	return Pt_CONSUME;
1490     }
1491     return Pt_PROCESS;
1492 }
1493 
1494     int
1495 gui_mch_dialog(
1496 	int	type,
1497 	char_u	*title,
1498 	char_u	*message,
1499 	char_u	*buttons,
1500 	int	default_button,
1501 	char_u	*textfield,
1502 	int	ex_cmd)
1503 {
1504     char_u	*str;
1505     char_u	**button_array;
1506     char_u	*buttons_copy;
1507 
1508     int		button_count;
1509     int		i, len;
1510     int		dialog_result = -1;
1511 
1512     // FIXME: the vertical option in guioptions is blatantly ignored
1513     // FIXME: so is the type
1514 
1515     button_count = len = i = 0;
1516 
1517     if (buttons == NULL || *buttons == NUL)
1518 	return -1;
1519 
1520     // There is one less separator than buttons, so bump up the button count
1521     button_count = 1;
1522 
1523     // Count string length and number of separators
1524     for (str = buttons; *str; str++)
1525     {
1526 	len++;
1527 	if (*str == DLG_BUTTON_SEP)
1528 	    button_count++;
1529     }
1530 
1531     if (title == NULL)
1532 	title = "Vim";
1533 
1534     buttons_copy = alloc(len + 1);
1535     button_array = ALLOC_MULT(char_u *, button_count);
1536     if (buttons_copy != NULL && button_array != NULL)
1537     {
1538 	STRCPY(buttons_copy, buttons);
1539 
1540 	/*
1541 	 * Convert DLG_BUTTON_SEP into NUL's and fill in
1542 	 * button_array with the pointer to each NUL terminated string
1543 	 */
1544 	str = buttons_copy;
1545 	for (i = 0; i < button_count; i++)
1546 	{
1547 	    button_array[ i ] = str;
1548 	    for (; *str; str++)
1549 	    {
1550 		if (*str == DLG_BUTTON_SEP)
1551 		{
1552 		    *str++ = NUL;
1553 		    break;
1554 		}
1555 	    }
1556 	}
1557 #ifndef FEAT_GUI_TEXTDIALOG
1558 	dialog_result = PtAlert(
1559 		gui.vimWindow, NULL,
1560 		title,
1561 		NULL,
1562 		message, NULL,
1563 		button_count, (const char **) button_array, NULL,
1564 		default_button, 0, Pt_MODAL);
1565 #else
1566 	// Writing the dialog ourselves lets us add extra features, like
1567 	// trapping the escape key and returning 0 to vim
1568 	{
1569 	    int n;
1570 	    PtArg_t args[5];
1571 	    PtWidget_t *dialog, *pane;
1572 	    PtModalCtrl_t modal_ctrl;
1573 	    PtDialogInfo_t di;
1574 
1575 	    memset(&di, 0, sizeof(di));
1576 	    memset(&modal_ctrl, 0, sizeof(modal_ctrl));
1577 
1578 	    n = 0;
1579 	    PtSetArg(&args[n++], Pt_ARG_GROUP_ROWS_COLS, 0, 0);
1580 	    PtSetArg(&args[n++], Pt_ARG_WIDTH, 350, 0);
1581 	    PtSetArg(&args[n++], Pt_ARG_GROUP_ORIENTATION,
1582 		    Pt_GROUP_VERTICAL, 0);
1583 	    PtSetArg(&args[n++], Pt_ARG_GROUP_FLAGS,
1584 		    Pt_TRUE, Pt_GROUP_NO_KEYS | Pt_GROUP_STRETCH_HORIZONTAL);
1585 	    PtSetArg(&args[n++], Pt_ARG_CONTAINER_FLAGS, Pt_FALSE, Pt_TRUE);
1586 	    pane = PtCreateWidget(PtGroup, NULL, n, args);
1587 
1588 	    n = 0;
1589 	    PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, message, 0);
1590 	    PtCreateWidget(PtLabel, pane, n, args);
1591 
1592 	    if (textfield != NULL)
1593 	    {
1594 		n = 0;
1595 		PtSetArg(&args[n++], Pt_ARG_MAX_LENGTH, IOSIZE - 1, 0);
1596 		PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, textfield, 0);
1597 		PtSetArg(&args[n++], Pt_ARG_POINTER, textfield, 0);
1598 		gui_ph_dialog_text = PtCreateWidget(PtText, pane, n, args);
1599 		PtAddCallback(gui_ph_dialog_text, Pt_CB_ACTIVATE,
1600 			gui_ph_dialog_text_enter, &modal_ctrl);
1601 	    }
1602 
1603 	    di.parent = gui.vimWindow;
1604 	    di.pane = pane;
1605 	    di.title = title;
1606 	    di.buttons = (const char **) button_array;
1607 	    di.nbtns = button_count;
1608 	    di.def_btn = default_button;
1609 	    // This is just to give the dialog the close button.
1610 	    // We check for the Escape key ourselves and return 0
1611 	    di.esc_btn = button_count;
1612 	    di.callback = gui_ph_dialog_close;
1613 	    di.data = &modal_ctrl;
1614 
1615 	    dialog = PtCreateDialog(&di);
1616 	    PtAddFilterCallback(dialog, Ph_EV_KEY,
1617 		    gui_ph_dialog_esc, &modal_ctrl);
1618 
1619 	    if (gui_ph_dialog_text != NULL)
1620 		PtGiveFocus(gui_ph_dialog_text, NULL);
1621 
1622 	    // Open dialog, block the vim window and wait for the dialog to close
1623 	    PtRealizeWidget(dialog);
1624 	    PtMakeModal(dialog, Ph_CURSOR_NOINPUT, Ph_CURSOR_DEFAULT_COLOR);
1625 	    dialog_result = (int) PtModalBlock(&modal_ctrl, 0);
1626 
1627 	    PtDestroyWidget(dialog);
1628 	    gui_ph_dialog_text = NULL;
1629 	}
1630 #endif
1631     }
1632 
1633     vim_free(button_array);
1634     vim_free(buttons_copy);
1635 
1636     return dialog_result;
1637 }
1638 #endif
1639 ////////////////////////////////////////////////////////////////////////////
1640 // window size/position/state
1641 
1642     int
1643 gui_mch_get_winpos(int *x, int *y)
1644 {
1645     PhPoint_t *pos;
1646 
1647     pos = PtWidgetPos(gui.vimWindow, NULL);
1648 
1649     *x = pos->x;
1650     *y = pos->y;
1651 
1652     return OK;
1653 }
1654 
1655     void
1656 gui_mch_set_winpos(int x, int y)
1657 {
1658     PhPoint_t pos = { x, y };
1659 
1660     PtSetResource(gui.vimWindow, Pt_ARG_POS, &pos, 0);
1661 }
1662 
1663     void
1664 gui_mch_set_shellsize(int width, int height,
1665 	int min_width, int min_height, int base_width, int base_height,
1666 	int direction)
1667 {
1668     PhDim_t window_size = { width, height };
1669     PhDim_t min_size = { min_width, min_height };
1670 
1671 #ifdef USE_PANEL_GROUP
1672     window_size.w += pg_margin_left + pg_margin_right;
1673     window_size.h += pg_margin_top + pg_margin_bottom;
1674 #endif
1675 
1676     PtSetResource(gui.vimWindow, Pt_ARG_MINIMUM_DIM, &min_size, 0);
1677     PtSetResource(gui.vimWindow, Pt_ARG_DIM, &window_size, 0);
1678 
1679     if (! PtWidgetIsRealized(gui.vimWindow))
1680 	gui_ph_resize_container();
1681 }
1682 
1683 /*
1684  * Return the amount of screen space that hasn't been allocated (such as
1685  * by the shelf).
1686  */
1687     void
1688 gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
1689 {
1690     PhRect_t console;
1691 
1692     PhWindowQueryVisible(Ph_QUERY_WORKSPACE, 0,
1693 	    PhInputGroup(NULL), &console);
1694 
1695     *screen_w = console.lr.x - console.ul.x + 1;
1696     *screen_h = console.lr.y - console.ul.y + 1;
1697 }
1698 
1699     void
1700 gui_mch_iconify(void)
1701 {
1702     PhWindowEvent_t event;
1703 
1704     memset(&event, 0, sizeof (event));
1705     event.event_f = Ph_WM_HIDE;
1706     event.event_state = Ph_WM_EVSTATE_HIDE;
1707     event.rid = PtWidgetRid(gui.vimWindow);
1708     PtForwardWindowEvent(&event);
1709 }
1710 
1711 #if defined(FEAT_EVAL) || defined(PROTO)
1712 /*
1713  * Bring the Vim window to the foreground.
1714  */
1715     void
1716 gui_mch_set_foreground(void)
1717 {
1718     PhWindowEvent_t event;
1719 
1720     memset(&event, 0, sizeof (event));
1721     event.event_f = Ph_WM_TOFRONT;
1722     event.event_state = Ph_WM_EVSTATE_FFRONT;
1723     event.rid = PtWidgetRid(gui.vimWindow);
1724     PtForwardWindowEvent(&event);
1725 }
1726 #endif
1727 
1728     void
1729 gui_mch_settitle(char_u *title,	char_u *icon)
1730 {
1731 #ifdef USE_PANEL_GROUP
1732     gui_ph_pg_set_buffer_num(curwin->w_buffer->b_fnum);
1733 #endif
1734     PtSetResource(gui.vimWindow, Pt_ARG_WINDOW_TITLE, title, 0);
1735     // Not sure what to do with the icon text, set balloon text somehow?
1736 }
1737 
1738 ////////////////////////////////////////////////////////////////////////////
1739 // Scrollbar
1740 
1741     void
1742 gui_mch_set_scrollbar_thumb(scrollbar_T *sb, int val, int size, int max)
1743 {
1744     int	    n = 0;
1745     PtArg_t args[3];
1746 
1747     PtSetArg(&args[ n++ ], Pt_ARG_MAXIMUM, max, 0);
1748     PtSetArg(&args[ n++ ], Pt_ARG_SLIDER_SIZE, size, 0);
1749     PtSetArg(&args[ n++ ], Pt_ARG_GAUGE_VALUE, val, 0);
1750     PtSetResources(sb->id, n, args);
1751 }
1752 
1753     void
1754 gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h)
1755 {
1756     PhArea_t area = {{ x, y }, { w, h }};
1757 
1758     PtSetResource(sb->id, Pt_ARG_AREA, &area, 0);
1759 }
1760 
1761     void
1762 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
1763 {
1764     int	    n = 0;
1765 //    int	    anchor_flags = 0;
1766     PtArg_t args[4];
1767 
1768     /*
1769      * Stop the scrollbar from being realized when the parent
1770      * is realized, so it can be explicitly realized by vim.
1771      *
1772      * Also, don't let the scrollbar get focus
1773      */
1774     PtSetArg(&args[ n++ ], Pt_ARG_FLAGS, Pt_DELAY_REALIZE,
1775 	    Pt_DELAY_REALIZE | Pt_GETS_FOCUS);
1776     PtSetArg(&args[ n++ ], Pt_ARG_SCROLLBAR_FLAGS, Pt_SCROLLBAR_SHOW_ARROWS, 0);
1777 #if 0
1778     // Don't need this anchoring for the scrollbars
1779     if (orient == SBAR_HORIZ)
1780     {
1781 	anchor_flags = Pt_BOTTOM_ANCHORED_BOTTOM |
1782 	    Pt_LEFT_ANCHORED_LEFT | Pt_RIGHT_ANCHORED_RIGHT;
1783     }
1784     else
1785     {
1786 	anchor_flags = Pt_BOTTOM_ANCHORED_BOTTOM | Pt_TOP_ANCHORED_TOP;
1787 	if (sb->wp != NULL)
1788 	{
1789 	    if (sb == &sb->wp->w_scrollbars[ SBAR_LEFT ])
1790 		anchor_flags |= Pt_LEFT_ANCHORED_LEFT;
1791 	    else
1792 		anchor_flags |= Pt_RIGHT_ANCHORED_RIGHT;
1793 	}
1794     }
1795     PtSetArg(&args[ n++ ], Pt_ARG_ANCHOR_FLAGS, anchor_flags, Pt_IS_ANCHORED);
1796 #endif
1797     PtSetArg(&args[ n++ ], Pt_ARG_ORIENTATION,
1798 	    (orient == SBAR_HORIZ) ? Pt_HORIZONTAL : Pt_VERTICAL, 0);
1799 #ifdef USE_PANEL_GROUP
1800     sb->id = PtCreateWidget(PtScrollbar, gui.vimPanelGroup, n, args);
1801 #else
1802     sb->id = PtCreateWidget(PtScrollbar, gui.vimContainer, n, args);
1803 #endif
1804 
1805     PtAddCallback(sb->id, Pt_CB_SCROLLBAR_MOVE, gui_ph_handle_scrollbar, sb);
1806 }
1807 
1808     void
1809 gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
1810 {
1811     if (flag != 0)
1812 	PtRealizeWidget(sb->id);
1813     else
1814 	PtUnrealizeWidget(sb->id);
1815 }
1816 
1817     void
1818 gui_mch_destroy_scrollbar(scrollbar_T *sb)
1819 {
1820     PtDestroyWidget(sb->id);
1821     sb->id = NULL;
1822 }
1823 
1824 ////////////////////////////////////////////////////////////////////////////
1825 // Mouse functions
1826 
1827 #if defined(FEAT_MOUSESHAPE) || defined(PROTO)
1828 // The last set mouse pointer shape is remembered, to be used when it goes
1829 // from hidden to not hidden.
1830 static int last_shape = 0;
1831 
1832 // Table for shape IDs.  Keep in sync with the mshape_names[] table in
1833 // misc2.c!
1834 static int mshape_ids[] =
1835 {
1836     Ph_CURSOR_POINTER,		// arrow
1837     Ph_CURSOR_NONE,		// blank
1838     Ph_CURSOR_INSERT,		// beam
1839     Ph_CURSOR_DRAG_VERTICAL,	// updown
1840     Ph_CURSOR_DRAG_VERTICAL,	// udsizing
1841     Ph_CURSOR_DRAG_HORIZONTAL,	// leftright
1842     Ph_CURSOR_DRAG_HORIZONTAL,	// lrsizing
1843     Ph_CURSOR_WAIT,		// busy
1844     Ph_CURSOR_DONT,		// no
1845     Ph_CURSOR_CROSSHAIR,	// crosshair
1846     Ph_CURSOR_FINGER,		// hand1
1847     Ph_CURSOR_FINGER,		// hand2
1848     Ph_CURSOR_FINGER,		// pencil
1849     Ph_CURSOR_QUESTION_POINT,	// question
1850     Ph_CURSOR_POINTER,		// right-arrow
1851     Ph_CURSOR_POINTER,		// up-arrow
1852     Ph_CURSOR_POINTER		// last one
1853 };
1854 
1855     void
1856 mch_set_mouse_shape(int shape)
1857 {
1858     int	    id;
1859 
1860     if (!gui.in_use)
1861 	return;
1862 
1863     if (shape == MSHAPE_HIDE || gui.pointer_hidden)
1864 	PtSetResource(gui.vimTextArea, Pt_ARG_CURSOR_TYPE, Ph_CURSOR_NONE,
1865 		0);
1866     else
1867     {
1868 	if (shape >= MSHAPE_NUMBERED)
1869 	    id = Ph_CURSOR_POINTER;
1870 	else
1871 	    id = mshape_ids[shape];
1872 
1873 	PtSetResource(gui.vimTextArea, Pt_ARG_CURSOR_TYPE, id,	0);
1874     }
1875     if (shape != MSHAPE_HIDE)
1876 	last_shape = shape;
1877 }
1878 #endif
1879 
1880     void
1881 gui_mch_mousehide(int hide)
1882 {
1883     if (gui.pointer_hidden != hide)
1884     {
1885 	gui.pointer_hidden = hide;
1886 #ifdef FEAT_MOUSESHAPE
1887 	if (hide)
1888 	    PtSetResource(gui.vimTextArea, Pt_ARG_CURSOR_TYPE,
1889 		    Ph_CURSOR_NONE, 0);
1890 	else
1891 	    mch_set_mouse_shape(last_shape);
1892 #else
1893 	PtSetResource(gui.vimTextArea, Pt_ARG_CURSOR_TYPE,
1894 		(hide == MOUSE_SHOW) ? GUI_PH_MOUSE_TYPE : Ph_CURSOR_NONE,
1895 		0);
1896 #endif
1897     }
1898 }
1899 
1900     void
1901 gui_mch_getmouse(int *x, int *y)
1902 {
1903     PhCursorInfo_t info;
1904     short ix, iy;
1905 
1906     // FIXME: does this return the correct position,
1907     // with respect to the border?
1908     PhQueryCursor(PhInputGroup(NULL), &info);
1909     PtGetAbsPosition(gui.vimTextArea , &ix, &iy);
1910 
1911     *x = info.pos.x - ix;
1912     *y = info.pos.y - iy;
1913 }
1914 
1915     void
1916 gui_mch_setmouse(int x, int y)
1917 {
1918     short abs_x, abs_y;
1919 
1920     PtGetAbsPosition(gui.vimTextArea, &abs_x, &abs_y);
1921     // Add the border offset?
1922     PhMoveCursorAbs(PhInputGroup(NULL), abs_x + x, abs_y + y);
1923 }
1924 
1925 ////////////////////////////////////////////////////////////////////////////
1926 // Colours
1927 
1928 /*
1929  * Return the RGB value of a pixel as a long.
1930  */
1931     guicolor_T
1932 gui_mch_get_rgb(guicolor_T pixel)
1933 {
1934     return (guicolor_T)(PgRGB(PgRedValue(pixel),
1935 				     PgGreenValue(pixel), PgBlueValue(pixel)));
1936 }
1937 
1938     void
1939 gui_mch_new_colors(void)
1940 {
1941 #if 0 // Don't bother changing the cursor colour
1942     short color_diff;
1943 
1944     /*
1945      * If there isn't enough difference between the background colour and
1946      * the mouse pointer colour then change the mouse pointer colour
1947      */
1948     color_diff = gui_get_lightness(gui_ph_mouse_color)
1949 					  - gui_get_lightness(gui.back_pixel);
1950 
1951     if (abs(color_diff) < 64)
1952     {
1953 	short r, g, b;
1954 	// not a great algorithm...
1955 	r = PgRedValue(gui_ph_mouse_color) ^ 255;
1956 	g = PgGreenValue(gui_ph_mouse_color) ^ 255;
1957 	b = PgBlueValue(gui_ph_mouse_color) ^ 255;
1958 
1959 #ifndef FEAT_MOUSESHAPE
1960 	gui_ph_mouse_color = PgRGB(r, g, b);
1961 	PtSetResource(gui.vimTextArea, Pt_ARG_CURSOR_COLOR,
1962 		gui_ph_mouse_color, 0);
1963 #endif
1964     }
1965 #endif
1966 
1967     PtSetResource(gui.vimTextArea, Pt_ARG_FILL_COLOR, gui.back_pixel, 0);
1968 }
1969 
1970 /*
1971  * This should be split out into a separate file,
1972  * every port does basically the same thing.
1973  *
1974  * This is the gui_w32.c version (i think..)
1975  * Return INVALCOLOR when failed.
1976  */
1977 
1978     guicolor_T
1979 gui_mch_get_color(char_u *name)
1980 {
1981     return gui_get_color_cmn(name);
1982 }
1983 
1984     guicolor_T
1985 gui_mch_get_rgb_color(int r, int g, int b)
1986 {
1987     return gui_get_rgb_color_cmn(r, g, b);
1988 }
1989 
1990     void
1991 gui_mch_set_fg_color(guicolor_T color)
1992 {
1993     PgSetTextColor(color);
1994 }
1995 
1996     void
1997 gui_mch_set_bg_color(guicolor_T color)
1998 {
1999     PgSetFillColor(color);
2000 }
2001 
2002     void
2003 gui_mch_set_sp_color(guicolor_T color)
2004 {
2005 }
2006 
2007     void
2008 gui_mch_invert_rectangle(int row, int col, int nr, int nc)
2009 {
2010     PhRect_t rect;
2011 
2012     rect.ul.x = FILL_X(col);
2013     rect.ul.y = FILL_Y(row);
2014 
2015     // FIXME: This has an off by one pixel problem
2016     rect.lr.x = rect.ul.x + nc * gui.char_width;
2017     rect.lr.y = rect.ul.y + nr * gui.char_height;
2018     if (nc > 0)
2019 	rect.lr.x -= 1;
2020     if (nr > 0)
2021 	rect.lr.y -= 1;
2022 
2023     DRAW_START;
2024     PgSetDrawMode(Pg_DrawModeDSTINVERT);
2025     PgDrawRect(&rect, Pg_DRAW_FILL);
2026     PgSetDrawMode(Pg_DrawModeSRCCOPY);
2027     DRAW_END;
2028 }
2029 
2030     void
2031 gui_mch_clear_block(int row1, int col1, int row2, int col2)
2032 {
2033     PhRect_t block = {
2034 	{ FILL_X(col1), FILL_Y(row1) },
2035 	{ FILL_X(col2 + 1) - 1, FILL_Y(row2 + 1) - 1}
2036     };
2037 
2038     DRAW_START;
2039     gui_mch_set_bg_color(gui.back_pixel);
2040     PgDrawRect(&block, Pg_DRAW_FILL);
2041     DRAW_END;
2042 }
2043 
2044     void
2045 gui_mch_clear_all(void)
2046 {
2047     PhRect_t text_rect = {
2048 	{ gui.border_width, gui.border_width },
2049 	{ Columns * gui.char_width + gui.border_width - 1 ,
2050 	    Rows * gui.char_height + gui.border_width - 1 }
2051     };
2052 
2053     if (is_ignore_draw == TRUE)
2054 	return;
2055 
2056     DRAW_START;
2057     gui_mch_set_bg_color(gui.back_pixel);
2058     PgDrawRect(&text_rect, Pg_DRAW_FILL);
2059     DRAW_END;
2060 }
2061 
2062     void
2063 gui_mch_delete_lines(int row, int num_lines)
2064 {
2065     PhRect_t    rect;
2066     PhPoint_t   delta;
2067 
2068     rect.ul.x = FILL_X(gui.scroll_region_left);
2069     rect.ul.y = FILL_Y(row + num_lines);
2070 
2071     rect.lr.x = FILL_X(gui.scroll_region_right + 1) - 1;
2072     rect.lr.y = FILL_Y(gui.scroll_region_bot + 1) - 1;
2073 
2074     PtWidgetOffset(gui.vimTextArea, &gui_ph_raw_offset);
2075     PhTranslatePoint(&gui_ph_raw_offset, PtWidgetPos(gui.vimTextArea, NULL));
2076     PhTranslateRect(&rect, &gui_ph_raw_offset);
2077 
2078     delta.x = 0;
2079     delta.y = -num_lines * gui.char_height;
2080 
2081     PgFlush();
2082 
2083     PhBlit(PtWidgetRid(PtFindDisjoint(gui.vimTextArea)), &rect, &delta);
2084 
2085     gui_clear_block(
2086 	gui.scroll_region_bot - num_lines + 1,
2087 	gui.scroll_region_left,
2088 	gui.scroll_region_bot,
2089 	gui.scroll_region_right);
2090 }
2091 
2092     void
2093 gui_mch_insert_lines(int row, int num_lines)
2094 {
2095     PhRect_t    rect;
2096     PhPoint_t   delta;
2097 
2098     rect.ul.x = FILL_X(gui.scroll_region_left);
2099     rect.ul.y = FILL_Y(row);
2100 
2101     rect.lr.x = FILL_X(gui.scroll_region_right + 1) - 1;
2102     rect.lr.y = FILL_Y(gui.scroll_region_bot - num_lines + 1) - 1;
2103 
2104     PtWidgetOffset(gui.vimTextArea, &gui_ph_raw_offset);
2105     PhTranslatePoint(&gui_ph_raw_offset, PtWidgetPos(gui.vimTextArea, NULL));
2106     PhTranslateRect(&rect, &gui_ph_raw_offset);
2107 
2108     delta.x = 0;
2109     delta.y = num_lines * gui.char_height;
2110 
2111     PgFlush();
2112 
2113     PhBlit(PtWidgetRid(PtFindDisjoint(gui.vimTextArea)) , &rect, &delta);
2114 
2115     gui_clear_block(row, gui.scroll_region_left,
2116 	    row + num_lines - 1, gui.scroll_region_right);
2117 }
2118 
2119     void
2120 gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
2121 {
2122     static char *utf8_buffer = NULL;
2123     static int	utf8_len = 0;
2124 
2125     PhPoint_t	pos = { TEXT_X(col), TEXT_Y(row) };
2126     PhRect_t	rect;
2127 
2128     if (is_ignore_draw == TRUE)
2129 	return;
2130 
2131     DRAW_START;
2132 
2133     if (!(flags & DRAW_TRANSP))
2134     {
2135 	PgDrawIRect(
2136 		FILL_X(col), FILL_Y(row),
2137 		FILL_X(col + len) - 1, FILL_Y(row + 1) - 1,
2138 		Pg_DRAW_FILL);
2139     }
2140 
2141     if (flags & DRAW_UNDERL)
2142 	PgSetUnderline(gui.norm_pixel, Pg_TRANSPARENT, 0);
2143 
2144     if (charset_translate != NULL && enc_utf8 == 0)
2145     {
2146 	int src_taken, dst_made;
2147 
2148 	// Use a static buffer to avoid large amounts of de/allocations
2149 	if (utf8_len < len)
2150 	{
2151 	    utf8_buffer = realloc(utf8_buffer, len * MB_LEN_MAX);
2152 	    utf8_len = len;
2153 	}
2154 
2155 	PxTranslateToUTF(
2156 		charset_translate,
2157 		s,
2158 		len,
2159 		&src_taken,
2160 		utf8_buffer,
2161 		utf8_len,
2162 		&dst_made);
2163 	s = utf8_buffer;
2164 	len = dst_made;
2165     }
2166 
2167     PgDrawText(s, len, &pos, 0);
2168 
2169     if (flags & DRAW_BOLD)
2170     {
2171 	// FIXME: try and only calculate these values once...
2172 	rect.ul.x = FILL_X(col) + 1;
2173 	rect.ul.y = FILL_Y(row);
2174 	rect.lr.x = FILL_X(col + len) - 1;
2175 	rect.lr.y = FILL_Y(row + 1) - 1;
2176 	// PgSetUserClip(NULL) causes the scrollbar to not redraw...
2177 #if 0
2178 	pos.x++;
2179 
2180 	PgSetUserClip(&rect);
2181 	PgDrawText(s, len, &pos, 0);
2182 	PgSetUserClip(NULL);
2183 #else
2184 	rect.lr.y -= (p_linespace + 1) / 2;
2185 	// XXX: DrawTextArea doesn't work with phditto
2186 	PgDrawTextArea(s, len, &rect, Pg_TEXT_BOTTOM);
2187 #endif
2188     }
2189 
2190     if (flags & DRAW_UNDERL)
2191 	PgSetUnderline(Pg_TRANSPARENT, Pg_TRANSPARENT, 0);
2192 
2193     DRAW_END;
2194 }
2195 
2196 ////////////////////////////////////////////////////////////////////////////
2197 // Cursor
2198 
2199     void
2200 gui_mch_draw_hollow_cursor(guicolor_T color)
2201 {
2202     PhRect_t r;
2203 
2204     // FIXME: Double width characters
2205 
2206     r.ul.x = FILL_X(gui.col);
2207     r.ul.y = FILL_Y(gui.row);
2208     r.lr.x = r.ul.x + gui.char_width - 1;
2209     r.lr.y = r.ul.y + gui.char_height - 1;
2210 
2211     DRAW_START;
2212     PgSetStrokeColor(color);
2213     PgDrawRect(&r, Pg_DRAW_STROKE);
2214     DRAW_END;
2215 }
2216 
2217     void
2218 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
2219 {
2220     PhRect_t r;
2221 
2222     r.ul.x = FILL_X(gui.col);
2223     r.ul.y = FILL_Y(gui.row) + gui.char_height - h;
2224     r.lr.x = r.ul.x + w - 1;
2225     r.lr.y = r.ul.y + h - 1;
2226 
2227     DRAW_START;
2228     gui_mch_set_bg_color(color);
2229     PgDrawRect(&r, Pg_DRAW_FILL);
2230     DRAW_END;
2231 }
2232 
2233     int
2234 gui_mch_is_blinking(void)
2235 {
2236     return blink_state != BLINK_NONE;
2237 }
2238 
2239     int
2240 gui_mch_is_blink_off(void)
2241 {
2242     return blink_state == BLINK_OFF;
2243 }
2244 
2245     void
2246 gui_mch_set_blinking(long wait, long on, long off)
2247 {
2248     blink_waittime = wait;
2249     blink_ontime = on;
2250     blink_offtime = off;
2251 }
2252 
2253     void
2254 gui_mch_start_blink(void)
2255 {
2256     // Only turn on the timer on if none of the times are zero
2257     if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
2258     {
2259 	PtSetResource(gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL,
2260 		blink_waittime, 0);
2261 	blink_state = BLINK_ON;
2262 	gui_update_cursor(TRUE, FALSE);
2263     }
2264 }
2265 
2266     void
2267 gui_mch_stop_blink(int may_call_gui_update_cursor)
2268 {
2269     PtSetResource(gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL, 0, 0);
2270 
2271     if (blink_state == BLINK_OFF && may_call_gui_update_cursor)
2272 	gui_update_cursor(TRUE, FALSE);
2273 
2274     blink_state = BLINK_NONE;
2275 }
2276 
2277 ////////////////////////////////////////////////////////////////////////////
2278 // miscellaneous functions
2279 
2280     void
2281 gui_mch_beep(void)
2282 {
2283     PtBeep();
2284 }
2285 
2286     void
2287 gui_mch_flash(int msec)
2288 {
2289     PgSetFillXORColor(Pg_BLACK, Pg_WHITE);
2290     PgSetDrawMode(Pg_DRAWMODE_XOR);
2291     gui_mch_clear_all();
2292     gui_mch_flush();
2293 
2294     ui_delay((long) msec, TRUE);
2295 
2296     gui_mch_clear_all();
2297     PgSetDrawMode(Pg_DRAWMODE_OPAQUE);
2298     gui_mch_flush();
2299 }
2300 
2301     void
2302 gui_mch_flush(void)
2303 {
2304     PgFlush();
2305 }
2306 
2307     void
2308 gui_mch_set_text_area_pos(int x, int y, int w, int h)
2309 {
2310     PhArea_t area = {{x, y}, {w, h}};
2311 
2312     PtSetResource(gui.vimTextArea, Pt_ARG_AREA, &area, 0);
2313 }
2314 
2315     int
2316 gui_mch_haskey(char_u *name)
2317 {
2318     int i;
2319 
2320     for (i = 0; special_keys[i].key_sym != 0; i++)
2321 	if (name[0] == special_keys[i].vim_code0 &&
2322 		 name[1] == special_keys[i].vim_code1)
2323 	    return OK;
2324     return FAIL;
2325 }
2326 
2327 ////////////////////////////////////////////////////////////////////////////
2328 // Menu
2329 
2330 #ifdef FEAT_TOOLBAR
2331 #include "toolbar.phi"
2332 
2333 static PhImage_t *gui_ph_toolbar_images[] = {
2334     &tb_new_phi,
2335     &tb_open_phi,
2336     &tb_save_phi,
2337     &tb_undo_phi,
2338     &tb_redo_phi,
2339     &tb_cut_phi,
2340     &tb_copy_phi,
2341     &tb_paste_phi,
2342     &tb_print_phi,
2343     &tb_help_phi,
2344     &tb_find_phi,
2345     &tb_save_all_phi,
2346     &tb_save_session_phi,
2347     &tb_new_session_phi,
2348     &tb_load_session_phi,
2349     &tb_macro_phi,
2350     &tb_replace_phi,
2351     &tb_close_phi,
2352     &tb_maximize_phi,
2353     &tb_minimize_phi,
2354     &tb_split_phi,
2355     &tb_shell_phi,
2356     &tb_find_prev_phi,
2357     &tb_find_next_phi,
2358     &tb_find_help_phi,
2359     &tb_make_phi,
2360     &tb_jump_phi,
2361     &tb_ctags_phi,
2362     &tb_vsplit_phi,
2363     &tb_maxwidth_phi,
2364     &tb_minwidth_phi
2365 };
2366 
2367 static PhImage_t *
2368 gui_ph_toolbar_load_icon(char_u *iconfile)
2369 {
2370     static PhImage_t external_icon;
2371     PhImage_t *temp_phi = NULL;
2372 
2373     temp_phi = PxLoadImage(iconfile, NULL);
2374     if (temp_phi != NULL)
2375     {
2376 	// The label widget will free the image/palette/etc. for us when
2377 	// it's destroyed
2378 	temp_phi->flags |= Ph_RELEASE_IMAGE_ALL;
2379 	memcpy(&external_icon, temp_phi, sizeof(external_icon));
2380 	free(temp_phi);
2381 
2382 	temp_phi = &external_icon;
2383     }
2384     return temp_phi;
2385 }
2386 
2387 /*
2388  * This returns either a builtin icon image, an external image or NULL
2389  * if it can't find either.  The caller can't and doesn't need to try and
2390  * free() the returned image, and it can't store the image pointer.
2391  * (When setting the Pt_ARG_LABEL_IMAGE resource, the contents of the
2392  * PhImage_t are copied, and the original PhImage_t aren't needed anymore).
2393  */
2394 static PhImage_t *
2395 gui_ph_toolbar_find_icon(vimmenu_T *menu)
2396 {
2397     char_u full_pathname[ MAXPATHL + 1 ];
2398     PhImage_t *icon = NULL;
2399 
2400     if (menu->icon_builtin == FALSE)
2401     {
2402 	if (menu->iconfile != NULL)
2403 	    // TODO: use gui_find_iconfile()
2404 	    icon = gui_ph_toolbar_load_icon(menu->iconfile);
2405 
2406 	// TODO: Restrict loading to just .png? Search for any format?
2407 	if ((icon == NULL) &&
2408 	    ((gui_find_bitmap(menu->name, full_pathname, "gif") == OK) ||
2409 	      (gui_find_bitmap(menu->name, full_pathname, "png") == OK)))
2410 	    icon = gui_ph_toolbar_load_icon(full_pathname);
2411 
2412 	if (icon != NULL)
2413 	    return icon;
2414     }
2415 
2416     if (menu->iconidx >= 0 &&
2417 	    (menu->iconidx < ARRAY_LENGTH(gui_ph_toolbar_images)))
2418 	return gui_ph_toolbar_images[menu->iconidx];
2419 
2420     return NULL;
2421 }
2422 #endif
2423 
2424 #if defined(FEAT_MENU) || defined(PROTO)
2425     void
2426 gui_mch_enable_menu(int flag)
2427 {
2428     if (flag != 0)
2429 	PtRealizeWidget(gui.vimMenuBar);
2430     else
2431 	PtUnrealizeWidget(gui.vimMenuBar);
2432 }
2433 
2434     void
2435 gui_mch_set_menu_pos(int x, int y, int w, int h)
2436 {
2437     // Nothing
2438 }
2439 
2440 /*
2441  * Change the position of a menu button in the parent
2442  */
2443     static void
2444 gui_ph_position_menu(PtWidget_t *widget, int priority)
2445 {
2446     PtWidget_t	*traverse;
2447     vimmenu_T	*menu;
2448 
2449     traverse = PtWidgetChildBack(PtWidgetParent(widget));
2450 
2451     // Iterate through the list of widgets in traverse, until
2452     // we find the position we want to insert our widget into
2453     // TODO: traverse from front to back, possible speedup?
2454     while (traverse != NULL)
2455     {
2456 	PtGetResource(traverse, Pt_ARG_POINTER, &menu, 0);
2457 
2458 	if (menu != NULL &&
2459 		priority < menu->priority &&
2460 		widget != traverse)
2461 	{
2462 	    // Insert the widget before the current traverse widget
2463 	    PtWidgetInsert(widget, traverse, 1);
2464 	    return;
2465 	}
2466 
2467 	traverse = PtWidgetBrotherInFront(traverse);
2468     }
2469 }
2470 
2471 /*
2472  * the index is ignored because it's not useful for our purposes
2473  */
2474     void
2475 gui_mch_add_menu(vimmenu_T *menu, int index)
2476 {
2477     vimmenu_T	*parent = menu->parent;
2478     char_u	*accel_key;
2479     char_u	mnemonic_str[MB_LEN_MAX];
2480     int	    n;
2481     PtArg_t args[5];
2482 
2483     menu->submenu_id = menu->id = NULL;
2484 
2485     if (menu_is_menubar(menu->name))
2486     {
2487 
2488 	accel_key = vim_strchr(menu->name, '&');
2489 	if (accel_key != NULL)
2490 	{
2491 	    mnemonic_str[0] = accel_key[1];
2492 	    mnemonic_str[1] = NUL;
2493 	}
2494 
2495 	// Create the menu button
2496 	n = 0;
2497 	PtSetArg(&args[ n++ ], Pt_ARG_TEXT_STRING, menu->dname, 0);
2498 	PtSetArg(&args[ n++ ], Pt_ARG_ACCEL_TEXT, menu->actext, 0);
2499 	if (accel_key != NULL)
2500 	    PtSetArg(&args[ n++ ], Pt_ARG_ACCEL_KEY, mnemonic_str, 0);
2501 	PtSetArg(&args[ n++ ], Pt_ARG_POINTER, menu, 0);
2502 
2503 	if (parent != NULL)
2504 	    PtSetArg(&args[ n++ ], Pt_ARG_BUTTON_TYPE, Pt_MENU_RIGHT, 0);
2505 
2506 	menu->id = PtCreateWidget(PtMenuButton,
2507 		(parent == NULL) ? gui.vimMenuBar : parent->submenu_id,
2508 		n, args);
2509 
2510 	PtAddCallback(menu->id, Pt_CB_ARM, gui_ph_handle_pulldown_menu, menu);
2511 
2512 	// Create the actual menu
2513 	n = 0;
2514 	if (parent != NULL)
2515 	    PtSetArg(&args[ n++ ], Pt_ARG_MENU_FLAGS, Pt_TRUE, Pt_MENU_CHILD);
2516 
2517 	menu->submenu_id = PtCreateWidget(PtMenu, menu->id, n, args);
2518 
2519 	if (parent == NULL)
2520 	{
2521 	    PtAddCallback(menu->submenu_id, Pt_CB_UNREALIZED,
2522 		    gui_ph_handle_menu_unrealized, menu);
2523 
2524 	    if (menu->mnemonic != 0)
2525 	    {
2526 		PtAddHotkeyHandler(gui.vimWindow, tolower(menu->mnemonic),
2527 			Pk_KM_Alt, 0, menu, gui_ph_handle_pulldown_menu);
2528 	    }
2529 	}
2530 
2531 	gui_ph_position_menu(menu->id, menu->priority);
2532 
2533 	// Redraw menubar here instead of gui_mch_draw_menubar
2534 	if (gui.menu_is_active)
2535 	    PtRealizeWidget(menu->id);
2536     }
2537     else if (menu_is_popup(menu->name))
2538     {
2539 	menu->submenu_id = PtCreateWidget(PtMenu, gui.vimWindow, 0, NULL);
2540 	PtAddCallback(menu->submenu_id, Pt_CB_UNREALIZED,
2541 		gui_ph_handle_menu_unrealized, menu);
2542     }
2543 }
2544 
2545     void
2546 gui_mch_add_menu_item(vimmenu_T *menu, int index)
2547 {
2548     vimmenu_T	*parent = menu->parent;
2549     char_u	*accel_key;
2550     char_u	mnemonic_str[MB_LEN_MAX];
2551     int	    n;
2552     PtArg_t args[13];
2553 
2554     n = 0;
2555     PtSetArg(&args[ n++ ], Pt_ARG_POINTER, menu, 0);
2556 
2557 #ifdef FEAT_TOOLBAR
2558     if (menu_is_toolbar(parent->name))
2559     {
2560 	if (menu_is_separator(menu->name))
2561 	{
2562 	    PtSetArg(&args[ n++ ], Pt_ARG_SEP_FLAGS,
2563 		    Pt_SEP_VERTICAL, Pt_SEP_ORIENTATION);
2564 	    PtSetArg(&args[ n++ ], Pt_ARG_SEP_TYPE, Pt_ETCHED_IN, 0);
2565 	    PtSetArg(&args[ n++ ], Pt_ARG_ANCHOR_FLAGS,
2566 		    Pt_TRUE, Pt_ANCHOR_TOP_BOTTOM);
2567 	    PtSetArg(&args[ n++ ], Pt_ARG_WIDTH, 2, 0);
2568 	    menu->id = PtCreateWidget(PtSeparator, gui.vimToolBar, n, args);
2569 	}
2570 	else
2571 	{
2572 	    if (strstr((const char *) p_toolbar, "text") != NULL)
2573 	    {
2574 		PtSetArg(&args[ n++ ], Pt_ARG_BALLOON_POSITION,
2575 			Pt_BALLOON_BOTTOM, 0);
2576 		PtSetArg(&args[ n++ ], Pt_ARG_TEXT_STRING, menu->dname, 0);
2577 		PtSetArg(&args[ n++ ], Pt_ARG_TEXT_FONT, "TextFont08", 0);
2578 	    }
2579 	    if ((strstr((const char *) p_toolbar, "icons") != NULL) &&
2580 		    (gui_ph_toolbar_images != NULL))
2581 	    {
2582 		PtSetArg(&args[ n++ ], Pt_ARG_LABEL_IMAGE,
2583 			gui_ph_toolbar_find_icon(menu), 0);
2584 		PtSetArg(&args[ n++ ], Pt_ARG_LABEL_TYPE, Pt_TEXT_IMAGE, 0);
2585 		PtSetArg(&args[ n++ ], Pt_ARG_TEXT_IMAGE_SPACING, 0, 0);
2586 	    }
2587 	    if (strstr((const char *) p_toolbar, "tooltips") != NULL)
2588 	    {
2589 		PtSetArg(&args[ n++ ], Pt_ARG_LABEL_BALLOON,
2590 			gui_ph_show_tooltip, 0);
2591 		PtSetArg(&args[ n++ ], Pt_ARG_LABEL_FLAGS,
2592 			Pt_TRUE, Pt_SHOW_BALLOON);
2593 	    }
2594 	    PtSetArg(&args[ n++ ], Pt_ARG_MARGIN_HEIGHT, 1, 0);
2595 	    PtSetArg(&args[ n++ ], Pt_ARG_MARGIN_WIDTH, 1, 0);
2596 	    PtSetArg(&args[ n++ ], Pt_ARG_FLAGS, Pt_FALSE,
2597 		    Pt_HIGHLIGHTED | Pt_GETS_FOCUS);
2598 	    PtSetArg(&args[ n++ ], Pt_ARG_FILL_COLOR, Pg_TRANSPARENT, 0);
2599 	    menu->id = PtCreateWidget(PtButton, gui.vimToolBar, n, args);
2600 
2601 	    PtAddCallback(menu->id, Pt_CB_ACTIVATE, gui_ph_handle_menu, menu);
2602 	}
2603 	// Update toolbar if it's open
2604 	if (PtWidgetIsRealized(gui.vimToolBar))
2605 	    PtRealizeWidget(menu->id);
2606     }
2607     else
2608 #endif
2609 	if (menu_is_separator(menu->name))
2610     {
2611 	menu->id = PtCreateWidget(PtSeparator, parent->submenu_id, n, args);
2612     }
2613     else
2614     {
2615 	accel_key = vim_strchr(menu->name, '&');
2616 	if (accel_key != NULL)
2617 	{
2618 	    mnemonic_str[0] = accel_key[1];
2619 	    mnemonic_str[1] = NUL;
2620 	}
2621 
2622 	PtSetArg(&args[ n++ ], Pt_ARG_TEXT_STRING, menu->dname, 0);
2623 	if (accel_key != NULL)
2624 	    PtSetArg(&args[ n++ ], Pt_ARG_ACCEL_KEY, mnemonic_str,
2625 		    0);
2626 
2627 	PtSetArg(&args[ n++ ], Pt_ARG_ACCEL_TEXT, menu->actext, 0);
2628 
2629 	menu->id = PtCreateWidget(PtMenuButton, parent->submenu_id, n, args);
2630 
2631 	PtAddCallback(menu->id, Pt_CB_ACTIVATE, gui_ph_handle_menu, menu);
2632 
2633 #ifdef USE_PANEL_GROUP
2634 	if (gui_ph_is_buffer_item(menu, parent) == TRUE)
2635 	{
2636 	    PtAddCallback(menu->id, Pt_CB_DESTROYED,
2637 		    gui_ph_handle_buffer_remove, menu);
2638 	    gui_ph_pg_add_buffer(menu->dname);
2639 	}
2640 #endif
2641     }
2642 
2643     gui_ph_position_menu(menu->id, menu->priority);
2644 }
2645 
2646     void
2647 gui_mch_destroy_menu(vimmenu_T *menu)
2648 {
2649     if (menu->submenu_id != NULL)
2650 	PtDestroyWidget(menu->submenu_id);
2651     if (menu->id != NULL)
2652 	PtDestroyWidget(menu->id);
2653 
2654     menu->submenu_id = NULL;
2655     menu->id = NULL;
2656 }
2657 
2658     void
2659 gui_mch_menu_grey(vimmenu_T *menu, int grey)
2660 {
2661     long    flags, mask, fields;
2662 
2663     if (menu->id == NULL)
2664 	return;
2665 
2666     flags = PtWidgetFlags(menu->id);
2667     if (PtWidgetIsClass(menu->id, PtMenuButton) &&
2668 	    PtWidgetIsClass(PtWidgetParent(menu->id), PtMenu))
2669     {
2670 	fields = Pt_FALSE;
2671 	mask = Pt_SELECTABLE | Pt_HIGHLIGHTED;
2672     }
2673     else
2674     {
2675 	fields = Pt_TRUE;
2676 	mask = Pt_BLOCKED | Pt_GHOST;
2677     }
2678 
2679     if (! grey)
2680 	fields = ~fields;
2681 
2682     PtSetResource(menu->id, Pt_ARG_FLAGS, fields,
2683 	    mask);
2684 }
2685 
2686     void
2687 gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
2688 {
2689     // TODO: [un]realize the widget?
2690 }
2691 
2692     void
2693 gui_mch_draw_menubar(void)
2694 {
2695     // The only time a redraw is needed is when a menu button
2696     // is added to the menubar, and that is detected and the bar
2697     // redrawn in gui_mch_add_menu_item
2698 }
2699 
2700     void
2701 gui_mch_show_popupmenu(vimmenu_T *menu)
2702 {
2703     PtSetResource(menu->submenu_id, Pt_ARG_POS, &abs_mouse, 0);
2704     PtRealizeWidget(menu->submenu_id);
2705 }
2706 
2707     void
2708 gui_mch_toggle_tearoffs(int enable)
2709 {
2710     // No tearoffs yet
2711 }
2712 
2713 #endif
2714 
2715 #if defined(FEAT_TOOLBAR) || defined(PROTO)
2716     void
2717 gui_mch_show_toolbar(int showit)
2718 {
2719     if (showit)
2720 	PtRealizeWidget(gui.vimToolBar);
2721     else
2722 	PtUnrealizeWidget(gui.vimToolBar);
2723 }
2724 #endif
2725 
2726 ////////////////////////////////////////////////////////////////////////////
2727 // Fonts
2728 
2729     static GuiFont
2730 gui_ph_get_font(
2731 	char_u	*font_name,
2732 	int_u	font_flags,
2733 	int_u	font_size,
2734 	// Check whether the resulting font has the font flags and size that
2735 	// was asked for
2736 	int_u	enforce
2737 	)
2738 {
2739     char_u	    *font_tag;
2740     FontQueryInfo   info;
2741     int_u	    style;
2742 
2743     font_tag = alloc(MAX_FONT_TAG);
2744     if (font_tag != NULL)
2745     {
2746 	if (PfGenerateFontName(font_name, font_flags, font_size,
2747 		    font_tag) != NULL)
2748 	{
2749 	    // Enforce some limits on the font used
2750 	    style = PHFONT_INFO_FIXED;
2751 
2752 	    if (enforce & PF_STYLE_BOLD)
2753 		style |= PHFONT_INFO_BOLD;
2754 	    if (enforce & PF_STYLE_ANTIALIAS)
2755 		style |= PHFONT_INFO_ALIAS;
2756 	    if (enforce & PF_STYLE_ITALIC)
2757 		style |= PHFONT_INFO_ITALIC;
2758 
2759 	    PfQueryFontInfo(font_tag, &info);
2760 
2761 	    if (info.size == 0)
2762 		font_size = 0;
2763 
2764 	    // Make sure font size matches, and that the font style
2765 	    // at least has the bits we're checking for
2766 	    if (font_size == info.size &&
2767 		    style == (info.style & style))
2768 		return (GuiFont)font_tag;
2769 	}
2770 	vim_free(font_tag);
2771     }
2772     return NULL;
2773 }
2774 
2775 /*
2776  * Split up the vim font name
2777  *
2778  * vim_font is in the form of
2779  * <name>:s<height>:a:b:i
2780  *
2781  * a = antialias
2782  * b = bold
2783  * i = italic
2784  *
2785  */
2786 
2787     static int
2788 gui_ph_parse_font_name(
2789 	char_u *vim_font,
2790 	char_u **font_name,
2791 	int_u *font_flags,
2792 	int_u *font_size)
2793 {
2794     char_u  *mark;
2795     int_u   name_len, size;
2796 
2797     mark = vim_strchr(vim_font, ':');
2798     if (mark == NULL)
2799 	name_len = STRLEN(vim_font);
2800     else
2801 	name_len = (int_u) (mark - vim_font);
2802 
2803     *font_name = vim_strnsave(vim_font, name_len);
2804     if (*font_name != NULL)
2805     {
2806 	if (mark != NULL)
2807 	{
2808 	    while (*mark != NUL && *mark++ == ':')
2809 	    {
2810 		switch (tolower(*mark++))
2811 		{
2812 		    case 'a': *font_flags |= PF_STYLE_ANTIALIAS; break;
2813 		    case 'b': *font_flags |= PF_STYLE_BOLD; break;
2814 		    case 'i': *font_flags |= PF_STYLE_ITALIC; break;
2815 
2816 		    case 's':
2817 			size = getdigits(&mark);
2818 			// Restrict the size to some vague limits
2819 			if (size < 1 || size > 100)
2820 			    size = 8;
2821 
2822 			*font_size = size;
2823 			break;
2824 
2825 		    default:
2826 			break;
2827 		}
2828 	    }
2829 	}
2830 	return TRUE;
2831     }
2832     return FALSE;
2833 }
2834 
2835     int
2836 gui_mch_init_font(char_u *vim_font_name, int fontset)
2837 {
2838     char_u  *font_tag;
2839     char_u  *font_name = NULL;
2840     int_u   font_flags = 0;
2841     int_u   font_size  = 12;
2842 
2843     FontQueryInfo info;
2844     PhRect_t extent;
2845 
2846     if (vim_font_name == NULL)
2847     {
2848 	// Default font
2849 	vim_font_name = "PC Terminal";
2850     }
2851 
2852     if (STRCMP(vim_font_name, "*") == 0)
2853     {
2854 	font_tag = PtFontSelection(gui.vimWindow, NULL, NULL,
2855 		"pcterm12", -1, PHFONT_FIXED, NULL);
2856 
2857 	if (font_tag == NULL)
2858 	    return FAIL;
2859 
2860 	gui_mch_free_font(gui.norm_font);
2861 	gui.norm_font = font_tag;
2862 
2863 	PfQueryFontInfo(font_tag, &info);
2864 	font_name = vim_strsave(info.font);
2865     }
2866     else
2867     {
2868 	if (gui_ph_parse_font_name(vim_font_name, &font_name, &font_flags,
2869 		    &font_size) == FALSE)
2870 	    return FAIL;
2871 
2872 	font_tag = gui_ph_get_font(font_name, font_flags, font_size, 0);
2873 	if (font_tag == NULL)
2874 	{
2875 	    vim_free(font_name);
2876 	    return FAIL;
2877 	}
2878 
2879 	gui_mch_free_font(gui.norm_font);
2880 	gui.norm_font = font_tag;
2881     }
2882 
2883     gui_mch_free_font(gui.bold_font);
2884     gui.bold_font = gui_ph_get_font(font_name, font_flags | PF_STYLE_BOLD,
2885 	    font_size, PF_STYLE_BOLD);
2886 
2887     gui_mch_free_font(gui.ital_font);
2888     gui.ital_font = gui_ph_get_font(font_name, font_flags | PF_STYLE_ITALIC,
2889 	    font_size, PF_STYLE_ITALIC);
2890 
2891     // This extent was brought to you by the letter 'g'
2892     PfExtentText(&extent, NULL, font_tag, "g", 1);
2893 
2894     gui.char_width = extent.lr.x - extent.ul.x + 1;
2895     gui.char_height = (- extent.ul.y) + extent.lr.y + 1;
2896     gui.char_ascent = - extent.ul.y;
2897 
2898     vim_free(font_name);
2899     return OK;
2900 }
2901 
2902 /*
2903  * Adjust gui.char_height (after 'linespace' was changed).
2904  */
2905     int
2906 gui_mch_adjust_charheight(void)
2907 {
2908     FontQueryInfo info;
2909 
2910     PfQueryFontInfo(gui.norm_font, &info);
2911 
2912     gui.char_height = - info.ascender + info.descender + p_linespace;
2913     gui.char_ascent = - info.ascender + p_linespace / 2;
2914 
2915     return OK;
2916 }
2917 
2918     GuiFont
2919 gui_mch_get_font(char_u *vim_font_name, int report_error)
2920 {
2921     char_u  *font_name;
2922     char_u  *font_tag;
2923     int_u   font_size = 12;
2924     int_u   font_flags = 0;
2925 
2926     if (gui_ph_parse_font_name(vim_font_name, &font_name, &font_flags,
2927 		&font_size) != FALSE)
2928     {
2929 	font_tag = gui_ph_get_font(font_name, font_flags, font_size, -1);
2930 	vim_free(font_name);
2931 
2932 	if (font_tag != NULL)
2933 	    return (GuiFont)font_tag;
2934     }
2935 
2936     if (report_error)
2937 	semsg(e_font, vim_font_name);
2938 
2939     return FAIL;
2940 }
2941 
2942 #if defined(FEAT_EVAL) || defined(PROTO)
2943 /*
2944  * Return the name of font "font" in allocated memory.
2945  * Don't know how to get the actual name, thus use the provided name.
2946  */
2947     char_u *
2948 gui_mch_get_fontname(GuiFont font, char_u *name)
2949 {
2950     if (name == NULL)
2951 	return NULL;
2952     return vim_strsave(name);
2953 }
2954 #endif
2955 
2956     void
2957 gui_mch_set_font(GuiFont font)
2958 {
2959     PgSetFont(font);
2960 }
2961 
2962     void
2963 gui_mch_free_font(GuiFont font)
2964 {
2965     vim_free(font);
2966 }
2967 
2968