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