1 /* vi:set ts=8 sts=4 sw=4:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * GUI support by Robert Webb
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.
13 * GUI support for Microsoft Windows 3.1x
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,
19 * Vince Negri then butchered the code to get it compiling for
25 * Include the common stuff for MS-Windows GUI.
31 /* Undocumented Windows Message - not even defined in some SDK headers */
32 #define WM_EXITSIZEMOVE 0x0232
36 # define CMD_TB_BASE (99)
44 #define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \
45 ((fn)((hwnd), (HDROP)(wParam)), 0L)
48 /* Local variables: */
51 static UINT s_menu_id
= 100;
55 #define VIM_NAME "vim"
56 #define VIM_CLASS "Vim"
58 #define DLG_ALLOC_SIZE 16 * 1024
61 * stuff for dialogs, menus, tearoffs etc.
63 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
64 static BOOL CALLBACK
dialog_callback(HWND
, UINT
, WPARAM
, LPARAM
);
78 static int dialog_default_button
= -1;
81 static void get_dialog_font_metrics(void);
84 static void initialise_toolbar(void);
90 * Figure out how high the menu bar is at the moment.
93 gui_mswin_get_menu_height(
94 int fix_window
) /* If TRUE, resize window if menu height changed */
96 static int old_menu_height
= -1;
101 if (gui
.menu_is_active
)
102 num
= GetMenuItemCount(s_menuBar
);
108 else if (gui
.starting
)
109 menu_height
= GetSystemMetrics(SM_CYMENU
);
113 int frameht
= GetSystemMetrics(SM_CYFRAME
);
114 int capht
= GetSystemMetrics(SM_CYCAPTION
);
116 /* get window rect of s_hwnd
117 * get client rect of s_hwnd
119 * subtract from window rect, the sum of client height,
120 * (if not maximized)frame thickness, and caption height.
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
);
128 if (fix_window
&& menu_height
!= old_menu_height
)
130 old_menu_height
= menu_height
;
131 gui_set_shellsize(FALSE
, FALSE
, RESIZE_VERT
);
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.
145 _OnWindowPosChanging(
150 if (!IsIconic(hwnd
) && !(lpwpos
->flags
& SWP_NOSIZE
))
152 gui_mswin_get_valid_dimensions(lpwpos
->cx
, lpwpos
->cy
,
153 &lpwpos
->cx
, &lpwpos
->cy
);
162 static LRESULT CALLBACK
170 TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n",
171 hwnd, uMsg, wParam, lParam);
174 HandleMouseHide(uMsg
, lParam
);
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
);
193 HANDLE_MSG(hwnd
, WM_COMMAND
, _OnMenu
);
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
);
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. */
210 if (wParam
) /* system only really goes down when wParam is TRUE */
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
221 if ( !gui
.menu_is_active
223 || (p_wak
[0] == 'm' && !gui_is_menu_shortcut((int)wParam
))
226 return HANDLE_WM_SYSCHAR((hwnd
), (wParam
), (lParam
), (_OnSysChar
));
229 return MyWindowProc(hwnd
, uMsg
, wParam
, lParam
);
234 /* Only when menu is active, ALT key is used for that. */
235 if (gui
.menu_is_active
)
237 return MyWindowProc(hwnd
, uMsg
, wParam
, lParam
);
243 #if defined(MENUHINTS) && defined(FEAT_MENU)
245 if (((UINT
) LOWORD(lParam
)
246 & (0xffff ^ (MF_MOUSESELECT
+ MF_BITMAP
+ MF_POPUP
)))
248 && (State
& CMDLINE
) == 0)
254 idButton
= (UINT
)LOWORD(wParam
);
255 pMenu
= gui_mswin_find_menu(root_menu
, idButton
);
258 idx
= MENU_INDEX_TIP
;
260 if (pMenu
->strings
[idx
])
261 msg(pMenu
->strings
[idx
]);
274 int xPos
= GET_X_LPARAM(lParam
);
276 result
= MyWindowProc(hwnd
, uMsg
, wParam
, lParam
);
277 if (result
== HTCLIENT
)
279 gui_mch_get_winpos(&x
, &y
);
282 if (xPos
< 48) /*<VN> TODO should use system metric?*/
285 return HTBOTTOMRIGHT
;
292 #ifdef MSWIN_FIND_REPLACE
293 if (uMsg
== s_findrep_msg
&& s_findrep_msg
!= 0)
298 return MyWindowProc(hwnd
, uMsg
, wParam
, lParam
);
307 * End of call-back routines
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.
317 gui_mch_prepare(int *argc
, char **argv
)
319 /* No special args for win16 GUI at the moment. */
324 * Initialise the GUI. Create all the windows, set up all the call-backs
330 const char szVimWndClass
[] = VIM_CLASS
;
331 const char szTextAreaClass
[] = "VimTextArea";
335 Ctl3dRegister(s_hinst
);
336 Ctl3dAutoSubclass(s_hinst
);
339 /* Display any pending error messages */
342 gui
.scrollbar_width
= GetSystemMetrics(SM_CXVSCROLL
);
343 gui
.scrollbar_height
= GetSystemMetrics(SM_CYHSCROLL
);
345 gui
.menu_height
= 0; /* Windows takes care of this */
347 gui
.border_width
= 0;
349 gui
.currBgColor
= INVALCOLOR
;
351 s_brush
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
353 if (GetClassInfo(s_hinst
, szVimWndClass
, &wndclass
) == 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
;
369 RegisterClass(&wndclass
)) == 0)
373 s_hwnd
= CreateWindow(
374 szVimWndClass
, "Vim MSWindows GUI",
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 */
387 global_ime_init(atom
, s_hwnd
);
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
;
403 if (RegisterClass(&wndclass
) == 0)
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 */
414 if (s_textArea
== NULL
)
418 s_menuBar
= CreateMenu();
420 s_hdc
= GetDC(s_textArea
);
422 #ifdef MSWIN16_FASTTEXT
423 SetBkMode(s_hdc
, OPAQUE
);
426 DragAcceptFiles(s_hwnd
, TRUE
);
428 /* Do we need to bother with this? */
429 /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */
431 /* Get background/foreground colors from the system */
432 gui_mch_def_colors();
434 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
439 * Check that none of the colors are the same as the background color.
440 * Then store the current values as the defaults.
443 gui
.def_norm_pixel
= gui
.norm_pixel
;
444 gui
.def_back_pixel
= gui
.back_pixel
;
446 /* Get the colors for the highlight groups (gui_check_colors() might have
448 highlight_gui_started();
451 * Start out by adding the configured border width into the border offset
453 gui
.border_offset
= gui
.border_width
;
457 * compute a couple of metrics used for the dialogs
459 get_dialog_font_metrics();
464 initialise_toolbar();
466 #ifdef MSWIN_FIND_REPLACE
468 * Initialise the dialog box stuff
470 s_findrep_msg
= RegisterWindowMessage(FINDMSGSTRING
);
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
;
487 * Set the size of the window to the given width and height in pixels.
490 gui_mch_set_shellsize(int width
, int height
,
491 int min_width
, int min_height
, int base_width
, int base_height
,
495 int win_width
, win_height
;
496 int win_xpos
, win_ypos
;
497 WINDOWPLACEMENT wndpl
;
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
);
508 /* get current posision of our window */
509 wndpl
.length
= sizeof(WINDOWPLACEMENT
);
510 GetWindowPlacement(s_hwnd
, &wndpl
);
511 if (wndpl
.showCmd
== SW_SHOWNORMAL
)
513 win_xpos
= wndpl
.rcNormalPosition
.left
;
514 win_ypos
= wndpl
.rcNormalPosition
.top
;
518 win_xpos
= workarea_rect
.left
;
519 win_ypos
= workarea_rect
.top
;
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
)
527 + gui_mswin_get_menu_height(FALSE
)
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
;
535 if ((direction
& RESIZE_HOR
) && win_xpos
< workarea_rect
.left
)
536 win_xpos
= workarea_rect
.left
;
538 if ((direction
& RESIZE_VERT
)
539 && win_ypos
+ win_height
> workarea_rect
.bottom
)
540 win_ypos
= workarea_rect
.bottom
- win_height
;
542 if ((direction
& RESIZE_VERT
) && win_ypos
< workarea_rect
.top
)
543 win_ypos
= workarea_rect
.top
;
545 /* set window position */
546 SetWindowPos(s_hwnd
, NULL
, win_xpos
, win_ypos
, win_width
, win_height
,
547 SWP_NOZORDER
| SWP_NOACTIVATE
);
550 /* Menu may wrap differently now */
551 gui_mswin_get_menu_height(!gui
.starting
);
556 gui_mch_set_scrollbar_thumb(
562 sb
->scroll_shift
= 0;
565 max
= (max
+ 1) >> 1;
571 if (sb
->scroll_shift
> 0)
574 SetScrollRange(sb
->id
, SB_CTL
, 0, (int) max
, FALSE
);
575 SetScrollPos(sb
->id
, SB_CTL
, (int) val
, TRUE
);
580 * Set the current text font.
583 gui_mch_set_font(GuiFont font
)
586 SelectFont(s_hdc
, gui
.currFont
);
590 * Set the current text foreground color.
593 gui_mch_set_fg_color(guicolor_T color
)
595 gui
.currFgColor
= color
;
596 SetTextColor(s_hdc
, gui
.currFgColor
);
600 * Set the current text background color.
603 gui_mch_set_bg_color(guicolor_T color
)
605 if (gui
.currBgColor
== color
)
608 gui
.currBgColor
= color
;
609 SetBkColor(s_hdc
, gui
.currBgColor
);
613 * Set the current text special color.
616 gui_mch_set_sp_color(guicolor_T color
)
631 #ifndef MSWIN16_FASTTEXT
632 static int *padding
= NULL
;
633 static int pad_size
= 0;
639 #ifndef MSWIN16_FASTTEXT
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.
656 if (!(flags
& DRAW_TRANSP
))
659 * Clear background first.
660 * Note: FillRect() excludes right and bottom of rectangle.
662 rc
.left
= FILL_X(col
);
663 rc
.top
= FILL_Y(row
);
669 /* Compute the length in display cells. */
670 for (n
= 0; n
< len
; n
+= MB_BYTE2LEN(text
[n
]))
671 cell_len
+= (*mb_ptr2cells
)(text
+ n
);
672 rc
.right
= FILL_X(col
+ cell_len
);
676 rc
.right
= FILL_X(col
+ len
);
677 rc
.bottom
= FILL_Y(row
+ 1);
678 hbr
= CreateSolidBrush(gui
.currBgColor
);
679 FillRect(s_hdc
, &rc
, hbr
);
682 SetBkMode(s_hdc
, TRANSPARENT
);
685 * When drawing block cursor, prevent inverted character spilling
686 * over character cell (can happen with bold/italic)
688 if (flags
& DRAW_CURSOR
)
691 foptions
= ETO_CLIPPED
;
696 * Alternative: write the characters in opaque mode, since we have blocked
697 * bold or italic fonts.
699 /* The OPAQUE mode and backcolour have already been set */
701 /* The forecolor and font have already been set */
703 #ifndef MSWIN16_FASTTEXT
705 if (pad_size
!= Columns
|| padding
== NULL
|| padding
[0] != gui
.char_width
)
710 padding
= (int *)alloc(pad_size
* sizeof(int));
712 for (i
= 0; i
< pad_size
; i
++)
713 padding
[i
] = gui
.char_width
;
718 * We have to provide the padding argument because italic and bold versions
719 * of fixed-width fonts are often one pixel or so wider than their normal
721 * No check for DRAW_BOLD, Windows will have done it already.
723 #ifndef MSWIN16_FASTTEXT
724 ExtTextOut(s_hdc
, TEXT_X(col
), TEXT_Y(row
), 0, NULL
,
725 (char *)text
, len
, padding
);
727 TextOut(s_hdc
, TEXT_X(col
), TEXT_Y(row
), (char *)text
, len
);
730 if (flags
& DRAW_UNDERL
)
732 hpen
= CreatePen(PS_SOLID
, 1, gui
.currFgColor
);
733 old_pen
= SelectObject(s_hdc
, hpen
);
734 /* When p_linespace is 0, overwrite the bottom row of pixels.
735 * Otherwise put the line just below the character. */
736 y
= FILL_Y(row
+ 1) - 1;
737 #ifndef MSWIN16_FASTTEXT
739 y
-= p_linespace
- 1;
741 MoveToEx(s_hdc
, FILL_X(col
), y
, NULL
);
742 /* Note: LineTo() excludes the last pixel in the line. */
743 LineTo(s_hdc
, FILL_X(col
+ len
), y
);
744 DeleteObject(SelectObject(s_hdc
, old_pen
));
753 /* Flush any output to the screen */
757 /* Is anything needed here? */
761 clear_rect(RECT
*rcp
)
763 /* Use trick for fast rect clear */
764 gui_mch_set_bg_color(gui
.back_pixel
);
765 ExtTextOut(s_hdc
, 0, 0, ETO_CLIPPED
| ETO_OPAQUE
, rcp
, NULL
, 0, NULL
);
770 gui_mch_get_screen_dimensions(int *screen_w
, int *screen_h
)
773 *screen_w
= GetSystemMetrics(SM_CXFULLSCREEN
)
774 - GetSystemMetrics(SM_CXFRAME
) * 2;
775 /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include
776 * the menubar for MSwin, we subtract it from the screen height, so that
777 * the window size can be made to fit on the screen. */
778 *screen_h
= GetSystemMetrics(SM_CYFULLSCREEN
)
779 - GetSystemMetrics(SM_CYFRAME
) * 2
781 - gui_mswin_get_menu_height(FALSE
)
787 #if defined(FEAT_MENU) || defined(PROTO)
789 * Add a sub menu to the menu bar.
796 vimmenu_T
*parent
= menu
->parent
;
798 menu
->submenu_id
= CreatePopupMenu();
799 menu
->id
= s_menu_id
++;
801 if (menu_is_menubar(menu
->name
))
803 InsertMenu((parent
== NULL
) ? s_menuBar
: parent
->submenu_id
,
804 (UINT
)pos
, MF_POPUP
| MF_STRING
| MF_BYPOSITION
,
805 (UINT
)menu
->submenu_id
, menu
->name
);
808 /* Fix window size if menu may have wrapped */
810 gui_mswin_get_menu_height(!gui
.starting
);
814 gui_mch_show_popupmenu(vimmenu_T
*menu
)
818 (void)GetCursorPos((LPPOINT
)&mp
);
819 gui_mch_show_popupmenu_at(menu
, (int)mp
.x
, (int)mp
.y
);
823 gui_make_popup(char_u
*path_name
, int mouse_pos
)
825 vimmenu_T
*menu
= gui_find_menu(path_name
);
829 /* Find the position of the current cursor */
832 temp_p
= GetDCOrg(s_hdc
);
833 p
.x
= LOWORD(temp_p
);
834 p
.y
= HIWORD(temp_p
);
839 gui_mch_getmouse(&mx
, &my
);
843 else if (curwin
!= NULL
)
845 p
.x
+= TEXT_X(W_WINCOL(curwin
) + curwin
->w_wcol
+ 1);
846 p
.y
+= TEXT_Y(W_WINROW(curwin
) + curwin
->w_wrow
+ 1);
849 gui_mch_show_popupmenu_at(menu
, (int)p
.x
, (int)p
.y
);
854 * Add a menu item to a menu
857 gui_mch_add_menu_item(
861 vimmenu_T
*parent
= menu
->parent
;
863 menu
->id
= s_menu_id
++;
864 menu
->submenu_id
= NULL
;
867 if (menu_is_toolbar(parent
->name
))
871 vim_memset(&newtb
, 0, sizeof(newtb
));
872 if (menu_is_separator(menu
->name
))
875 newtb
.fsStyle
= TBSTYLE_SEP
;
879 if (menu
->iconidx
>= TOOLBAR_BITMAP_COUNT
)
882 newtb
.iBitmap
= menu
->iconidx
;
883 newtb
.fsStyle
= TBSTYLE_BUTTON
;
885 newtb
.idCommand
= menu
->id
;
886 newtb
.fsState
= TBSTATE_ENABLED
;
887 SendMessage(s_toolbarhwnd
, TB_INSERTBUTTON
, (WPARAM
)idx
,
889 menu
->submenu_id
= (HMENU
)-1;
894 InsertMenu(parent
->submenu_id
, (UINT
)idx
,
895 (menu_is_separator(menu
->name
) ? MF_SEPARATOR
: MF_STRING
)
897 (UINT
)menu
->id
, menu
->name
);
902 * Destroy the machine specific menu widget.
905 gui_mch_destroy_menu(vimmenu_T
*menu
)
908 char pants
[80]; /*<VN> hack*/
911 * is this a toolbar button?
913 if (menu
->submenu_id
== (HMENU
)-1)
917 iButton
= SendMessage(s_toolbarhwnd
, TB_COMMANDTOINDEX
, (WPARAM
)menu
->id
, 0);
918 SendMessage(s_toolbarhwnd
, TB_DELETEBUTTON
, (WPARAM
)iButton
, 0);
924 * negri: horrible API bug when running 16-bit programs under Win9x or
925 * NT means that we can't use MF_BYCOMMAND for menu items which have
926 * submenus, including the top-level headings. We have to find the menu
927 * item and use MF_BYPOSITION instead. :-p
929 if (menu
->parent
!= NULL
930 && menu_is_popup(menu
->parent
->dname
)
931 && menu
->parent
->submenu_id
!= NULL
)
932 RemoveMenu(menu
->parent
->submenu_id
, menu
->id
, MF_BYCOMMAND
);
933 else if (menu
->submenu_id
== NULL
)
934 RemoveMenu(s_menuBar
, menu
->id
, MF_BYCOMMAND
);
935 else if (menu
->parent
!= NULL
)
937 i
= GetMenuItemCount(menu
->parent
->submenu_id
);
938 for (j
= 0; j
< i
; ++j
)
940 GetMenuString(menu
->parent
->submenu_id
, j
,
941 pants
, 80, MF_BYPOSITION
);
942 if (strcmp(pants
, menu
->name
) == 0)
944 RemoveMenu(menu
->parent
->submenu_id
, j
, MF_BYPOSITION
);
951 i
= GetMenuItemCount(s_menuBar
);
952 for (j
= 0; j
< i
; ++j
)
954 GetMenuString(s_menuBar
, j
, pants
, 80, MF_BYPOSITION
);
955 if (strcmp(pants
, menu
->name
) == 0)
957 RemoveMenu(s_menuBar
, j
, MF_BYPOSITION
);
963 if (menu
->submenu_id
!= NULL
)
964 DestroyMenu(menu
->submenu_id
);
971 * Make a menu either grey or not grey.
980 * is this a toolbar button?
982 if (menu
->submenu_id
== (HMENU
)-1)
984 SendMessage(s_toolbarhwnd
, TB_ENABLEBUTTON
,
985 (WPARAM
)menu
->id
, (LPARAM
) MAKELONG((grey
? FALSE
: TRUE
), 0) );
990 EnableMenuItem(s_menuBar
, menu
->id
, MF_BYCOMMAND
| MF_GRAYED
);
992 EnableMenuItem(s_menuBar
, menu
->id
, MF_BYCOMMAND
| MF_ENABLED
);
1000 /* define some macros used to make the dialogue creation more readable */
1002 #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1)
1003 #define add_word(x) *p++ = (x)
1004 #define add_byte(x) *((LPSTR)p)++ = (x)
1005 #define add_long(x) *((LPDWORD)p)++ = (x)
1007 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
1013 * The callback routine used by all the dialogs. Very simple. First,
1014 * acknowledges the INITDIALOG message so that Windows knows to do standard
1015 * dialog stuff (Return = default, Esc = cancel....) Second, if a button is
1016 * pressed, return that button's ID - IDCANCEL (2), which is the button's
1019 static BOOL CALLBACK
1026 if (message
== WM_INITDIALOG
)
1028 CenterWindow(hwnd
, GetWindow(hwnd
, GW_OWNER
));
1029 /* Set focus to the dialog. Set the default button, if specified. */
1030 (void)SetFocus(hwnd
);
1031 if (dialog_default_button
> IDCANCEL
)
1032 (void)SetFocus(GetDlgItem(hwnd
, dialog_default_button
));
1033 // if (dialog_default_button > 0)
1034 // (void)SetFocus(GetDlgItem(hwnd, dialog_default_button + IDCANCEL));
1038 if (message
== WM_COMMAND
)
1040 int button
= LOWORD(wParam
);
1042 /* Don't end the dialog if something was selected that was
1045 if (button
>= DLG_NONBUTTON_CONTROL
)
1048 /* If the edit box exists, copy the string. */
1049 if (s_textfield
!= NULL
)
1050 GetDlgItemText(hwnd
, DLG_NONBUTTON_CONTROL
+ 2,
1051 s_textfield
, IOSIZE
);
1054 * Need to check for IDOK because if the user just hits Return to
1055 * accept the default value, some reason this is what we get.
1058 EndDialog(hwnd
, dialog_default_button
);
1060 EndDialog(hwnd
, button
- IDCANCEL
);
1064 if ((message
== WM_SYSCOMMAND
) && (wParam
== SC_CLOSE
))
1073 * Create a dialog dynamically from the parameter strings.
1074 * type = type of dialog (question, alert, etc.)
1075 * title = dialog title. may be NULL for default title.
1076 * message = text to display. Dialog sizes to accommodate it.
1077 * buttons = '\n' separated list of button captions, default first.
1078 * dfltbutton = number of default button.
1080 * This routine returns 1 if the first button is pressed,
1081 * 2 for the second, etc.
1083 * 0 indicates Esc was pressed.
1084 * -1 for unexpected error
1086 * If stubbing out this fn, return 1.
1089 static const char_u dlg_icons
[] = /* must match names in resource file */
1108 LPWORD p
, pnumitems
;
1110 int *buttonWidths
, *buttonPositions
;
1126 TEXTMETRIC fontInfo
;
1128 int textWidth
, minButtonWidth
, messageWidth
;
1133 HGLOBAL hglbDlgTemp
;
1136 /* Don't output anything in silent mode ("ex -s") */
1138 return dfltbutton
; /* return default option */
1141 /* If there is no window yet, open it. */
1142 if (s_hwnd
== NULL
&& gui_mch_init() == FAIL
)
1145 if ((type
< 0) || (type
> VIM_LAST_TYPE
))
1148 /* allocate some memory for dialog template */
1149 /* TODO should compute this really*/
1151 hglbDlgTemp
= GlobalAlloc(GHND
, DLG_ALLOC_SIZE
);
1152 if (hglbDlgTemp
== NULL
)
1155 p
= (LPWORD
) GlobalLock(hglbDlgTemp
);
1161 * make a copy of 'buttons' to fiddle with it. complier grizzles because
1162 * vim_strsave() doesn't take a const arg (why not?), so cast away the
1165 tbuffer
= vim_strsave(buttons
);
1166 if (tbuffer
== NULL
)
1169 --dfltbutton
; /* Change from one-based to zero-based */
1173 for (i
= 0; tbuffer
[i
] != '\0'; i
++)
1175 if (tbuffer
[i
] == DLG_BUTTON_SEP
)
1178 if (dfltbutton
>= numButtons
)
1181 /* Allocate array to hold the width of each button */
1182 buttonWidths
= (int *) lalloc(numButtons
* sizeof(int), TRUE
);
1183 if (buttonWidths
== NULL
)
1186 /* Allocate array to hold the X position of each button */
1187 buttonPositions
= (int *) lalloc(numButtons
* sizeof(int), TRUE
);
1188 if (buttonPositions
== NULL
)
1192 * Calculate how big the dialog must be.
1194 hwnd
= GetDesktopWindow();
1195 hdc
= GetWindowDC(hwnd
);
1196 oldFont
= SelectFont(hdc
, GetStockObject(SYSTEM_FONT
));
1197 dlgPaddingX
= DLG_OLD_STYLE_PADDING_X
;
1198 dlgPaddingY
= DLG_OLD_STYLE_PADDING_Y
;
1200 GetTextMetrics(hdc
, &fontInfo
);
1201 fontHeight
= fontInfo
.tmHeight
;
1203 /* Minimum width for horizontal button */
1204 minButtonWidth
= GetTextWidth(hdc
, "Cancel", 6);
1206 /* Maximum width of a dialog, if possible */
1207 GetWindowRect(s_hwnd
, &rect
);
1208 maxDialogWidth
= rect
.right
- rect
.left
1209 - GetSystemMetrics(SM_CXFRAME
) * 2;
1210 if (maxDialogWidth
< DLG_MIN_MAX_WIDTH
)
1211 maxDialogWidth
= DLG_MIN_MAX_WIDTH
;
1213 /* Set dlgwidth to width of message */
1219 pend
= vim_strchr(pstart
, DLG_BUTTON_SEP
);
1221 pend
= pstart
+ STRLEN(pstart
); /* Last line of message. */
1222 msgheight
+= fontHeight
;
1223 textWidth
= GetTextWidth(hdc
, pstart
, pend
- pstart
);
1224 if (textWidth
> messageWidth
)
1225 messageWidth
= textWidth
;
1227 } while (*pend
!= NUL
);
1228 dlgwidth
= messageWidth
;
1230 /* Add width of icon to dlgwidth, and some space */
1231 dlgwidth
+= DLG_ICON_WIDTH
+ 3 * dlgPaddingX
;
1233 if (msgheight
< DLG_ICON_HEIGHT
)
1234 msgheight
= DLG_ICON_HEIGHT
;
1237 * Check button names. A long one will make the dialog wider.
1239 vertical
= (vim_strchr(p_go
, GO_VERTICAL
) != NULL
);
1242 // Place buttons horizontally if they fit.
1243 horizWidth
= dlgPaddingX
;
1248 pend
= vim_strchr(pstart
, DLG_BUTTON_SEP
);
1250 pend
= pstart
+ STRLEN(pstart
); // Last button name.
1251 textWidth
= GetTextWidth(hdc
, pstart
, pend
- pstart
);
1252 if (textWidth
< minButtonWidth
)
1253 textWidth
= minButtonWidth
;
1254 textWidth
+= dlgPaddingX
; /* Padding within button */
1255 buttonWidths
[i
] = textWidth
;
1256 buttonPositions
[i
++] = horizWidth
;
1257 horizWidth
+= textWidth
+ dlgPaddingX
; /* Pad between buttons */
1259 } while (*pend
!= NUL
);
1261 if (horizWidth
> maxDialogWidth
)
1262 vertical
= TRUE
; // Too wide to fit on the screen.
1263 else if (horizWidth
> dlgwidth
)
1264 dlgwidth
= horizWidth
;
1269 // Stack buttons vertically.
1273 pend
= vim_strchr(pstart
, DLG_BUTTON_SEP
);
1275 pend
= pstart
+ STRLEN(pstart
); // Last button name.
1276 textWidth
= GetTextWidth(hdc
, pstart
, pend
- pstart
);
1277 textWidth
+= dlgPaddingX
; /* Padding within button */
1278 textWidth
+= DLG_VERT_PADDING_X
* 2; /* Padding around button */
1279 if (textWidth
> dlgwidth
)
1280 dlgwidth
= textWidth
;
1282 } while (*pend
!= NUL
);
1285 if (dlgwidth
< DLG_MIN_WIDTH
)
1286 dlgwidth
= DLG_MIN_WIDTH
; /* Don't allow a really thin dialog!*/
1288 /* start to fill in the dlgtemplate information. addressing by WORDs */
1289 lStyle
= DS_MODALFRAME
| WS_CAPTION
| WS_VISIBLE
;
1292 pnumitems
= p
; /*save where the number of items must be stored*/
1293 add_byte(0); // NumberOfItems(will change later)
1296 add_word(PixelToDialogX(dlgwidth
));
1300 dlgheight
= msgheight
+ 2 * dlgPaddingY
+
1301 DLG_VERT_PADDING_Y
+ 2 * fontHeight
* numButtons
;
1303 dlgheight
= msgheight
+ 3 * dlgPaddingY
+ 2 * fontHeight
;
1305 // Dialog needs to be taller if contains an edit box.
1306 editboxheight
= fontHeight
+ dlgPaddingY
+ 4 * DLG_VERT_PADDING_Y
;
1307 if (textfield
!= NULL
)
1308 dlgheight
+= editboxheight
;
1310 add_word(PixelToDialogY(dlgheight
));
1313 add_byte(0); //class
1315 /* copy the title of the dialog */
1316 add_string(title
? title
: ("Vim"VIM_VERSION_MEDIUM
));
1318 buttonYpos
= msgheight
+ 2 * dlgPaddingY
;
1320 if (textfield
!= NULL
)
1321 buttonYpos
+= editboxheight
;
1323 pstart
= tbuffer
; //dflt_text
1324 horizWidth
= (dlgwidth
- horizWidth
) / 2; /* Now it's X offset */
1325 for (i
= 0; i
< numButtons
; i
++)
1327 /* get end of this button. */
1328 for ( pend
= pstart
;
1329 *pend
&& (*pend
!= DLG_BUTTON_SEP
);
1338 * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets
1339 * the focus to the first tab-able button and in so doing makes that
1340 * the default!! Grrr. Workaround: Make the default button the only
1341 * one with WS_TABSTOP style. Means user can't tab between buttons, but
1342 * he/she can use arrow keys.
1344 * NOTE (Thore): Setting BS_DEFPUSHBUTTON works fine when it's the
1345 * first one, so I changed the correct button to be this style. This
1346 * is necessary because when an edit box is added, we need a button to
1347 * be default. The edit box will be the default control, and when the
1348 * user presses enter from the edit box we want the default button to
1353 p
= add_dialog_element(p
,
1354 ((i
== dfltbutton
|| dfltbutton
< 0) && textfield
!= NULL
1355 ? BS_DEFPUSHBUTTON
: BS_PUSHBUTTON
) | WS_TABSTOP
,
1356 PixelToDialogX(DLG_VERT_PADDING_X
),
1357 PixelToDialogY(buttonYpos
/* TBK */
1358 + 2 * fontHeight
* i
),
1359 PixelToDialogX(dlgwidth
- 2 * DLG_VERT_PADDING_X
),
1360 (WORD
)(PixelToDialogY(2 * fontHeight
) - 1),
1361 (WORD
)(IDCANCEL
+ 1 + i
), (BYTE
)0x80, pstart
);
1365 p
= add_dialog_element(p
,
1366 ((i
== dfltbutton
|| dfltbutton
< 0) && textfield
!= NULL
1367 ? BS_DEFPUSHBUTTON
: BS_PUSHBUTTON
) | WS_TABSTOP
,
1368 PixelToDialogX(horizWidth
+ buttonPositions
[i
]),
1369 PixelToDialogY(buttonYpos
), /* TBK */
1370 PixelToDialogX(buttonWidths
[i
]),
1371 (WORD
)(PixelToDialogY(2 * fontHeight
) - 1),
1372 (WORD
)(IDCANCEL
+ 1 + i
), (BYTE
)0x80, pstart
);
1375 pstart
= pend
+ 1; /*next button*/
1378 *pnumitems
+= numButtons
;
1381 p
= add_dialog_element(p
, SS_ICON
,
1382 PixelToDialogX(dlgPaddingX
),
1383 PixelToDialogY(dlgPaddingY
),
1384 PixelToDialogX(DLG_ICON_WIDTH
),
1385 PixelToDialogY(DLG_ICON_HEIGHT
),
1386 DLG_NONBUTTON_CONTROL
+ 0, (BYTE
)0x82,
1390 /* Dialog message */
1391 p
= add_dialog_element(p
, SS_LEFT
,
1392 PixelToDialogX(2 * dlgPaddingX
+ DLG_ICON_WIDTH
),
1393 PixelToDialogY(dlgPaddingY
),
1394 (WORD
)(PixelToDialogX(messageWidth
) + 1),
1395 PixelToDialogY(msgheight
),
1396 DLG_NONBUTTON_CONTROL
+ 1, (BYTE
)0x82, message
);
1399 if (textfield
!= NULL
)
1401 p
= add_dialog_element(p
, ES_LEFT
| ES_AUTOHSCROLL
| WS_TABSTOP
| WS_BORDER
,
1402 PixelToDialogX(2 * dlgPaddingX
),
1403 PixelToDialogY(2 * dlgPaddingY
+ msgheight
),
1404 PixelToDialogX(dlgwidth
- 4 * dlgPaddingX
),
1405 PixelToDialogY(fontHeight
+ dlgPaddingY
),
1406 DLG_NONBUTTON_CONTROL
+ 2, (BYTE
)0x81, textfield
);
1412 SelectFont(hdc
, oldFont
);
1413 ReleaseDC(hwnd
, hdc
);
1414 dp
= MakeProcInstance((FARPROC
)dialog_callback
, s_hinst
);
1417 /* Let the dialog_callback() function know which button to make default
1418 * If we have an edit box, make that the default. We also need to tell
1419 * dialog_callback() if this dialog contains an edit box or not. We do
1420 * this by setting s_textfield if it does.
1422 if (textfield
!= NULL
)
1424 dialog_default_button
= DLG_NONBUTTON_CONTROL
+ 2;
1425 s_textfield
= textfield
;
1429 dialog_default_button
= IDCANCEL
+ 1 + dfltbutton
;
1433 /*show the dialog box modally and get a return value*/
1434 nchar
= DialogBoxIndirect(
1436 (HGLOBAL
) hglbDlgTemp
,
1440 FreeProcInstance( dp
);
1441 GlobalUnlock(hglbDlgTemp
);
1442 GlobalFree(hglbDlgTemp
);
1444 vim_free(buttonWidths
);
1445 vim_free(buttonPositions
);
1452 * Put a simple element (basic class) onto a dialog template in memory.
1453 * return a pointer to where the next item should be added.
1456 * lStyle = additional style flags
1457 * x,y = x & y positions IN DIALOG UNITS
1458 * w,h = width and height IN DIALOG UNITS
1459 * Id = ID used in messages
1460 * clss = class ID, e.g 0x80 for a button, 0x82 for a static
1461 * caption = usually text or resource name
1463 * TODO: use the length information noted here to enable the dialog creation
1464 * routines to work out more exactly how much memory they need to alloc.
1476 const char *caption
)
1479 lStyle
= lStyle
| WS_VISIBLE
| WS_CHILD
;
1488 if (((lStyle
& SS_ICON
) != 0) && (clss
== 0x82))
1490 /* Use resource ID */
1495 add_string(caption
);
1497 add_byte(0); //# of extra bytes following
1508 #endif /* FEAT_GUI_DIALOG */
1511 get_dialog_font_metrics(void)
1514 dlgFontSize
= GetDialogBaseUnits(); /* fall back to big old system*/
1515 s_dlgfntwidth
= LOWORD(dlgFontSize
);
1516 s_dlgfntheight
= HIWORD(dlgFontSize
);
1520 #if defined(FEAT_TOOLBAR) || defined(PROTO)
1521 #include "gui_w3~1.h"
1523 * Create the toolbar, initially unpopulated.
1524 * (just like the menu, there are no defaults, it's all
1525 * set up through menu.vim)
1528 initialise_toolbar(void)
1530 s_toolbarhwnd
= CreateToolbar(
1532 WS_CHILD
| WS_VISIBLE
,
1533 CMD_TB_BASE
, /*<vn>*/
1534 31, //number of images in initial bitmap
1536 IDR_TOOLBAR1
, // id of initial bitmap
1538 0 // initial number of buttons
1541 gui_mch_show_toolbar(vim_strchr(p_go
, GO_TOOLBAR
) != NULL
);
1545 #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO)
1547 * Make the GUI window come to the foreground.
1550 gui_mch_set_foreground(void)
1552 if (IsIconic(s_hwnd
))
1553 SendMessage(s_hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0);
1554 SetActiveWindow(s_hwnd
);