Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 |
| 51 | static 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) |
| 64 | static BOOL CALLBACK dialog_callback(HWND, UINT, WPARAM, LPARAM); |
| 65 | |
| 66 | static LPWORD |
| 67 | add_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 | |
| 78 | static int dialog_default_button = -1; |
| 79 | #endif |
| 80 | |
| 81 | static void get_dialog_font_metrics(void); |
| 82 | |
| 83 | #ifdef FEAT_TOOLBAR |
| 84 | static 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 |
| 93 | gui_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; |
Bram Moolenaar | 2e2a281 | 2006-03-27 20:55:21 +0000 | [diff] [blame] | 131 | gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 |
| 317 | gui_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 |
| 328 | gui_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 |
| 490 | gui_mch_set_shellsize(int width, int height, |
Bram Moolenaar | 2e2a281 | 2006-03-27 20:55:21 +0000 | [diff] [blame] | 491 | int min_width, int min_height, int base_width, int base_height, |
| 492 | int direction) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 */ |
Bram Moolenaar | 2e2a281 | 2006-03-27 20:55:21 +0000 | [diff] [blame] | 532 | if ((direction & RESIZE_HOR) && win_xpos + win_width > workarea_rect.right) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 533 | win_xpos = workarea_rect.right - win_width; |
| 534 | |
Bram Moolenaar | 2e2a281 | 2006-03-27 20:55:21 +0000 | [diff] [blame] | 535 | if ((direction & RESIZE_HOR) && win_xpos < workarea_rect.left) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 536 | win_xpos = workarea_rect.left; |
| 537 | |
Bram Moolenaar | 2e2a281 | 2006-03-27 20:55:21 +0000 | [diff] [blame] | 538 | if ((direction & RESIZE_VERT) |
| 539 | && win_ypos + win_height > workarea_rect.bottom) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 540 | win_ypos = workarea_rect.bottom - win_height; |
| 541 | |
Bram Moolenaar | 2e2a281 | 2006-03-27 20:55:21 +0000 | [diff] [blame] | 542 | if ((direction & RESIZE_VERT) && win_ypos < workarea_rect.top) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 |
| 556 | gui_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 |
| 583 | gui_mch_set_font(GuiFont font) |
| 584 | { |
| 585 | gui.currFont = font; |
| 586 | SelectFont(s_hdc, gui.currFont); |
| 587 | } |
| 588 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 589 | /* |
| 590 | * Set the current text foreground color. |
| 591 | */ |
| 592 | void |
| 593 | gui_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 |
| 603 | gui_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 | |
Bram Moolenaar | 3918c95 | 2005-03-15 22:34:55 +0000 | [diff] [blame] | 612 | /* |
| 613 | * Set the current text special color. |
| 614 | */ |
| 615 | void |
| 616 | gui_mch_set_sp_color(guicolor_T color) |
| 617 | { |
| 618 | /* TODO */ |
| 619 | } |
| 620 | |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 621 | |
| 622 | |
| 623 | void |
| 624 | gui_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 | { |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 667 | /* Compute the length in display cells. */ |
Bram Moolenaar | 72597a5 | 2010-07-18 15:31:08 +0200 | [diff] [blame^] | 668 | rc.right = FILL_X(col + mb_string2cells(text, len)); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 |
| 751 | gui_mch_flush(void) |
| 752 | { |
| 753 | /* Is anything needed here? */ |
| 754 | } |
| 755 | |
| 756 | static void |
| 757 | clear_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 | |
Bram Moolenaar | b1b715d | 2006-01-21 22:09:43 +0000 | [diff] [blame] | 765 | void |
| 766 | gui_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 | } |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 781 | |
| 782 | |
| 783 | #if defined(FEAT_MENU) || defined(PROTO) |
| 784 | /* |
| 785 | * Add a sub menu to the menu bar. |
| 786 | */ |
| 787 | void |
| 788 | gui_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 |
| 810 | gui_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 |
Bram Moolenaar | ec8a10a | 2005-07-08 22:19:11 +0000 | [diff] [blame] | 819 | gui_make_popup(char_u *path_name, int mouse_pos) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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); |
Bram Moolenaar | ec8a10a | 2005-07-08 22:19:11 +0000 | [diff] [blame] | 831 | if (mouse_pos) |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 832 | { |
Bram Moolenaar | ec8a10a | 2005-07-08 22:19:11 +0000 | [diff] [blame] | 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); |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 |
| 853 | gui_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 |
| 901 | gui_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 |
| 970 | gui_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 |
| 1016 | dialog_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 | |
| 1085 | static 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 |
| 1095 | gui_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 |
| 1463 | add_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 |
| 1507 | get_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 |
| 1524 | initialise_toolbar(void) |
| 1525 | { |
| 1526 | s_toolbarhwnd = CreateToolbar( |
| 1527 | s_hwnd, |
| 1528 | WS_CHILD | WS_VISIBLE, |
| 1529 | CMD_TB_BASE, /*<vn>*/ |
Bram Moolenaar | 7263a77 | 2007-05-10 17:35:54 +0000 | [diff] [blame] | 1530 | 31, //number of images in initial bitmap |
Bram Moolenaar | 071d427 | 2004-06-13 20:20:40 +0000 | [diff] [blame] | 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 |
| 1546 | gui_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 |