1/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved		by Bram Moolenaar
4 *				GUI support by Robert Webb
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 * See README.txt for an overview of the Vim source code.
9 */
10/*
11 * gui_w16.c
12 *
13 * GUI support for Microsoft Windows 3.1x
14 *
15 * George V. Reilly <george@reilly.org> wrote the original Win32 GUI.
16 * Robert Webb reworked it to use the existing GUI stuff and added menu,
17 * scrollbars, etc.
18 *
19 * Vince Negri then butchered the code to get it compiling for
20 * 16-bit windows.
21 *
22 */
23
24/*
25 * Include the common stuff for MS-Windows GUI.
26 */
27#include "gui_w48.c"
28
29#include "guiw16rc.h"
30
31/* Undocumented Windows Message - not even defined in some SDK headers */
32#define WM_EXITSIZEMOVE			0x0232
33
34
35#ifdef FEAT_TOOLBAR
36# define CMD_TB_BASE (99)
37# include <vimtbar.h>
38#endif
39
40#ifdef PROTO
41# define WINAPI
42#endif
43
44#define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
45    ((fn)((hwnd), (HDROP)(wParam)), 0L)
46
47
48/* Local variables: */
49
50#ifdef FEAT_MENU
51static UINT	s_menu_id = 100;
52#endif
53
54
55#define VIM_NAME	"vim"
56#define VIM_CLASS	"Vim"
57
58#define DLG_ALLOC_SIZE 16 * 1024
59
60/*
61 * stuff for dialogs, menus, tearoffs etc.
62 */
63#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
64static BOOL CALLBACK dialog_callback(HWND, UINT, WPARAM, LPARAM);
65
66static LPWORD
67add_dialog_element(
68	LPWORD p,
69	DWORD lStyle,
70	WORD x,
71	WORD y,
72	WORD w,
73	WORD h,
74	WORD Id,
75	BYTE clss,
76	const char *caption);
77
78static int dialog_default_button = -1;
79#endif
80
81static void get_dialog_font_metrics(void);
82
83#ifdef FEAT_TOOLBAR
84static void initialise_toolbar(void);
85#endif
86
87
88#ifdef FEAT_MENU
89/*
90 * Figure out how high the menu bar is at the moment.
91 */
92    static int
93gui_mswin_get_menu_height(
94    int	    fix_window)	    /* If TRUE, resize window if menu height changed */
95{
96    static int	old_menu_height = -1;
97
98    int	    num;
99    int	    menu_height;
100
101    if (gui.menu_is_active)
102	num = GetMenuItemCount(s_menuBar);
103    else
104	num = 0;
105
106    if (num == 0)
107	menu_height = 0;
108    else if (gui.starting)
109	menu_height = GetSystemMetrics(SM_CYMENU);
110    else
111    {
112	RECT r1, r2;
113	int frameht = GetSystemMetrics(SM_CYFRAME);
114	int capht = GetSystemMetrics(SM_CYCAPTION);
115
116	/*	get window rect of s_hwnd
117		 * get client rect of s_hwnd
118		 * get cap height
119		 * subtract from window rect, the sum of client height,
120		 * (if not maximized)frame thickness, and caption height.
121	 */
122	GetWindowRect(s_hwnd, &r1);
123	GetClientRect(s_hwnd, &r2);
124	menu_height = r1.bottom - r1.top - (r2.bottom-r2.top +
125			       2 * frameht * (!IsZoomed(s_hwnd)) + capht);
126    }
127
128    if (fix_window && menu_height != old_menu_height)
129    {
130	old_menu_height = menu_height;
131	gui_set_shellsize(FALSE, FALSE, RESIZE_VERT);
132    }
133
134    return menu_height;
135}
136#endif /*FEAT_MENU*/
137
138
139/*
140 * Even though we have _DuringSizing() which makes the rubber band a valid
141 * size, we need this for when the user maximises the window.
142 * TODO: Doesn't seem to adjust the width though for some reason.
143 */
144    static BOOL
145_OnWindowPosChanging(
146    HWND hwnd,
147    LPWINDOWPOS lpwpos)
148{
149
150    if (!IsIconic(hwnd) && !(lpwpos->flags & SWP_NOSIZE))
151    {
152	gui_mswin_get_valid_dimensions(lpwpos->cx, lpwpos->cy,
153				     &lpwpos->cx, &lpwpos->cy);
154    }
155    return 0;
156}
157
158
159
160
161
162    static LRESULT CALLBACK
163_WndProc(
164    HWND hwnd,
165    UINT uMsg,
166    WPARAM wParam,
167    LPARAM lParam)
168{
169    /*
170    TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
171	  hwnd, uMsg, wParam, lParam);
172    */
173
174    HandleMouseHide(uMsg, lParam);
175
176    s_uMsg = uMsg;
177    s_wParam = wParam;
178    s_lParam = lParam;
179
180    switch (uMsg)
181    {
182	HANDLE_MSG(hwnd, WM_DEADCHAR,	_OnDeadChar);
183	HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar);
184	/* HANDLE_MSG(hwnd, WM_ACTIVATE,    _OnActivate); */
185	HANDLE_MSG(hwnd, WM_CHAR,	_OnChar);
186	HANDLE_MSG(hwnd, WM_CLOSE,	_OnClose);
187	/* HANDLE_MSG(hwnd, WM_COMMAND,	_OnCommand); */
188	HANDLE_MSG(hwnd, WM_DESTROY,	_OnDestroy);
189	HANDLE_MSG(hwnd, WM_DROPFILES,	_OnDropFiles);
190	HANDLE_MSG(hwnd, WM_HSCROLL,	_OnScroll);
191	HANDLE_MSG(hwnd, WM_KILLFOCUS,	_OnKillFocus);
192#ifdef FEAT_MENU
193	HANDLE_MSG(hwnd, WM_COMMAND,	_OnMenu);
194#endif
195	/* HANDLE_MSG(hwnd, WM_MOVE,	    _OnMove); */
196	/* HANDLE_MSG(hwnd, WM_NCACTIVATE,  _OnNCActivate); */
197	HANDLE_MSG(hwnd, WM_SETFOCUS,	_OnSetFocus);
198	HANDLE_MSG(hwnd, WM_SIZE,	_OnSize);
199	/* HANDLE_MSG(hwnd, WM_SYSCOMMAND,  _OnSysCommand); */
200	/* HANDLE_MSG(hwnd, WM_SYSKEYDOWN,  _OnAltKey); */
201	HANDLE_MSG(hwnd, WM_VSCROLL,	_OnScroll);
202	HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING,	_OnWindowPosChanging);
203	HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp);
204
205    case WM_QUERYENDSESSION:	/* System wants to go down. */
206	gui_shell_closed();	/* Will exit when no changed buffers. */
207	return FALSE;		/* Do NOT allow system to go down. */
208
209    case WM_ENDSESSION:
210	if (wParam)	/* system only really goes down when wParam is TRUE */
211	    _OnEndSession();
212	break;
213
214    case WM_SYSCHAR:
215	/*
216	 * if 'winaltkeys' is "no", or it's "menu" and it's not a menu
217	 * shortcut key, handle like a typed ALT key, otherwise call Windows
218	 * ALT key handling.
219	 */
220#ifdef FEAT_MENU
221	if (	!gui.menu_is_active
222		|| p_wak[0] == 'n'
223		|| (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam))
224		)
225#endif
226	    return HANDLE_WM_SYSCHAR((hwnd), (wParam), (lParam), (_OnSysChar));
227#ifdef FEAT_MENU
228	else
229	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
230#endif
231
232    case WM_SYSKEYUP:
233#ifdef FEAT_MENU
234	/* Only when menu is active, ALT key is used for that. */
235	if (gui.menu_is_active)
236	{
237	    return MyWindowProc(hwnd, uMsg, wParam, lParam);
238	}
239	else
240#endif
241	    return 0;
242
243#if defined(MENUHINTS) && defined(FEAT_MENU)
244    case WM_MENUSELECT:
245	if (((UINT) LOWORD(lParam)
246		    & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP)))
247		== MF_HILITE
248		&& (State & CMDLINE) == 0)
249	{
250	    UINT idButton;
251	    int	idx;
252	    vimmenu_T *pMenu;
253
254	    idButton = (UINT)LOWORD(wParam);
255	    pMenu = gui_mswin_find_menu(root_menu, idButton);
256	    if (pMenu)
257	    {
258		idx = MENU_INDEX_TIP;
259		msg_clr_cmdline();
260		if (pMenu->strings[idx])
261		    msg(pMenu->strings[idx]);
262		else
263		    msg("");
264		setcursor();
265		out_flush();
266	    }
267	}
268	break;
269#endif
270    case WM_NCHITTEST:
271	{
272	    LRESULT	result;
273	    int x, y;
274	    int xPos = GET_X_LPARAM(lParam);
275
276	    result = MyWindowProc(hwnd, uMsg, wParam, lParam);
277	    if (result == HTCLIENT)
278	    {
279		gui_mch_get_winpos(&x, &y);
280		xPos -= x;
281
282		if (xPos < 48) /*<VN> TODO should use system metric?*/
283		    return HTBOTTOMLEFT;
284		else
285		    return HTBOTTOMRIGHT;
286		}
287	    else
288		return result;
289	}
290	/* break; */
291    default:
292#ifdef MSWIN_FIND_REPLACE
293	if (uMsg == s_findrep_msg && s_findrep_msg != 0)
294	{
295	    _OnFindRepl();
296	}
297#endif
298	return MyWindowProc(hwnd, uMsg, wParam, lParam);
299    }
300
301    return 1;
302}
303
304
305
306/*
307 * End of call-back routines
308 */
309
310
311/*
312 * Parse the GUI related command-line arguments.  Any arguments used are
313 * deleted from argv, and *argc is decremented accordingly.  This is called
314 * when vim is started, whether or not the GUI has been started.
315 */
316    void
317gui_mch_prepare(int *argc, char **argv)
318{
319    /* No special args for win16 GUI at the moment. */
320
321}
322
323/*
324 * Initialise the GUI.	Create all the windows, set up all the call-backs
325 * etc.
326 */
327    int
328gui_mch_init(void)
329{
330    const char szVimWndClass[] = VIM_CLASS;
331    const char szTextAreaClass[] = "VimTextArea";
332    WNDCLASS wndclass;
333
334#ifdef WIN16_3DLOOK
335    Ctl3dRegister(s_hinst);
336    Ctl3dAutoSubclass(s_hinst);
337#endif
338
339    /* Display any pending error messages */
340    display_errors();
341
342    gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL);
343    gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL);
344#ifdef FEAT_MENU
345    gui.menu_height = 0;	/* Windows takes care of this */
346#endif
347    gui.border_width = 0;
348
349    gui.currBgColor = INVALCOLOR;
350
351    s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
352
353    if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0) {
354	wndclass.style = 0;
355	wndclass.lpfnWndProc = _WndProc;
356	wndclass.cbClsExtra = 0;
357	wndclass.cbWndExtra = 0;
358	wndclass.hInstance = s_hinst;
359	wndclass.hIcon = LoadIcon(wndclass.hInstance, MAKEINTRESOURCE(IDR_VIM));
360	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
361	wndclass.hbrBackground = s_brush;
362	wndclass.lpszMenuName = NULL;
363	wndclass.lpszClassName = szVimWndClass;
364
365    if ((
366#ifdef GLOBAL_IME
367	atom =
368#endif
369		RegisterClass(&wndclass)) == 0)
370	    return FAIL;
371    }
372
373    s_hwnd = CreateWindow(
374	szVimWndClass, "Vim MSWindows GUI",
375	WS_OVERLAPPEDWINDOW,
376	gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x,
377	gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y,
378	100,				/* Any value will do */
379	100,				/* Any value will do */
380	NULL, NULL,
381	s_hinst, NULL);
382
383    if (s_hwnd == NULL)
384	return FAIL;
385
386#ifdef GLOBAL_IME
387    global_ime_init(atom, s_hwnd);
388#endif
389
390    /* Create the text area window */
391    if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0) {
392	wndclass.style = CS_OWNDC;
393	wndclass.lpfnWndProc = _TextAreaWndProc;
394	wndclass.cbClsExtra = 0;
395	wndclass.cbWndExtra = 0;
396	wndclass.hInstance = s_hinst;
397	wndclass.hIcon = NULL;
398	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
399	wndclass.hbrBackground = NULL;
400	wndclass.lpszMenuName = NULL;
401	wndclass.lpszClassName = szTextAreaClass;
402
403	if (RegisterClass(&wndclass) == 0)
404	    return FAIL;
405    }
406    s_textArea = CreateWindow(
407	szTextAreaClass, "Vim text area",
408	WS_CHILD | WS_VISIBLE, 0, 0,
409	100,				/* Any value will do for now */
410	100,				/* Any value will do for now */
411	s_hwnd, NULL,
412	s_hinst, NULL);
413
414    if (s_textArea == NULL)
415	return FAIL;
416
417#ifdef FEAT_MENU
418    s_menuBar = CreateMenu();
419#endif
420    s_hdc = GetDC(s_textArea);
421
422#ifdef MSWIN16_FASTTEXT
423    SetBkMode(s_hdc, OPAQUE);
424#endif
425
426    DragAcceptFiles(s_hwnd, TRUE);
427
428    /* Do we need to bother with this? */
429    /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
430
431    /* Get background/foreground colors from the system */
432    gui_mch_def_colors();
433
434    /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
435     * file) */
436    set_normal_colors();
437
438    /*
439     * Check that none of the colors are the same as the background color.
440     * Then store the current values as the defaults.
441     */
442    gui_check_colors();
443    gui.def_norm_pixel = gui.norm_pixel;
444    gui.def_back_pixel = gui.back_pixel;
445
446    /* Get the colors for the highlight groups (gui_check_colors() might have
447     * changed them) */
448    highlight_gui_started();
449
450    /*
451     * Start out by adding the configured border width into the border offset
452     */
453    gui.border_offset = gui.border_width;
454
455
456    /*
457     * compute a couple of metrics used for the dialogs
458     */
459    get_dialog_font_metrics();
460#ifdef FEAT_TOOLBAR
461    /*
462     * Create the toolbar
463     */
464    initialise_toolbar();
465#endif
466#ifdef MSWIN_FIND_REPLACE
467    /*
468     * Initialise the dialog box stuff
469     */
470    s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING);
471
472    /* Initialise the struct */
473    s_findrep_struct.lStructSize = sizeof(s_findrep_struct);
474    s_findrep_struct.lpstrFindWhat = alloc(MSWIN_FR_BUFSIZE);
475    s_findrep_struct.lpstrFindWhat[0] = NUL;
476    s_findrep_struct.lpstrReplaceWith = alloc(MSWIN_FR_BUFSIZE);
477    s_findrep_struct.lpstrReplaceWith[0] = NUL;
478    s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE;
479    s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE;
480#endif
481
482    return OK;
483}
484
485
486/*
487 * Set the size of the window to the given width and height in pixels.
488 */
489    void
490gui_mch_set_shellsize(int width, int height,
491	int min_width, int min_height, int base_width, int base_height,
492	int direction)
493{
494    RECT	workarea_rect;
495    int		win_width, win_height;
496    int		win_xpos, win_ypos;
497    WINDOWPLACEMENT wndpl;
498
499    /* try to keep window completely on screen */
500    /* get size of the screen work area - use SM_CYFULLSCREEN
501     * instead of SM_CYSCREEN so that we don't overlap the
502     * taskbar if someone fires us up on Win95/NT */
503    workarea_rect.left = 0;
504    workarea_rect.top = 0;
505    workarea_rect.right = GetSystemMetrics(SM_CXSCREEN);
506    workarea_rect.bottom = GetSystemMetrics(SM_CYFULLSCREEN);
507
508    /* get current posision of our window */
509    wndpl.length = sizeof(WINDOWPLACEMENT);
510    GetWindowPlacement(s_hwnd, &wndpl);
511    if (wndpl.showCmd == SW_SHOWNORMAL)
512    {
513	win_xpos = wndpl.rcNormalPosition.left;
514	win_ypos = wndpl.rcNormalPosition.top;
515    }
516    else
517    {
518	win_xpos = workarea_rect.left;
519	win_ypos = workarea_rect.top;
520    }
521
522    /* compute the size of the outside of the window */
523    win_width = width + GetSystemMetrics(SM_CXFRAME) * 2;
524    win_height = height + GetSystemMetrics(SM_CYFRAME) * 2
525			+ GetSystemMetrics(SM_CYCAPTION)
526#ifdef FEAT_MENU
527			+ gui_mswin_get_menu_height(FALSE)
528#endif
529			;
530
531    /* if the window is going off the screen, move it on to the screen */
532    if ((direction & RESIZE_HOR) && win_xpos + win_width > workarea_rect.right)
533	win_xpos = workarea_rect.right - win_width;
534
535    if ((direction & RESIZE_HOR) && win_xpos < workarea_rect.left)
536	win_xpos = workarea_rect.left;
537
538    if ((direction & RESIZE_VERT)
539			       && win_ypos + win_height > workarea_rect.bottom)
540	win_ypos = workarea_rect.bottom - win_height;
541
542    if ((direction & RESIZE_VERT) && win_ypos < workarea_rect.top)
543	win_ypos = workarea_rect.top;
544
545    /* set window position */
546    SetWindowPos(s_hwnd, NULL, win_xpos, win_ypos, win_width, win_height,
547		 SWP_NOZORDER | SWP_NOACTIVATE);
548
549#ifdef FEAT_MENU
550    /* Menu may wrap differently now */
551    gui_mswin_get_menu_height(!gui.starting);
552#endif
553}
554
555    void
556gui_mch_set_scrollbar_thumb(
557    scrollbar_T     *sb,
558    long	    val,
559    long	    size,
560    long	    max)
561{
562    sb->scroll_shift = 0;
563    while (max > 32767)
564    {
565	max = (max + 1) >> 1;
566	val  >>= 1;
567	size >>= 1;
568	++sb->scroll_shift;
569    }
570
571    if (sb->scroll_shift > 0)
572	++size;
573
574    SetScrollRange(sb->id, SB_CTL, 0, (int) max, FALSE);
575    SetScrollPos(sb->id, SB_CTL, (int) val, TRUE);
576}
577
578
579/*
580 * Set the current text font.
581 */
582    void
583gui_mch_set_font(GuiFont font)
584{
585    gui.currFont = font;
586    SelectFont(s_hdc, gui.currFont);
587}
588
589/*
590 * Set the current text foreground color.
591 */
592    void
593gui_mch_set_fg_color(guicolor_T color)
594{
595    gui.currFgColor = color;
596    SetTextColor(s_hdc, gui.currFgColor);
597}
598
599/*
600 * Set the current text background color.
601 */
602    void
603gui_mch_set_bg_color(guicolor_T color)
604{
605    if (gui.currBgColor == color)
606	return;
607
608    gui.currBgColor = color;
609    SetBkColor(s_hdc, gui.currBgColor);
610}
611
612/*
613 * Set the current text special color.
614 */
615    void
616gui_mch_set_sp_color(guicolor_T color)
617{
618    /* TODO */
619}
620
621
622
623    void
624gui_mch_draw_string(
625    int		row,
626    int		col,
627    char_u	*text,
628    int		len,
629    int		flags)
630{
631#ifndef MSWIN16_FASTTEXT
632    static int	*padding = NULL;
633    static int	pad_size = 0;
634    int		i;
635#endif
636    HPEN	hpen, old_pen;
637    int		y;
638
639#ifndef MSWIN16_FASTTEXT
640    /*
641     * Italic and bold text seems to have an extra row of pixels at the bottom
642     * (below where the bottom of the character should be).  If we draw the
643     * characters with a solid background, the top row of pixels in the
644     * character below will be overwritten.  We can fix this by filling in the
645     * background ourselves, to the correct character proportions, and then
646     * writing the character in transparent mode.  Still have a problem when
647     * the character is "_", which gets written on to the character below.
648     * New fix: set gui.char_ascent to -1.  This shifts all characters up one
649     * pixel in their slots, which fixes the problem with the bottom row of
650     * pixels.	We still need this code because otherwise the top row of pixels
651     * becomes a problem. - webb.
652     */
653    HBRUSH	hbr;
654    RECT	rc;
655
656    if (!(flags & DRAW_TRANSP))
657    {
658	/*
659	 * Clear background first.
660	 * Note: FillRect() excludes right and bottom of rectangle.
661	 */
662	rc.left = FILL_X(col);
663	rc.top = FILL_Y(row);
664#ifdef FEAT_MBYTE
665	if (has_mbyte)
666	{
667	    /* Compute the length in display cells. */
668	    rc.right = FILL_X(col + mb_string2cells(text, len));
669	}
670	else
671#endif
672	    rc.right = FILL_X(col + len);
673	rc.bottom = FILL_Y(row + 1);
674	hbr = CreateSolidBrush(gui.currBgColor);
675	FillRect(s_hdc, &rc, hbr);
676	DeleteBrush(hbr);
677
678	SetBkMode(s_hdc, TRANSPARENT);
679
680	/*
681	 * When drawing block cursor, prevent inverted character spilling
682	 * over character cell (can happen with bold/italic)
683	 */
684	if (flags & DRAW_CURSOR)
685	{
686	    pcliprect = &rc;
687	    foptions = ETO_CLIPPED;
688	}
689    }
690#else
691    /*
692     * Alternative: write the characters in opaque mode, since we have blocked
693     * bold or italic fonts.
694     */
695    /* The OPAQUE mode and backcolour have already been set */
696#endif
697    /* The forecolor and font have already been set */
698
699#ifndef MSWIN16_FASTTEXT
700
701    if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width)
702    {
703	vim_free(padding);
704	pad_size = Columns;
705
706	padding = (int *)alloc(pad_size * sizeof(int));
707	if (padding != NULL)
708	    for (i = 0; i < pad_size; i++)
709		padding[i] = gui.char_width;
710    }
711#endif
712
713    /*
714     * We have to provide the padding argument because italic and bold versions
715     * of fixed-width fonts are often one pixel or so wider than their normal
716     * versions.
717     * No check for DRAW_BOLD, Windows will have done it already.
718     */
719#ifndef MSWIN16_FASTTEXT
720    ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row), 0, NULL,
721						     (char *)text, len, padding);
722#else
723    TextOut(s_hdc, TEXT_X(col), TEXT_Y(row), (char *)text, len);
724#endif
725
726    if (flags & DRAW_UNDERL)
727    {
728	hpen = CreatePen(PS_SOLID, 1, gui.currFgColor);
729	old_pen = SelectObject(s_hdc, hpen);
730	/* When p_linespace is 0, overwrite the bottom row of pixels.
731	 * Otherwise put the line just below the character. */
732	y = FILL_Y(row + 1) - 1;
733#ifndef MSWIN16_FASTTEXT
734	if (p_linespace > 1)
735	    y -= p_linespace - 1;
736#endif
737	MoveToEx(s_hdc, FILL_X(col), y, NULL);
738	/* Note: LineTo() excludes the last pixel in the line. */
739	LineTo(s_hdc, FILL_X(col + len), y);
740	DeleteObject(SelectObject(s_hdc, old_pen));
741    }
742}
743
744
745/*
746 * Output routines.
747 */
748
749/* Flush any output to the screen */
750    void
751gui_mch_flush(void)
752{
753    /* Is anything needed here? */
754}
755
756    static void
757clear_rect(RECT *rcp)
758{
759    /* Use trick for fast rect clear */
760    gui_mch_set_bg_color(gui.back_pixel);
761    ExtTextOut(s_hdc, 0, 0, ETO_CLIPPED | ETO_OPAQUE, rcp, NULL, 0, NULL);
762}
763
764
765    void
766gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
767{
768
769    *screen_w = GetSystemMetrics(SM_CXFULLSCREEN)
770	      - GetSystemMetrics(SM_CXFRAME) * 2;
771    /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
772     * the menubar for MSwin, we subtract it from the screen height, so that
773     * the window size can be made to fit on the screen. */
774    *screen_h = GetSystemMetrics(SM_CYFULLSCREEN)
775	      - GetSystemMetrics(SM_CYFRAME) * 2
776#ifdef FEAT_MENU
777	      - gui_mswin_get_menu_height(FALSE)
778#endif
779	      ;
780}
781
782
783#if defined(FEAT_MENU) || defined(PROTO)
784/*
785 * Add a sub menu to the menu bar.
786 */
787    void
788gui_mch_add_menu(
789    vimmenu_T	*menu,
790    int		pos)
791{
792    vimmenu_T	*parent = menu->parent;
793
794    menu->submenu_id = CreatePopupMenu();
795    menu->id = s_menu_id++;
796
797    if (menu_is_menubar(menu->name))
798    {
799	InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id,
800		(UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION,
801		(UINT)menu->submenu_id,  menu->name);
802    }
803
804    /* Fix window size if menu may have wrapped */
805    if (parent == NULL)
806	gui_mswin_get_menu_height(!gui.starting);
807}
808
809    void
810gui_mch_show_popupmenu(vimmenu_T *menu)
811{
812    POINT mp;
813
814    (void)GetCursorPos((LPPOINT)&mp);
815    gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y);
816}
817
818    void
819gui_make_popup(char_u *path_name, int mouse_pos)
820{
821    vimmenu_T	*menu = gui_find_menu(path_name);
822
823    if (menu != NULL)
824    {
825	/* Find the position of the current cursor */
826	DWORD	temp_p;
827	POINT	p;
828	temp_p = GetDCOrg(s_hdc);
829	p.x = LOWORD(temp_p);
830	p.y = HIWORD(temp_p);
831	if (mouse_pos)
832	{
833	    int	mx, my;
834
835	    gui_mch_getmouse(&mx, &my);
836	    p.x += mx;
837	    p.y += my;
838	}
839	else if (curwin != NULL)
840	{
841	    p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1);
842	    p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1);
843	}
844	msg_scroll = FALSE;
845	gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y);
846    }
847}
848
849/*
850 * Add a menu item to a menu
851 */
852    void
853gui_mch_add_menu_item(
854    vimmenu_T	*menu,
855    int		idx)
856{
857    vimmenu_T	*parent = menu->parent;
858
859    menu->id = s_menu_id++;
860    menu->submenu_id = NULL;
861
862#ifdef FEAT_TOOLBAR
863    if (menu_is_toolbar(parent->name))
864    {
865	TBBUTTON newtb;
866
867	vim_memset(&newtb, 0, sizeof(newtb));
868	if (menu_is_separator(menu->name))
869	{
870	    newtb.iBitmap = 0;
871	    newtb.fsStyle = TBSTYLE_SEP;
872	}
873	else
874	{
875	    if (menu->iconidx >= TOOLBAR_BITMAP_COUNT)
876		newtb.iBitmap = -1;
877	    else
878		newtb.iBitmap = menu->iconidx;
879	    newtb.fsStyle = TBSTYLE_BUTTON;
880	}
881	newtb.idCommand = menu->id;
882	newtb.fsState = TBSTATE_ENABLED;
883	SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx,
884							     (LPARAM)&newtb);
885	menu->submenu_id = (HMENU)-1;
886    }
887    else
888#endif
889    {
890	InsertMenu(parent->submenu_id, (UINT)idx,
891		(menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING)
892							      | MF_BYPOSITION,
893		(UINT)menu->id, menu->name);
894    }
895}
896
897/*
898 * Destroy the machine specific menu widget.
899 */
900    void
901gui_mch_destroy_menu(vimmenu_T *menu)
902{
903    UINT i, j;
904    char pants[80]; /*<VN> hack*/
905#ifdef FEAT_TOOLBAR
906    /*
907     * is this a toolbar button?
908     */
909    if (menu->submenu_id == (HMENU)-1)
910    {
911	int iButton;
912
913	iButton = SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX, (WPARAM)menu->id, 0);
914	SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0);
915    }
916    else
917#endif
918    {
919	/*
920	 * negri: horrible API bug when running 16-bit programs under Win9x or
921	 * NT means that we can't use MF_BYCOMMAND for menu items which have
922	 * submenus, including the top-level headings. We have to find the menu
923	 * item and use MF_BYPOSITION instead. :-p
924	 */
925    if (menu->parent != NULL
926	    && menu_is_popup(menu->parent->dname)
927	    && menu->parent->submenu_id != NULL)
928	RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND);
929    else if (menu->submenu_id == NULL)
930	RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND);
931    else if (menu->parent != NULL)
932    {
933	i = GetMenuItemCount(menu->parent->submenu_id);
934	for (j = 0; j < i; ++j)
935	{
936	    GetMenuString(menu->parent->submenu_id, j,
937		    pants, 80, MF_BYPOSITION);
938	    if (strcmp(pants, menu->name) == 0)
939	    {
940		RemoveMenu(menu->parent->submenu_id, j, MF_BYPOSITION);
941		break;
942	    }
943	}
944    }
945    else
946    {
947	i = GetMenuItemCount(s_menuBar);
948	for (j = 0; j < i; ++j)
949	{
950	    GetMenuString(s_menuBar, j, pants, 80, MF_BYPOSITION);
951	    if (strcmp(pants, menu->name) == 0)
952	    {
953		RemoveMenu(s_menuBar, j, MF_BYPOSITION);
954		break;
955	    }
956	}
957    }
958
959    if (menu->submenu_id != NULL)
960	DestroyMenu(menu->submenu_id);
961    }
962    DrawMenuBar(s_hwnd);
963}
964
965
966/*
967 * Make a menu either grey or not grey.
968 */
969    void
970gui_mch_menu_grey(
971    vimmenu_T *menu,
972    int	    grey)
973{
974#ifdef FEAT_TOOLBAR
975    /*
976     * is this a toolbar button?
977     */
978    if (menu->submenu_id == (HMENU)-1)
979    {
980	SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON,
981	    (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) );
982    }
983    else
984#endif
985    if (grey)
986	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED);
987    else
988	EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED);
989
990}
991
992
993#endif /*FEAT_MENU*/
994
995
996/* define some macros used to make the dialogue creation more readable */
997
998#define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
999#define add_word(x)		*p++ = (x)
1000#define add_byte(x)		*((LPSTR)p)++ = (x)
1001#define add_long(x)		*((LPDWORD)p)++ = (x)
1002
1003#if defined(FEAT_GUI_DIALOG) || defined(PROTO)
1004/*
1005 * stuff for dialogs
1006 */
1007
1008/*
1009 * The callback routine used by all the dialogs.  Very simple.  First,
1010 * acknowledges the INITDIALOG message so that Windows knows to do standard
1011 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
1012 * pressed, return that button's ID - IDCANCEL (2), which is the button's
1013 * number.
1014 */
1015	 static BOOL CALLBACK
1016dialog_callback(
1017	 HWND hwnd,
1018	 UINT message,
1019	 WPARAM wParam,
1020	 LPARAM lParam)
1021{
1022    if (message == WM_INITDIALOG)
1023    {
1024	CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER));
1025	/* Set focus to the dialog.  Set the default button, if specified. */
1026	(void)SetFocus(hwnd);
1027	if (dialog_default_button > IDCANCEL)
1028	    (void)SetFocus(GetDlgItem(hwnd, dialog_default_button));
1029//	if (dialog_default_button > 0)
1030//	    (void)SetFocus(GetDlgItem(hwnd, dialog_default_button + IDCANCEL));
1031	return FALSE;
1032    }
1033
1034    if (message == WM_COMMAND)
1035    {
1036	int	button = LOWORD(wParam);
1037
1038	/* Don't end the dialog if something was selected that was
1039	 * not a button.
1040	 */
1041	if (button >= DLG_NONBUTTON_CONTROL)
1042	    return TRUE;
1043
1044	/* If the edit box exists, copy the string. */
1045	if (s_textfield != NULL)
1046	    GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2,
1047							 s_textfield, IOSIZE);
1048
1049	/*
1050	 * Need to check for IDOK because if the user just hits Return to
1051	 * accept the default value, some reason this is what we get.
1052	 */
1053	if (button == IDOK)
1054	    EndDialog(hwnd, dialog_default_button);
1055	else
1056	    EndDialog(hwnd, button - IDCANCEL);
1057	return TRUE;
1058    }
1059
1060    if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE))
1061    {
1062	EndDialog(hwnd, 0);
1063	return TRUE;
1064    }
1065    return FALSE;
1066}
1067
1068/*
1069 * Create a dialog dynamically from the parameter strings.
1070 * type		= type of dialog (question, alert, etc.)
1071 * title	= dialog title. may be NULL for default title.
1072 * message	= text to display. Dialog sizes to accommodate it.
1073 * buttons	= '\n' separated list of button captions, default first.
1074 * dfltbutton	= number of default button.
1075 *
1076 * This routine returns 1 if the first button is pressed,
1077 *			2 for the second, etc.
1078 *
1079 *			0 indicates Esc was pressed.
1080 *			-1 for unexpected error
1081 *
1082 * If stubbing out this fn, return 1.
1083 */
1084
1085static const char_u dlg_icons[] = /* must match names in resource file */
1086{
1087    IDR_VIM,
1088    IDR_VIM_ERROR,
1089    IDR_VIM_ALERT,
1090    IDR_VIM_INFO,
1091    IDR_VIM_QUESTION
1092};
1093
1094    int
1095gui_mch_dialog(
1096    int		 type,
1097    char_u	*title,
1098    char_u	*message,
1099    char_u	*buttons,
1100    int		 dfltbutton,
1101    char_u	*textfield)
1102{
1103    FARPROC	dp;
1104    LPWORD	p, pnumitems;
1105    int		numButtons;
1106    int		*buttonWidths, *buttonPositions;
1107    int		buttonYpos;
1108    int		nchar, i;
1109    DWORD	lStyle;
1110    int		dlgwidth = 0;
1111    int		dlgheight;
1112    int		editboxheight;
1113    int		horizWidth;
1114    int		msgheight;
1115    char_u	*pstart;
1116    char_u	*pend;
1117    char_u	*tbuffer;
1118    RECT	rect;
1119    HWND	hwnd;
1120    HDC		hdc;
1121    HFONT	oldFont;
1122    TEXTMETRIC	fontInfo;
1123    int		fontHeight;
1124    int		textWidth, minButtonWidth, messageWidth;
1125    int		maxDialogWidth;
1126    int		vertical;
1127    int		dlgPaddingX;
1128    int		dlgPaddingY;
1129    HGLOBAL	hglbDlgTemp;
1130
1131#ifndef NO_CONSOLE
1132    /* Don't output anything in silent mode ("ex -s") */
1133    if (silent_mode)
1134	return dfltbutton;   /* return default option */
1135#endif
1136
1137    /* If there is no window yet, open it. */
1138    if (s_hwnd == NULL && gui_mch_init() == FAIL)
1139	return dfltbutton;
1140
1141    if ((type < 0) || (type > VIM_LAST_TYPE))
1142	type = 0;
1143
1144    /* allocate some memory for dialog template */
1145    /* TODO should compute this really*/
1146
1147    hglbDlgTemp = GlobalAlloc(GHND,  DLG_ALLOC_SIZE);
1148    if (hglbDlgTemp == NULL)
1149	return -1;
1150
1151    p = (LPWORD) GlobalLock(hglbDlgTemp);
1152
1153    if (p == NULL)
1154	return -1;
1155
1156    /*
1157     * make a copy of 'buttons' to fiddle with it.  complier grizzles because
1158     * vim_strsave() doesn't take a const arg (why not?), so cast away the
1159     * const.
1160     */
1161    tbuffer = vim_strsave(buttons);
1162    if (tbuffer == NULL)
1163	return -1;
1164
1165    --dfltbutton;   /* Change from one-based to zero-based */
1166
1167    /* Count buttons */
1168    numButtons = 1;
1169    for (i = 0; tbuffer[i] != '\0'; i++)
1170    {
1171	if (tbuffer[i] == DLG_BUTTON_SEP)
1172	    numButtons++;
1173    }
1174    if (dfltbutton >= numButtons)
1175	dfltbutton = 0;
1176
1177    /* Allocate array to hold the width of each button */
1178    buttonWidths = (int *) lalloc(numButtons * sizeof(int), TRUE);
1179    if (buttonWidths == NULL)
1180	return -1;
1181
1182    /* Allocate array to hold the X position of each button */
1183    buttonPositions = (int *) lalloc(numButtons * sizeof(int), TRUE);
1184    if (buttonPositions == NULL)
1185	return -1;
1186
1187    /*
1188     * Calculate how big the dialog must be.
1189     */
1190    hwnd = GetDesktopWindow();
1191    hdc = GetWindowDC(hwnd);
1192    oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT));
1193    dlgPaddingX = DLG_OLD_STYLE_PADDING_X;
1194    dlgPaddingY = DLG_OLD_STYLE_PADDING_Y;
1195
1196    GetTextMetrics(hdc, &fontInfo);
1197    fontHeight = fontInfo.tmHeight;
1198
1199    /* Minimum width for horizontal button */
1200    minButtonWidth = GetTextWidth(hdc, "Cancel", 6);
1201
1202    /* Maximum width of a dialog, if possible */
1203    GetWindowRect(s_hwnd, &rect);
1204    maxDialogWidth = rect.right - rect.left
1205		     - GetSystemMetrics(SM_CXFRAME) * 2;
1206    if (maxDialogWidth < DLG_MIN_MAX_WIDTH)
1207	maxDialogWidth = DLG_MIN_MAX_WIDTH;
1208
1209    /* Set dlgwidth to width of message */
1210    pstart = message;
1211    messageWidth = 0;
1212    msgheight = 0;
1213    do
1214    {
1215	pend = vim_strchr(pstart, DLG_BUTTON_SEP);
1216	if (pend == NULL)
1217	    pend = pstart + STRLEN(pstart);	/* Last line of message. */
1218	msgheight += fontHeight;
1219	textWidth = GetTextWidth(hdc, pstart, pend - pstart);
1220	if (textWidth > messageWidth)
1221	    messageWidth = textWidth;
1222	pstart = pend + 1;
1223    } while (*pend != NUL);
1224    dlgwidth = messageWidth;
1225
1226    /* Add width of icon to dlgwidth, and some space */
1227    dlgwidth += DLG_ICON_WIDTH + 3 * dlgPaddingX;
1228
1229    if (msgheight < DLG_ICON_HEIGHT)
1230	msgheight = DLG_ICON_HEIGHT;
1231
1232    /*
1233     * Check button names.  A long one will make the dialog wider.
1234     */
1235	 vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
1236    if (!vertical)
1237    {
1238	// Place buttons horizontally if they fit.
1239	horizWidth = dlgPaddingX;
1240	pstart = tbuffer;
1241	i = 0;
1242	do
1243	{
1244	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
1245	    if (pend == NULL)
1246		pend = pstart + STRLEN(pstart);	// Last button name.
1247	    textWidth = GetTextWidth(hdc, pstart, pend - pstart);
1248	    if (textWidth < minButtonWidth)
1249		textWidth = minButtonWidth;
1250	    textWidth += dlgPaddingX;	    /* Padding within button */
1251	    buttonWidths[i] = textWidth;
1252	    buttonPositions[i++] = horizWidth;
1253	    horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */
1254	    pstart = pend + 1;
1255	} while (*pend != NUL);
1256
1257	if (horizWidth > maxDialogWidth)
1258	    vertical = TRUE;	// Too wide to fit on the screen.
1259	else if (horizWidth > dlgwidth)
1260	    dlgwidth = horizWidth;
1261    }
1262
1263    if (vertical)
1264    {
1265	// Stack buttons vertically.
1266	pstart = tbuffer;
1267	do
1268	{
1269	    pend = vim_strchr(pstart, DLG_BUTTON_SEP);
1270	    if (pend == NULL)
1271		pend = pstart + STRLEN(pstart);	// Last button name.
1272	    textWidth = GetTextWidth(hdc, pstart, pend - pstart);
1273	    textWidth += dlgPaddingX;		/* Padding within button */
1274	    textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */
1275	    if (textWidth > dlgwidth)
1276		dlgwidth = textWidth;
1277	    pstart = pend + 1;
1278	} while (*pend != NUL);
1279    }
1280
1281    if (dlgwidth < DLG_MIN_WIDTH)
1282	dlgwidth = DLG_MIN_WIDTH;	/* Don't allow a really thin dialog!*/
1283
1284    /* start to fill in the dlgtemplate information.  addressing by WORDs */
1285    lStyle = DS_MODALFRAME | WS_CAPTION | WS_VISIBLE ;
1286
1287    add_long(lStyle);
1288    pnumitems = p;	/*save where the number of items must be stored*/
1289    add_byte(0);	// NumberOfItems(will change later)
1290    add_word(10);	// x
1291    add_word(10);	// y
1292    add_word(PixelToDialogX(dlgwidth));
1293
1294    // Dialog height.
1295    if (vertical)
1296	dlgheight = msgheight + 2 * dlgPaddingY +
1297			      DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons;
1298    else
1299	dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight;
1300
1301    // Dialog needs to be taller if contains an edit box.
1302    editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y;
1303    if (textfield != NULL)
1304	dlgheight += editboxheight;
1305
1306    add_word(PixelToDialogY(dlgheight));
1307
1308    add_byte(0);	//menu
1309    add_byte(0);	//class
1310
1311    /* copy the title of the dialog */
1312    add_string(title ? title : ("Vim"VIM_VERSION_MEDIUM));
1313
1314    buttonYpos = msgheight + 2 * dlgPaddingY;
1315
1316    if (textfield != NULL)
1317	buttonYpos += editboxheight;
1318
1319    pstart = tbuffer; //dflt_text
1320    horizWidth = (dlgwidth - horizWidth) / 2;	/* Now it's X offset */
1321    for (i = 0; i < numButtons; i++)
1322    {
1323	/* get end of this button. */
1324	for (	pend = pstart;
1325		*pend && (*pend != DLG_BUTTON_SEP);
1326		pend++)
1327	    ;
1328
1329	if (*pend)
1330	    *pend = '\0';
1331
1332	/*
1333	 * NOTE:
1334	 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
1335	 * the focus to the first tab-able button and in so doing makes that
1336	 * the default!! Grrr.  Workaround: Make the default button the only
1337	 * one with WS_TABSTOP style. Means user can't tab between buttons, but
1338	 * he/she can use arrow keys.
1339	 *
1340	 * NOTE (Thore): Setting BS_DEFPUSHBUTTON works fine when it's the
1341	 * first one, so I changed the correct button to be this style. This
1342	 * is necessary because when an edit box is added, we need a button to
1343	 * be default.  The edit box will be the default control, and when the
1344	 * user presses enter from the edit box we want the default button to
1345	 * be pressed.
1346	 */
1347	if (vertical)
1348	{
1349	    p = add_dialog_element(p,
1350		    ((i == dfltbutton || dfltbutton < 0) && textfield != NULL
1351			    ?  BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
1352		    PixelToDialogX(DLG_VERT_PADDING_X),
1353		    PixelToDialogY(buttonYpos /* TBK */
1354				   + 2 * fontHeight * i),
1355		    PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X),
1356		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
1357		    (WORD)(IDCANCEL + 1 + i), (BYTE)0x80, pstart);
1358	}
1359	else
1360	{
1361	    p = add_dialog_element(p,
1362		    ((i == dfltbutton || dfltbutton < 0) && textfield != NULL
1363			     ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP,
1364		    PixelToDialogX(horizWidth + buttonPositions[i]),
1365		    PixelToDialogY(buttonYpos), /* TBK */
1366		    PixelToDialogX(buttonWidths[i]),
1367		    (WORD)(PixelToDialogY(2 * fontHeight) - 1),
1368		    (WORD)(IDCANCEL + 1 + i), (BYTE)0x80, pstart);
1369	}
1370
1371	pstart = pend + 1;	/*next button*/
1372
1373    }
1374    *pnumitems += numButtons;
1375
1376    /* Vim icon */
1377    p = add_dialog_element(p, SS_ICON,
1378	    PixelToDialogX(dlgPaddingX),
1379	    PixelToDialogY(dlgPaddingY),
1380	    PixelToDialogX(DLG_ICON_WIDTH),
1381	    PixelToDialogY(DLG_ICON_HEIGHT),
1382	    DLG_NONBUTTON_CONTROL + 0, (BYTE)0x82,
1383	    &dlg_icons[type]);
1384
1385
1386    /* Dialog message */
1387    p = add_dialog_element(p, SS_LEFT,
1388	    PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH),
1389	    PixelToDialogY(dlgPaddingY),
1390	    (WORD)(PixelToDialogX(messageWidth) + 1),
1391	    PixelToDialogY(msgheight),
1392	    DLG_NONBUTTON_CONTROL + 1, (BYTE)0x82, message);
1393
1394    /* Edit box */
1395    if (textfield != NULL)
1396    {
1397	p = add_dialog_element(p, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP | WS_BORDER,
1398		PixelToDialogX(2 * dlgPaddingX),
1399		PixelToDialogY(2 * dlgPaddingY + msgheight),
1400		PixelToDialogX(dlgwidth - 4 * dlgPaddingX),
1401		PixelToDialogY(fontHeight + dlgPaddingY),
1402		DLG_NONBUTTON_CONTROL + 2, (BYTE)0x81, textfield);
1403	*pnumitems += 1;
1404    }
1405
1406    *pnumitems += 2;
1407
1408    SelectFont(hdc, oldFont);
1409    ReleaseDC(hwnd, hdc);
1410    dp = MakeProcInstance((FARPROC)dialog_callback, s_hinst);
1411
1412
1413    /* Let the dialog_callback() function know which button to make default
1414     * If we have an edit box, make that the default. We also need to tell
1415     * dialog_callback() if this dialog contains an edit box or not. We do
1416     * this by setting s_textfield if it does.
1417     */
1418    if (textfield != NULL)
1419    {
1420	dialog_default_button = DLG_NONBUTTON_CONTROL + 2;
1421	s_textfield = textfield;
1422    }
1423    else
1424    {
1425	dialog_default_button = IDCANCEL + 1 + dfltbutton;
1426	s_textfield = NULL;
1427    }
1428
1429    /*show the dialog box modally and get a return value*/
1430    nchar = DialogBoxIndirect(
1431	    s_hinst,
1432	    (HGLOBAL) hglbDlgTemp,
1433	    s_hwnd,
1434	    (DLGPROC)dp);
1435
1436    FreeProcInstance( dp );
1437    GlobalUnlock(hglbDlgTemp);
1438    GlobalFree(hglbDlgTemp);
1439    vim_free(tbuffer);
1440    vim_free(buttonWidths);
1441    vim_free(buttonPositions);
1442
1443
1444    return nchar;
1445}
1446
1447/*
1448 * Put a simple element (basic class) onto a dialog template in memory.
1449 * return a pointer to where the next item should be added.
1450 *
1451 * parameters:
1452 *  lStyle = additional style flags
1453 *  x,y = x & y positions IN DIALOG UNITS
1454 *  w,h = width and height IN DIALOG UNITS
1455 *  Id	= ID used in messages
1456 *  clss  = class ID, e.g 0x80 for a button, 0x82 for a static
1457 *  caption = usually text or resource name
1458 *
1459 *  TODO: use the length information noted here to enable the dialog creation
1460 *  routines to work out more exactly how much memory they need to alloc.
1461 */
1462    static LPWORD
1463add_dialog_element(
1464    LPWORD p,
1465    DWORD lStyle,
1466    WORD x,
1467    WORD y,
1468    WORD w,
1469    WORD h,
1470    WORD Id,
1471    BYTE clss,
1472    const char *caption)
1473{
1474
1475    lStyle = lStyle | WS_VISIBLE | WS_CHILD;
1476
1477    add_word(x);
1478    add_word(y);
1479    add_word(w);
1480    add_word(h);
1481    add_word(Id);
1482    add_long(lStyle);
1483    add_byte(clss);
1484    if (((lStyle & SS_ICON) != 0) && (clss == 0x82))
1485    {
1486	/* Use resource ID */
1487	add_byte(0xff);
1488	add_byte(*caption);
1489    }
1490    else
1491	add_string(caption);
1492
1493    add_byte(0);    //# of extra bytes following
1494
1495
1496    return p;
1497}
1498
1499#undef add_byte
1500#undef add_string
1501#undef add_long
1502#undef add_word
1503
1504#endif /* FEAT_GUI_DIALOG */
1505
1506    static void
1507get_dialog_font_metrics(void)
1508{
1509    DWORD	    dlgFontSize;
1510	dlgFontSize = GetDialogBaseUnits();	/* fall back to big old system*/
1511	s_dlgfntwidth = LOWORD(dlgFontSize);
1512	s_dlgfntheight = HIWORD(dlgFontSize);
1513}
1514
1515
1516#if defined(FEAT_TOOLBAR) || defined(PROTO)
1517#include "gui_w3~1.h"
1518/*
1519 * Create the toolbar, initially unpopulated.
1520 *  (just like the menu, there are no defaults, it's all
1521 *  set up through menu.vim)
1522 */
1523    static void
1524initialise_toolbar(void)
1525{
1526    s_toolbarhwnd = CreateToolbar(
1527		    s_hwnd,
1528		    WS_CHILD | WS_VISIBLE,
1529		    CMD_TB_BASE, /*<vn>*/
1530		    31,			//number of images in initial bitmap
1531		    s_hinst,
1532		    IDR_TOOLBAR1,	// id of initial bitmap
1533		    NULL,
1534		    0			// initial number of buttons
1535		    );
1536
1537    gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL);
1538}
1539#endif
1540
1541#if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
1542/*
1543 * Make the GUI window come to the foreground.
1544 */
1545    void
1546gui_mch_set_foreground(void)
1547{
1548    if (IsIconic(s_hwnd))
1549	 SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
1550    SetActiveWindow(s_hwnd);
1551}
1552#endif
1553