This is gui_w32.c in view mode; [Download] [Up]
/* vi:set ts=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. * * Windows GUI. * * GUI support for Microsoft Windows. Win32 initially; maybe Win16 later * * George V. Reilly <gvr@halcyon.com> wrote the original Win32 GUI. * Robert Webb reworked it to use the existing GUI stuff and added menu, * scrollbars, etc. * * Note: Clipboard stuff, for cutting and pasting text to other windows, is in * os_win32.c. (It can also be done from the terminal version). */ #include "vim.h" #include "globals.h" #include "proto.h" #include "option.h" #include <windows.h> #include <shellapi.h> #ifdef WIN32 # include <windowsx.h> #else /* WIN16 */ # include <windowsx.h16> #endif #ifdef PROTO /* * Define a few things for generating prototypes. This is just to avoid * syntax errors, the defines do not need to be correct. */ # define HINSTANCE void * # define HWND void * # define HMENU void * # define UINT int # define WPARAM int # define LPARAM int typedef int LOGFONT[]; # define VOID void # define CALLBACK # define DWORD int # define HDROP int # define BOOL int # define LPRECT int # define LRESULT int # define WINAPI # define LPSTR int # define LPWINDOWPOS int #endif /* Local variables: */ static HINSTANCE s_hinst = NULL; static HWND s_hwnd = NULL; static HWND s_textArea = NULL; static HMENU s_menuBar = NULL; static UINT s_menu_id = 0; static UINT s_wait_timer = 0; /* Timer for get char from user */ static int s_alt_key_down = FALSE; static UINT s_uMsg = 0; static WPARAM s_wParam = 0; static LPARAM s_lParam = 0; static int s_timed_out = FALSE; static const LOGFONT s_lfDefault = { -12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FIXED_PITCH | FF_DONTCARE, "Fixedsys" /* see _ReadVimIni */ }; static LOGFONT s_lfNormal; static struct { UINT key_sym; char_u vim_code0; char_u vim_code1; } special_keys[] = { {VK_UP, 'k', 'u'}, {VK_DOWN, 'k', 'd'}, {VK_LEFT, 'k', 'l'}, {VK_RIGHT, 'k', 'r'}, {VK_F1, 'k', '1'}, {VK_F2, 'k', '2'}, {VK_F3, 'k', '3'}, {VK_F4, 'k', '4'}, {VK_F5, 'k', '5'}, {VK_F6, 'k', '6'}, {VK_F7, 'k', '7'}, {VK_F8, 'k', '8'}, {VK_F9, 'k', '9'}, {VK_F10, 'k', ';'}, {VK_F11, 'F', '1'}, {VK_F12, 'F', '2'}, {VK_F13, 'F', '3'}, {VK_F14, 'F', '4'}, {VK_F15, 'F', '5'}, {VK_F16, 'F', '6'}, {VK_F17, 'F', '7'}, {VK_F18, 'F', '8'}, {VK_F19, 'F', '9'}, {VK_F20, 'F', 'A'}, {VK_F21, 'F', 'B'}, {VK_F22, 'F', 'C'}, {VK_F23, 'F', 'D'}, {VK_F24, 'F', 'E'}, /* winuser.h defines up to F24 */ {VK_HELP, '%', '1'}, {VK_BACK, 'k', 'b'}, {VK_INSERT, 'k', 'I'}, {VK_DELETE, 'k', 'D'}, {VK_HOME, 'k', 'h'}, {VK_END, '@', '7'}, {VK_PRIOR, 'k', 'P'}, {VK_NEXT, 'k', 'N'}, {VK_PRINT, '%', '9'}, /* Keys that we want to be able to use any modifier with: */ {VK_SPACE, ' ', NUL}, {VK_TAB, TAB, NUL}, {VK_ESCAPE, ESC, NUL}, {NL, NL, NUL}, {CR, CR, NUL}, /* End of list marker: */ {0, 0, 0} }; #define VIM_NAME "vim" #define VIM_CLASS "Vim" #ifdef DEBUG /* Print out the last Windows error message */ static void print_windows_error() { LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL); TRACE("Error: %s\n", lpMsgBuf); LocalFree(lpMsgBuf); } #endif /* DEBUG */ /* * Figure out how high the menu bar is at the moment. */ static int gui_w32_get_menu_height(fix_window) int fix_window; /* If TRUE, resize window if menu height changed */ { static int old_menu_height = -1; RECT rc1, rc2; int num; int menu_height; num = GetMenuItemCount(s_menuBar); if (num == 0) menu_height = 0; else { GetMenuItemRect(s_hwnd, s_menuBar, 0, &rc1); GetMenuItemRect(s_hwnd, s_menuBar, num - 1, &rc2); menu_height = rc2.bottom - rc1.top + 1; } if (fix_window && menu_height != old_menu_height) gui_set_winsize(FALSE); old_menu_height = menu_height; return menu_height; } /* * Call-back routines. */ static VOID CALLBACK _OnTimer( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { MSG msg; /* TRACE("Got timer event, id %d, s_wait_timer %d\n", idEvent, s_wait_timer); */ KillTimer(NULL, idEvent); s_timed_out = TRUE; /* Eat spurious WM_TIMER messages */ while(PeekMessage(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)); if (idEvent == s_wait_timer) s_wait_timer = 0; } /* * Get this message when the user clicks on the cross in the top right corner * of a Windows95 window. */ static void _OnDestroy( HWND hwnd) { preserve_exit(); /* output IObuff, preserve files and exit */ FORWARD_WM_DESTROY(hwnd, DefWindowProc); } static void _OnDropFiles( HWND hwnd, HDROP hDrop) { char szFile[_MAX_PATH]; UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, szFile, _MAX_PATH); UINT i; char_u *fname; char_u string[1]; /* TRACE("_OnDropFiles: %d files dropped\n", cFiles); */ /* reset_VIsual(); */ if (VIsual_active) { end_visual_mode(); update_curbuf(NOT_VALID); /* delete the inversion */ } for (i = 0; i < cFiles; ++i) { DragQueryFile(hDrop, i, szFile, _MAX_PATH); /* TRACE(" dropped %2u: '%s'\n", i, szFile); */ mch_dirname(IObuff, IOSIZE); fname = shorten_fname(szFile, IObuff); if (fname == NULL) fname = szFile; win_split(0, TRUE); (void)do_ecmd(0, fname, NULL, NULL, 0, ECMD_FORCEIT); } DragFinish(hDrop); /* This makes it redraw properly */ string[0] = Ctrl('L'); add_to_input_buf(string, 1); } static void _OnChar( HWND hwnd, UINT ch, int cRepeat) { char_u string[1]; /* TRACE("OnChar(%d, %c)\n", ch, ch); */ string[0] = ch; if (string[0] == Ctrl('C')) { trash_input_buf(); got_int = TRUE; } add_to_input_buf(string, 1); } static void _OnAltKey( HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags) { /* This doesn't work */ /* if (s_uMsg == WM_SYSKEYDOWN) s_alt_key_down = TRUE; else if (s_uMsg == WM_SYSKEYUP) s_alt_key_down = FALSE; TRACE("Alt key %d\n", s_alt_key_down); */ /* FORWARD_WM_KEYDOWN(hwnd, vk, cRepeat, flags, DefWindowProc); */ } static void _OnMouseEvent( int button, int x, int y, int repeated_click, UINT keyFlags) { int vim_modifiers = 0x0; if (keyFlags & MK_SHIFT) vim_modifiers |= MOUSE_SHIFT; if (keyFlags & MK_CONTROL) vim_modifiers |= MOUSE_CTRL; /* * When we find M- in a mapping, use either Alt or the Menu key, since we * haven't got Alt working very well. Hey, it seems the Menu key IS the * Alt key?? */ if ((GetKeyState(VK_MENU) & 0x8000) || s_alt_key_down) vim_modifiers |= MOUSE_ALT; gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers); } static void _OnMouseButtonDown( HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) { static LONG s_prevTime = 0; LONG currentTime = GetMessageTime(); int button = -1; int repeated_click; /* * While button is down, keep grabbing mouse move events when the mouse * goes outside the window */ SetCapture(s_textArea); if (s_uMsg == WM_LBUTTONDOWN || s_uMsg == WM_LBUTTONDBLCLK) button = MOUSE_LEFT; else if (s_uMsg == WM_MBUTTONDOWN || s_uMsg == WM_MBUTTONDBLCLK) button = MOUSE_MIDDLE; else if (s_uMsg == WM_RBUTTONDOWN || s_uMsg == WM_RBUTTONDBLCLK) button = MOUSE_RIGHT; if (button >= 0) { repeated_click = ((int)(currentTime - s_prevTime) < p_mouset); /* * TODO: Fix this, it doesn't work: */ /* * Holding down the left and right buttons simulates pushing the middle * button. Hmm, this doesn't really work because we have already * passed through the first click as a left or right button click. */ if (repeated_click && ((button == MOUSE_LEFT && (keyFlags & MK_RBUTTON)) || (button == MOUSE_RIGHT && (keyFlags & MK_LBUTTON)))) { /* * Hmm, gui.c will ignore more than one button down at a time, so * pretend we let go of it first. */ gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, 0x0); button = MOUSE_MIDDLE; repeated_click = FALSE; } s_prevTime = currentTime; /* TRACE("Button down at x %d, y %d\n", x, y); */ _OnMouseEvent(button, x, y, repeated_click, keyFlags); } } static void _OnMouseMoveOrRelease( HWND hwnd, int x, int y, UINT keyFlags) { int button; if (s_uMsg == WM_MOUSEMOVE) { /* * It's only a MOUSE_DRAG if one or more mouse buttons are being held * down. */ if (!(keyFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) return; button = MOUSE_DRAG; /* TRACE(" move at x %d, y %d\n", x, y); */ } else { ReleaseCapture(); button = MOUSE_RELEASE; /* TRACE(" up at x %d, y %d\n", x, y); */ } _OnMouseEvent(button, x, y, FALSE, keyFlags); } static void _OnPaint( HWND hwnd) { if (!IsMinimized(hwnd)) { PAINTSTRUCT ps; HDC hdc; hdc = BeginPaint(hwnd, &ps); if (!IsRectEmpty(&ps.rcPaint)) gui_redraw(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left + 1, ps.rcPaint.bottom - ps.rcPaint.top + 1); EndPaint(hwnd, &ps); } } static void _OnSize( HWND hwnd, UINT state, int cx, int cy) { if (!IsMinimized(hwnd)) { gui_resize_window(cx, cy); /* Menu bar may wrap differently now */ gui_w32_get_menu_height(TRUE); } } static void _OnKillFocus( HWND hwnd, HWND hwndNewFocus) { clip_mch_lose_focus(); gui.in_focus = FALSE; gui_update_cursor(TRUE); s_alt_key_down = FALSE; } static void _OnMenu( HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { MENUITEMINFO info; info.cbSize = sizeof(info); info.fMask = MIIM_DATA; /* * The menu id is only 16-bit, so we store the menu pointer in the data * field instead. */ GetMenuItemInfo(s_menuBar, id, FALSE, &info); gui_menu_cb((GuiMenu *)info.dwItemData); } static void _OnSetFocus( HWND hwnd, HWND hwndOldFocus) { gui.in_focus = TRUE; gui_update_cursor(TRUE); /* reset_modifiers(); */ } /* * Find the scrollbar with the given hwnd. */ static GuiScrollbar * gui_w32_find_scrollbar(hwnd) HWND hwnd; { WIN *wp; if (gui.bottom_sbar.id == hwnd) return &gui.bottom_sbar; for (wp = firstwin; wp != NULL; wp = wp->w_next) { if (wp->w_scrollbars[SBAR_LEFT].id == hwnd) return &wp->w_scrollbars[SBAR_LEFT]; if (wp->w_scrollbars[SBAR_RIGHT].id == hwnd) return &wp->w_scrollbars[SBAR_RIGHT]; } return NULL; } static void _OnScroll( HWND hwnd, HWND hwndCtl, UINT code, int pos) { GuiScrollbar *sb, *sb_info; int val; int dragging = FALSE; SCROLLINFO si; int c; sb = gui_w32_find_scrollbar(hwndCtl); if (sb == NULL) return; else if (sb->wp != NULL) /* Left or right scrollbar */ { /* * Careful: need to get scrollbar info out of first (left) scrollbar * for window, but keep real scrollbar too because we must pass it to * gui_drag_scrollbar(). */ sb_info = &sb->wp->w_scrollbars[0]; } else /* Bottom scrollbar */ sb_info = sb; val = sb_info->value; switch (code) { case SB_THUMBTRACK: /* TRACE("SB_THUMBTRACK, %d\n", pos); */ val = pos; dragging = TRUE; break; case SB_LINEDOWN: /* TRACE("SB_LINEDOWN\n"); */ val++; break; case SB_LINEUP: /* TRACE("SB_LINEUP\n"); */ val--; break; case SB_PAGEDOWN: /* TRACE("SB_PAGEDOWN\n"); */ val += (sb_info->size > 2 ? sb_info->size - 2 : 1); break; case SB_PAGEUP: /* TRACE("SB_PAGEUP\n"); */ val -= (sb_info->size > 2 ? sb_info->size - 2 : 1); break; case SB_TOP: /* TRACE("SB_TOP\n"); */ val = 0; break; case SB_BOTTOM: /* TRACE("SB_BOTTOM\n"); */ val = sb_info->max; break; case SB_ENDSCROLL: /* * "pos" only gives us 16-bit data. In case of large file, use * GetScrollPos() which returns 32-bit. Unfortunately it is not * valid while the scrollbar is being dragged. */ /* TRACE("SB_ENDSCROLL\n"); */ val = GetScrollPos(hwndCtl, SB_CTL); break; default: /* TRACE("Unknown scrollbar event %d\n", code); */ return; } si.cbSize = sizeof(si); si.fMask = SIF_POS; si.nPos = val; SetScrollInfo(hwndCtl, SB_CTL, &si, TRUE); /* * Scrollbars seem to grab focus and vim doesn't read the input queue until * you stop dragging the scrollbar. We get here each time the scrollbar is * dragged another pixel, but as far as the rest of vim goes, it thinks * we're just hanging in the call to DispatchMessage() in * process_message(). The DispatchMessage() call that hangs was passed a * mouse button click event in the scrollbar window. -- webb. */ gui_drag_scrollbar(sb, val, dragging); if (code == SB_THUMBTRACK) { /* * We need to process the messages we just stuck in the queue; if we * don't, we will end up with nothing happening until we release the * thumb, then the screen will scroll wildly. Really ugly! * This is a hack though, is there no way to stop the scrollbar from * taking over in the first place? */ c = vgetc(); if (c == K_SCROLLBAR) { if (State & NORMAL) { gui_do_scroll(); setcursor(); } else if (State & INSERT) { /* * Get some random text inserted sometimes after scrolling in * insert mode, why? Sometimes you get text that you've * already typed elsewhere! -- webb */ ins_scroll(); setcursor(); } else if (State & CMDLINE) { if (!msg_scrolled) { gui_do_scroll(); redrawcmdline(); } } flushbuf(); } else if (c == K_HORIZ_SCROLLBAR) { if (State & NORMAL) { gui_do_horiz_scroll(); setcursor(); } else if (State & INSERT) { ins_horscroll(); setcursor(); } else if (State & CMDLINE) { if (!msg_scrolled) { gui_do_horiz_scroll(); redrawcmdline(); } } flushbuf(); } else { /* Woops! Shouldn't have got this */ vungetc(c); } } } static void gui_w32_get_valid_dimensions( int w, int h, int *valid_w, int *valid_h) { int base_width, base_height; base_width = gui_get_base_width() + GetSystemMetrics(SM_CXSIZEFRAME) * 2; base_height = gui_get_base_height() + GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION) + gui_w32_get_menu_height(FALSE); *valid_w = base_width + ((w - base_width) / gui.char_width) * gui.char_width; *valid_h = base_height + ((h - base_height) / gui.char_height) * gui.char_height; } /* * 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 (!(lpwpos->flags & SWP_NOSIZE)) { gui_w32_get_valid_dimensions(lpwpos->cx, lpwpos->cy, &lpwpos->cx, &lpwpos->cy); } return 0; } static int _DuringSizing( HWND hwnd, UINT fwSide, LPRECT lprc) { int w, h; int valid_w, valid_h; int w_offset, h_offset; w = lprc->right - lprc->left; h = lprc->bottom - lprc->top; gui_w32_get_valid_dimensions(w, h, &valid_w, &valid_h); w_offset = w - valid_w; h_offset = h - valid_h; if (fwSide == WMSZ_LEFT || fwSide == WMSZ_TOPLEFT || fwSide == WMSZ_BOTTOMLEFT) lprc->left += w_offset; else if (fwSide == WMSZ_RIGHT || fwSide == WMSZ_TOPRIGHT || fwSide == WMSZ_BOTTOMRIGHT) lprc->right -= w_offset; if (fwSide == WMSZ_TOP || fwSide == WMSZ_TOPLEFT || fwSide == WMSZ_TOPRIGHT) lprc->top += h_offset; else if (fwSide == WMSZ_BOTTOM || fwSide == WMSZ_BOTTOMLEFT || fwSide == WMSZ_BOTTOMRIGHT) lprc->bottom -= h_offset; return TRUE; } 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); */ s_uMsg = uMsg; s_wParam = wParam; s_lParam = lParam; switch (uMsg) { /* 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_CREATE, _OnCreate); */ HANDLE_MSG(hwnd, WM_DESTROY, _OnDestroy); HANDLE_MSG(hwnd, WM_DROPFILES, _OnDropFiles); HANDLE_MSG(hwnd, WM_HSCROLL, _OnScroll); HANDLE_MSG(hwnd, WM_KILLFOCUS, _OnKillFocus); HANDLE_MSG(hwnd, WM_COMMAND, _OnMenu); /* 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_SYSKEYUP, _OnAltKey); HANDLE_MSG(hwnd, WM_VSCROLL, _OnScroll); HANDLE_MSG(hwnd, WM_WINDOWPOSCHANGING, _OnWindowPosChanging); case WM_SIZING: /* HANDLE_MSG doesn't seem to handle this one */ return _DuringSizing(hwnd, wParam, (LPRECT)lParam); default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } } static LRESULT CALLBACK _TextAreaWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { /* TRACE("TextAreaWndProc: hwnd = %08x, msg = %x, wParam = %x, lParam = %x\n", hwnd, uMsg, wParam, lParam); */ s_uMsg = uMsg; s_wParam = wParam; s_lParam = lParam; switch (uMsg) { HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_LBUTTONDOWN,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_LBUTTONUP, _OnMouseMoveOrRelease); HANDLE_MSG(hwnd, WM_MBUTTONDBLCLK,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_MBUTTONDOWN,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_MBUTTONUP, _OnMouseMoveOrRelease); HANDLE_MSG(hwnd, WM_MOUSEMOVE, _OnMouseMoveOrRelease); HANDLE_MSG(hwnd, WM_PAINT, _OnPaint); HANDLE_MSG(hwnd, WM_RBUTTONDBLCLK,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_RBUTTONDOWN,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_RBUTTONUP, _OnMouseMoveOrRelease); default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } } /* * 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(argc, argv) int *argc; char **argv; { /* We don't have any command line arguments for the Windows GUI yet */ } /* * Initialise the GUI. Create all the windows, set up all the call-backs * etc. */ int gui_mch_init() { const char szVimWndClass[] = VIM_CLASS; const char szTextAreaClass[] = "VimTextArea"; WNDCLASS wndclass; gui.scrollbar_width = GetSystemMetrics(SM_CXVSCROLL); gui.scrollbar_height = GetSystemMetrics(SM_CYHSCROLL); gui.menu_height = 0; /* Windows takes care of this */ gui.border_width = 0; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = _WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = s_hinst; wndclass.hIcon = LoadIcon(wndclass.hInstance, "IDR_VIM"); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = GetStockBrush(LTGRAY_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szVimWndClass; if (RegisterClass(&wndclass) == 0) return FAIL; s_hwnd = CreateWindow( szVimWndClass, "Vim W32 GUI", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 100, /* Any value will do */ 100, /* Any value will do */ NULL, NULL, s_hinst, NULL); if (s_hwnd == NULL) return FAIL; /* Create the text area window */ wndclass.style = CS_HREDRAW | CS_VREDRAW; 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; s_menuBar = CreateMenu(); DragAcceptFiles(s_hwnd, TRUE); /* Do we need to bother with this? */ /* m_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT); */ /* * Load resources. * Reads from vim.ini in the Windows directory. * Default to 9pt Fixedsys (yuk), which everyone should have. */ GetPrivateProfileString("Font", "FaceName", "Fixedsys", gui.dflt_font_name, LF_FACESIZE, "vim.ini"); /* Height should be (-4 * point size) / 3; e.g., 9pt = -12 */ gui.dflt_font_height = GetPrivateProfileInt("Font", "Height", -12, "vim.ini"); gui.back_pixel = gui_mch_get_color((char_u *)"Black"); gui.norm_pixel = gui_mch_get_color((char_u *)"LightGray"); gui.bold_pixel = gui_mch_get_color((char_u *)"Yellow"); gui.ital_pixel = gui.norm_pixel; gui.underline_pixel = gui.norm_pixel; gui.cursor_pixel = gui_mch_get_color((char_u *)"LightGreen"); /* * Check that none of the colors are the same as the background color */ if (gui.norm_pixel == gui.back_pixel) { gui.norm_pixel = gui_mch_get_color((char_u *)"White"); if (gui.norm_pixel == gui.back_pixel) gui.norm_pixel = gui_mch_get_color((char_u *)"Black"); } if (gui.bold_pixel == gui.back_pixel) gui.bold_pixel = gui.norm_pixel; if (gui.ital_pixel == gui.back_pixel) gui.ital_pixel = gui.norm_pixel; if (gui.underline_pixel == gui.back_pixel) gui.underline_pixel = gui.norm_pixel; if (gui.cursor_pixel == gui.back_pixel) gui.cursor_pixel = gui.norm_pixel; /* * Start out by adding the configured border width into the border offset */ gui.border_offset = gui.border_width; return OK; } /* * Open the GUI window which was created by a call to gui_mch_init(). */ int gui_mch_open() { /* Actually open the window */ ShowWindow(s_hwnd, SW_SHOWNORMAL); return OK; } void gui_mch_exit() { /* Can't think of anything to do here */ } /* * Set the size of the window to the given width and height in pixels. */ void gui_mch_set_winsize(width, height, min_width, min_height, base_width, base_height) int width; int height; int min_width; int min_height; int base_width; int base_height; { SetWindowPos(s_hwnd, NULL, 0, 0, width + GetSystemMetrics(SM_CXSIZEFRAME) * 2, height + GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION) + gui_w32_get_menu_height(FALSE), SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); /* Menu may wrap differently now */ gui_w32_get_menu_height(TRUE); } void gui_mch_get_screen_dimensions(screen_w, screen_h) int *screen_w; int *screen_h; { *screen_w = GetSystemMetrics(SM_CXSCREEN) - GetSystemMetrics(SM_CXSIZEFRAME) * 2; *screen_h = GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYSIZEFRAME) * 2 - GetSystemMetrics(SM_CYCAPTION) - gui_w32_get_menu_height(); } void gui_mch_set_text_area_pos(x, y, w, h) int x; int y; int w; int h; { SetWindowPos(s_textArea, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE); } /* * Scrollbar stuff: */ void gui_mch_enable_scrollbar(sb, flag) GuiScrollbar *sb; int flag; { ShowScrollBar(sb->id, SB_CTL, flag); } void gui_mch_set_scrollbar_thumb(sb, val, size, max) GuiScrollbar *sb; int val; int size; int max; { SCROLLINFO info; info.cbSize = sizeof(info); info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; info.nPos = val; info.nMin = 0; info.nMax = max; info.nPage = size; SetScrollInfo(sb->id, SB_CTL, &info, TRUE); } void gui_mch_set_scrollbar_pos(sb, x, y, w, h) GuiScrollbar *sb; int x; int y; int w; int h; { SetWindowPos(sb->id, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE); } void gui_mch_create_scrollbar(sb, orient) GuiScrollbar *sb; int orient; /* SBAR_VERT or SBAR_HORIZ */ { sb->id = CreateWindow( "SCROLLBAR", "Scrollbar", WS_CHILD | ((orient == SBAR_VERT) ? SBS_VERT : SBS_HORZ), 0, 0, 10, /* Any value will do for now */ 10, /* Any value will do for now */ s_hwnd, NULL, s_hinst, NULL); } void gui_mch_destroy_scrollbar(sb) GuiScrollbar *sb; { DestroyWindow(sb->id); } static void GetFontSize(GuiFont font) { static const char ach[] = {'W', 'f', 'g', 'M'}; HWND hwnd = GetDesktopWindow(); HDC hdc = GetWindowDC(hwnd); HFONT hfntOld = SelectFont(hdc, (HFONT)font); SIZE siz; GetTextExtentPoint(hdc, ach, sizeof(ach), &siz); gui.char_width = siz.cx / sizeof(ach); gui.char_height = siz.cy; /* TRACE("GetFontSize: h %d, w %d\n", gui.char_height, gui.char_width); */ SelectFont(hdc, hfntOld); ReleaseDC(hwnd, hdc); } static GuiFont get_font_handle(lf) LOGFONT *lf; { HFONT *font = NULL; /* Load the font */ font = CreateFontIndirect(lf); if (font == NULL) return (GuiFont)0; return (GuiFont)font; } static void get_logfont(lf, name) LOGFONT *lf; char_u *name; { char_u *p; if (name == NULL) { *lf = s_lfDefault; return; } *lf = s_lfNormal; if (name != NULL && *name != NUL) { /* * Split name up, it could be <name>_h<height> */ for (p = name; *p && *p != '_'; p++) ; if (p - name + 1 > LF_FACESIZE) return; /* Name too long */ if (p != name) strncpy(lf->lfFaceName, name, p - name); while (*p == '_') p++; while (*p) { switch (*p++) { case 'h': lf->lfHeight = atoi(p); if (*p == '-') p++; p = skipdigits(p); break; case 'w': lf->lfWidth = atoi(p); if (*p == '-') p++; p = skipdigits(p); break; case 'b': lf->lfWeight = FW_BOLD; break; case 'i': lf->lfItalic = TRUE; break; case 'u': lf->lfUnderline = TRUE; break; case 's': lf->lfStrikeOut = TRUE; break; default: sprintf((char *)IObuff, "Illegal char '%c' in font name \"%s\"", p[-1], name); EMSG(IObuff); break; } while (*p == '_') p++; } } } /* * Initialise vim to use the font with the given name. Return FAIL if the font * could not be loaded, OK otherwise. */ int gui_mch_init_font(font_name) char_u *font_name; { LOGFONT lf; GuiFont font = (GuiFont)0; char_u str[80]; /* Load the font */ if (font_name == NULL) { /* * If none of the fonts in 'font' could be loaded, try the one set in * the vim.ini resource file, and finally just try using s_lfDefault, * which will hopefully always be there. */ sprintf(str, "%s_h%d", gui.dflt_font_name, gui.dflt_font_height); get_logfont(&lf, str); font = get_font_handle(&lf); if (font == (GuiFont)0) { get_logfont(&lf, NULL); font = get_font_handle(&lf); } } else { get_logfont(&lf, font_name); font = get_font_handle(&lf); } if (font == (GuiFont)0) return FAIL; if (gui.norm_font != (GuiFont)0) gui_mch_free_font(gui.norm_font); gui.norm_font = font; s_lfNormal = lf; GetFontSize(font); /* * Move characters up one pixel so that italic and bold fonts don't draw * off the bottom of their character space. Also means that we can * underline an underscore for normal text. */ gui.char_ascent = -1; if (!lf.lfItalic) { lf.lfItalic = TRUE; gui.ital_font = get_font_handle(&lf); lf.lfItalic = FALSE; } if (lf.lfWeight < FW_BOLD) { lf.lfWeight = FW_BOLD; gui.bold_font = get_font_handle(&lf); if (!lf.lfItalic) { lf.lfItalic = TRUE; gui.boldital_font = get_font_handle(&lf); } } return OK; } GuiFont gui_mch_get_font(name, giveErrorIfMissing) char_u *name; int giveErrorIfMissing; { LOGFONT lf; GuiFont font; get_logfont(&lf, name); font = get_font_handle(&lf); if (font == (GuiFont)0 && giveErrorIfMissing) EMSG2("Unknown font: %s", name); return font; } /* * Set the current text font. */ void gui_mch_set_font(font) GuiFont font; { gui.currFont = font; } /* * Return TRUE if the two fonts given are equivalent. */ int gui_mch_same_font(f1, f2) GuiFont f1; GuiFont f2; { return f1 == f2; } void gui_mch_free_font(font) GuiFont font; { DeleteObject((HFONT)font); } static int hex_digit(c) int c; { if (isdigit(c)) return c - '0'; c = TO_LOWER(c); if (c >= 'a' && c <= 'f') return c - 'a' + 10; return -1000; } GuiColor gui_mch_get_color(name) char_u *name; { typedef struct GuiColorTable { char *name; COLORREF color; } GuiColorTable; static GuiColorTable table[] = { {"black", RGB(0x00, 0x00, 0x00)}, {"blue", RGB(0x00, 0x00, 0x80)}, {"green", RGB(0x00, 0x80, 0x00)}, {"cyan", RGB(0x00, 0x80, 0x80)}, {"red", RGB(0x80, 0x00, 0x00)}, {"magenta", RGB(0x80, 0x00, 0x80)}, {"brown", RGB(0x80, 0x40, 0x40)}, {"lightgray", RGB(0xC0, 0xC0, 0xC0)}, {"darkgray", RGB(0x80, 0x80, 0x80)}, {"lightblue", RGB(0x00, 0x00, 0xFF)}, {"lightgreen", RGB(0x00, 0xFF, 0x00)}, {"lightcyan", RGB(0x00, 0xFF, 0xFF)}, {"lightred", RGB(0xFF, 0x00, 0x00)}, {"lightmagenta", RGB(0xFF, 0x00, 0xFF)}, {"yellow", RGB(0xFF, 0xFF, 0x00)}, {"white", RGB(0xFF, 0xFF, 0xFF)}, }; int r, g, b; int i; if (!gui.in_use) /* can't do this when GUI not running */ return (GuiColor)0; if (name[0] == '#' && strlen(name) == 7) { /* Name is in "#rrggbb" format */ r = hex_digit(name[1]) * 16 + hex_digit(name[2]); g = hex_digit(name[3]) * 16 + hex_digit(name[4]); b = hex_digit(name[5]) * 16 + hex_digit(name[6]); if (r < 0 || g < 0 || b < 0) return (GuiColor)0; return RGB(r, g, b); } else { /* Check if the name is one of the colors we know */ for (i = 0; i < sizeof(table) / sizeof(table[0]); i++) if (STRICMP(name, table[i].name) == 0) return table[i].color; } return (GuiColor)0; } /* * Set the current text foreground color. */ void gui_mch_set_fg_color(color) GuiColor color; { gui.currFgColor = color; } /* * Set the current text background color. */ void gui_mch_set_bg_color(color) GuiColor color; { gui.currBgColor = color; } void gui_mch_draw_string(row, col, s, len, fake_bold, underline) int row; int col; char_u *s; int len; int fake_bold; int underline; { static int *padding = NULL; static int pad_size = 0; HDC hdc = GetWindowDC(s_textArea); HPEN hpen, old_pen; HFONT old_font; int y; int i; #if 1 /* * 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; /* * Clear background first. * Note: FillRect() excludes right and bottom of rectangle. */ rc.left = FILL_X(col); rc.top = FILL_Y(row); rc.right = FILL_X(col + len); /* Add +1 to erase fake bold? */ rc.bottom = FILL_Y(row + 1); hbr = CreateSolidBrush(gui.currBgColor); FillRect(hdc, &rc, hbr); DeleteBrush(hbr); SetBkMode(hdc, TRANSPARENT); #else /* * The alternative would be to write the characters in opaque mode, but * when the text is not exactly the same proportions as normal text, too * big or too little a rectangle gets drawn for the background. */ SetBkMode(hdc, OPAQUE); SetBkColor(hdc, gui.currBgColor); #endif SetTextColor(hdc, gui.currFgColor); old_font = SelectFont(hdc, gui.currFont); if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) { vim_free(padding); pad_size = Columns; padding = (int *)alloc(pad_size); if (padding != NULL) for (i = 0; i < pad_size; i++) padding[i] = gui.char_width; } /* * What am I doing wrong here? If I use "padding" as the last argument to * the two calls to ExtTextOut() below instead of "NULL", all the spacing * goes strange, and some text doesn't get displayed even after ^L, but it * does when you move the cursor over it. -- webb. */ ExtTextOut(hdc, TEXT_X(col), TEXT_Y(row), 0, NULL, (char *)s, len, NULL); if (fake_bold) ExtTextOut(hdc, TEXT_X(col), TEXT_Y(row), 0, NULL, (char *)s, len, NULL); if (underline) { /* * Note: LineTo() excludes the last pixel in the line. */ hpen = CreatePen(PS_SOLID, 1, gui.currFgColor); old_pen = SelectObject(hdc, hpen); y = FILL_Y(row + 1) - 1; MoveToEx(hdc, FILL_X(col), y, NULL); LineTo(hdc, FILL_X(col + len), y); DeleteObject(hpen); SelectObject(hdc, old_pen); } SelectFont(hdc, old_font); ReleaseDC(s_textArea, hdc); } /* * Return OK if the key with the termcap name "name" is supported. */ int gui_mch_haskey(name) char_u *name; { int i; for (i = 0; special_keys[i].vim_code1 != NUL; i++) if (name[0] == special_keys[i].vim_code0 && name[1] == special_keys[i].vim_code1) return OK; return FAIL; } void gui_mch_beep() { /* * TODO: Seems to wait or not beep at all. Can we use Beep()? What args? */ MessageBeep(0xFFFFFFFF); } void gui_mch_flash() { HDC hdc = GetWindowDC(s_textArea); RECT rc; /* * Note: InvertRect() excludes right and bottom of rectangle. */ rc.left = 0; rc.top = 0; rc.right = gui.num_cols * gui.char_width; rc.bottom = gui.num_rows * gui.char_height; InvertRect(hdc, &rc); ui_delay(20L, TRUE); /* wait 1/50 of a second */ InvertRect(hdc, &rc); ReleaseDC(s_textArea, hdc); } /* * Invert a rectangle from row r, column c, for nr rows and nc columns. */ void gui_mch_invert_rectangle(r, c, nr, nc) int r; int c; int nr; int nc; { HDC hdc = GetWindowDC(s_textArea); RECT rc; /* * Note: InvertRect() excludes right and bottom of rectangle. */ rc.left = FILL_X(c); rc.top = FILL_Y(r); rc.right = rc.left + nc * gui.char_width; rc.bottom = rc.top + nr * gui.char_height; InvertRect(hdc, &rc); ReleaseDC(s_textArea, hdc); } /* * Iconify the GUI window. */ void gui_mch_iconify() { /* TODO */ } /* * Set the window title */ void gui_mch_settitle(title, icon) char_u *title; char_u *icon; { SetWindowText(s_hwnd, (LPCSTR)title); } /* * Draw a cursor without focus. */ void gui_mch_draw_hollow_cursor() { HDC hdc = GetWindowDC(s_textArea); HBRUSH hbr; RECT rc; /* * Note: FrameRect() excludes right and bottom of rectangle. */ rc.left = FILL_X(gui.col); rc.top = FILL_Y(gui.row); rc.right = rc.left + gui.char_width; rc.bottom = rc.top + gui.char_height; hbr = CreateSolidBrush(gui.cursor_pixel); FrameRect(hdc, &rc, hbr); DeleteBrush(hbr); ReleaseDC(s_textArea, hdc); } /* * Draw part of a cursor, only w pixels wide, and h pixels high. */ void gui_mch_draw_part_cursor(w, h) int w; int h; { HDC hdc = GetWindowDC(s_textArea); HBRUSH hbr; RECT rc; /* * Note: FillRect() excludes right and bottom of rectangle. */ rc.left = FILL_X(gui.col); rc.top = FILL_Y(gui.row) + gui.char_height - h; rc.right = rc.left + w; rc.bottom = rc.top + h; hbr = CreateSolidBrush(gui.cursor_pixel); FillRect(hdc, &rc, hbr); DeleteBrush(hbr); ReleaseDC(s_textArea, hdc); } /* * Process a single Windows message. If one is not available we hang until one * is. */ static void process_message() { MSG msg; UINT vk; /* Virtual key */ char_u string[3]; int num; int i; GetMessage(&msg, NULL, 0, 0); /* * Check if it's a special key that we recognise. If not, call * TranslateMessage(). */ if (msg.message == WM_KEYDOWN) { vk = (int) msg.wParam; for (i = 0; special_keys[i].key_sym != 0; i++) { if (special_keys[i].key_sym == vk) { string[0] = CSI; string[1] = KS_MODIFIER; string[2] = 0; if (GetKeyState(VK_SHIFT) & 0x8000) string[2] ^= MOD_MASK_SHIFT; /* * Don't use caps-lock as shift, because these are special keys * being considered here, and we only want letters to get * shifted -- webb */ /* if (GetKeyState(VK_CAPITAL) & 0x0001) string[2] ^= MOD_MASK_SHIFT; */ if (GetKeyState(VK_CONTROL) & 0x8000) string[2] |= MOD_MASK_CTRL; /* * When we find M- in a mapping, use either Alt or the Menu * key, since we haven't got Alt working very well. Hey, it * seems the Menu key IS the Alt key?? */ if ((GetKeyState(VK_MENU) & 0x8000) || s_alt_key_down) string[2] |= MOD_MASK_ALT; if (string[2] != 0) add_to_input_buf(string, 3); num = 0; if (special_keys[i].vim_code1 == NUL) string[num++] = special_keys[i].vim_code0; else { string[num++] = (char_u)CSI; string[num++] = special_keys[i].vim_code0; string[num++] = special_keys[i].vim_code1; } add_to_input_buf(string, num); break; } } if (special_keys[i].key_sym == 0) { /* Some keys need C-S- where they should only need C- */ if (GetKeyState(VK_CONTROL) & 0x8000) { if (vk == '6') { string[0] = Ctrl('^'); add_to_input_buf(string, 1); } else if (vk == '-') /* Why doesn't this work? */ { string[0] = Ctrl('_'); add_to_input_buf(string, 1); } else TranslateMessage(&msg); } else TranslateMessage(&msg); } } DispatchMessage(&msg); } /* * Catch up with any queued events. This may put keyboard input into the * input buffer, call resize call-backs, trigger timers etc. If there is * nothing in the event queue (& no timers pending), then we return * immediately. */ void gui_mch_update() { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) && !is_input_buf_full()) process_message(); } /* * GUI input routine called by gui_wait_for_chars(). Waits for a character * from the keyboard. * wtime == -1 Wait forever. * wtime == 0 This should never happen. * wtime > 0 Wait wtime milliseconds for a character. * Returns OK if a character was found to be available within the given time, * or FAIL otherwise. */ int gui_mch_wait_for_chars(wtime) int wtime; { MSG msg; s_timed_out = FALSE; if (wtime > 0) s_wait_timer = SetTimer(NULL, 0, (UINT)wtime, (TIMERPROC)_OnTimer); while (!s_timed_out) { /* * Don't use gui_mch_update() because then we will spin-lock until a * char arrives, instead we use GetMessage() to hang until an * event arrives. No need to check for input_buf_full because we are * returning as soon as it contains a single char -- webb */ process_message(); if (!is_input_buf_empty()) { if (s_wait_timer != 0 && !s_timed_out) { KillTimer(NULL, s_wait_timer); /* Eat spurious WM_TIMER messages */ while(PeekMessage(&msg, s_hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)); s_wait_timer = 0; } return OK; } } return FAIL; } /* * Output routines. */ /* Flush any output to the screen */ void gui_mch_flush() { /* Is anything needed here? */ } /* * Clear a rectangular region of the screen from text pos (row1, col1) to * (row2, col2) inclusive. */ void gui_mch_clear_block(row1, col1, row2, col2) int row1; int col1; int row2; int col2; { HDC hdc = GetWindowDC(s_textArea); HBRUSH hbr; RECT rc; /* * Clear one extra pixel at the right, for when bold characters have * spilled over to the next column. Note: FillRect() excludes right and * bottom of rectangle. * Can this ever erase part of the next character? - webb */ rc.left = FILL_X(col1); rc.top = FILL_Y(row1); rc.right = FILL_X(col2 + 1) + 1; rc.bottom = FILL_Y(row2 + 1); hbr = CreateSolidBrush(gui.back_pixel); FillRect(hdc, &rc, hbr); DeleteBrush(hbr); ReleaseDC(s_textArea, hdc); } /* * Delete the given number of lines from the given row, scrolling up any * text further down within the scroll region. */ void gui_mch_delete_lines(row, num_lines) int row; int num_lines; { if (num_lines <= 0) return; if (row + num_lines > gui.scroll_region_bot) { /* Scrolled out of region, just blank the lines out */ gui_clear_block(row, 0, gui.scroll_region_bot, Columns - 1); } else { #if 0 /* Doesn't work when part of the window is off the screen */ RECT rc; rc.left = FILL_X(0); rc.right = FILL_X(Columns); rc.top = FILL_Y(row); rc.bottom = FILL_Y(gui.scroll_region_bot + 1); ScrollWindowEx(s_textArea, 0, -num_lines * gui.char_height, &rc, &rc, NULL, NULL, 0x0); #else /* HACK: */ gui_redraw_block(gui.scroll_region_top, 0, gui.scroll_region_bot, Columns - 1); #endif /* Update gui.cursor_row if the cursor scrolled or copied over */ if (gui.cursor_row >= row) { if (gui.cursor_row < row + num_lines) INVALIDATE_CURSOR(); else if (gui.cursor_row <= gui.scroll_region_bot) gui.cursor_row -= num_lines; } gui_clear_block(gui.scroll_region_bot - num_lines + 1, 0, gui.scroll_region_bot, Columns - 1); } } /* * Insert the given number of lines before the given row, scrolling down any * following text within the scroll region. */ void gui_mch_insert_lines(row, num_lines) int row; int num_lines; { if (num_lines <= 0) return; if (row + num_lines > gui.scroll_region_bot) { /* Scrolled out of region, just blank the lines out */ gui_clear_block(row, 0, gui.scroll_region_bot, Columns - 1); } else { #if 0 /* Doesn't work when part of the window is off the screen */ RECT rc; rc.left = FILL_X(0); rc.right = FILL_X(Columns); rc.top = FILL_Y(row); rc.bottom = FILL_Y(gui.scroll_region_bot + 1); ScrollWindowEx(s_textArea, 0, num_lines * gui.char_height, &rc, &rc, NULL, NULL, 0x0); #else /* HACK: */ gui_redraw_block(gui.scroll_region_top, 0, gui.scroll_region_bot, Columns - 1); #endif /* Update gui.cursor_row if the cursor scrolled or copied over */ if (gui.cursor_row >= gui.row) { if (gui.cursor_row <= gui.scroll_region_bot - num_lines) gui.cursor_row += num_lines; else if (gui.cursor_row <= gui.scroll_region_bot) INVALIDATE_CURSOR(); } gui_clear_block(row, 0, row + num_lines - 1, Columns - 1); } } /* * Menu stuff. */ void gui_mch_enable_menu(flag) int flag; { SetMenu(s_hwnd, flag ? s_menuBar : NULL); } void gui_mch_set_menu_pos(x, y, w, h) int x; int y; int w; int h; { /* It will be in the right place anyway */ } /* * Add a sub menu to the menu bar. */ void gui_mch_add_menu(menu, parent) GuiMenu *menu; GuiMenu *parent; { MENUITEMINFO info; menu->submenu_id = CreatePopupMenu(); info.cbSize = sizeof(info); info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID | MIIM_SUBMENU; info.dwItemData = (DWORD)menu; info.wID = menu->id = s_menu_id++; info.fType = MFT_STRING; info.dwTypeData = (LPTSTR)menu->name; info.cch = STRLEN(menu->name); info.hSubMenu = menu->submenu_id; InsertMenuItem((parent == NULL) ? s_menuBar : parent->submenu_id, (UINT)99999, TRUE, &info); /* * The "Help" menu is a special case, and should be placed at the far right * hand side of the menu-bar. */ if (parent == NULL && STRCMP((char *)menu->name, "Help") == 0) { /* TODO: Make this the help menu */ } /* Fix window size if menu wrapped */ if (parent == NULL) gui_w32_get_menu_height(TRUE); DrawMenuBar(s_hwnd); } /* * Add a menu item to a menu */ void gui_mch_add_menu_item(menu, parent) GuiMenu *menu; GuiMenu *parent; { MENUITEMINFO info; info.cbSize = sizeof(info); info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID; info.dwItemData = (DWORD)menu; info.wID = menu->id = s_menu_id++; info.fType = MFT_STRING; info.dwTypeData = (LPTSTR)menu->name; info.cch = STRLEN(menu->name); InsertMenuItem(parent->submenu_id, (UINT)99999, TRUE, &info); menu->submenu_id = NULL; DrawMenuBar(s_hwnd); } /* * Destroy the machine specific menu widget. */ void gui_mch_destroy_menu(menu) GuiMenu *menu; { RemoveMenu(s_menuBar, menu->id, MF_BYCOMMAND); 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(menu, grey) GuiMenu *menu; int grey; { if (grey) EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_GRAYED); else EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED); } /* * Make menu item hidden or not hidden */ void gui_mch_menu_hidden(menu, hidden) GuiMenu *menu; int hidden; { /* * This doesn't do what we want. Hmm, just grey the menu items for now. */ /* if (hidden) EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_DISABLED); else EnableMenuItem(s_menuBar, menu->id, MF_BYCOMMAND | MF_ENABLED); */ gui_mch_menu_grey(menu, hidden); } /* * This is called after setting all the menus to grey/hidden or not. */ void gui_mch_draw_menubar() { DrawMenuBar(s_hwnd); } /* cproto doesn't create a prototype for main() */ int main __PARMS((int argc, char **argv)); #ifndef PROTO int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow) { #if 0 TRACE("vim WinMain, hInst = %x, hPrev = %x, cmd = `%s', show = %d\n", hInstance, hPrevInst, lpszCmdLine, nCmdShow); #endif int argc; char **argv; #ifdef __BORLANDC__ int i; char* pch; char* pszNewCmdLine; char prog[256]; char *p; int fIsQuote; /* * Ron: added full path name so that the $VIM variable will get set to our * startup path (so the .vimrc file can be found w/o a VIM env. var.) */ GetModuleFileName(NULL, prog, 255); p = strrchr(prog, '.'); if (p != NULL) *p = '\0'; /* * Add the size of the string, the separating space, and a terminating '\0' */ pszNewCmdLine = (char *) malloc(STRLEN(lpszCmdLine) + STRLEN(prog) + 2); if (pszNewCmdLine == NULL) return 0; STRCPY(pszNewCmdLine, prog); STRCAT(pszNewCmdLine, " "); STRCAT(pszNewCmdLine, lpszCmdLine); while ((*pszNewCmdLine != '\0') && (*pszNewCmdLine == ' ')) pszNewCmdLine++; /* start on 1st non-space */ pch = pszNewCmdLine; argc = 0; while ( *pch != '\0' ) { /* Ron: Handle quoted strings in args list */ fIsQuote = (*pch == '\"'); if (fIsQuote) ++pch; argc++; /* this is an argument */ if (fIsQuote) { while ((*pch != '\0') && (*pch != '\"')) pch++; /* advance until a closing quote */ if (*pch) pch++; } else { while ((*pch != '\0') && (*pch != ' ')) pch++; /* advance until a space */ } while (*pch && *pch == ' ' ) pch++; /* advance until a non-space */ } argv = (char**) malloc((argc+1) * sizeof(char*)); if (argv == NULL ) return 0; /* malloc error */ i = 0; pch = pszNewCmdLine; while ((i < argc) && (*pch != '\0')) { fIsQuote = (*pch == '\"'); if (fIsQuote) ++pch; argv[i++] = pch; if (fIsQuote) { while (*pch != '\0' && *pch != '\"') pch++; /* advance until the closing quote */ } else { while (*pch != '\0' && *pch != ' ') pch++; /* advance until a space */ } if (*pch != '\0') *(pch++) = '\0'; /* parse argument here */ while (*pch && *pch == ' ') pch++; /* advance until a non-space */ } ASSERT(i == argc); argv[argc] = (char *) NULL; /* NULL-terminated list */ #else /* * Are __argc and __argv portable? Because the above doesn't handle * something like `gvim "line func.c"'. */ argc = __argc; argv = __argv; #endif s_hinst = hInstance; main (argc, argv); #ifdef __BORLANDC__ free(argv); free(pszNewCmdLine); #endif return 0; } #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.