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.