summaryrefslogtreecommitdiffstats
path: root/src/gui_w16.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui_w16.c')
-rw-r--r--src/gui_w16.c1524
1 files changed, 1524 insertions, 0 deletions
diff --git a/src/gui_w16.c b/src/gui_w16.c
new file mode 100644
index 0000000000..e6aa850577
--- /dev/null
+++ b/src/gui_w16.c
@@ -0,0 +1,1524 @@
+/* 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);
+ }
+
+ 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)
+{
+ 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 (win_xpos + win_width > workarea_rect.right)
+ win_xpos = workarea_rect.right - win_width;
+
+ if (win_xpos < workarea_rect.left)
+ win_xpos = workarea_rect.left;
+
+ if (win_ypos + win_height > workarea_rect.bottom)
+ win_ypos = workarea_rect.bottom - win_height;
+
+ if (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);
+}
+
+
+
+ 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)
+ {
+ int cell_len = 0;
+
+ /* Compute the length in display cells. */
+ for (n = 0; n < len; n += MB_BYTE2LEN(text[n]))
+ cell_len += (*mb_ptr2cells)(text + n);
+ rc.right = FILL_X(col + cell_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);
+}
+
+
+
+
+#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)
+{
+ 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 (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)
+{
+ 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)</