Mercurial > vim
view src/gui_w16.c @ 2894:fe9c7da98b5e v7.3.220
updated for version 7.3.220
Problem: Python 3: vim.error is a 'str' instead of an 'Exception' object,
so 'except' or 'raise' it causes a 'SystemError' exception.
Buffer objects do not support slice assignment.
When exchanging text between Vim and Python, multibyte texts become
gabage or cause Unicode Expceptions, etc.
'py3file' tries to read in the file as Unicode, sometimes causes
UnicodeDecodeException
Solution: Fix the problems. (lilydjwg)
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Sun, 19 Jun 2011 00:27:51 +0200 |
parents | 951641b8784d |
children | 2e4539dc2de7 |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * GUI support by Robert Webb * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * See README.txt for an overview of the Vim source code. */ /* * gui_w16.c * * GUI support for Microsoft Windows 3.1x * * George V. Reilly <george@reilly.org> wrote the original Win32 GUI. * Robert Webb reworked it to use the existing GUI stuff and added menu, * scrollbars, etc. * * Vince Negri then butchered the code to get it compiling for * 16-bit windows. * */ /* * Include the common stuff for MS-Windows GUI. */ #include "gui_w48.c" #include "guiw16rc.h" /* Undocumented Windows Message - not even defined in some SDK headers */ #define WM_EXITSIZEMOVE 0x0232 #ifdef FEAT_TOOLBAR # define CMD_TB_BASE (99) # include <vimtbar.h> #endif #ifdef PROTO # define WINAPI #endif #define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn) \ ((fn)((hwnd), (HDROP)(wParam)), 0L) /* Local variables: */ #ifdef FEAT_MENU static UINT s_menu_id = 100; #endif #define VIM_NAME "vim" #define VIM_CLASS "Vim" #define DLG_ALLOC_SIZE 16 * 1024 /* * stuff for dialogs, menus, tearoffs etc. */ #if defined(FEAT_GUI_DIALOG) || defined(PROTO) static BOOL CALLBACK dialog_callback(HWND, UINT, WPARAM, LPARAM); static LPWORD add_dialog_element( LPWORD p, DWORD lStyle, WORD x, WORD y, WORD w, WORD h, WORD Id, BYTE clss, const char *caption); static int dialog_default_button = -1; #endif static void get_dialog_font_metrics(void); #ifdef FEAT_TOOLBAR static void initialise_toolbar(void); #endif #ifdef FEAT_MENU /* * Figure out how high the menu bar is at the moment. */ static int gui_mswin_get_menu_height( int fix_window) /* If TRUE, resize window if menu height changed */ { static int old_menu_height = -1; int num; int menu_height; if (gui.menu_is_active) num = GetMenuItemCount(s_menuBar); else num = 0; if (num == 0) menu_height = 0; else if (gui.starting) menu_height = GetSystemMetrics(SM_CYMENU); else { RECT r1, r2; int frameht = GetSystemMetrics(SM_CYFRAME); int capht = GetSystemMetrics(SM_CYCAPTION); /* get window rect of s_hwnd * get client rect of s_hwnd * get cap height * subtract from window rect, the sum of client height, * (if not maximized)frame thickness, and caption height. */ GetWindowRect(s_hwnd, &r1); GetClientRect(s_hwnd, &r2); menu_height = r1.bottom - r1.top - (r2.bottom-r2.top + 2 * frameht * (!IsZoomed(s_hwnd)) + capht); } if (fix_window && menu_height != old_menu_height) { old_menu_height = menu_height; gui_set_shellsize(FALSE, FALSE, RESIZE_VERT); } return menu_height; } #endif /*FEAT_MENU*/ /* * Even though we have _DuringSizing() which makes the rubber band a valid * size, we need this for when the user maximises the window. * TODO: Doesn't seem to adjust the width though for some reason. */ static BOOL _OnWindowPosChanging( HWND hwnd, LPWINDOWPOS lpwpos) { if (!IsIconic(hwnd) && !(lpwpos->flags & SWP_NOSIZE)) { gui_mswin_get_valid_dimensions(lpwpos->cx, lpwpos->cy, &lpwpos->cx, &lpwpos->cy); } return 0; } static LRESULT CALLBACK _WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { /* TRACE("WndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", hwnd, uMsg, wParam, lParam); */ HandleMouseHide(uMsg, lParam); s_uMsg = uMsg; s_wParam = wParam; s_lParam = lParam; switch (uMsg) { HANDLE_MSG(hwnd, WM_DEADCHAR, _OnDeadChar); HANDLE_MSG(hwnd, WM_SYSDEADCHAR, _OnDeadChar); /* HANDLE_MSG(hwnd, WM_ACTIVATE, _OnActivate); */ HANDLE_MSG(hwnd, WM_CHAR, _OnChar); HANDLE_MSG(hwnd, WM_CLOSE, _OnClose); /* HANDLE_MSG(hwnd, WM_COMMAND, _OnCommand); */ HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy); HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles); HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll); HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus); #ifdef FEAT_MENU HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu); #endif /* HANDLE_MSG(hwnd, WM_MOVE, _OnMove); */ /* HANDLE_MSG(hwnd, WM_NCACTIVATE, _OnNCActivate); */ HANDLE_MSG(hwnd, WM_SETFOCUS, _OnSetFocus); HANDLE_MSG(hwnd, WM_SIZE, _OnSize); /* HANDLE_MSG(hwnd, WM_SYSCOMMAND, _OnSysCommand); */ /* HANDLE_MSG(hwnd, WM_SYSKEYDOWN, _OnAltKey); */ HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll); HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging); HANDLE_MSG(hwnd, WM_ACTIVATEAPP, _OnActivateApp); case WM_QUERYENDSESSION: /* System wants to go down. */ gui_shell_closed(); /* Will exit when no changed buffers. */ return FALSE; /* Do NOT allow system to go down. */ case WM_ENDSESSION: if (wParam) /* system only really goes down when wParam is TRUE */ _OnEndSession(); break; case WM_SYSCHAR: /* * if 'winaltkeys' is "no", or it's "menu" and it's not a menu * shortcut key, handle like a typed ALT key, otherwise call Windows * ALT key handling. */ #ifdef FEAT_MENU if ( !gui.menu_is_active || p_wak[0] == 'n' || (p_wak[0] == 'm' && !gui_is_menu_shortcut((int)wParam)) ) #endif return HANDLE_WM_SYSCHAR((hwnd), (wParam), (lParam), (_OnSysChar)); #ifdef FEAT_MENU else return MyWindowProc(hwnd, uMsg, wParam, lParam); #endif case WM_SYSKEYUP: #ifdef FEAT_MENU /* Only when menu is active, ALT key is used for that. */ if (gui.menu_is_active) { return MyWindowProc(hwnd, uMsg, wParam, lParam); } else #endif return 0; #if defined(MENUHINTS) && defined(FEAT_MENU) case WM_MENUSELECT: if (((UINT) LOWORD(lParam) & (0xffff ^ (MF_MOUSESELECT + MF_BITMAP + MF_POPUP))) == MF_HILITE && (State & CMDLINE) == 0) { UINT idButton; int idx; vimmenu_T *pMenu; idButton = (UINT)LOWORD(wParam); pMenu = gui_mswin_find_menu(root_menu, idButton); if (pMenu) { idx = MENU_INDEX_TIP; msg_clr_cmdline(); if (pMenu->strings[idx]) msg(pMenu->strings[idx]); else msg(""); setcursor(); out_flush(); } } break; #endif case WM_NCHITTEST: { LRESULT result; int x, y; int xPos = GET_X_LPARAM(lParam); result = MyWindowProc(hwnd, uMsg, wParam, lParam); if (result == HTCLIENT) { gui_mch_get_winpos(&x, &y); xPos -= x; if (xPos < 48) /*<VN> TODO should use system metric?*/ return HTBOTTOMLEFT; else return HTBOTTOMRIGHT; } else return result; } /* break; */ default: #ifdef MSWIN_FIND_REPLACE if (uMsg == s_findrep_msg && s_findrep_msg != 0) { _OnFindRepl(); } #endif return MyWindowProc(hwnd, uMsg, wParam, lParam); } return 1; } /* * End of call-back routines */ /* * Parse the GUI related command-line arguments. Any arguments used are * deleted from argv, and *argc is decremented accordingly. This is called * when vim is started, whether or not the GUI has been started. */ void gui_mch_prepare(int *argc, char **argv) { /* No special args for win16 GUI at the moment. */ } /* * Initialise the GUI. Create all the windows, set up all the call-backs * etc. */ int gui_mch_init(void) { const char szVimWndClass[] = VIM_CLASS; const char szTextAreaClass[] = "VimTextArea"; WNDCLASS wndclass; #ifdef WIN16_3DLOOK Ctl3dRegister(s_hinst); Ctl3dAutoSubclass(s_hinst); #endif /* Display any pending error messages */ display_errors(); gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL); gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL); #ifdef FEAT_MENU gui.menu_height = 0; /* Windows takes care of this */ #endif gui.border_width = 0; gui.currBgColor = INVALCOLOR; s_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE)); if (GetClassInfo(s_hinst, szVimWndClass, &wndclass) == 0) { wndclass.style = 0; wndclass.lpfnWndProc = _WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = s_hinst; wndclass.hIcon = LoadIcon(wndclass.hInstance, MAKEINTRESOURCE(IDR_VIM)); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = s_brush; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szVimWndClass; if (( #ifdef GLOBAL_IME atom = #endif RegisterClass(&wndclass)) == 0) return FAIL; } s_hwnd = CreateWindow( szVimWndClass, "Vim MSWindows GUI", WS_OVERLAPPEDWINDOW, gui_win_x == -1 ? CW_USEDEFAULT : gui_win_x, gui_win_y == -1 ? CW_USEDEFAULT : gui_win_y, 100, /* Any value will do */ 100, /* Any value will do */ NULL, NULL, s_hinst, NULL); if (s_hwnd == NULL) return FAIL; #ifdef GLOBAL_IME global_ime_init(atom, s_hwnd); #endif /* Create the text area window */ if (GetClassInfo(s_hinst, szTextAreaClass, &wndclass) == 0) { wndclass.style = CS_OWNDC; wndclass.lpfnWndProc = _TextAreaWndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = s_hinst; wndclass.hIcon = NULL; wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szTextAreaClass; if (RegisterClass(&wndclass) == 0) return FAIL; } s_textArea = CreateWindow( szTextAreaClass, "Vim text area", WS_CHILD | WS_VISIBLE, 0, 0, 100, /* Any value will do for now */ 100, /* Any value will do for now */ s_hwnd, NULL, s_hinst, NULL); if (s_textArea == NULL) return FAIL; #ifdef FEAT_MENU s_menuBar = CreateMenu(); #endif s_hdc = GetDC(s_textArea); #ifdef MSWIN16_FASTTEXT SetBkMode(s_hdc, OPAQUE); #endif DragAcceptFiles(s_hwnd, TRUE); /* Do we need to bother with this? */ /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */ /* Get background/foreground colors from the system */ gui_mch_def_colors(); /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc * file) */ set_normal_colors(); /* * Check that none of the colors are the same as the background color. * Then store the current values as the defaults. */ gui_check_colors(); gui.def_norm_pixel = gui.norm_pixel; gui.def_back_pixel = gui.back_pixel; /* Get the colors for the highlight groups (gui_check_colors() might have * changed them) */ highlight_gui_started(); /* * Start out by adding the configured border width into the border offset */ gui.border_offset = gui.border_width; /* * compute a couple of metrics used for the dialogs */ get_dialog_font_metrics(); #ifdef FEAT_TOOLBAR /* * Create the toolbar */ initialise_toolbar(); #endif #ifdef MSWIN_FIND_REPLACE /* * Initialise the dialog box stuff */ s_findrep_msg = RegisterWindowMessage(FINDMSGSTRING); /* Initialise the struct */ s_findrep_struct.lStructSize = sizeof(s_findrep_struct); s_findrep_struct.lpstrFindWhat = alloc(MSWIN_FR_BUFSIZE); s_findrep_struct.lpstrFindWhat[0] = NUL; s_findrep_struct.lpstrReplaceWith = alloc(MSWIN_FR_BUFSIZE); s_findrep_struct.lpstrReplaceWith[0] = NUL; s_findrep_struct.wFindWhatLen = MSWIN_FR_BUFSIZE; s_findrep_struct.wReplaceWithLen = MSWIN_FR_BUFSIZE; #endif return OK; } /* * Set the size of the window to the given width and height in pixels. */ void gui_mch_set_shellsize(int width, int height, int min_width, int min_height, int base_width, int base_height, int direction) { RECT workarea_rect; int win_width, win_height; int win_xpos, win_ypos; WINDOWPLACEMENT wndpl; /* try to keep window completely on screen */ /* get size of the screen work area - use SM_CYFULLSCREEN * instead of SM_CYSCREEN so that we don't overlap the * taskbar if someone fires us up on Win95/NT */ workarea_rect.left = 0; workarea_rect.top = 0; workarea_rect.right = GetSystemMetrics(SM_CXSCREEN); workarea_rect.bottom = GetSystemMetrics(SM_CYFULLSCREEN); /* get current posision of our window */ wndpl.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(s_hwnd, &wndpl); if (wndpl.showCmd == SW_SHOWNORMAL) { win_xpos = wndpl.rcNormalPosition.left; win_ypos = wndpl.rcNormalPosition.top; } else { win_xpos = workarea_rect.left; win_ypos = workarea_rect.top; } /* compute the size of the outside of the window */ win_width = width + GetSystemMetrics(SM_CXFRAME) * 2; win_height = height + GetSystemMetrics(SM_CYFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION) #ifdef FEAT_MENU + gui_mswin_get_menu_height(FALSE) #endif ; /* if the window is going off the screen, move it on to the screen */ if ((direction & RESIZE_HOR) && win_xpos + win_width > workarea_rect.right) win_xpos = workarea_rect.right - win_width; if ((direction & RESIZE_HOR) && win_xpos < workarea_rect.left) win_xpos = workarea_rect.left; if ((direction & RESIZE_VERT) && win_ypos + win_height > workarea_rect.bottom) win_ypos = workarea_rect.bottom - win_height; if ((direction & RESIZE_VERT) && win_ypos < workarea_rect.top) win_ypos = workarea_rect.top; /* set window position */ SetWindowPos(s_hwnd, NULL, win_xpos, win_ypos, win_width, win_height, SWP_NOZORDER | SWP_NOACTIVATE); #ifdef FEAT_MENU /* Menu may wrap differently now */ gui_mswin_get_menu_height(!gui.starting); #endif } void gui_mch_set_scrollbar_thumb( scrollbar_T *sb, long val, long size, long max) { sb->scroll_shift = 0; while (max > 32767) { max = (max + 1) >> 1; val >>= 1; size >>= 1; ++sb->scroll_shift; } if (sb->scroll_shift > 0) ++size; SetScrollRange(sb->id, SB_CTL, 0, (int) max, FALSE); SetScrollPos(sb->id, SB_CTL, (int) val, TRUE); } /* * Set the current text font. */ void gui_mch_set_font(GuiFont font) { gui.currFont = font; SelectFont(s_hdc, gui.currFont); } /* * Set the current text foreground color. */ void gui_mch_set_fg_color(guicolor_T color) { gui.currFgColor = color; SetTextColor(s_hdc, gui.currFgColor); } /* * Set the current text background color. */ void gui_mch_set_bg_color(guicolor_T color) { if (gui.currBgColor == color) return; gui.currBgColor = color; SetBkColor(s_hdc, gui.currBgColor); } /* * Set the current text special color. */ void gui_mch_set_sp_color(guicolor_T color) { /* TODO */ } void gui_mch_draw_string( int row, int col, char_u *text, int len, int flags) { #ifndef MSWIN16_FASTTEXT static int *padding = NULL; static int pad_size = 0; int i; #endif HPEN hpen, old_pen; int y; #ifndef MSWIN16_FASTTEXT /* * Italic and bold text seems to have an extra row of pixels at the bottom * (below where the bottom of the character should be). If we draw the * characters with a solid background, the top row of pixels in the * character below will be overwritten. We can fix this by filling in the * background ourselves, to the correct character proportions, and then * writing the character in transparent mode. Still have a problem when * the character is "_", which gets written on to the character below. * New fix: set gui.char_ascent to -1. This shifts all characters up one * pixel in their slots, which fixes the problem with the bottom row of * pixels. We still need this code because otherwise the top row of pixels * becomes a problem. - webb. */ HBRUSH hbr; RECT rc; if (!(flags & DRAW_TRANSP)) { /* * Clear background first. * Note: FillRect() excludes right and bottom of rectangle. */ rc.left = FILL_X(col); rc.top = FILL_Y(row); #ifdef FEAT_MBYTE if (has_mbyte) { /* Compute the length in display cells. */ rc.right = FILL_X(col + mb_string2cells(text, len)); } else #endif rc.right = FILL_X(col + len); rc.bottom = FILL_Y(row + 1); hbr = CreateSolidBrush(gui.currBgColor); FillRect(s_hdc, &rc, hbr); DeleteBrush(hbr); SetBkMode(s_hdc, TRANSPARENT); /* * When drawing block cursor, prevent inverted character spilling * over character cell (can happen with bold/italic) */ if (flags & DRAW_CURSOR) { pcliprect = &rc; foptions = ETO_CLIPPED; } } #else /* * Alternative: write the characters in opaque mode, since we have blocked * bold or italic fonts. */ /* The OPAQUE mode and backcolour have already been set */ #endif /* The forecolor and font have already been set */ #ifndef MSWIN16_FASTTEXT if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) { vim_free(padding); pad_size = Columns; padding = (int *)alloc(pad_size * sizeof(int)); if (padding != NULL) for (i = 0; i < pad_size; i++) padding[i] = gui.char_width; } #endif /* * We have to provide the padding argument because italic and bold versions * of fixed-width fonts are often one pixel or so wider than their normal * versions. * No check for DRAW_BOLD, Windows will have done it already. */ #ifndef MSWIN16_FASTTEXT ExtTextOut(s_hdc, TEXT_X(col), TEXT_Y(row), 0, NULL, (char *)text, len, padding); #else TextOut(s_hdc, TEXT_X(col), TEXT_Y(row), (char *)text, len); #endif if (flags & DRAW_UNDERL) { hpen = CreatePen(PS_SOLID, 1, gui.currFgColor); old_pen = SelectObject(s_hdc, hpen); /* When p_linespace is 0, overwrite the bottom row of pixels. * Otherwise put the line just below the character. */ y = FILL_Y(row + 1) - 1; #ifndef MSWIN16_FASTTEXT if (p_linespace > 1) y -= p_linespace - 1; #endif MoveToEx(s_hdc, FILL_X(col), y, NULL); /* Note: LineTo() excludes the last pixel in the line. */ LineTo(s_hdc, FILL_X(col + len), y); DeleteObject(SelectObject(s_hdc, old_pen)); } } /* * Output routines. */ /* Flush any output to the screen */ void gui_mch_flush(void) { /* Is anything needed here? */ } static void clear_rect(RECT *rcp) { /* Use trick for fast rect clear */ gui_mch_set_bg_color(gui.back_pixel); ExtTextOut(s_hdc, 0, 0, ETO_CLIPPED | ETO_OPAQUE, rcp, NULL, 0, NULL); } void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) { *screen_w = GetSystemMetrics(SM_CXFULLSCREEN) - GetSystemMetrics(SM_CXFRAME) * 2; /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include * the menubar for MSwin, we subtract it from the screen height, so that * the window size can be made to fit on the screen. */ *screen_h = GetSystemMetrics(SM_CYFULLSCREEN) - GetSystemMetrics(SM_CYFRAME) * 2 #ifdef FEAT_MENU - gui_mswin_get_menu_height(FALSE) #endif ; } #if defined(FEAT_MENU) || defined(PROTO) /* * Add a sub menu to the menu bar. */ void gui_mch_add_menu( vimmenu_T *menu, int pos) { vimmenu_T *parent = menu->parent; menu->submenu_id = CreatePopupMenu(); menu->id = s_menu_id++; if (menu_is_menubar(menu->name)) { InsertMenu((parent == NULL) ? s_menuBar : parent->submenu_id, (UINT)pos, MF_POPUP | MF_STRING | MF_BYPOSITION, (UINT)menu->submenu_id, menu->name); } /* Fix window size if menu may have wrapped */ if (parent == NULL) gui_mswin_get_menu_height(!gui.starting); } void gui_mch_show_popupmenu(vimmenu_T *menu) { POINT mp; (void)GetCursorPos((LPPOINT)&mp); gui_mch_show_popupmenu_at(menu, (int)mp.x, (int)mp.y); } void gui_make_popup(char_u *path_name, int mouse_pos) { vimmenu_T *menu = gui_find_menu(path_name); if (menu != NULL) { /* Find the position of the current cursor */ DWORD temp_p; POINT p; temp_p = GetDCOrg(s_hdc); p.x = LOWORD(temp_p); p.y = HIWORD(temp_p); if (mouse_pos) { int mx, my; gui_mch_getmouse(&mx, &my); p.x += mx; p.y += my; } else if (curwin != NULL) { p.x += TEXT_X(W_WINCOL(curwin) + curwin->w_wcol + 1); p.y += TEXT_Y(W_WINROW(curwin) + curwin->w_wrow + 1); } msg_scroll = FALSE; gui_mch_show_popupmenu_at(menu, (int)p.x, (int)p.y); } } /* * Add a menu item to a menu */ void gui_mch_add_menu_item( vimmenu_T *menu, int idx) { vimmenu_T *parent = menu->parent; menu->id = s_menu_id++; menu->submenu_id = NULL; #ifdef FEAT_TOOLBAR if (menu_is_toolbar(parent->name)) { TBBUTTON newtb; vim_memset(&newtb, 0, sizeof(newtb)); if (menu_is_separator(menu->name)) { newtb.iBitmap = 0; newtb.fsStyle = TBSTYLE_SEP; } else { if (menu->iconidx >= TOOLBAR_BITMAP_COUNT) newtb.iBitmap = -1; else newtb.iBitmap = menu->iconidx; newtb.fsStyle = TBSTYLE_BUTTON; } newtb.idCommand = menu->id; newtb.fsState = TBSTATE_ENABLED; SendMessage(s_toolbarhwnd, TB_INSERTBUTTON, (WPARAM)idx, (LPARAM)&newtb); menu->submenu_id = (HMENU)-1; } else #endif { InsertMenu(parent->submenu_id, (UINT)idx, (menu_is_separator(menu->name) ? MF_SEPARATOR : MF_STRING) | MF_BYPOSITION, (UINT)menu->id, menu->name); } } /* * Destroy the machine specific menu widget. */ void gui_mch_destroy_menu(vimmenu_T *menu) { UINT i, j; char pants[80]; /*<VN> hack*/ #ifdef FEAT_TOOLBAR /* * is this a toolbar button? */ if (menu->submenu_id == (HMENU)-1) { int iButton; iButton = SendMessage(s_toolbarhwnd, TB_COMMANDTOINDEX, (WPARAM)menu->id, 0); SendMessage(s_toolbarhwnd, TB_DELETEBUTTON, (WPARAM)iButton, 0); } else #endif { /* * negri: horrible API bug when running 16-bit programs under Win9x or * NT means that we can't use MF_BYCOMMAND for menu items which have * submenus, including the top-level headings. We have to find the menu * item and use MF_BYPOSITION instead. :-p */ if (menu->parent != NULL && menu_is_popup(menu->parent->dname) && menu->parent->submenu_id != NULL) RemoveMenu(menu->parent->submenu_id, menu->id, MF_BYCOMMAND); else if (menu->submenu_id == NULL) RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND); else if (menu->parent != NULL) { i = GetMenuItemCount(menu->parent->submenu_id); for (j = 0; j < i; ++j) { GetMenuString(menu->parent->submenu_id, j, pants, 80, MF_BYPOSITION); if (strcmp(pants, menu->name) == 0) { RemoveMenu(menu->parent->submenu_id, j, MF_BYPOSITION); break; } } } else { i = GetMenuItemCount(s_menuBar); for (j = 0; j < i; ++j) { GetMenuString(s_menuBar, j, pants, 80, MF_BYPOSITION); if (strcmp(pants, menu->name) == 0) { RemoveMenu(s_menuBar, j, MF_BYPOSITION); break; } } } if (menu->submenu_id != NULL) DestroyMenu(menu->submenu_id); } DrawMenuBar(s_hwnd); } /* * Make a menu either grey or not grey. */ void gui_mch_menu_grey( vimmenu_T *menu, int grey) { #ifdef FEAT_TOOLBAR /* * is this a toolbar button? */ if (menu->submenu_id == (HMENU)-1) { SendMessage(s_toolbarhwnd, TB_ENABLEBUTTON, (WPARAM)menu->id, (LPARAM) MAKELONG((grey ? FALSE : TRUE), 0) ); } else #endif if (grey) EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED); else EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED); } #endif /*FEAT_MENU*/ /* define some macros used to make the dialogue creation more readable */ #define add_string(s) strcpy((LPSTR)p, s); (LPSTR)p += (strlen((LPSTR)p) + 1) #define add_word(x) *p++ = (x) #define add_byte(x) *((LPSTR)p)++ = (x) #define add_long(x) *((LPDWORD)p)++ = (x) #if defined(FEAT_GUI_DIALOG) || defined(PROTO) /* * stuff for dialogs */ /* * The callback routine used by all the dialogs. Very simple. First, * acknowledges the INITDIALOG message so that Windows knows to do standard * dialog stuff (Return = default, Esc = cancel....) Second, if a button is * pressed, return that button's ID - IDCANCEL (2), which is the button's * number. */ static BOOL CALLBACK dialog_callback( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_INITDIALOG) { CenterWindow(hwnd, GetWindow(hwnd, GW_OWNER)); /* Set focus to the dialog. Set the default button, if specified. */ (void)SetFocus(hwnd); if (dialog_default_button > IDCANCEL) (void)SetFocus(GetDlgItem(hwnd, dialog_default_button)); // if (dialog_default_button > 0) // (void)SetFocus(GetDlgItem(hwnd, dialog_default_button + IDCANCEL)); return FALSE; } if (message == WM_COMMAND) { int button = LOWORD(wParam); /* Don't end the dialog if something was selected that was * not a button. */ if (button >= DLG_NONBUTTON_CONTROL) return TRUE; /* If the edit box exists, copy the string. */ if (s_textfield != NULL) GetDlgItemText(hwnd, DLG_NONBUTTON_CONTROL + 2, s_textfield, IOSIZE); /* * Need to check for IDOK because if the user just hits Return to * accept the default value, some reason this is what we get. */ if (button == IDOK) EndDialog(hwnd, dialog_default_button); else EndDialog(hwnd, button - IDCANCEL); return TRUE; } if ((message == WM_SYSCOMMAND) && (wParam == SC_CLOSE)) { EndDialog(hwnd, 0); return TRUE; } return FALSE; } /* * Create a dialog dynamically from the parameter strings. * type = type of dialog (question, alert, etc.) * title = dialog title. may be NULL for default title. * message = text to display. Dialog sizes to accommodate it. * buttons = '\n' separated list of button captions, default first. * dfltbutton = number of default button. * * This routine returns 1 if the first button is pressed, * 2 for the second, etc. * * 0 indicates Esc was pressed. * -1 for unexpected error * * If stubbing out this fn, return 1. */ static const char_u dlg_icons[] = /* must match names in resource file */ { IDR_VIM, IDR_VIM_ERROR, IDR_VIM_ALERT, IDR_VIM_INFO, IDR_VIM_QUESTION }; int gui_mch_dialog( int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton, char_u *textfield, int ex_cmd) { FARPROC dp; LPWORD p, pnumitems; int numButtons; int *buttonWidths, *buttonPositions; int buttonYpos; int nchar, i; DWORD lStyle; int dlgwidth = 0; int dlgheight; int editboxheight; int horizWidth; int msgheight; char_u *pstart; char_u *pend; char_u *tbuffer; RECT rect; HWND hwnd; HDC hdc; HFONT oldFont; TEXTMETRIC fontInfo; int fontHeight; int textWidth, minButtonWidth, messageWidth; int maxDialogWidth; int vertical; int dlgPaddingX; int dlgPaddingY; HGLOBAL hglbDlgTemp; #ifndef NO_CONSOLE /* Don't output anything in silent mode ("ex -s") */ if (silent_mode) return dfltbutton; /* return default option */ #endif /* If there is no window yet, open it. */ if (s_hwnd == NULL && gui_mch_init() == FAIL) return dfltbutton; if ((type < 0) || (type > VIM_LAST_TYPE)) type = 0; /* allocate some memory for dialog template */ /* TODO should compute this really*/ hglbDlgTemp = GlobalAlloc(GHND, DLG_ALLOC_SIZE); if (hglbDlgTemp == NULL) return -1; p = (LPWORD) GlobalLock(hglbDlgTemp); if (p == NULL) return -1; /* * make a copy of 'buttons' to fiddle with it. complier grizzles because * vim_strsave() doesn't take a const arg (why not?), so cast away the * const. */ tbuffer = vim_strsave(buttons); if (tbuffer == NULL) return -1; --dfltbutton; /* Change from one-based to zero-based */ /* Count buttons */ numButtons = 1; for (i = 0; tbuffer[i] != '\0'; i++) { if (tbuffer[i] == DLG_BUTTON_SEP) numButtons++; } if (dfltbutton >= numButtons) dfltbutton = 0; /* Allocate array to hold the width of each button */ buttonWidths = (int *) lalloc(numButtons * sizeof(int), TRUE); if (buttonWidths == NULL) return -1; /* Allocate array to hold the X position of each button */ buttonPositions = (int *) lalloc(numButtons * sizeof(int), TRUE); if (buttonPositions == NULL) return -1; /* * Calculate how big the dialog must be. */ hwnd = GetDesktopWindow(); hdc = GetWindowDC(hwnd); oldFont = SelectFont(hdc, GetStockObject(SYSTEM_FONT)); dlgPaddingX = DLG_OLD_STYLE_PADDING_X; dlgPaddingY = DLG_OLD_STYLE_PADDING_Y; GetTextMetrics(hdc, &fontInfo); fontHeight = fontInfo.tmHeight; /* Minimum width for horizontal button */ minButtonWidth = GetTextWidth(hdc, "Cancel", 6); /* Maximum width of a dialog, if possible */ GetWindowRect(s_hwnd, &rect); maxDialogWidth = rect.right - rect.left - GetSystemMetrics(SM_CXFRAME) * 2; if (maxDialogWidth < DLG_MIN_MAX_WIDTH) maxDialogWidth = DLG_MIN_MAX_WIDTH; /* Set dlgwidth to width of message */ pstart = message; messageWidth = 0; msgheight = 0; do { pend = vim_strchr(pstart, DLG_BUTTON_SEP); if (pend == NULL) pend = pstart + STRLEN(pstart); /* Last line of message. */ msgheight += fontHeight; textWidth = GetTextWidth(hdc, pstart, pend - pstart); if (textWidth > messageWidth) messageWidth = textWidth; pstart = pend + 1; } while (*pend != NUL); dlgwidth = messageWidth; /* Add width of icon to dlgwidth, and some space */ dlgwidth += DLG_ICON_WIDTH + 3 * dlgPaddingX; if (msgheight < DLG_ICON_HEIGHT) msgheight = DLG_ICON_HEIGHT; /* * Check button names. A long one will make the dialog wider. */ vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL); if (!vertical) { // Place buttons horizontally if they fit. horizWidth = dlgPaddingX; pstart = tbuffer; i = 0; do { pend = vim_strchr(pstart, DLG_BUTTON_SEP); if (pend == NULL) pend = pstart + STRLEN(pstart); // Last button name. textWidth = GetTextWidth(hdc, pstart, pend - pstart); if (textWidth < minButtonWidth) textWidth = minButtonWidth; textWidth += dlgPaddingX; /* Padding within button */ buttonWidths[i] = textWidth; buttonPositions[i++] = horizWidth; horizWidth += textWidth + dlgPaddingX; /* Pad between buttons */ pstart = pend + 1; } while (*pend != NUL); if (horizWidth > maxDialogWidth) vertical = TRUE; // Too wide to fit on the screen. else if (horizWidth > dlgwidth) dlgwidth = horizWidth; } if (vertical) { // Stack buttons vertically. pstart = tbuffer; do { pend = vim_strchr(pstart, DLG_BUTTON_SEP); if (pend == NULL) pend = pstart + STRLEN(pstart); // Last button name. textWidth = GetTextWidth(hdc, pstart, pend - pstart); textWidth += dlgPaddingX; /* Padding within button */ textWidth += DLG_VERT_PADDING_X * 2; /* Padding around button */ if (textWidth > dlgwidth) dlgwidth = textWidth; pstart = pend + 1; } while (*pend != NUL); } if (dlgwidth < DLG_MIN_WIDTH) dlgwidth = DLG_MIN_WIDTH; /* Don't allow a really thin dialog!*/ /* start to fill in the dlgtemplate information. addressing by WORDs */ lStyle = DS_MODALFRAME | WS_CAPTION | WS_VISIBLE ; add_long(lStyle); pnumitems = p; /*save where the number of items must be stored*/ add_byte(0); // NumberOfItems(will change later) add_word(10); // x add_word(10); // y add_word(PixelToDialogX(dlgwidth)); // Dialog height. if (vertical) dlgheight = msgheight + 2 * dlgPaddingY + DLG_VERT_PADDING_Y + 2 * fontHeight * numButtons; else dlgheight = msgheight + 3 * dlgPaddingY + 2 * fontHeight; // Dialog needs to be taller if contains an edit box. editboxheight = fontHeight + dlgPaddingY + 4 * DLG_VERT_PADDING_Y; if (textfield != NULL) dlgheight += editboxheight; add_word(PixelToDialogY(dlgheight)); add_byte(0); //menu add_byte(0); //class /* copy the title of the dialog */ add_string(title ? title : ("Vim"VIM_VERSION_MEDIUM)); buttonYpos = msgheight + 2 * dlgPaddingY; if (textfield != NULL) buttonYpos += editboxheight; pstart = tbuffer; //dflt_text horizWidth = (dlgwidth - horizWidth) / 2; /* Now it's X offset */ for (i = 0; i < numButtons; i++) { /* get end of this button. */ for ( pend = pstart; *pend && (*pend != DLG_BUTTON_SEP); pend++) ; if (*pend) *pend = '\0'; /* * NOTE: * setting the BS_DEFPUSHBUTTON style doesn't work because Windows sets * the focus to the first tab-able button and in so doing makes that * the default!! Grrr. Workaround: Make the default button the only * one with WS_TABSTOP style. Means user can't tab between buttons, but * he/she can use arrow keys. * * NOTE (Thore): Setting BS_DEFPUSHBUTTON works fine when it's the * first one, so I changed the correct button to be this style. This * is necessary because when an edit box is added, we need a button to * be default. The edit box will be the default control, and when the * user presses enter from the edit box we want the default button to * be pressed. */ if (vertical) { p = add_dialog_element(p, ((i == dfltbutton || dfltbutton < 0) && textfield != NULL ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP, PixelToDialogX(DLG_VERT_PADDING_X), PixelToDialogY(buttonYpos /* TBK */ + 2 * fontHeight * i), PixelToDialogX(dlgwidth - 2 * DLG_VERT_PADDING_X), (WORD)(PixelToDialogY(2 * fontHeight) - 1), (WORD)(IDCANCEL + 1 + i), (BYTE)0x80, pstart); } else { p = add_dialog_element(p, ((i == dfltbutton || dfltbutton < 0) && textfield != NULL ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON) | WS_TABSTOP, PixelToDialogX(horizWidth + buttonPositions[i]), PixelToDialogY(buttonYpos), /* TBK */ PixelToDialogX(buttonWidths[i]), (WORD)(PixelToDialogY(2 * fontHeight) - 1), (WORD)(IDCANCEL + 1 + i), (BYTE)0x80, pstart); } pstart = pend + 1; /*next button*/ } *pnumitems += numButtons; /* Vim icon */ p = add_dialog_element(p, SS_ICON, PixelToDialogX(dlgPaddingX), PixelToDialogY(dlgPaddingY), PixelToDialogX(DLG_ICON_WIDTH), PixelToDialogY(DLG_ICON_HEIGHT), DLG_NONBUTTON_CONTROL + 0, (BYTE)0x82, &dlg_icons[type]); /* Dialog message */ p = add_dialog_element(p, SS_LEFT, PixelToDialogX(2 * dlgPaddingX + DLG_ICON_WIDTH), PixelToDialogY(dlgPaddingY), (WORD)(PixelToDialogX(messageWidth) + 1), PixelToDialogY(msgheight), DLG_NONBUTTON_CONTROL + 1, (BYTE)0x82, message); /* Edit box */ if (textfield != NULL) { p = add_dialog_element(p, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP | WS_BORDER, PixelToDialogX(2 * dlgPaddingX), PixelToDialogY(2 * dlgPaddingY + msgheight), PixelToDialogX(dlgwidth - 4 * dlgPaddingX), PixelToDialogY(fontHeight + dlgPaddingY), DLG_NONBUTTON_CONTROL + 2, (BYTE)0x81, textfield); *pnumitems += 1; } *pnumitems += 2; SelectFont(hdc, oldFont); ReleaseDC(hwnd, hdc); dp = MakeProcInstance((FARPROC)dialog_callback, s_hinst); /* Let the dialog_callback() function know which button to make default * If we have an edit box, make that the default. We also need to tell * dialog_callback() if this dialog contains an edit box or not. We do * this by setting s_textfield if it does. */ if (textfield != NULL) { dialog_default_button = DLG_NONBUTTON_CONTROL + 2; s_textfield = textfield; } else { dialog_default_button = IDCANCEL + 1 + dfltbutton; s_textfield = NULL; } /*show the dialog box modally and get a return value*/ nchar = DialogBoxIndirect( s_hinst, (HGLOBAL) hglbDlgTemp, s_hwnd, (DLGPROC)dp); FreeProcInstance( dp ); GlobalUnlock(hglbDlgTemp); GlobalFree(hglbDlgTemp); vim_free(tbuffer); vim_free(buttonWidths); vim_free(buttonPositions); return nchar; } /* * Put a simple element (basic class) onto a dialog template in memory. * return a pointer to where the next item should be added. * * parameters: * lStyle = additional style flags * x,y = x & y positions IN DIALOG UNITS * w,h = width and height IN DIALOG UNITS * Id = ID used in messages * clss = class ID, e.g 0x80 for a button, 0x82 for a static * caption = usually text or resource name * * TODO: use the length information noted here to enable the dialog creation * routines to work out more exactly how much memory they need to alloc. */ static LPWORD add_dialog_element( LPWORD p, DWORD lStyle, WORD x, WORD y, WORD w, WORD h, WORD Id, BYTE clss, const char *caption) { lStyle = lStyle | WS_VISIBLE | WS_CHILD; add_word(x); add_word(y); add_word(w); add_word(h); add_word(Id); add_long(lStyle); add_byte(clss); if (((lStyle & SS_ICON) != 0) && (clss == 0x82)) { /* Use resource ID */ add_byte(0xff); add_byte(*caption); } else add_string(caption); add_byte(0); //# of extra bytes following return p; } #undef add_byte #undef add_string #undef add_long #undef add_word #endif /* FEAT_GUI_DIALOG */ static void get_dialog_font_metrics(void) { DWORD dlgFontSize; dlgFontSize = GetDialogBaseUnits(); /* fall back to big old system*/ s_dlgfntwidth = LOWORD(dlgFontSize); s_dlgfntheight = HIWORD(dlgFontSize); } #if defined(FEAT_TOOLBAR) || defined(PROTO) #include "gui_w3~1.h" /* * Create the toolbar, initially unpopulated. * (just like the menu, there are no defaults, it's all * set up through menu.vim) */ static void initialise_toolbar(void) { s_toolbarhwnd = CreateToolbar( s_hwnd, WS_CHILD | WS_VISIBLE, CMD_TB_BASE, /*<vn>*/ 31, //number of images in initial bitmap s_hinst, IDR_TOOLBAR1, // id of initial bitmap NULL, 0 // initial number of buttons ); gui_mch_show_toolbar(vim_strchr(p_go, GO_TOOLBAR) != NULL); } #endif #if defined(FEAT_OLE) || defined(FEAT_EVAL) || defined(PROTO) /* * Make the GUI window come to the foreground. */ void gui_mch_set_foreground(void) { if (IsIconic(s_hwnd)) SendMessage(s_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); SetActiveWindow(s_hwnd); } #endif