view src/gui_haiku.cc @ 19742:810eee1b42e3 v8.2.0427

patch 8.2.0427: it is not possible to check for a typo in a feature name Commit: https://github.com/vim/vim/commit/7929651e05b081fe55e0e745725a7ad78c51be16 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Mar 22 16:17:14 2020 +0100 patch 8.2.0427: it is not possible to check for a typo in a feature name Problem: It is not possible to check for a typo in a feature name. Solution: Add an extra argument to has().
author Bram Moolenaar <Bram@vim.org>
date Sun, 22 Mar 2020 16:30:03 +0100
parents 22f0dda71638
children 352701a626ed
line wrap: on
line source

/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *    BeBox GUI support Copyright 1998 by Olaf Seibert.
 *                  All Rights Reserved.
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 *
 * Based on "GUI support for the Buzzword Enhanced Operating System."
 *
 * Ported to R4 by Richard Offer <richard@whitequeen.com> Jul 99
 *
 * Haiku support by Siarzhuk Zharski <imker@gmx.li> Apr-Mai 2009
 *
 */

/*
 * Structure of the Haiku GUI code:
 *
 * There are 3 threads.
 * 1. The initial thread. In gui_mch_prepare() this gets to run the
 *    BApplication message loop. But before it starts doing that,
 *    it creates thread 2
 * 2. The main() thread. This thread is created in gui_mch_prepare()
 *    and its purpose in life is to call main(argc, argv) again.
 *    This thread is doing the bulk of the work.
 * 3. Sooner or later, a window is opened by the main() thread. This
 *    causes a second message loop to be created: the window thread.
 *
 * == alternatively ===
 *
 * #if RUN_BAPPLICATION_IN_NEW_THREAD...
 *
 * 1. The initial thread. In gui_mch_prepare() this gets to spawn
 *    thread 2. After doing that, it returns to main() to do the
 *    bulk of the work, being the main() thread.
 * 2. Runs the BApplication.
 * 3. The window thread, just like in the first case.
 *
 * This second alternative is cleaner from Vim's viewpoint. However,
 * the BeBook seems to assume everywhere that the BApplication *must*
 * run in the initial thread. So perhaps doing otherwise is very wrong.
 *
 * However, from a B_SINGLE_LAUNCH viewpoint, the first is better.
 * If Vim is marked "Single Launch" in its application resources,
 * and a file is dropped on the Vim icon, and another Vim is already
 * running, the file is passed on to the earlier Vim. This happens
 * in BApplication::Run(). So we want Vim to terminate if
 * BApplication::Run() terminates. (See the BeBook, on BApplication.
 * However, it seems that the second copy of Vim isn't even started
 * in this case... which is for the better since I wouldn't know how
 * to detect this case.)
 *
 * Communication between these threads occurs mostly by translating
 * BMessages that come in and posting an appropriate translation on
 * the VDCMP (Vim Direct Communication Message Port). Therefore the
 * actions required for keypresses and window resizes, etc, are mostly
 * performed in the main() thread.
 *
 * A notable exception to this is the Draw() event. The redrawing of
 * the window contents is performed asynchronously from the window
 * thread. To make this work correctly, a locking protocol is used when
 * any thread is accessing the essential variables that are used by
 * the window thread.
 *
 * This locking protocol consists of locking Vim's window. This is both
 * convenient and necessary.
 */

extern "C" {

#include <assert.h>
#include <float.h>
#include <syslog.h>

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "version.h"

}	// extern "C"

// ---------------- start of header part ----------------

//#include <Alert.h>
#include <Application.h>
#include <Beep.h>
#include <Bitmap.h>
#include <Box.h>
#include <Button.h>
#include <Clipboard.h>
#include <Debug.h>
//#include <Directory.h>
//#include <Entry.h>
#include <File.h>
#include <FilePanel.h>
#include <FindDirectory.h>
//#include <Font.h>
#include <IconUtils.h>
#include <Input.h>
#include <ListView.h>
#include <MenuBar.h>
#include <MenuItem.h>
//#include <MessageQueue.h>
//#include <OS.h>
#include <Path.h>
#include <PictureButton.h>
#include <PopUpMenu.h>
//#include <Region.h>
#include <Resources.h>
//#include <Roster.h>
#include <Screen.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <String.h>
#include <StringView.h>
//#include <SupportDefs.h>
#include <TabView.h>
#include <TextControl.h>
#include <TextView.h>
#include <TranslationUtils.h>
#include <TranslatorFormats.h>
#include <View.h>
#include <Window.h>

class VimApp;
class VimFormView;
class VimTextAreaView;
class VimWindow;
class VimToolbar;
class VimTabLine;

extern key_map *keyMap;
extern char *keyMapChars;

extern int main(int argc, char **argv);

#ifndef B_MAX_PORT_COUNT
#define B_MAX_PORT_COUNT    255
#endif

// VimApp seems comparable to the X "vimShell"
class VimApp: public BApplication
{
	typedef BApplication Inherited;
	public:
	VimApp(const char *appsig);
	~VimApp();

	// callbacks:
#if 0
	virtual void DispatchMessage(BMessage *m, BHandler *h)
	{
		m->PrintToStream();
		Inherited::DispatchMessage(m, h);
	}
#endif
	virtual void ReadyToRun();
	virtual void ArgvReceived(int32 argc, char **argv);
	virtual void RefsReceived(BMessage *m);
	virtual bool QuitRequested();
	virtual void MessageReceived(BMessage *m);

	static void SendRefs(BMessage *m, bool changedir);

	sem_id		fFilePanelSem;
	BFilePanel*	fFilePanel;
	BPath		fBrowsedPath;
	private:
};

class VimWindow: public BWindow
{
	typedef BWindow Inherited;
	public:
	VimWindow();
	~VimWindow();

	//    virtual void DispatchMessage(BMessage *m, BHandler *h);
	virtual void WindowActivated(bool active);
	virtual bool QuitRequested();

	VimFormView		*formView;

	private:
	void init();

};

class VimFormView: public BView
{
	typedef BView Inherited;
	public:
	VimFormView(BRect frame);
	~VimFormView();

	// callbacks:
	virtual void AllAttached();
	virtual void FrameResized(float new_width, float new_height);

#define MENUBAR_MARGIN	1
	float MenuHeight() const
	{ return menuBar ? menuBar->Frame().Height() + MENUBAR_MARGIN: 0; }
	BMenuBar *MenuBar() const
	{ return menuBar; }

	private:
	void init(BRect);

	BMenuBar		*menuBar;
	VimTextAreaView	*textArea;

#ifdef FEAT_TOOLBAR
	public:
	float ToolbarHeight() const;
	VimToolbar *ToolBar() const
	{ return toolBar; }
	private:
	VimToolbar		*toolBar;
#endif

#ifdef FEAT_GUI_TABLINE
	public:
	VimTabLine *TabLine() const	{ return tabLine; }
	bool IsShowingTabLine() const { return showingTabLine; }
	void SetShowingTabLine(bool showing) { showingTabLine = showing;	}
	float TablineHeight() const;
	private:
	VimTabLine	*tabLine;
	int	showingTabLine;
#endif
};

class VimTextAreaView: public BView
{
	typedef BView Inherited;
	public:
	VimTextAreaView(BRect frame);
	~VimTextAreaView();

	// callbacks:
	virtual void Draw(BRect updateRect);
	virtual void KeyDown(const char *bytes, int32 numBytes);
	virtual void MouseDown(BPoint point);
	virtual void MouseUp(BPoint point);
	virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
	virtual void MessageReceived(BMessage *m);

	// own functions:
	int mchInitFont(char_u *name);
	void mchDrawString(int row, int col, char_u *s, int len, int flags);
	void mchClearBlock(int row1, int col1, int row2, int col2);
	void mchClearAll();
	void mchDeleteLines(int row, int num_lines);
	void mchInsertLines(int row, int num_lines);

	static void guiSendMouseEvent(int button, int x, int y, int repeated_click, int_u modifiers);
	static void guiMouseMoved(int x, int y);
	static void guiBlankMouse(bool should_hide);
	static int_u mouseModifiersToVim(int32 beModifiers);

	int32 mouseDragEventCount;

#ifdef FEAT_MBYTE_IME
	void DrawIMString(void);
#endif

	private:
	void init(BRect);

	int_u	    vimMouseButton;
	int_u	    vimMouseModifiers;

#ifdef FEAT_MBYTE_IME
	struct {
		BMessenger* messenger;
		BMessage* message;
		BPoint location;
		int row;
		int col;
		int count;
	} IMData;
#endif
};

class VimScrollBar: public BScrollBar
{
	typedef BScrollBar Inherited;
	public:
	VimScrollBar(scrollbar_T *gsb, orientation posture);
	~VimScrollBar();

	virtual void ValueChanged(float newValue);
	virtual void MouseUp(BPoint where);
	void SetValue(float newval);
	scrollbar_T *getGsb()
	{ return gsb; }

	int32	    scrollEventCount;

	private:
	scrollbar_T *gsb;
	float	ignoreValue;
};


#ifdef FEAT_TOOLBAR

class VimToolbar : public BBox
{
	static BBitmap *normalButtonsBitmap;
	static BBitmap *grayedButtonsBitmap;

	BBitmap *LoadVimBitmap(const char* fileName);
	bool GetPictureFromBitmap(BPicture *pictureTo, int32 index, BBitmap *bitmapFrom, bool pressed);
	bool ModifyBitmapToGrayed(BBitmap *bitmap);

	BList fButtonsList;
	void InvalidateLayout();

	public:
	VimToolbar(BRect frame, const char * name);
	~VimToolbar();

	bool PrepareButtonBitmaps();

	bool AddButton(int32 index, vimmenu_T *menu);
	bool RemoveButton(vimmenu_T *menu);
	bool GrayButton(vimmenu_T *menu, int grey);

	float ToolbarHeight() const;
	virtual void AttachedToWindow();
};

BBitmap *VimToolbar::normalButtonsBitmap  = NULL;
BBitmap *VimToolbar::grayedButtonsBitmap  = NULL;

const float ToolbarMargin = 3.;
const float ButtonMargin  = 3.;

#endif //FEAT_TOOLBAR

#ifdef FEAT_GUI_TABLINE

class VimTabLine : public BTabView
{
	public:
		class VimTab : public BTab {
			public:
				VimTab() : BTab(new BView(BRect(), "-Empty-", 0, 0)) {}

			virtual void Select(BView* owner);
		};

		VimTabLine(BRect r) : BTabView(r, "vimTabLine", B_WIDTH_FROM_LABEL,
			   B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT, B_WILL_DRAW | B_FRAME_EVENTS) {}

	float TablineHeight() const;
	virtual void MouseDown(BPoint point);
};

#endif //FEAT_GUI_TABLINE


// For caching the fonts that are used;
// Vim seems rather sloppy in this regard.
class VimFont: public BFont
{
	typedef BFont Inherited;
	public:
	VimFont();
	VimFont(const VimFont *rhs);
	VimFont(const BFont *rhs);
	VimFont(const VimFont &rhs);
	~VimFont();

	VimFont *next;
	int refcount;
	char_u *name;

	private:
	void init();
};

#if defined(FEAT_GUI_DIALOG)

class VimDialog : public BWindow
{
	typedef BWindow Inherited;

	BButton* _CreateButton(int32 which, const char* label);

	public:

	class View : public BView {
		typedef BView Inherited;

		public:
		View(BRect frame);
		~View();

		virtual void Draw(BRect updateRect);
		void InitIcon(int32 type);

		private:
		BBitmap*	fIconBitmap;
	};

	VimDialog(int type, const char *title, const char *message,
			const char *buttons, int dfltbutton, const char *textfield,
			int ex_cmd);
	~VimDialog();

	int Go();

	virtual void MessageReceived(BMessage *msg);

	private:
	sem_id			fDialogSem;
	int				fDialogValue;
	BList			fButtonsList;
	BTextView*		fMessageView;
	BTextControl*	fInputControl;
	const char*		fInputValue;
};

class VimSelectFontDialog : public BWindow
{
	typedef BWindow Inherited;

	void _CleanList(BListView* list);
	void _UpdateFontStyles();
	void _UpdateSizeInputPreview();
	void _UpdateFontPreview();
	bool _UpdateFromListItem(BListView* list, char* text, int textSize);
	public:

	VimSelectFontDialog(font_family* family, font_style* style, float* size);
	~VimSelectFontDialog();

	bool Go();

	virtual void MessageReceived(BMessage *msg);

	private:
	status_t		fStatus;
	sem_id			fDialogSem;
	bool			fDialogValue;
	font_family*	fFamily;
	font_style*		fStyle;
	float*			fSize;
	font_family		fFontFamily;
	font_style		fFontStyle;
	float			fFontSize;
	BStringView*	fPreview;
	BListView*		fFamiliesList;
	BListView*		fStylesList;
	BListView*		fSizesList;
	BTextControl*	fSizesInput;
};

#endif // FEAT_GUI_DIALOG

// ---------------- end of GUI classes ----------------

struct MainArgs {
	int		 argc;
	char	**argv;
};

// These messages are copied through the VDCMP.
// Therefore they ought not to have anything fancy.
// They must be of POD type (Plain Old Data)
// as the C++ standard calls them.

#define	KEY_MSG_BUFSIZ	7
#if KEY_MSG_BUFSIZ < MAX_KEY_CODE_LEN
#error Increase KEY_MSG_BUFSIZ!
#endif

struct VimKeyMsg {
	char_u	length;
	char_u	chars[KEY_MSG_BUFSIZ];	// contains Vim encoding
	bool    csi_escape;
};

struct VimResizeMsg {
	int		width;
	int		height;
};

struct VimScrollBarMsg {
	VimScrollBar *sb;
	long	value;
	int		stillDragging;
};

struct VimMenuMsg {
	vimmenu_T	*guiMenu;
};

struct VimMouseMsg {
	int		button;
	int		x;
	int		y;
	int		repeated_click;
	int_u	modifiers;
};

struct VimMouseMovedMsg {
	int		x;
	int		y;
};

struct VimFocusMsg {
	bool	active;
};

struct VimRefsMsg {
	BMessage   *message;
	bool	changedir;
};

struct VimTablineMsg {
	int		index;
};

struct VimTablineMenuMsg {
	int		index;
	int		event;
};

struct VimMsg {
	enum VimMsgType {
		Key, Resize, ScrollBar, Menu, Mouse, MouseMoved, Focus, Refs, Tabline, TablineMenu
	};

	union {
		struct VimKeyMsg	Key;
		struct VimResizeMsg	NewSize;
		struct VimScrollBarMsg	Scroll;
		struct VimMenuMsg	Menu;
		struct VimMouseMsg	Mouse;
		struct VimMouseMovedMsg	MouseMoved;
		struct VimFocusMsg	Focus;
		struct VimRefsMsg	Refs;
		struct VimTablineMsg	Tabline;
		struct VimTablineMenuMsg	TablineMenu;
	} u;
};

#define RGB(r, g, b)	((char_u)(r) << 16 | (char_u)(g) << 8 | (char_u)(b) << 0)
#define GUI_TO_RGB(g)	{ (g) >> 16, (g) >> 8, (g) >> 0, 255 }

// ---------------- end of header part ----------------

static struct specialkey
{
	uint16  BeKeys;
#define KEY(a,b)	((a)<<8|(b))
#define K(a)		KEY(0,a)		    // for ASCII codes
#define F(b)		KEY(1,b)		    // for scancodes
	char_u  vim_code0;
	char_u  vim_code1;
} special_keys[] =
{
	{K(B_UP_ARROW),	    'k', 'u'},
	{K(B_DOWN_ARROW),	    'k', 'd'},
	{K(B_LEFT_ARROW),	    'k', 'l'},
	{K(B_RIGHT_ARROW),	    'k', 'r'},
	{K(B_BACKSPACE),	    'k', 'b'},
	{K(B_INSERT),	    'k', 'I'},
	{K(B_DELETE),	    'k', 'D'},
	{K(B_HOME),		    'k', 'h'},
	{K(B_END),		    '@', '7'},
	{K(B_PAGE_UP),	    'k', 'P'},	    // XK_Prior
	{K(B_PAGE_DOWN),	    'k', 'N'},	    // XK_Next,

#define FIRST_FUNCTION_KEY  11
	{F(B_F1_KEY),	    'k', '1'},
	{F(B_F2_KEY),	    'k', '2'},
	{F(B_F3_KEY),	    'k', '3'},
	{F(B_F4_KEY),	    'k', '4'},
	{F(B_F5_KEY),	    'k', '5'},
	{F(B_F6_KEY),	    'k', '6'},
	{F(B_F7_KEY),	    'k', '7'},
	{F(B_F8_KEY),	    'k', '8'},
	{F(B_F9_KEY),	    'k', '9'},
	{F(B_F10_KEY),	    'k', ';'},

	{F(B_F11_KEY),	    'F', '1'},
	{F(B_F12_KEY),	    'F', '2'},
	//  {XK_F13,		    'F', '3'},	// would be print screen
	// sysreq
	{F(0x0F),		    'F', '4'},		// scroll lock
	{F(0x10),		    'F', '5'},		// pause/break
	//  {XK_F16,	    'F', '6'},
	//  {XK_F17,	    'F', '7'},
	//  {XK_F18,	    'F', '8'},
	//  {XK_F19,	    'F', '9'},
	//   {XK_F20,	    'F', 'A'},
	//  {XK_F21,	    'F', 'B'},
	//  {XK_F22,	    'F', 'C'},
	//  {XK_F23,	    'F', 'D'},
	//  {XK_F24,	    'F', 'E'},
	//  {XK_F25,	    'F', 'F'},
	//  {XK_F26,	    'F', 'G'},
	//  {XK_F27,	    'F', 'H'},
	//  {XK_F28,	    'F', 'I'},
	//  {XK_F29,	    'F', 'J'},
	//  {XK_F30,	    'F', 'K'},
	//  {XK_F31,	    'F', 'L'},
	//  {XK_F32,	    'F', 'M'},
	//  {XK_F33,	    'F', 'N'},
	//  {XK_F34,	    'F', 'O'},
	//  {XK_F35,	    'F', 'P'},	    // keysymdef.h defines up to F35

	//  {XK_Help,	    '%', '1'},	    // XK_Help
	{F(B_PRINT_KEY),	    '%', '9'},

#if 0
	// Keypad keys:
	{F(0x48),	    'k', 'l'},	    // XK_KP_Left
	{F(0x4A),	    'k', 'r'},	    // XK_KP_Right
	{F(0x38),	    'k', 'u'},	    // XK_KP_Up
	{F(0x59),	    'k', 'd'},	    // XK_KP_Down
	{F(0x64),	    'k', 'I'},	    // XK_KP_Insert
	{F(0x65),	    'k', 'D'},	    // XK_KP_Delete
	{F(0x37),	    'k', 'h'},	    // XK_KP_Home
	{F(0x58),	    '@', '7'},	    // XK_KP_End
	{F(0x39),	    'k', 'P'},	    // XK_KP_Prior
	{F(0x60),	    'k', 'N'},	    // XK_KP_Next
	{F(0x49),	    '&', '8'},	    // XK_Undo, keypad 5
#endif

	// End of list marker:
	{0,			    0, 0}
};

#define NUM_SPECIAL_KEYS    (sizeof(special_keys)/sizeof(special_keys[0]))

// ---------------- VimApp ----------------

	static void
docd(BPath &path)
{
	mch_chdir((char *)path.Path());
	// Do this to get the side effects of a :cd command
	do_cmdline_cmd((char_u *)"cd .");
}

        static void
drop_callback(void *cookie)
{
    // TODO here we could handle going to a specific position in the dropped
    // file (see src/gui_mac.c)
    // Update the screen display
    update_screen(NOT_VALID);
}

    // Really handle dropped files and folders.
        static void
RefsReceived(BMessage *m, bool changedir)
{
	uint32 type;
	int32 count;

	m->PrintToStream();
	switch (m->what) {
		case B_REFS_RECEIVED:
		case B_SIMPLE_DATA:
			m->GetInfo("refs", &type, &count);
			if (type != B_REF_TYPE)
				goto bad;
			break;
		case B_ARGV_RECEIVED:
			m->GetInfo("argv", &type, &count);
			if (type != B_STRING_TYPE)
				goto bad;
			if (changedir) {
				char *dirname;
				if (m->FindString("cwd", (const char **) &dirname) == B_OK) {
					chdir(dirname);
					do_cmdline_cmd((char_u *)"cd .");
				}
			}
			break;
		default:
bad:
			/*fprintf(stderr, "bad!\n"); */
			delete m;
			return;
	}

#ifdef FEAT_VISUAL
	reset_VIsual();
#endif

	char_u  **fnames;
	fnames = (char_u **) alloc(count * sizeof(char_u *));
	int fname_index = 0;

	switch (m->what) {
		case B_REFS_RECEIVED:
		case B_SIMPLE_DATA:
			// fprintf(stderr, "case B_REFS_RECEIVED\n");
			for (int i = 0; i < count; ++i)
			{
				entry_ref ref;
				if (m->FindRef("refs", i, &ref) == B_OK) {
					BEntry entry(&ref, false);
					BPath path;
					entry.GetPath(&path);

					// Change to parent directory?
					if (changedir) {
						BPath parentpath;
						path.GetParent(&parentpath);
						docd(parentpath);
					}

					// Is it a directory? If so, cd into it.
					BDirectory bdir(&ref);
					if (bdir.InitCheck() == B_OK) {
						// don't cd if we already did it
						if (!changedir)
							docd(path);
					} else {
						mch_dirname(IObuff, IOSIZE);
						char_u *fname = shorten_fname((char_u *)path.Path(), IObuff);
						if (fname == NULL)
							fname = (char_u *)path.Path();
						fnames[fname_index++] = vim_strsave(fname);
						// fprintf(stderr, "%s\n", fname);
					}

					// Only do it for the first file/dir
					changedir = false;
				}
			}
			break;
		case B_ARGV_RECEIVED:
			// fprintf(stderr, "case B_ARGV_RECEIVED\n");
			for (int i = 1; i < count; ++i)
			{
				char *fname;

				if (m->FindString("argv", i, (const char **) &fname) == B_OK) {
					fnames[fname_index++] = vim_strsave((char_u *)fname);
				}
			}
			break;
		default:
			// fprintf(stderr, "case default\n");
			break;
	}

	delete m;

	// Handle the drop, :edit to get to the file
	if (fname_index > 0) {
		handle_drop(fname_index, fnames, FALSE, drop_callback, NULL);

		setcursor();
		out_flush();
	} else {
		vim_free(fnames);
	}
}

VimApp::VimApp(const char *appsig):
	BApplication(appsig),
	fFilePanelSem(-1),
	fFilePanel(NULL)
{
}

VimApp::~VimApp()
{
}

	void
VimApp::ReadyToRun()
{
	/*
	 * Apparently signals are inherited by the created thread -
	 * disable the most annoying ones.
	 */
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
}

	void
VimApp::ArgvReceived(int32 arg_argc, char **arg_argv)
{
	if (!IsLaunching()) {
		/*
		 * This can happen if we are set to Single or Exclusive
		 * Launch. Be nice and open the file(s).
		 */
		if (gui.vimWindow)
			gui.vimWindow->Minimize(false);
		BMessage *m = CurrentMessage();
		DetachCurrentMessage();
		SendRefs(m, true);
	}
}

	void
VimApp::RefsReceived(BMessage *m)
{
	// Horrible hack!!! XXX XXX XXX
	// The real problem is that b_start_ffc is set too late for
	// the initial empty buffer. As a result the window will be
	// split instead of abandoned.
	int limit = 15;
	while (--limit >= 0 && (curbuf == NULL || curbuf->b_start_ffc == 0))
		snooze(100000);    //  0.1 s
	if (gui.vimWindow)
		gui.vimWindow->Minimize(false);
	DetachCurrentMessage();
	SendRefs(m, true);
}

/*
 * Pass a BMessage on to the main() thread.
 * Caller must have detached the message.
 */
	void
VimApp::SendRefs(BMessage *m, bool changedir)
{
	VimRefsMsg rm;
	rm.message = m;
	rm.changedir = changedir;

	write_port(gui.vdcmp, VimMsg::Refs, &rm, sizeof(rm));
	//  calls ::RefsReceived
}

	void
VimApp::MessageReceived(BMessage *m)
{
	switch (m->what) {
		case 'save':
			{
				entry_ref refDirectory;
				m->FindRef("directory", &refDirectory);
				fBrowsedPath.SetTo(&refDirectory);
				BString strName;
				m->FindString("name", &strName);
				fBrowsedPath.Append(strName.String());
			}
			break;
		case 'open':
			{
				entry_ref ref;
				m->FindRef("refs", &ref);
				fBrowsedPath.SetTo(&ref);
			}
			break;
		case B_CANCEL:
			{
				BFilePanel *panel;
				m->FindPointer("source", (void**)&panel);
				if(fFilePanelSem != -1 && panel == fFilePanel)
				{
					delete_sem(fFilePanelSem);
					fFilePanelSem = -1;
				}

			}
			break;
		default:
			Inherited::MessageReceived(m);
			break;
	}
}

	bool
VimApp::QuitRequested()
{
	(void)Inherited::QuitRequested();
	return false;
}

// ---------------- VimWindow ----------------

VimWindow::VimWindow():
	BWindow(BRect(40, 40, 150, 150),
			"Vim",
			B_TITLED_WINDOW,
			0,
			B_CURRENT_WORKSPACE)

{
	init();
}

VimWindow::~VimWindow()
{
	if (formView) {
		RemoveChild(formView);
		delete formView;
	}
	gui.vimWindow = NULL;
}

	void
VimWindow::init()
{
	// Attach the VimFormView
	formView = new VimFormView(Bounds());
	if (formView != NULL) {
		AddChild(formView);
	}
}

#if 0  //  disabled in zeta patch
	void
VimWindow::DispatchMessage(BMessage *m, BHandler *h)
{
	/*
	 * Route B_MOUSE_UP messages to MouseUp(), in
	 * a manner that should be compatible with the
	 * intended future system behaviour.
	 */
	switch (m->what) {
		case B_MOUSE_UP:
			//  if (!h) h = PreferredHandler();
			//  gcc isn't happy without this extra set of braces, complains about
			//  jump to case label crosses init of 'class BView * v'
			//  richard@whitequeen.com jul 99
			{
				BView *v = dynamic_cast<BView *>(h);
				if (v) {
					// m->PrintToStream();
					BPoint where;
					m->FindPoint("where", &where);
					v->MouseUp(where);
				} else {
					Inherited::DispatchMessage(m, h);
				}
			}
			break;
		default:
			Inherited::DispatchMessage(m, h);
	}
}
#endif

	void
VimWindow::WindowActivated(bool active)
{
	Inherited::WindowActivated(active);
	// the textArea gets the keyboard action
	if (active && gui.vimTextArea)
		gui.vimTextArea->MakeFocus(true);

	struct VimFocusMsg fm;
	fm.active = active;

	write_port(gui.vdcmp, VimMsg::Focus, &fm, sizeof(fm));
}

	bool
VimWindow::QuitRequested()
{
	struct VimKeyMsg km;
	km.length = 5;
	memcpy((char *)km.chars, "\033:qa\r", km.length);
	km.csi_escape = false;
	write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
	return false;
}

// ---------------- VimFormView ----------------

VimFormView::VimFormView(BRect frame):
	BView(frame, "VimFormView", B_FOLLOW_ALL_SIDES,
			B_WILL_DRAW | B_FRAME_EVENTS),
	menuBar(NULL),
#ifdef FEAT_TOOLBAR
	toolBar(NULL),
#endif
#ifdef FEAT_GUI_TABLINE
//	showingTabLine(false),
	tabLine(NULL),
#endif
	textArea(NULL)
{
	init(frame);
}

VimFormView::~VimFormView()
{
	if (menuBar) {
		RemoveChild(menuBar);
#ifdef never
		//  deleting the menuBar leads to SEGV on exit
		//  richard@whitequeen.com Jul 99
		delete menuBar;
#endif
	}

#ifdef FEAT_TOOLBAR
	delete toolBar;
#endif

#ifdef FEAT_GUI_TABLINE
	delete tabLine;
#endif

	if (textArea) {
		RemoveChild(textArea);
		delete textArea;
	}
	gui.vimForm = NULL;
}

	void
VimFormView::init(BRect frame)
{
	menuBar = new BMenuBar(BRect(0,0,-MENUBAR_MARGIN,-MENUBAR_MARGIN),
			"VimMenuBar");

	AddChild(menuBar);

#ifdef FEAT_TOOLBAR
	toolBar = new VimToolbar(BRect(0,0,0,0), "VimToolBar");
	toolBar->PrepareButtonBitmaps();
	AddChild(toolBar);
#endif

#ifdef FEAT_GUI_TABLINE
	tabLine = new VimTabLine(BRect(0,0,0,0));
//	tabLine->PrepareButtonBitmaps();
	AddChild(tabLine);
#endif

	BRect remaining = frame;
	textArea = new VimTextAreaView(remaining);
	AddChild(textArea);
	// The textArea will be resized later when menus are added

	gui.vimForm = this;
}

#ifdef FEAT_TOOLBAR
	float
VimFormView::ToolbarHeight() const
{
	return toolBar ? toolBar->ToolbarHeight() : 0.;
}
#endif

#ifdef FEAT_GUI_TABLINE
	float
VimFormView::TablineHeight() const
{
	return (tabLine && IsShowingTabLine()) ? tabLine->TablineHeight() : 0.;
}
#endif

	void
VimFormView::AllAttached()
{
	/*
	 * Apparently signals are inherited by the created thread -
	 * disable the most annoying ones.
	 */
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);

	if (menuBar && textArea) {
		/*
		 * Resize the textArea to fill the space left over by the menu.
		 * This is somewhat futile since it will be done again once
		 * menus are added to the menu bar.
		 */
		BRect remaining = Bounds();

#ifdef FEAT_MENU
		remaining.top += MenuHeight();
		menuBar->ResizeTo(remaining.right, remaining.top);
		gui.menu_height = (int) MenuHeight();
#endif

#ifdef FEAT_TOOLBAR
		toolBar->MoveTo(remaining.left, remaining.top);
		toolBar->ResizeTo(remaining.right, ToolbarHeight());
		remaining.top += ToolbarHeight();
		gui.toolbar_height = ToolbarHeight();
#endif

#ifdef FEAT_GUI_TABLINE
		tabLine->MoveTo(remaining.left, remaining.top);
		tabLine->ResizeTo(remaining.right + 1, TablineHeight());
		remaining.top += TablineHeight();
		gui.tabline_height = TablineHeight();
#endif

		textArea->ResizeTo(remaining.Width(), remaining.Height());
		textArea->MoveTo(remaining.left, remaining.top);
	}


	Inherited::AllAttached();
}

	void
VimFormView::FrameResized(float new_width, float new_height)
{
	struct VimResizeMsg sm;
	int adjust_h, adjust_w;

	new_width += 1;	    //  adjust from width to number of pixels occupied
	new_height += 1;

	sm.width = (int) new_width;
	sm.height = (int) new_height;
	adjust_w = ((int)new_width - gui_get_base_width()) % gui.char_width;
	adjust_h = ((int)new_height - gui_get_base_height()) % gui.char_height;

	if (adjust_w > 0 || adjust_h > 0) {
		sm.width  -= adjust_w;
		sm.height -= adjust_h;
	}

	write_port(gui.vdcmp, VimMsg::Resize, &sm, sizeof(sm));
	//  calls gui_resize_shell(new_width, new_height);

	return;

	/*
	 * The area below the vertical scrollbar is erased to the colour
	 * set with SetViewColor() automatically, because we had set
	 * B_WILL_DRAW. Resizing the window tight around the vertical
	 * scroll bar also helps to avoid debris.
	 */
}

// ---------------- VimTextAreaView ----------------

VimTextAreaView::VimTextAreaView(BRect frame):
	BView(frame, "VimTextAreaView", B_FOLLOW_ALL_SIDES,
#ifdef FEAT_MBYTE_IME
		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_INPUT_METHOD_AWARE),
#else
		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
#endif
	mouseDragEventCount(0)
{
#ifdef FEAT_MBYTE_IME
	IMData.messenger = NULL;
	IMData.message = NULL;
#endif
	init(frame);
}

VimTextAreaView::~VimTextAreaView()
{
	gui.vimTextArea = NULL;
}

	void
VimTextAreaView::init(BRect frame)
{
	// set up global var for fast access
	gui.vimTextArea = this;

	/*
	 * Tell the app server not to erase the view: we will
	 * fill it in completely by ourselves.
	 * (Does this really work? Even if not, it won't harm either.)
	 */
	SetViewColor(B_TRANSPARENT_32_BIT);
#define PEN_WIDTH   1
	SetPenSize(PEN_WIDTH);
#define W_WIDTH(curwin)   0
}

	void
VimTextAreaView::Draw(BRect updateRect)
{
	/*
	 * XXX Other ports call here:
	 * out_flush();	     * make sure all output has been processed *
	 * but we can't do that, since it involves too much information
	 * that is owned by other threads...
	 */

	/*
	 *  No need to use gui.vimWindow->Lock(): we are locked already.
	 *  However, it would not hurt.
	 */
	rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
	SetLowColor(rgb);
	FillRect(updateRect, B_SOLID_LOW);
	gui_redraw((int) updateRect.left, (int) updateRect.top,
			(int) (updateRect.Width() + PEN_WIDTH), (int) (updateRect.Height() + PEN_WIDTH));

	// Clear the border areas if needed
	SetLowColor(rgb);

	if (updateRect.left < FILL_X(0))	//  left border
		FillRect(BRect(updateRect.left, updateRect.top,
					FILL_X(0)-PEN_WIDTH, updateRect.bottom), B_SOLID_LOW);
	if (updateRect.top < FILL_Y(0))	//  top border
		FillRect(BRect(updateRect.left, updateRect.top,
					updateRect.right, FILL_Y(0)-PEN_WIDTH), B_SOLID_LOW);
	if (updateRect.right >= FILL_X(Columns)) //  right border
		FillRect(BRect(FILL_X((int)Columns), updateRect.top,
					updateRect.right, updateRect.bottom), B_SOLID_LOW);
	if (updateRect.bottom >= FILL_Y(Rows))   //  bottom border
		FillRect(BRect(updateRect.left, FILL_Y((int)Rows),
					updateRect.right, updateRect.bottom), B_SOLID_LOW);

#ifdef FEAT_MBYTE_IME
	DrawIMString();
#endif
}

	void
VimTextAreaView::KeyDown(const char *bytes, int32 numBytes)
{
	struct VimKeyMsg km;
	char_u *dest = km.chars;

	bool canHaveVimModifiers = false;

	BMessage *msg = Window()->CurrentMessage();
	assert(msg);
	// msg->PrintToStream();

	/*
	 * Convert special keys to Vim codes.
	 * I think it is better to do it in the window thread
	 * so we use at least a little bit of the potential
	 * of our 2 CPUs. Besides, due to the fantastic mapping
	 * of special keys to UTF-8, we have quite some work to
	 * do...
	 * TODO: I'm not quite happy with detection of special
	 * keys. Perhaps I should use scan codes after all...
	 */
	if (numBytes > 1) {
		// This cannot be a special key
		if (numBytes > KEY_MSG_BUFSIZ)
			numBytes = KEY_MSG_BUFSIZ;	    //  should never happen... ???
		km.length = numBytes;
		memcpy((char *)dest, bytes, numBytes);
		km.csi_escape = true;
	} else {
		int32 scancode = 0;
		msg->FindInt32("key", &scancode);

		int32 beModifiers = 0;
		msg->FindInt32("modifiers", &beModifiers);

		char_u string[3];
		int len = 0;
		km.length = 0;

		/*
		 * For normal, printable ASCII characters, don't look them up
		 * to check if they might be a special key. They aren't.
		 */
		assert(B_BACKSPACE <= 0x20);
		assert(B_DELETE == 0x7F);
		if (((char_u)bytes[0] <= 0x20 || (char_u)bytes[0] == 0x7F) &&
				numBytes == 1) {
			/*
			 * Due to the great nature of Be's mapping of special keys,
			 * viz. into the range of the control characters,
			 * we can only be sure it is *really* a special key if
			 * if it is special without using ctrl. So, only if ctrl is
			 * used, we need to check it unmodified.
			 */
			if (beModifiers & B_CONTROL_KEY) {
				int index = keyMap->normal_map[scancode];
				int newNumBytes = keyMapChars[index];
				char_u *newBytes = (char_u *)&keyMapChars[index + 1];

				/*
				 * Check if still special without the control key.
				 * This is needed for BACKSPACE: that key does produce
				 * different values with modifiers (DEL).
				 * Otherwise we could simply have checked for equality.
				 */
				if (newNumBytes != 1 || (*newBytes > 0x20 &&
							*newBytes != 0x7F )) {
					goto notspecial;
				}
				bytes = (char *)newBytes;
			}
			canHaveVimModifiers = true;

			uint16 beoskey;
			int first, last;

			/*
			 * If numBytes == 0 that probably always indicates a special key.
			 * (does not happen yet)
			 */
			if (numBytes == 0 || bytes[0] == B_FUNCTION_KEY) {
				beoskey = F(scancode);
				first = FIRST_FUNCTION_KEY;
				last = NUM_SPECIAL_KEYS;
			} else if (*bytes == '\n' && scancode == 0x47) {
				// remap the (non-keypad) ENTER key from \n to \r.
				string[0] = '\r';
				len = 1;
				first = last = 0;
			} else {
				beoskey = K(bytes[0]);
				first = 0;
				last = FIRST_FUNCTION_KEY;
			}

			for (int i = first; i < last; i++) {
				if (special_keys[i].BeKeys == beoskey) {
					string[0] = CSI;
					string[1] = special_keys[i].vim_code0;
					string[2] = special_keys[i].vim_code1;
					len = 3;
				}
			}
		}
notspecial:
		if (len == 0) {
			string[0] = bytes[0];
			len = 1;
		}

		// Special keys (and a few others) may have modifiers
#if 0
		if (len == 3 ||
				bytes[0] == B_SPACE || bytes[0] == B_TAB ||
				bytes[0] == B_RETURN || bytes[0] == '\r' ||
				bytes[0] == B_ESCAPE)
#else
			if (canHaveVimModifiers)
#endif
			{
				int modifiers;
				modifiers = 0;
				if (beModifiers & B_SHIFT_KEY)
					modifiers |= MOD_MASK_SHIFT;
				if (beModifiers & B_CONTROL_KEY)
					modifiers |= MOD_MASK_CTRL;
				if (beModifiers & B_OPTION_KEY)
					modifiers |= MOD_MASK_ALT;

				/*
				 * For some keys a shift modifier is translated into another key
				 * code.  Do we need to handle the case where len != 1 and
				 * string[0] != CSI? (Not for BeOS, since len == 3 implies
				 * string[0] == CSI...)
				 */
				int key;
				if (string[0] == CSI && len == 3)
					key = TO_SPECIAL(string[1], string[2]);
				else
					key = string[0];
				key = simplify_key(key, &modifiers);
				if (IS_SPECIAL(key))
				{
					string[0] = CSI;
					string[1] = K_SECOND(key);
					string[2] = K_THIRD(key);
					len = 3;
				}
				else
				{
					string[0] = key;
					len = 1;
				}

				if (modifiers)
				{
					*dest++ = CSI;
					*dest++ = KS_MODIFIER;
					*dest++ = modifiers;
					km.length = 3;
				}
			}
		memcpy((char *)dest, string, len);
		km.length += len;
		km.csi_escape = false;
	}

	write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));

	/*
	 * blank out the pointer if necessary
	 */
	if (p_mh && !gui.pointer_hidden)
	{
		guiBlankMouse(true);
		gui.pointer_hidden = TRUE;
	}
}
void
VimTextAreaView::guiSendMouseEvent(
		int	    button,
		int	    x,
		int	    y,
		int	    repeated_click,
		int_u   modifiers)
{
	VimMouseMsg mm;

	mm.button = button;
	mm.x = x;
	mm.y = y;
	mm.repeated_click = repeated_click;
	mm.modifiers = modifiers;

	write_port(gui.vdcmp, VimMsg::Mouse, &mm, sizeof(mm));
	//  calls gui_send_mouse_event()

	/*
	 * if our pointer is currently hidden, then we should show it.
	 */
	if (gui.pointer_hidden)
	{
		guiBlankMouse(false);
		gui.pointer_hidden = FALSE;
	}
}

void
VimTextAreaView::guiMouseMoved(
		int	    x,
		int	    y)
{
	VimMouseMovedMsg mm;

	mm.x = x;
	mm.y = y;

	write_port(gui.vdcmp, VimMsg::MouseMoved, &mm, sizeof(mm));

	if (gui.pointer_hidden)
	{
		guiBlankMouse(false);
		gui.pointer_hidden = FALSE;
	}
}

	void
VimTextAreaView::guiBlankMouse(bool should_hide)
{
	if (should_hide) {
		// gui.vimApp->HideCursor();
		gui.vimApp->ObscureCursor();
		/*
		 * ObscureCursor() would even be easier, but then
		 * Vim's idea of mouse visibility does not necessarily
		 * correspond to reality.
		 */
	} else {
		// gui.vimApp->ShowCursor();
	}
}

	int_u
VimTextAreaView::mouseModifiersToVim(int32 beModifiers)
{
	int_u vim_modifiers = 0x0;

	if (beModifiers & B_SHIFT_KEY)
		vim_modifiers |= MOUSE_SHIFT;
	if (beModifiers & B_CONTROL_KEY)
		vim_modifiers |= MOUSE_CTRL;
	if (beModifiers & B_OPTION_KEY)	    // Alt or Meta key
		vim_modifiers |= MOUSE_ALT;

	return vim_modifiers;
}

	void
VimTextAreaView::MouseDown(BPoint point)
{
	BMessage *m = Window()->CurrentMessage();
	assert(m);

	int32 buttons = 0;
	m->FindInt32("buttons", &buttons);

	int vimButton;

	if (buttons & B_PRIMARY_MOUSE_BUTTON)
		vimButton = MOUSE_LEFT;
	else if (buttons & B_SECONDARY_MOUSE_BUTTON)
		vimButton = MOUSE_RIGHT;
	else if (buttons & B_TERTIARY_MOUSE_BUTTON)
		vimButton = MOUSE_MIDDLE;
	else
		return;			// Unknown button

	vimMouseButton = 1;		// don't care which one

	// Handle multiple clicks
	int32 clicks = 0;
	m->FindInt32("clicks", &clicks);

	int32 modifiers = 0;
	m->FindInt32("modifiers", &modifiers);

	vimMouseModifiers = mouseModifiersToVim(modifiers);

	guiSendMouseEvent(vimButton, point.x, point.y,
			clicks > 1 /* = repeated_click*/, vimMouseModifiers);
}

	void
VimTextAreaView::MouseUp(BPoint point)
{
	vimMouseButton = 0;

	BMessage *m = Window()->CurrentMessage();
	assert(m);
	// m->PrintToStream();

	int32 modifiers = 0;
	m->FindInt32("modifiers", &modifiers);

	vimMouseModifiers = mouseModifiersToVim(modifiers);

	guiSendMouseEvent(MOUSE_RELEASE, point.x, point.y,
			0 /* = repeated_click*/, vimMouseModifiers);

	Inherited::MouseUp(point);
}

	void
VimTextAreaView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
{
	/*
	 * if our pointer is currently hidden, then we should show it.
	 */
	if (gui.pointer_hidden)
	{
		guiBlankMouse(false);
		gui.pointer_hidden = FALSE;
	}

	if (!vimMouseButton) {    // could also check m->"buttons"
		guiMouseMoved(point.x, point.y);
		return;
	}

	atomic_add(&mouseDragEventCount, 1);

	// Don't care much about "transit"
	guiSendMouseEvent(MOUSE_DRAG, point.x, point.y, 0, vimMouseModifiers);
}

	void
VimTextAreaView::MessageReceived(BMessage *m)
{
	switch (m->what) {
		case 'menu':
			{
				VimMenuMsg mm;
				mm.guiMenu = NULL;	// in case no pointer in msg
				m->FindPointer("VimMenu", (void **)&mm.guiMenu);
				write_port(gui.vdcmp, VimMsg::Menu, &mm, sizeof(mm));
			}
			break;
		case B_MOUSE_WHEEL_CHANGED:
			{
				VimScrollBar* scb = curwin->w_scrollbars[1].id;
				float small=0, big=0, dy=0;
				m->FindFloat("be:wheel_delta_y", &dy);
				scb->GetSteps(&small, &big);
				scb->SetValue(scb->Value()+small*dy*3);
				scb->ValueChanged(scb->Value());
#if 0
				scb = curwin->w_scrollbars[0].id;
				scb->GetSteps(&small, &big);
				scb->SetValue(scb->Value()+small*dy);
				scb->ValueChanged(scb->Value());
#endif
			}
			break;
#ifdef FEAT_MBYTE_IME
		case B_INPUT_METHOD_EVENT:
			{
				int32 opcode;
				m->FindInt32("be:opcode", &opcode);
				switch(opcode)
				{
					case B_INPUT_METHOD_STARTED:
						if(!IMData.messenger) delete IMData.messenger;
						IMData.messenger = new BMessenger();
						m->FindMessenger("be:reply_to", IMData.messenger);
						break;
					case B_INPUT_METHOD_CHANGED:
						{
							BString str;
							bool confirmed;
							if(IMData.message) *(IMData.message) = *m;
							else               IMData.message = new BMessage(*m);
							DrawIMString();
							m->FindBool("be:confirmed", &confirmed);
							if (confirmed)
							{
								m->FindString("be:string", &str);
								char_u *chars = (char_u*)str.String();
								struct VimKeyMsg km;
								km.csi_escape = true;
								int clen;
								int i = 0;
								while (i < str.Length())
								{
									clen = utf_ptr2len(chars+i);
									memcpy(km.chars, chars+i, clen);
									km.length = clen;
									write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
									i += clen;
								}
							}
						}
						break;
					case B_INPUT_METHOD_LOCATION_REQUEST:
						{
							BMessage msg(B_INPUT_METHOD_EVENT);
							msg.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST);
							msg.AddPoint("be:location_reply", IMData.location);
							msg.AddFloat("be:height_reply", FILL_Y(1));
							IMData.messenger->SendMessage(&msg);
						}
						break;
					case B_INPUT_METHOD_STOPPED:
						delete IMData.messenger;
						delete IMData.message;
						IMData.messenger = NULL;
						IMData.message = NULL;
						break;
				}
			}
			// TODO: sz: break here???
#endif
		default:
			if (m->WasDropped()) {
				BWindow *w = Window();
				w->DetachCurrentMessage();
				w->Minimize(false);
				VimApp::SendRefs(m, (modifiers() & B_SHIFT_KEY) != 0);
			} else {
				Inherited::MessageReceived(m);
			}
			break;
	}
}

	int
VimTextAreaView::mchInitFont(char_u *name)
{
	VimFont *newFont = (VimFont *)gui_mch_get_font(name, 1);
	if(newFont != NOFONT) {
		gui.norm_font = (GuiFont)newFont;
		gui_mch_set_font((GuiFont)newFont);
		if (name && STRCMP(name, "*") != 0)
			hl_set_font_name(name);

		SetDrawingMode(B_OP_COPY);

		/*
		 * Try to load other fonts for bold, italic, and bold-italic.
		 * We should also try to work out what font to use for these when they are
		 * not specified by X resources, but we don't yet.
		 */
		return OK;
	}
	return FAIL;
}

	void
VimTextAreaView::mchDrawString(int row, int col, char_u *s, int len, int flags)
{
	/*
	 * First we must erase the area, because DrawString won't do
	 * that for us. XXX Most of the time this is a waste of effort
	 * since the bachground has been erased already... DRAW_TRANSP
	 * should be set when appropriate!!!
	 * (Rectangles include the bottom and right edge)
	 */
	if (!(flags & DRAW_TRANSP)) {
		int cells;
		cells = 0;
		for(int i=0; i<len; i++) {
			int cn = utf_ptr2cells((char_u *)(s+i));
			if(cn<4) cells += cn;
		}

		BRect r(FILL_X(col), FILL_Y(row),
				FILL_X(col + cells) - PEN_WIDTH, FILL_Y(row + 1) - PEN_WIDTH);
		FillRect(r, B_SOLID_LOW);
	}

	BFont font;
	this->GetFont(&font);
	if(!font.IsFixed())
	{
		char* p = (char*)s;
		int32 clen, lastpos = 0;
		BPoint where;
		int cells;
		while((p - (char*)s) < len) {
			clen = utf_ptr2len((u_char*)p);
			where.Set(TEXT_X(col+lastpos), TEXT_Y(row));
			DrawString(p, clen, where);
			if (flags & DRAW_BOLD) {
				where.x += 1.0;
				SetDrawingMode(B_OP_BLEND);
				DrawString(p, clen, where);
				SetDrawingMode(B_OP_COPY);
			}
			cells = utf_ptr2cells((char_u *)p);
			if(cells<4) lastpos += cells;
			else        lastpos++;
			p += clen;
		}
	}
	else
	{
		BPoint where(TEXT_X(col), TEXT_Y(row));
		DrawString((char*)s, len, where);
		if (flags & DRAW_BOLD) {
			where.x += 1.0;
			SetDrawingMode(B_OP_BLEND);
			DrawString((char*)s, len, where);
			SetDrawingMode(B_OP_COPY);
		}
	}

	if (flags & DRAW_UNDERL) {
		int cells;
		cells = 0;
		for(int i=0; i<len; i++) {
			int cn = utf_ptr2cells((char_u *)(s+i));
			if(cn<4) cells += cn;
		}

		BPoint start(FILL_X(col), FILL_Y(row + 1) - PEN_WIDTH);
		BPoint end(FILL_X(col + cells) - PEN_WIDTH, start.y);

		StrokeLine(start, end);
	}
}

void
VimTextAreaView::mchClearBlock(
		int		row1,
		int		col1,
		int		row2,
		int		col2)
{
	BRect r(FILL_X(col1), FILL_Y(row1),
			FILL_X(col2 + 1) - PEN_WIDTH, FILL_Y(row2 + 1) - PEN_WIDTH);
	gui_mch_set_bg_color(gui.back_pixel);
	FillRect(r, B_SOLID_LOW);
}

	void
VimTextAreaView::mchClearAll()
{
	gui_mch_set_bg_color(gui.back_pixel);
	FillRect(Bounds(), B_SOLID_LOW);
}

/*
 * mchDeleteLines() Lock()s the window by itself.
 */
	void
VimTextAreaView::mchDeleteLines(int row, int num_lines)
{
	BRect source, dest;
	source.left = FILL_X(gui.scroll_region_left);
	source.top = FILL_Y(row + num_lines);
	source.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
	source.bottom = FILL_Y(gui.scroll_region_bot + 1) - PEN_WIDTH;

	dest.left = FILL_X(gui.scroll_region_left);
	dest.top = FILL_Y(row);
	dest.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
	dest.bottom = FILL_Y(gui.scroll_region_bot - num_lines + 1) - PEN_WIDTH;

	if (gui.vimWindow->Lock()) {
		// Clear one column more for when bold has spilled over
		CopyBits(source, dest);
		gui_clear_block(gui.scroll_region_bot - num_lines + 1,
				gui.scroll_region_left,
				gui.scroll_region_bot, gui.scroll_region_right);


		gui.vimWindow->Unlock();
		/*
		 * The Draw() callback will be called now if some of the source
		 * bits were not in the visible region.
		 */
	}
	// gui_x11_check_copy_area();
	//  }
}

/*
 * mchInsertLines() Lock()s the window by itself.
 */
	void
VimTextAreaView::mchInsertLines(int row, int num_lines)
{
	BRect source, dest;

	// XXX Attempt at a hack:
	gui.vimWindow->UpdateIfNeeded();
	source.left = FILL_X(gui.scroll_region_left);
	source.top = FILL_Y(row);
	source.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
	source.bottom = FILL_Y(gui.scroll_region_bot - num_lines + 1) - PEN_WIDTH;

	dest.left = FILL_X(gui.scroll_region_left);
	dest.top = FILL_Y(row + num_lines);
	dest.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
	dest.bottom = FILL_Y(gui.scroll_region_bot + 1) - PEN_WIDTH;

	if (gui.vimWindow->Lock()) {
		// Clear one column more for when bold has spilled over
		CopyBits(source, dest);
		gui_clear_block(row, gui.scroll_region_left,
				row + num_lines - 1, gui.scroll_region_right);

		gui.vimWindow->Unlock();
		/*
		 * The Draw() callback will be called now if some of the source
		 * bits were not in the visible region.
		 * However, if we scroll too fast it can't keep up and the
		 * update region gets messed up. This seems to be because copying
		 * un-Draw()n bits does not generate Draw() calls for the copy...
		 * I moved the hack to before the CopyBits() to reduce the
		 * amount of additional waiting needed.
		 */

		// gui_x11_check_copy_area();

	}
}

#ifdef FEAT_MBYTE_IME
/*
 * DrawIMString draws string with IMData.message.
 */
void VimTextAreaView::DrawIMString(void)
{
	static const rgb_color r_highlight = {255, 152, 152, 255},
				 b_highlight = {152, 203, 255, 255};
	BString str;
	const char* s;
	int len;
	BMessage* msg = IMData.message;
	if (!msg)
		return;
	gui_redraw_block(IMData.row, 0,
			IMData.row + IMData.count, W_WIDTH(curwin), GUI_MON_NOCLEAR);
	bool confirmed = false;
	msg->FindBool("be:confirmed", &confirmed);
	if (confirmed)
		return;
	rgb_color hcolor = HighColor(), lcolor = LowColor();
	msg->FindString("be:string", &str);
	s = str.String();
	len = str.Length();
	SetHighColor(0, 0, 0);
	IMData.row = gui.row;
	IMData.col = gui.col;
	int32 sel_start = 0, sel_end = 0;
	msg->FindInt32("be:selection", 0, &sel_start);
	msg->FindInt32("be:selection", 1, &sel_end);
	int clen, cn;
	BPoint pos(IMData.col, 0);
	BRect r;
	BPoint where;
	IMData.location = ConvertToScreen(
			BPoint(FILL_X(pos.x), FILL_Y(IMData.row + pos.y)));
	for (int i=0; i<len; i+=clen)
	{
		cn = utf_ptr2cells((char_u *)(s+i));
		clen = utf_ptr2len((char_u *)(s+i));
		if (pos.x + cn > W_WIDTH(curwin))
		{
			pos.y++;
			pos.x = 0;
		}
		if (sel_start<=i && i<sel_end)
		{
			SetLowColor(r_highlight);
			IMData.location = ConvertToScreen(
					BPoint(FILL_X(pos.x), FILL_Y(IMData.row + pos.y)));
		}
		else
		{
			SetLowColor(b_highlight);
		}
		r.Set(FILL_X(pos.x), FILL_Y(IMData.row + pos.y),
				FILL_X(pos.x + cn) - PEN_WIDTH,
				FILL_Y(IMData.row + pos.y + 1) - PEN_WIDTH);
		FillRect(r, B_SOLID_LOW);
		where.Set(TEXT_X(pos.x), TEXT_Y(IMData.row + pos.y));
		DrawString((s+i), clen, where);
		pos.x += cn;
	}
	IMData.count = (int)pos.y;

	SetHighColor(hcolor);
	SetLowColor(lcolor);
}
#endif
// ---------------- VimScrollBar ----------------

/*
 * BUG: XXX
 * It seems that BScrollBar determine their direction not from
 * "posture" but from if they are "tall" or "wide" in shape...
 *
 * Also, place them out of sight, because Vim enables them before
 * they are positioned.
 */
VimScrollBar::VimScrollBar(scrollbar_T *g, orientation posture):
	BScrollBar(posture == B_HORIZONTAL ?  BRect(-100,-100,-10,-90) :
			BRect(-100,-100,-90,-10),
			"vim scrollbar", (BView *)NULL,
			0.0, 10.0, posture),
	ignoreValue(-1),
	scrollEventCount(0)
{
	gsb = g;
	SetResizingMode(B_FOLLOW_NONE);
}

VimScrollBar::~VimScrollBar()
{
}

	void
VimScrollBar::ValueChanged(float newValue)
{
	if (ignoreValue >= 0.0 && newValue == ignoreValue) {
		ignoreValue = -1;
		return;
	}
	ignoreValue = -1;
	/*
	 * We want to throttle the amount of scroll messages generated.
	 * Normally I presume you won't get a new message before we've
	 * handled the previous one, but because we're passing them on this
	 * happens very quickly. So instead we keep a counter of how many
	 * scroll events there are (or will be) in the VDCMP, and the
	 * throttling happens at the receiving end.
	 */
	atomic_add(&scrollEventCount, 1);

	struct VimScrollBarMsg sm;

	sm.sb = this;
	sm.value = (long) newValue;
	sm.stillDragging = TRUE;

	write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));

	//  calls gui_drag_scrollbar(sb, newValue, TRUE);
}

/*
 * When the mouse goes up, report that scrolling has stopped.
 * MouseUp() is NOT called when the mouse-up occurs outside
 * the window, even though the thumb does move while the mouse
 * is outside... This has some funny effects... XXX
 * So we do special processing when the window de/activates.
 */
	void
VimScrollBar::MouseUp(BPoint where)
{
	// BMessage *m = Window()->CurrentMessage();
	// m->PrintToStream();

	atomic_add(&scrollEventCount, 1);

	struct VimScrollBarMsg sm;

	sm.sb = this;
	sm.value = (long) Value();
	sm.stillDragging = FALSE;

	write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));

	//  calls gui_drag_scrollbar(sb, newValue, FALSE);

	Inherited::MouseUp(where);
}

	void
VimScrollBar::SetValue(float newValue)
{
	if (newValue == Value())
		return;

	ignoreValue = newValue;
	Inherited::SetValue(newValue);
}

// ---------------- VimFont ----------------

VimFont::VimFont(): BFont()
{
	init();
}

VimFont::VimFont(const VimFont *rhs): BFont(rhs)
{
	init();
}

VimFont::VimFont(const BFont *rhs): BFont(rhs)
{
	init();
}

VimFont::VimFont(const VimFont &rhs): BFont(rhs)
{
	init();
}

VimFont::~VimFont()
{
}

	void
VimFont::init()
{
	next = NULL;
	refcount = 1;
	name = NULL;
}

// ---------------- VimDialog ----------------

#if defined(FEAT_GUI_DIALOG)

const unsigned int	kVimDialogButtonMsg = 'VMDB';
const unsigned int	kVimDialogIconStripeWidth = 30;
const unsigned int	kVimDialogButtonsSpacingX = 9;
const unsigned int	kVimDialogButtonsSpacingY = 4;
const unsigned int	kVimDialogSpacingX = 6;
const unsigned int	kVimDialogSpacingY = 10;
const unsigned int	kVimDialogMinimalWidth  = 310;
const unsigned int	kVimDialogMinimalHeight = 75;
const BRect			kDefaultRect =
BRect(0, 0, kVimDialogMinimalWidth, kVimDialogMinimalHeight);

VimDialog::VimDialog(int type, const char *title, const char *message,
		const char *buttons, int dfltbutton, const char *textfield, int ex_cmd)
: BWindow(kDefaultRect, title, B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
		B_NOT_CLOSABLE | B_NOT_RESIZABLE |
		B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS)
	, fDialogSem(-1)
	, fDialogValue(dfltbutton)
	, fMessageView(NULL)
	, fInputControl(NULL)
	, fInputValue(textfield)
{
	//  master view
	VimDialog::View* view = new VimDialog::View(Bounds());
	if(view == NULL)
		return;

	if(title == NULL)
		SetTitle("Vim " VIM_VERSION_MEDIUM);

	AddChild(view);

	//  icon
	view->InitIcon(type);

	//  buttons
	int32 which = 1;
	float maxButtonWidth  = 0;
	float maxButtonHeight = 0;
	float buttonsWidth    = 0;
	float buttonsHeight   = 0;
	BString strButtons(buttons);
	strButtons.RemoveAll("&");
	do {
		int32 end = strButtons.FindFirst('\n');
		if(end != B_ERROR)
			strButtons.SetByteAt(end, '\0');

		BButton *button = _CreateButton(which++, strButtons.String());
		view->AddChild(button);
		fButtonsList.AddItem(button);

		maxButtonWidth  = max_c(maxButtonWidth,  button->Bounds().Width());
		maxButtonHeight = max_c(maxButtonHeight, button->Bounds().Height());
		buttonsWidth   += button->Bounds().Width();
		buttonsHeight  += button->Bounds().Height();

		if(end == B_ERROR)
			break;

		strButtons.Remove(0, end + 1);
	} while(true);

	int32 buttonsCount = fButtonsList.CountItems();
	buttonsWidth      += kVimDialogButtonsSpacingX * (buttonsCount - 1);
	buttonsHeight     += kVimDialogButtonsSpacingY * (buttonsCount - 1);
	float dialogWidth  = buttonsWidth + kVimDialogIconStripeWidth +
		kVimDialogSpacingX * 2;
	float dialogHeight = maxButtonHeight + kVimDialogSpacingY * 3;

	// Check 'v' flag in 'guioptions': vertical button placement.
	bool vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL) ||
		dialogWidth >= gui.vimWindow->Bounds().Width();
	if(vertical) {
		dialogWidth  -= buttonsWidth;
		dialogWidth  += maxButtonWidth;
		dialogHeight -= maxButtonHeight;
		dialogHeight += buttonsHeight;
	}

	dialogWidth  = max_c(dialogWidth,  kVimDialogMinimalWidth);

	//  message view
	BRect rect(0, 0, dialogWidth, 0);
	rect.left  += kVimDialogIconStripeWidth + 16 + kVimDialogSpacingX;
	rect.top   += kVimDialogSpacingY;
	rect.right -= kVimDialogSpacingX;
	rect.bottom = rect.top;
	fMessageView = new BTextView(rect, "_tv_", rect.OffsetByCopy(B_ORIGIN),
			B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);

	fMessageView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
	fMessageView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
	fMessageView->SetText(message);
	fMessageView->MakeEditable(false);
	fMessageView->MakeSelectable(false);
	fMessageView->SetWordWrap(true);
	AddChild(fMessageView);

	float messageHeight = fMessageView->TextHeight(0, fMessageView->CountLines());
	fMessageView->ResizeBy(0, messageHeight);
	fMessageView->SetTextRect(BRect(0, 0, rect.Width(), messageHeight));

	dialogHeight += messageHeight;

	//  input view
	if(fInputValue != NULL) {
		rect.top     =
			rect.bottom += messageHeight + kVimDialogSpacingY;
		fInputControl = new BTextControl(rect, "_iv_", NULL, fInputValue, NULL,
				B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE |  B_PULSE_NEEDED);
		fInputControl->TextView()->SetText(fInputValue);
		fInputControl->TextView()->SetWordWrap(false);
		AddChild(fInputControl);

		float width = 0.f, height = 0.f;
		fInputControl->GetPreferredSize(&width, &height);
		fInputControl->MakeFocus(true);

		dialogHeight += height + kVimDialogSpacingY * 1.5;
	}

	dialogHeight = max_c(dialogHeight, kVimDialogMinimalHeight);

	ResizeTo(dialogWidth, dialogHeight);
	MoveTo((gui.vimWindow->Bounds().Width() - dialogWidth) / 2,
			(gui.vimWindow->Bounds().Height() - dialogHeight) / 2);

	//  adjust layout of buttons
	float buttonWidth = max_c(maxButtonWidth, rect.Width() * 0.66);
	BPoint origin(dialogWidth, dialogHeight);
	origin.x -= kVimDialogSpacingX + (vertical ? buttonWidth : buttonsWidth);
	origin.y -= kVimDialogSpacingY + (vertical ? buttonsHeight  : maxButtonHeight);

	for(int32 i = 0 ; i < buttonsCount; i++) {
		BButton *button = (BButton*)fButtonsList.ItemAt(i);
		button->MoveTo(origin);
		if(vertical) {
			origin.y += button->Frame().Height() + kVimDialogButtonsSpacingY;
			button->ResizeTo(buttonWidth, button->Frame().Height());
		} else
			origin.x += button->Frame().Width() + kVimDialogButtonsSpacingX;

		if(dfltbutton == i + 1) {
			button->MakeDefault(true);
			button->MakeFocus(fInputControl == NULL);
		}
	}
}

VimDialog::~VimDialog()
{
	if(fDialogSem > B_OK)
		delete_sem(fDialogSem);
}

	int
VimDialog::Go()
{
	fDialogSem = create_sem(0, "VimDialogSem");
	if(fDialogSem < B_OK) {
		Quit();
		return fDialogValue;
	}

	Show();

	while(acquire_sem(fDialogSem) == B_INTERRUPTED);

	int retValue = fDialogValue;
	if(fInputValue != NULL)
		vim_strncpy((char_u*)fInputValue, (char_u*)fInputControl->Text(), IOSIZE - 1);

	if(Lock())
		Quit();

	return retValue;
}

void VimDialog::MessageReceived(BMessage *msg)
{
	int32 which = 0;
	if(msg->what != kVimDialogButtonMsg ||
			msg->FindInt32("which", &which) != B_OK)
		return BWindow::MessageReceived(msg);

	fDialogValue = which;
	delete_sem(fDialogSem);
	fDialogSem = -1;
}

BButton* VimDialog::_CreateButton(int32 which, const char* label)
{
	BMessage *message = new BMessage(kVimDialogButtonMsg);
	message->AddInt32("which", which);

	BRect rect(0, 0, 0, 0);
	BString name;
	name << "_b" << which << "_";

	BButton* button = new BButton(rect, name.String(), label, message,
			B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);

	float width = 0.f, height = 0.f;
	button->GetPreferredSize(&width, &height);
	button->ResizeTo(width, height);

	return button;
}

VimDialog::View::View(BRect frame)
	:	BView(frame, "VimDialogView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
	fIconBitmap(NULL)
{
	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}

VimDialog::View::~View()
{
	delete fIconBitmap;
}

void VimDialog::View::Draw(BRect updateRect)
{
	BRect stripeRect = Bounds();
	stripeRect.right = kVimDialogIconStripeWidth;
	SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
	FillRect(stripeRect);

	if(fIconBitmap == NULL)
		return;

	SetDrawingMode(B_OP_ALPHA);
	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
	DrawBitmapAsync(fIconBitmap, BPoint(18, 6));
}

void VimDialog::View::InitIcon(int32 type)
{
	if(type == VIM_GENERIC)
		return;

	BPath path;
	status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
	if(status != B_OK) {
		fprintf(stderr, "Cannot retrieve app info:%s\n", strerror(status));
		return;
	}

	path.Append("app_server");

	BFile file(path.Path(), O_RDONLY);
	if(file.InitCheck() != B_OK) {
		fprintf(stderr, "App file assignment failed:%s\n",
				strerror(file.InitCheck()));
		return;
	}

	BResources resources(&file);
	if(resources.InitCheck() != B_OK) {
		fprintf(stderr, "App server resources assignment failed:%s\n",
				strerror(resources.InitCheck()));
		return;
	}

	const char *name = "";
	switch(type) {
		case VIM_ERROR:		name = "stop"; break;
		case VIM_WARNING:	name = "warn"; break;
		case VIM_INFO:		name = "info"; break;
		case VIM_QUESTION:	name = "idea"; break;
		default: return;
	}

	int32 iconSize = 32;
	fIconBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
	if(fIconBitmap == NULL || fIconBitmap->InitCheck() != B_OK) {
		fprintf(stderr, "Icon bitmap allocation failed:%s\n",
				(fIconBitmap == NULL) ? "null" : strerror(fIconBitmap->InitCheck()));
		return;
	}

	size_t size = 0;
	const uint8* iconData = NULL;
	//  try vector icon first?
	iconData = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE, name, &size);
	if(iconData != NULL && BIconUtils::GetVectorIcon(iconData, size, fIconBitmap) == B_OK)
		return;

	//  try bitmap icon now
	iconData = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE, name, &size);
	if(iconData == NULL) {
		fprintf(stderr, "Bitmap icon resource not found\n");
		delete fIconBitmap;
		fIconBitmap = NULL;
		return;
	}

	if(fIconBitmap->ColorSpace() != B_CMAP8)
		BIconUtils::ConvertFromCMAP8(iconData, iconSize, iconSize, iconSize, fIconBitmap);
}

const unsigned int	kVimDialogOKButtonMsg = 'FDOK';
const unsigned int	kVimDialogCancelButtonMsg = 'FDCN';
const unsigned int	kVimDialogSizeInputMsg = 'SICH';
const unsigned int	kVimDialogFamilySelectMsg = 'MSFM';
const unsigned int	kVimDialogStyleSelectMsg = 'MSST';
const unsigned int	kVimDialogSizeSelectMsg = 'MSSZ';

VimSelectFontDialog::VimSelectFontDialog(font_family* family, font_style* style, float* size)
: BWindow(kDefaultRect, "Font Selection", B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
		B_NOT_CLOSABLE | B_NOT_RESIZABLE |
		B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS)
	, fStatus(B_NO_INIT)
	, fDialogSem(-1)
	, fDialogValue(false)
	, fFamily(family)
	, fStyle(style)
	, fSize(size)
	, fFontSize(*size)
	, fPreview(0)
	, fFamiliesList(0)
	, fStylesList(0)
	, fSizesList(0)
	, fSizesInput(0)
{
	strncpy(fFontFamily, *family, B_FONT_FAMILY_LENGTH);
	strncpy(fFontStyle, *style, B_FONT_STYLE_LENGTH);

	//  "client" area view
	BBox *clientBox = new BBox(Bounds(), B_EMPTY_STRING, B_FOLLOW_ALL_SIDES,
					B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP | B_PULSE_NEEDED,
					B_PLAIN_BORDER);
	AddChild(clientBox);

	//  client view
	BRect RC = clientBox->Bounds();
	RC.InsetBy(kVimDialogSpacingX, kVimDialogSpacingY);
	BRect rc(RC.LeftTop(), RC.LeftTop());

	//  at first create all controls
	fPreview = new BStringView(rc, "preview", "DejaVu Sans Mono");
	clientBox->AddChild(fPreview);

	BBox* boxDivider = new BBox(rc, B_EMPTY_STRING,
			B_FOLLOW_NONE, B_WILL_DRAW, B_FANCY_BORDER);
	clientBox->AddChild(boxDivider);

	BStringView *labelFamily = new BStringView(rc, "labelFamily", "Family:");
	clientBox->AddChild(labelFamily);
	labelFamily->ResizeToPreferred();

	BStringView *labelStyle = new BStringView(rc, "labelStyle", "Style:");
	clientBox->AddChild(labelStyle);
	labelStyle->ResizeToPreferred();

	BStringView *labelSize = new BStringView(rc, "labelSize", "Size:");
	clientBox->AddChild(labelSize);
	labelSize->ResizeToPreferred();

	fFamiliesList = new BListView(rc, "listFamily",
			B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
	BScrollView *scrollFamilies = new BScrollView("scrollFamily",
			fFamiliesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
	clientBox->AddChild(scrollFamilies);

	fStylesList= new BListView(rc, "listStyles",
			B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
	BScrollView *scrollStyles = new BScrollView("scrollStyle",
			fStylesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
	clientBox->AddChild(scrollStyles);

	fSizesInput = new BTextControl(rc, "inputSize", NULL, "???",
			new BMessage(kVimDialogSizeInputMsg));
	clientBox->AddChild(fSizesInput);
	fSizesInput->ResizeToPreferred();

	fSizesList = new BListView(rc, "listSizes",
			B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
	BScrollView *scrollSizes = new BScrollView("scrollSize",
			fSizesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
	clientBox->AddChild(scrollSizes);

	BButton *buttonOK = new BButton(rc, "buttonOK", "OK",
						new BMessage(kVimDialogOKButtonMsg));
	clientBox->AddChild(buttonOK);
	buttonOK->ResizeToPreferred();

	BButton *buttonCancel = new BButton(rc, "buttonCancel", "Cancel",
						new BMessage(kVimDialogCancelButtonMsg));
	clientBox->AddChild(buttonCancel);
	buttonCancel->ResizeToPreferred();

	//  layout controls
	float lineHeight = labelFamily->Bounds().Height();
	float previewHeight = lineHeight * 3;
	float offsetYLabels = previewHeight + kVimDialogSpacingY;
	float offsetYLists = offsetYLabels + lineHeight + kVimDialogSpacingY / 2;
	float offsetYSizes = offsetYLists + fSizesInput->Bounds().Height() + kVimDialogSpacingY / 2;
	float listsHeight = lineHeight * 9;
	float offsetYButtons = offsetYLists + listsHeight +  kVimDialogSpacingY;
	float maxControlsHeight = offsetYButtons + buttonOK->Bounds().Height();
	float familiesWidth = labelFamily->Bounds().Width() * 5;
	float offsetXStyles = familiesWidth + kVimDialogSpacingX;
	float stylesWidth = labelStyle->Bounds().Width() * 4;
	float offsetXSizes = offsetXStyles + stylesWidth + kVimDialogSpacingX;
	float sizesWidth = labelSize->Bounds().Width() * 2;
	float maxControlsWidth = offsetXSizes + sizesWidth;

	ResizeTo(maxControlsWidth + kVimDialogSpacingX * 2,
		maxControlsHeight + kVimDialogSpacingY * 2);

	BRect rcVim = gui.vimWindow->Frame();
	MoveTo(rcVim.left + (rcVim.Width() - Frame().Width()) / 2,
			rcVim.top + (rcVim.Height() - Frame().Height()) / 2);

	fPreview->ResizeTo(maxControlsWidth, previewHeight);
	fPreview->SetAlignment(B_ALIGN_CENTER);

	boxDivider->MoveBy(0.f, previewHeight + kVimDialogSpacingY / 2);
	boxDivider->ResizeTo(maxControlsWidth, 1.f);

	labelFamily->MoveBy(0.f, offsetYLabels);
	labelStyle->MoveBy(offsetXStyles, offsetYLabels);
	labelSize->MoveBy(offsetXSizes, offsetYLabels);

	//  text control alignment issues
	float insetX = fSizesInput->TextView()->Bounds().Width() - fSizesInput->Bounds().Width();
	float insetY = fSizesInput->TextView()->Bounds().Width() - fSizesInput->Bounds().Width();

	scrollFamilies->MoveBy(0.f, offsetYLists);
	scrollStyles->MoveBy(offsetXStyles, offsetYLists);
	fSizesInput->MoveBy(offsetXSizes + insetX / 2, offsetYLists + insetY / 2);
	scrollSizes->MoveBy(offsetXSizes, offsetYSizes);

	fSizesInput->SetAlignment(B_ALIGN_CENTER, B_ALIGN_CENTER);

	scrollFamilies->ResizeTo(familiesWidth, listsHeight);
	scrollStyles->ResizeTo(stylesWidth, listsHeight);
	fSizesInput->ResizeTo(sizesWidth, fSizesInput->Bounds().Height());
	scrollSizes->ResizeTo(sizesWidth,
			listsHeight - (offsetYSizes - offsetYLists));

	buttonOK->MoveBy(maxControlsWidth - buttonOK->Bounds().Width(), offsetYButtons);
	buttonCancel->MoveBy(maxControlsWidth - buttonOK->Bounds().Width()
			- buttonCancel->Bounds().Width() - kVimDialogSpacingX, offsetYButtons);

	//  fill lists
	int selIndex = -1;
	int count = count_font_families();
	for (int i = 0; i < count; i++) {
		font_family family;
		if (get_font_family(i, &family ) == B_OK) {
			fFamiliesList->AddItem(new BStringItem((const char*)family));
			if (strncmp(family, fFontFamily, B_FONT_FAMILY_LENGTH) == 0)
				selIndex = i;
		}
	}

	if (selIndex >= 0) {
		fFamiliesList->Select(selIndex);
		fFamiliesList->ScrollToSelection();
	}

	_UpdateFontStyles();

	selIndex = -1;
	for (int size = 8, index = 0; size <= 18; size++, index++) {
		BString str;
		str << size;
		fSizesList->AddItem(new BStringItem(str));
		if (size == fFontSize)
			selIndex = index;

	}

	if (selIndex >= 0) {
		fSizesList->Select(selIndex);
		fSizesList->ScrollToSelection();
	}

	fFamiliesList->SetSelectionMessage(new BMessage(kVimDialogFamilySelectMsg));
	fStylesList->SetSelectionMessage(new BMessage(kVimDialogStyleSelectMsg));
	fSizesList->SetSelectionMessage(new BMessage(kVimDialogSizeSelectMsg));
	fSizesInput->SetModificationMessage(new BMessage(kVimDialogSizeInputMsg));

	_UpdateSizeInputPreview();
	_UpdateFontPreview();

	fStatus = B_OK;
}

VimSelectFontDialog::~VimSelectFontDialog()
{
	_CleanList(fFamiliesList);
	_CleanList(fStylesList);
	_CleanList(fSizesList);

	if (fDialogSem > B_OK)
		delete_sem(fDialogSem);
}

	void
VimSelectFontDialog::_CleanList(BListView* list)
{
	while(0 < list->CountItems())
		delete (dynamic_cast<BStringItem*>(list->RemoveItem((int32)0)));
}

	bool
VimSelectFontDialog::Go()
{
	if (fStatus != B_OK) {
		Quit();
		return NOFONT;
	}

	fDialogSem = create_sem(0, "VimFontSelectDialogSem");
	if(fDialogSem < B_OK) {
		Quit();
		return fDialogValue;
	}

	Show();

	while(acquire_sem(fDialogSem) == B_INTERRUPTED);

	bool retValue = fDialogValue;

	if(Lock())
		Quit();

	return retValue;
}


void VimSelectFontDialog::_UpdateFontStyles()
{
	_CleanList(fStylesList);

	int32 selIndex = -1;
	int32 count = count_font_styles(fFontFamily);
	for (int32 i = 0; i < count; i++) {
		font_style style;
		uint32 flags = 0;
		if (get_font_style(fFontFamily, i, &style, &flags) == B_OK) {
			fStylesList->AddItem(new BStringItem((const char*)style));
			if (strncmp(style, fFontStyle, B_FONT_STYLE_LENGTH) == 0)
				selIndex = i;
		}
	}

	if (selIndex >= 0) {
		fStylesList->Select(selIndex);
		fStylesList->ScrollToSelection();
	} else
		fStylesList->Select(0);
}


void VimSelectFontDialog::_UpdateSizeInputPreview()
{
	char buf[10] = {0};
	vim_snprintf(buf, sizeof(buf), (char*)"%.0f", fFontSize);
	fSizesInput->SetText(buf);
}


void VimSelectFontDialog::_UpdateFontPreview()
{
	BFont font;
	fPreview->GetFont(&font);
	font.SetSize(fFontSize);
	font.SetFamilyAndStyle(fFontFamily, fFontStyle);
	fPreview->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE);

	BString str;
	str << fFontFamily << " " << fFontStyle << ", " << (int)fFontSize << " pt.";
	fPreview->SetText(str);
}


	bool
VimSelectFontDialog::_UpdateFromListItem(BListView* list, char* text, int textSize)
{
	int32 index = list->CurrentSelection();
	if (index < 0)
		return false;
	BStringItem* item = (BStringItem*)list->ItemAt(index);
	if (item == NULL)
		return false;
	strncpy(text, item->Text(), textSize);
	return true;
}


void VimSelectFontDialog::MessageReceived(BMessage *msg)
{
	switch (msg->what) {
		case kVimDialogOKButtonMsg:
			strncpy(*fFamily, fFontFamily, B_FONT_FAMILY_LENGTH);
			strncpy(*fStyle, fFontStyle, B_FONT_STYLE_LENGTH);
			*fSize = fFontSize;
			fDialogValue = true;
		case kVimDialogCancelButtonMsg:
			delete_sem(fDialogSem);
			fDialogSem = -1;
			return;
		case B_KEY_UP:
			{
				int32 key = 0;
				if (msg->FindInt32("raw_char", &key) == B_OK
						&& key == B_ESCAPE) {
					delete_sem(fDialogSem);
					fDialogSem = -1;
				}
			}
			break;

		case kVimDialogFamilySelectMsg:
			if (_UpdateFromListItem(fFamiliesList,
					fFontFamily, B_FONT_FAMILY_LENGTH)) {
				_UpdateFontStyles();
				_UpdateFontPreview();
			}
			break;
		case kVimDialogStyleSelectMsg:
			if (_UpdateFromListItem(fStylesList,
					fFontStyle, B_FONT_STYLE_LENGTH))
				_UpdateFontPreview();
			break;
		case kVimDialogSizeSelectMsg:
			{
				char buf[10] = {0};
				if (_UpdateFromListItem(fSizesList,	buf, sizeof(buf))) {
					float size = atof(buf);
					if (size > 0.f) {
						fFontSize = size;
						_UpdateSizeInputPreview();
						_UpdateFontPreview();
					}
				}
			}
			break;
		case kVimDialogSizeInputMsg:
			{
				float size = atof(fSizesInput->Text());
				if (size > 0.f) {
					fFontSize = size;
					_UpdateFontPreview();
				}
			}
			break;
		default:
			break;
	}
	return BWindow::MessageReceived(msg);
}

#endif // FEAT_GUI_DIALOG

#ifdef FEAT_TOOLBAR

//  some forward declaration required by toolbar functions...
static BMessage * MenuMessage(vimmenu_T *menu);

VimToolbar::VimToolbar(BRect frame, const char *name) :
	BBox(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER)
{
}

VimToolbar::~VimToolbar()
{
	int32 count = fButtonsList.CountItems();
	for(int32 i = 0; i < count; i++)
		delete (BPictureButton*)fButtonsList.ItemAt(i);
	fButtonsList.MakeEmpty();

	delete normalButtonsBitmap;
	delete grayedButtonsBitmap;
	normalButtonsBitmap    = NULL;
	grayedButtonsBitmap  = NULL;
}

	void
VimToolbar::AttachedToWindow()
{
	BBox::AttachedToWindow();

	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}

	float
VimToolbar::ToolbarHeight() const
{
	float size = NULL == normalButtonsBitmap ? 18. : normalButtonsBitmap->Bounds().Height();
	return size + ToolbarMargin * 2 + ButtonMargin * 2 + 1;
}

	bool
VimToolbar::ModifyBitmapToGrayed(BBitmap *bitmap)
{
	float height = bitmap->Bounds().Height();
	float width  = bitmap->Bounds().Width();

	rgb_color *bits = (rgb_color*)bitmap->Bits();
	int32 pixels = bitmap->BitsLength() / 4;
	for(int32 i = 0; i < pixels; i++) {
		bits[i].red = bits[i].green =
		bits[i].blue = ((uint32)bits[i].red + bits[i].green + bits[i].blue) / 3;
		bits[i].alpha /= 4;
	}

	return true;
}

	bool
VimToolbar::PrepareButtonBitmaps()
{
	//  first try to load potentially customized $VIRUNTIME/bitmaps/builtin-tools.png
	normalButtonsBitmap = LoadVimBitmap("builtin-tools.png");
	if(normalButtonsBitmap == NULL)
		//  customized not found? dig application resources for "builtin-tools" one
		normalButtonsBitmap = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "builtin-tools");

	if(normalButtonsBitmap == NULL)
		return false;

	BMessage archive;
	normalButtonsBitmap->Archive(&archive);

	grayedButtonsBitmap = new BBitmap(&archive);
	if(grayedButtonsBitmap == NULL)
		return false;

	//  modify grayed bitmap
	ModifyBitmapToGrayed(grayedButtonsBitmap);

	return true;
}

BBitmap *VimToolbar::LoadVimBitmap(const char* fileName)
{
	BBitmap *bitmap = NULL;

	int mustfree = 0;
	char_u* runtimePath = vim_getenv((char_u*)"VIMRUNTIME", &mustfree);
	if(runtimePath != NULL && fileName != NULL) {
		BString strPath((char*)runtimePath);
		strPath << "/bitmaps/" << fileName;
		bitmap = BTranslationUtils::GetBitmap(strPath.String());
	}

	if(mustfree)
		vim_free(runtimePath);

	return bitmap;
}

	bool
VimToolbar::GetPictureFromBitmap(BPicture *pictureTo, int32 index, BBitmap *bitmapFrom, bool pressed)
{
	float size = bitmapFrom->Bounds().Height() + 1.;

	BView view(BRect(0, 0, size, size), "", 0, 0);

	AddChild(&view);
	view.BeginPicture(pictureTo);

	view.SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
	view.FillRect(view.Bounds());
	view.SetDrawingMode(B_OP_OVER);

	BRect source(0, 0, size - 1, size - 1);
	BRect destination(source);

	source.OffsetBy(size * index, 0);
	destination.OffsetBy(ButtonMargin, ButtonMargin);

	view.DrawBitmap(bitmapFrom, source, destination);

	if(pressed)	{
		rgb_color shineColor  = ui_color(B_SHINE_COLOR);
		rgb_color shadowColor = ui_color(B_SHADOW_COLOR);
		size += ButtonMargin * 2 - 1;
		view.BeginLineArray(4);
		view.AddLine(BPoint(0, 0),		 BPoint(size, 0),	 shadowColor);
		view.AddLine(BPoint(size, 0),	 BPoint(size, size), shineColor);
		view.AddLine(BPoint(size, size), BPoint(0, size),	 shineColor);
		view.AddLine(BPoint(0, size),	 BPoint(0, 0),		 shadowColor);
		view.EndLineArray();
	}

	view.EndPicture();
	RemoveChild(&view);

	return true;
}

	bool
VimToolbar::AddButton(int32 index, vimmenu_T *menu)
{
	BPictureButton *button = NULL;
	if(!menu_is_separator(menu->name)) {
		float size = normalButtonsBitmap ?
			normalButtonsBitmap->Bounds().Height() + 1. + ButtonMargin * 2 : 18.;
		BRect frame(0, 0, size, size);
		BPicture pictureOn;
		BPicture pictureOff;
		BPicture pictureGray;

		if(menu->iconfile == NULL && menu->iconidx >= 0 && normalButtonsBitmap) {
			GetPictureFromBitmap(&pictureOn,  menu->iconidx, normalButtonsBitmap, true);
			GetPictureFromBitmap(&pictureOff, menu->iconidx, normalButtonsBitmap, false);
			GetPictureFromBitmap(&pictureGray, menu->iconidx, grayedButtonsBitmap, false);
		} else {

			char_u buffer[MAXPATHL] = {0};
			BBitmap *bitmap = NULL;

			if(menu->iconfile) {
				gui_find_iconfile(menu->iconfile, buffer, (char*)"png");
				bitmap = BTranslationUtils::GetBitmap((char*)buffer);
			}

			if(bitmap == NULL && gui_find_bitmap(menu->name, buffer, (char*)"png") == OK)
				bitmap = BTranslationUtils::GetBitmap((char*)buffer);

			if(bitmap == NULL)
				bitmap = new BBitmap(BRect(0, 0, size, size), B_RGB32);

			GetPictureFromBitmap(&pictureOn,   0, bitmap, true);
			GetPictureFromBitmap(&pictureOff,  0, bitmap, false);
			ModifyBitmapToGrayed(bitmap);
			GetPictureFromBitmap(&pictureGray, 0, bitmap, false);

			delete bitmap;
		}

		button = new BPictureButton(frame, (char*)menu->name,
					&pictureOff, &pictureOn, MenuMessage(menu));

		button->SetDisabledOn(&pictureGray);
		button->SetDisabledOff(&pictureGray);

		button->SetTarget(gui.vimTextArea);

		AddChild(button);

		menu->button = button;
	}

	bool result = fButtonsList.AddItem(button, index);
	InvalidateLayout();
	return result;
}

	bool
VimToolbar::RemoveButton(vimmenu_T *menu)
{
	if(menu->button) {
		if(fButtonsList.RemoveItem(menu->button)) {
			delete menu->button;
			menu->button = NULL;
		}
	}
}

	bool
VimToolbar::GrayButton(vimmenu_T *menu, int grey)
{
	if(menu->button) {
		int32 index = fButtonsList.IndexOf(menu->button);
		if(index >= 0)
			menu->button->SetEnabled(grey ? false : true);
	}
}

	void
VimToolbar::InvalidateLayout()
{
	int32 offset = ToolbarMargin;
	int32 count = fButtonsList.CountItems();
	for(int32 i = 0; i < count; i++) {
		BPictureButton *button = (BPictureButton *)fButtonsList.ItemAt(i);
		if(button) {
			button->MoveTo(offset, ToolbarMargin);
			offset += button->Bounds().Width() + ToolbarMargin;
		} else
			offset += ToolbarMargin * 3;
	}
}

#endif /*FEAT_TOOLBAR*/

#if defined(FEAT_GUI_TABLINE)

	float
VimTabLine::TablineHeight() const
{
//	float size = NULL == normalButtonsBitmap ? 18. : normalButtonsBitmap->Bounds().Height();
//	return size + ToolbarMargin * 2 + ButtonMargin * 2 + 1;
	return TabHeight(); //  + ToolbarMargin;
}

void
VimTabLine::MouseDown(BPoint point)
{
	if(!gui_mch_showing_tabline())
		return;

	BMessage *m = Window()->CurrentMessage();
	assert(m);

	int32 buttons = 0;
	m->FindInt32("buttons", &buttons);

	int32 clicks = 0;
	m->FindInt32("clicks", &clicks);

	int index = 0; //  0 means here - no tab found
	for (int i = 0; i < CountTabs(); i++) {
		if(TabFrame(i).Contains(point)) {
			index = i + 1; //  indexes are 1-based
			break;
		}
	}

	int event = -1;

	if ((buttons & B_PRIMARY_MOUSE_BUTTON) && clicks > 1)
		//  left button double click on - create new tab
		event = TABLINE_MENU_NEW;

	else if (buttons & B_TERTIARY_MOUSE_BUTTON)
		//  middle button click - close the pointed tab
		//  or create new one in case empty space
		event = index > 0 ? TABLINE_MENU_CLOSE : TABLINE_MENU_NEW;

	else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
		//  right button click - show context menu
		BPopUpMenu* popUpMenu = new BPopUpMenu("tabLineContextMenu", false, false);
		popUpMenu->AddItem(new BMenuItem(_("Close tabi R"), new BMessage(TABLINE_MENU_CLOSE)));
		popUpMenu->AddItem(new BMenuItem(_("New tab    T"), new BMessage(TABLINE_MENU_NEW)));
		popUpMenu->AddItem(new BMenuItem(_("Open tab..."), new BMessage(TABLINE_MENU_OPEN)));

		ConvertToScreen(&point);
		BMenuItem* item = popUpMenu->Go(point);
		if (item != NULL) {
			event = item->Command();
		}

		delete popUpMenu;

	} else {
		//  default processing
		BTabView::MouseDown(point);
		return;
	}

	if (event < 0)
		return;

	VimTablineMenuMsg tmm;
	tmm.index = index;
	tmm.event = event;
	write_port(gui.vdcmp, VimMsg::TablineMenu, &tmm, sizeof(tmm));
}

void
VimTabLine::VimTab::Select(BView* owner)
{
	BTab::Select(owner);

	VimTabLine *tabLine = gui.vimForm->TabLine();
	if(tabLine != NULL) {

		int32 i = 0;
		for (; i < tabLine->CountTabs(); i++)
			if(this == tabLine->TabAt(i))
				break;

//		printf("%d:%d:%s\n", i, tabLine->CountTabs(), tabLine->TabAt(i)->Label());
		if(i < tabLine->CountTabs()) {
			VimTablineMsg tm;
			tm.index = i + 1;
			write_port(gui.vdcmp, VimMsg::Tabline, &tm, sizeof(tm));
		}
	}
}

#endif //  defined(FEAT_GUI_TABLINE)

// ---------------- ----------------

//  some global variables
static char appsig[] = "application/x-vnd.Haiku-Vim-8";
key_map *keyMap;
char *keyMapChars;
int main_exitcode = 127;

	status_t
gui_haiku_process_event(bigtime_t timeout)
{
	struct VimMsg vm;
	int32 what;
	ssize_t size;

	size = read_port_etc(gui.vdcmp, &what, &vm, sizeof(vm),
			B_TIMEOUT, timeout);

	if (size >= 0) {
		switch (what) {
			case VimMsg::Key:
				{
					char_u *string = vm.u.Key.chars;
					int len = vm.u.Key.length;
					if (len == 1 && string[0] == Ctrl_chr('C')) {
						trash_input_buf();
						got_int = TRUE;
					}

					if (vm.u.Key.csi_escape)
#ifndef FEAT_MBYTE_IME
					{
						int		i;
						char_u	buf[2];

						for (i = 0; i < len; ++i)
						{
							add_to_input_buf(string + i, 1);
							if (string[i] == CSI)
							{
								// Turn CSI into K_CSI.
								buf[0] = KS_EXTRA;
								buf[1] = (int)KE_CSI;
								add_to_input_buf(buf, 2);
							}
						}
					}
#else
					add_to_input_buf_csi(string, len);
#endif
					else
						add_to_input_buf(string, len);
				}
				break;
			case VimMsg::Resize:
				gui_resize_shell(vm.u.NewSize.width, vm.u.NewSize.height);
				break;
			case VimMsg::ScrollBar:
				{
					/*
					 * If loads of scroll messages queue up, use only the last
					 * one. Always report when the scrollbar stops dragging.
					 * This is not perfect yet anyway: these events are queued
					 * yet again, this time in the keyboard input buffer.
					 */
					int32 oldCount =
						atomic_add(&vm.u.Scroll.sb->scrollEventCount, -1);
					if (oldCount <= 1 || !vm.u.Scroll.stillDragging)
						gui_drag_scrollbar(vm.u.Scroll.sb->getGsb(),
								vm.u.Scroll.value, vm.u.Scroll.stillDragging);
				}
				break;
#if defined(FEAT_MENU)
			case VimMsg::Menu:
				gui_menu_cb(vm.u.Menu.guiMenu);
				break;
#endif
			case VimMsg::Mouse:
				{
					int32 oldCount;
					if (vm.u.Mouse.button == MOUSE_DRAG)
						oldCount =
							atomic_add(&gui.vimTextArea->mouseDragEventCount, -1);
					else
						oldCount = 0;
					if (oldCount <= 1)
						gui_send_mouse_event(vm.u.Mouse.button, vm.u.Mouse.x,
								vm.u.Mouse.y, vm.u.Mouse.repeated_click,
								vm.u.Mouse.modifiers);
				}
				break;
			case VimMsg::MouseMoved:
				{
					gui_mouse_moved(vm.u.MouseMoved.x, vm.u.MouseMoved.y);
				}
				break;
			case VimMsg::Focus:
				gui.in_focus = vm.u.Focus.active;
				// XXX Signal that scrollbar dragging has stopped?
				// This is needed because we don't get a MouseUp if
				// that happens while outside the window... :-(
				if (gui.dragged_sb) {
					gui.dragged_sb = SBAR_NONE;
				}
				//  gui_update_cursor(TRUE, FALSE);
				break;
			case VimMsg::Refs:
				::RefsReceived(vm.u.Refs.message, vm.u.Refs.changedir);
				break;
			case VimMsg::Tabline:
				send_tabline_event(vm.u.Tabline.index);
				break;
			case VimMsg::TablineMenu:
				send_tabline_menu_event(vm.u.TablineMenu.index, vm.u.TablineMenu.event);
				break;
			default:
				//  unrecognised message, ignore it
				break;
		}
	}

	/*
	 * If size < B_OK, it is an error code.
	 */
	return size;
}

/*
 * Here are some functions to protect access to ScreenLines[] and
 * LineOffset[]. These are used from the window thread to respond
 * to a Draw() callback. When that occurs, the window is already
 * locked by the system.
 *
 * Other code that needs to lock is any code that changes these
 * variables. Other read-only access, or access merely to the
 * contents of the screen buffer, need not be locked.
 *
 * If there is no window, don't call Lock() but do succeed.
 */

	int
vim_lock_screen()
{
	return !gui.vimWindow || gui.vimWindow->Lock();
}

	void
vim_unlock_screen()
{
	if (gui.vimWindow)
		gui.vimWindow->Unlock();
}

#define RUN_BAPPLICATION_IN_NEW_THREAD	0

#if RUN_BAPPLICATION_IN_NEW_THREAD

	int32
run_vimapp(void *args)
{
	VimApp app(appsig);

	gui.vimApp = &app;
	app.Run();			    // Run until Quit() called

	return 0;
}

#else

	int32
call_main(void *args)
{
	struct MainArgs *ma = (MainArgs *)args;

	return main(ma->argc, ma->argv);
}
#endif

/*
 * 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)
{
	/*
	 * We don't have any command line arguments for the BeOS GUI yet,
	 * but this is an excellent place to create our Application object.
	 */
	if (!gui.vimApp) {
		thread_info tinfo;
		get_thread_info(find_thread(NULL), &tinfo);

		// May need the port very early on to process RefsReceived()
		gui.vdcmp = create_port(B_MAX_PORT_COUNT, "vim VDCMP");

#if RUN_BAPPLICATION_IN_NEW_THREAD
		thread_id tid = spawn_thread(run_vimapp, "vim VimApp",
				tinfo.priority, NULL);
		if (tid >= B_OK) {
			resume_thread(tid);
		} else {
			getout(1);
		}
#else
		MainArgs ma = { *argc, argv };
		thread_id tid = spawn_thread(call_main, "vim main()",
				tinfo.priority, &ma);
		if (tid >= B_OK) {
			VimApp app(appsig);

			gui.vimApp = &app;
			resume_thread(tid);
			/*
			 * This is rather horrible.
			 * call_main will call main() again...
			 * There will be no infinite recursion since
			 * gui.vimApp is set now.
			 */
			app.Run();			    // Run until Quit() called
			// fprintf(stderr, "app.Run() returned...\n");
			status_t dummy_exitcode;
			(void)wait_for_thread(tid, &dummy_exitcode);

			/*
			 * This path should be the normal one taken to exit Vim.
			 * The main() thread calls mch_exit() which calls
			 * gui_mch_exit() which terminates its thread.
			 */
			exit(main_exitcode);
		}
#endif
	}
	// Don't fork() when starting the GUI. Spawned threads are not
	// duplicated with a fork(). The result is a mess.
	gui.dofork = FALSE;
	/*
	 * XXX Try to determine whether we were started from
	 * the Tracker or the terminal.
	 * It would be nice to have this work, because the Tracker
	 * follows symlinks, so even if you double-click on gvim,
	 * when it is a link to vim it will still pass a command name
	 * of vim...
	 * We try here to see if stdin comes from /dev/null. If so,
	 * (or if there is an error, which should never happen) start the GUI.
	 * This does the wrong thing for vim - </dev/null, and we're
	 * too early to see the command line parsing. Tough.
	 * On the other hand, it starts the gui for vim file & which is nice.
	 */
	if (!isatty(0)) {
		struct stat stat_stdin, stat_dev_null;

		if (fstat(0, &stat_stdin) == -1 ||
				stat("/dev/null", &stat_dev_null) == -1 ||
				(stat_stdin.st_dev == stat_dev_null.st_dev &&
				 stat_stdin.st_ino == stat_dev_null.st_ino))
			gui.starting = TRUE;
	}
}

/*
 * Check if the GUI can be started.  Called before gvimrc is sourced.
 * Return OK or FAIL.
 */
	int
gui_mch_init_check(void)
{
	return OK;		// TODO: GUI can always be started?
}

/*
 * Initialise the GUI.  Create all the windows, set up all the call-backs
 * etc.
 */
	int
gui_mch_init()
{
    display_errors();
	gui.def_norm_pixel = RGB(0x00, 0x00, 0x00);	//  black
	gui.def_back_pixel = RGB(0xFF, 0xFF, 0xFF);	//  white
	gui.norm_pixel = gui.def_norm_pixel;
	gui.back_pixel = gui.def_back_pixel;

	gui.scrollbar_width = (int) B_V_SCROLL_BAR_WIDTH;
	gui.scrollbar_height = (int) B_H_SCROLL_BAR_HEIGHT;
#ifdef FEAT_MENU
	gui.menu_height = 19;	//  initial guess -
	//  correct for my default settings
#endif
	gui.border_offset = 3;	//  coordinates are inside window borders

	if (gui.vdcmp < B_OK)
		return FAIL;
	get_key_map(&keyMap, &keyMapChars);

	gui.vimWindow = new VimWindow();	// hidden and locked
	if (!gui.vimWindow)
		return FAIL;

	gui.vimWindow->Run();		// Run() unlocks but does not show

	// 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
	 */
	gui_check_colors();

	// Get the colors for the highlight groups (gui_check_colors() might have
	// changed them)
	highlight_gui_started();		// re-init colors and fonts

	gui_mch_new_colors();		// window must exist for this

	return OK;
}

/*
 * Called when the foreground or background color has been changed.
 */
	void
gui_mch_new_colors()
{
	rgb_color rgb = GUI_TO_RGB(gui.back_pixel);

	if (gui.vimWindow->Lock()) {
		gui.vimForm->SetViewColor(rgb);
		//  Does this not have too much effect for those small rectangles?
		gui.vimForm->Invalidate();
		gui.vimWindow->Unlock();
	}
}

/*
 * Open the GUI window which was created by a call to gui_mch_init().
 */
	int
gui_mch_open()
{
	if (gui_win_x != -1 && gui_win_y != -1)
		gui_mch_set_winpos(gui_win_x, gui_win_y);

	// Actually open the window
	if (gui.vimWindow->Lock()) {
		gui.vimWindow->Show();
		gui.vimWindow->Unlock();
		return OK;
	}

	return FAIL;
}

	void
gui_mch_exit(int vim_exitcode)
{
	if (gui.vimWindow) {
		thread_id tid = gui.vimWindow->Thread();
		gui.vimWindow->Lock();
		gui.vimWindow->Quit();
		// Wait until it is truely gone
		int32 exitcode;
		wait_for_thread(tid, &exitcode);
	}
	delete_port(gui.vdcmp);
#if !RUN_BAPPLICATION_IN_NEW_THREAD
	/*
	 * We are in the main() thread - quit the App thread and
	 * quit ourselves (passing on the exitcode). Use a global since the
	 * value from exit_thread() is only used if wait_for_thread() is
	 * called in time (race condition).
	 */
#endif
	if (gui.vimApp) {
		VimTextAreaView::guiBlankMouse(false);

		main_exitcode = vim_exitcode;
#if RUN_BAPPLICATION_IN_NEW_THREAD
		thread_id tid = gui.vimApp->Thread();
		int32 exitcode;
		gui.vimApp->Lock();
		gui.vimApp->Quit();
		gui.vimApp->Unlock();
		wait_for_thread(tid, &exitcode);
#else
		gui.vimApp->Lock();
		gui.vimApp->Quit();
		gui.vimApp->Unlock();
		// suicide
		exit_thread(vim_exitcode);
#endif
	}
	// If we are somehow still here, let mch_exit() handle things.
}

/*
 * Get the position of the top left corner of the window.
 */
	int
gui_mch_get_winpos(int *x, int *y)
{
	if (gui.vimWindow->Lock()) {
		BRect r;
		r = gui.vimWindow->Frame();
		gui.vimWindow->Unlock();
		*x = (int)r.left;
		*y = (int)r.top;
		return OK;
	}
	else
		return FAIL;
}

/*
 * Set the position of the top left corner of the window to the given
 * coordinates.
 */
	void
gui_mch_set_winpos(int x, int y)
{
	if (gui.vimWindow->Lock()) {
		gui.vimWindow->MoveTo(x, y);
		gui.vimWindow->Unlock();
	}
}

/*
 * Set the size of the window to the given width and height in pixels.
 */
void
gui_mch_set_shellsize(
		int		width,
		int		height,
		int		min_width,
		int		min_height,
		int		base_width,
		int		base_height,
		int		direction) // TODO: utilize?
{
	/*
	 * We are basically given the size of the VimForm, if I understand
	 * correctly. Since it fills the window completely, this will also
	 * be the size of the window.
	 */
	if (gui.vimWindow->Lock()) {
		gui.vimWindow->ResizeTo(width - PEN_WIDTH, height - PEN_WIDTH);

		// set size limits
		float minWidth, maxWidth, minHeight, maxHeight;

		gui.vimWindow->GetSizeLimits(&minWidth, &maxWidth,
				&minHeight, &maxHeight);
		gui.vimWindow->SetSizeLimits(min_width, maxWidth,
				min_height, maxHeight);

		/*
		 * Set the resizing alignment depending on font size.
		 */
		gui.vimWindow->SetWindowAlignment(
				B_PIXEL_ALIGNMENT,		//  window_alignment mode,
				1,				//  int32 h,
				0,				//  int32 hOffset = 0,
				gui.char_width,		//  int32 width = 0,
				base_width,			//  int32 widthOffset = 0,
				1,				//  int32 v = 0,
				0,				//  int32 vOffset = 0,
				gui.char_height,		//  int32 height = 0,
				base_height			//  int32 heightOffset = 0
				);

		gui.vimWindow->Unlock();
	}
}

void
gui_mch_get_screen_dimensions(
		int		*screen_w,
		int		*screen_h)
{
	BRect frame;

	{
		BScreen screen(gui.vimWindow);

		if (screen.IsValid()) {
			frame = screen.Frame();
		} else {
			frame.right = 640;
			frame.bottom = 480;
		}
	}

	// XXX approximations...
	*screen_w = (int) frame.right - 2 * gui.scrollbar_width - 20;
	*screen_h = (int) frame.bottom - gui.scrollbar_height
#ifdef FEAT_MENU
		- gui.menu_height
#endif
		- 30;
}

void
gui_mch_set_text_area_pos(
		int		x,
		int		y,
		int		w,
		int		h)
{
	if (!gui.vimTextArea)
		return;

	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->MoveTo(x, y);
		gui.vimTextArea->ResizeTo(w - PEN_WIDTH, h - PEN_WIDTH);

/*#ifdef FEAT_GUI_TABLINE
		if(gui.vimForm->TabLine() != NULL) {
			gui.vimForm->TabLine()->ResizeTo(w, gui.vimForm->TablineHeight());
		}
#endif // FEAT_GUI_TABLINE

		gui.vimWindow->Unlock();
	}
}


/*
 * Scrollbar stuff:
 */

void
gui_mch_enable_scrollbar(
		scrollbar_T	*sb,
		int		flag)
{
	VimScrollBar *vsb = sb->id;
	if (gui.vimWindow->Lock()) {
		/*
		 * This function is supposed to be idempotent, but Show()/Hide()
		 * is not. Therefore we test if they are needed.
		 */
		if (flag) {
			if (vsb->IsHidden()) {
				vsb->Show();
			}
		} else {
			if (!vsb->IsHidden()) {
				vsb->Hide();
			}
		}
		gui.vimWindow->Unlock();
	}
}

void
gui_mch_set_scrollbar_thumb(
		scrollbar_T *sb,
		int		val,
		int		size,
		int		max)
{
	if (gui.vimWindow->Lock()) {
		VimScrollBar *s = sb->id;
		if (max == 0) {
			s->SetValue(0);
			s->SetRange(0.0, 0.0);
		} else {
			s->SetProportion((float)size / (max + 1.0));
			s->SetSteps(1.0, size > 5 ? size - 2 : size);
#ifndef SCROLL_PAST_END		//  really only defined in gui.c...
			max = max + 1 - size;
#endif
			if (max < s->Value()) {
				/*
				 * If the new maximum is lower than the current value,
				 * setting it would cause the value to be clipped and
				 * therefore a ValueChanged() call.
				 * We avoid this by setting the value first, because
				 * it presumably is <= max.
				 */
				s->SetValue(val);
				s->SetRange(0.0, max);
			} else {
				/*
				 * In the other case, set the range first, since the
				 * new value might be higher than the current max.
				 */
				s->SetRange(0.0, max);
				s->SetValue(val);
			}
		}
		gui.vimWindow->Unlock();
	}
}

void
gui_mch_set_scrollbar_pos(
		scrollbar_T *sb,
		int		x,
		int		y,
		int		w,
		int		h)
{
	if (gui.vimWindow->Lock()) {
		BRect winb = gui.vimWindow->Bounds();
		float vsbx = x, vsby = y;
		VimScrollBar *vsb = sb->id;
		vsb->ResizeTo(w - PEN_WIDTH, h - PEN_WIDTH);
		if(winb.right-(x+w)<w) vsbx = winb.right - (w - PEN_WIDTH);
		vsb->MoveTo(vsbx, vsby);
		gui.vimWindow->Unlock();
	}
}

void
gui_mch_create_scrollbar(
		scrollbar_T *sb,
		int		orient)		// SBAR_VERT or SBAR_HORIZ
{
	orientation posture =
		(orient == SBAR_HORIZ) ? B_HORIZONTAL : B_VERTICAL;

	VimScrollBar *vsb = sb->id = new VimScrollBar(sb, posture);
	if (gui.vimWindow->Lock()) {
		vsb->SetTarget(gui.vimTextArea);
		vsb->Hide();
		gui.vimForm->AddChild(vsb);
		gui.vimWindow->Unlock();
	}
}

#if defined(FEAT_WINDOWS) || defined(PROTO)
void
gui_mch_destroy_scrollbar(
		scrollbar_T	*sb)
{
	if (gui.vimWindow->Lock()) {
		sb->id->RemoveSelf();
		delete sb->id;
		gui.vimWindow->Unlock();
	}
}
#endif

/*
 * Cursor does not flash
 */
    int
gui_mch_is_blink_off(void)
{
    return FALSE;
}

/*
 * Cursor blink functions.
 *
 * This is a simple state machine:
 * BLINK_NONE	not blinking at all
 * BLINK_OFF	blinking, cursor is not shown
 * BLINK_ON	blinking, cursor is shown
 */

#define BLINK_NONE  0
#define BLINK_OFF   1
#define BLINK_ON    2

static int		blink_state = BLINK_NONE;
static long_u		blink_waittime = 700;
static long_u		blink_ontime = 400;
static long_u		blink_offtime = 250;
static int	blink_timer = 0;

void
gui_mch_set_blinking(
		long    waittime,
		long    on,
		long    off)
{
	// TODO
	blink_waittime = waittime;
	blink_ontime = on;
	blink_offtime = off;
}

/*
 * Stop the cursor blinking.  Show the cursor if it wasn't shown.
 */
	void
gui_mch_stop_blink()
{
	// TODO
	if (blink_timer != 0)
	{
		// XtRemoveTimeOut(blink_timer);
		blink_timer = 0;
	}
	if (blink_state == BLINK_OFF)
		gui_update_cursor(TRUE, FALSE);
	blink_state = BLINK_NONE;
}

/*
 * Start the cursor blinking.  If it was already blinking, this restarts the
 * waiting time and shows the cursor.
 */
	void
gui_mch_start_blink()
{
	// TODO
	if (blink_timer != 0)
		;// XtRemoveTimeOut(blink_timer);
	// Only switch blinking on if none of the times is zero
	if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
	{
		blink_timer = 1; // XtAppAddTimeOut(app_context, blink_waittime,
		blink_state = BLINK_ON;
		gui_update_cursor(TRUE, FALSE);
	}
}

/*
 * 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(
		char_u		*font_name,
		int			fontset)
{
	if (gui.vimWindow->Lock())
	{
		int rc = gui.vimTextArea->mchInitFont(font_name);
		gui.vimWindow->Unlock();

		return rc;
	}

	return FAIL;
}


	int
gui_mch_adjust_charsize()
{
	return FAIL;
}


	int
gui_mch_font_dialog(font_family* family, font_style* style, float* size)
{
#if defined(FEAT_GUI_DIALOG)
		// gui.vimWindow->Unlock();
	VimSelectFontDialog *dialog = new VimSelectFontDialog(family, style, size);
	return dialog->Go();
#else
	return NOFONT;
#endif // FEAT_GUI_DIALOG
}


GuiFont
gui_mch_get_font(
		char_u		*name,
		int			giveErrorIfMissing)
{
	static VimFont *fontList = NULL;

	if (!gui.in_use)	//  can't do this when GUI not running
		return NOFONT;

	//  storage for locally modified name;
	const int buff_size = B_FONT_FAMILY_LENGTH + B_FONT_STYLE_LENGTH + 20;
	static char font_name[buff_size] = {0};
	font_family family = {0};
	font_style  style  = {0};
	float size = 0.f;

	if (name == 0 && be_fixed_font == 0) {
		if(giveErrorIfMissing)
                        semsg(_(e_font), name);
		return NOFONT;
	}

	bool useSelectGUI = false;
	if (name != NULL)
		if (STRCMP(name, "*") == 0) {
			useSelectGUI = true;
			STRNCPY(font_name, hl_get_font_name(), buff_size);
		} else
			STRNCPY(font_name, name, buff_size);

	if (font_name[0] == 0) {
		be_fixed_font->GetFamilyAndStyle(&family, &style);
		size = be_fixed_font->Size();
		vim_snprintf(font_name, buff_size,
			(char*)"%s/%s/%.0f", family, style, size);
	}

	//  replace underscores with spaces
	char* end = 0;
	while (end = strchr((char *)font_name, '_'))
		*end = ' ';

	//  store the name before strtok corrupt the buffer ;-)
	static char buff[buff_size] = {0};
	STRNCPY(buff, font_name, buff_size);
	STRNCPY(family, strtok(buff, "/\0"), B_FONT_FAMILY_LENGTH);
	char* style_s = strtok(0, "/\0");
	if (style_s != 0)
		STRNCPY(style, style_s, B_FONT_STYLE_LENGTH);
	size = atof((style_s != 0) ? strtok(0, "/\0") : "0");

	if (useSelectGUI) {
		if(gui_mch_font_dialog(&family, &style, &size) == NOFONT)
			return FAIL;
		//  compose for further processing
		vim_snprintf(font_name, buff_size,
				(char*)"%s/%s/%.0f", family, style, size);
		hl_set_font_name((char_u*)font_name);

		//  Set guifont to the name of the selected font.
		char_u* new_p_guifont = alloc(STRLEN(font_name) + 1);
		if (new_p_guifont != NULL) {
			STRCPY(new_p_guifont, font_name);
			vim_free(p_guifont);
			p_guifont = new_p_guifont;
			//  Replace spaces in the font name with underscores.
			for ( ; *new_p_guifont; ++new_p_guifont)
				if (*new_p_guifont == ' ')
					*new_p_guifont = '_';
		}
	}

	VimFont *flp;
	for (flp = fontList; flp; flp = flp->next) {
		if (STRCMP(font_name, flp->name) == 0) {
			flp->refcount++;
			return (GuiFont)flp;
		}
	}

	VimFont *font = new VimFont();
	font->name = vim_strsave((char_u*)font_name);

	if(count_font_styles(family) <= 0) {
		if (giveErrorIfMissing)
                        semsg(_(e_font), font->name);
		delete font;
		return NOFONT;
	}

	//  Remember font in the static list for later use
	font->next = fontList;
	fontList = font;

	font->SetFamilyAndStyle(family, style);
	if(size > 0.f)
		font->SetSize(size);

	font->SetSpacing(B_FIXED_SPACING);
	font->SetEncoding(B_UNICODE_UTF8);

	return (GuiFont)font;
}

/*
 * Set the current text font.
 */
void
gui_mch_set_font(
		GuiFont	font)
{
	if (gui.vimWindow->Lock()) {
		VimFont *vf = (VimFont *)font;

		gui.vimTextArea->SetFont(vf);

		gui.char_width = (int) vf->StringWidth("n");
		font_height fh;
		vf->GetHeight(&fh);
		gui.char_height = (int)(fh.ascent + 0.9999)
			+ (int)(fh.descent + 0.9999) + (int)(fh.leading + 0.9999);
		gui.char_ascent = (int)(fh.ascent + 0.9999);

		gui.vimWindow->Unlock();
	}
}

// XXX TODO This is apparently never called...
void
gui_mch_free_font(
		GuiFont	font)
{
	if(font == NOFONT)
		return;
	VimFont *f = (VimFont *)font;
	if (--f->refcount <= 0) {
		if (f->refcount < 0)
			fprintf(stderr, "VimFont: refcount < 0\n");
		delete f;
	}
}

	char_u *
gui_mch_get_fontname(GuiFont font, char_u *name)
{
	if (name == NULL)
		return NULL;
	return vim_strsave(name);
}

/*
 * Adjust gui.char_height (after 'linespace' was changed).
 */
	int
gui_mch_adjust_charheight()
{

	// TODO: linespace support?

// #ifdef FEAT_XFONTSET
//	if (gui.fontset != NOFONTSET)
//	{
//	gui.char_height = fontset_height((XFontSet)gui.fontset) + p_linespace;
//	gui.char_ascent = fontset_ascent((XFontSet)gui.fontset)
//	+ p_linespace / 2;
//	}
//	else
// #endif
	{
		VimFont *font = (VimFont *)gui.norm_font;
		font_height fh = {0};
		font->GetHeight(&fh);
		gui.char_height = (int)(fh.ascent + fh.descent + 0.5) + p_linespace;
		gui.char_ascent = (int)(fh.ascent + 0.5) + p_linespace / 2;
	}
	return OK;
}

/*
 * Display the saved error message(s).
 */
#ifdef USE_MCH_ERRMSG
    void
display_errors(void)
{
    char	*p;
    char_u	pError[256];

    if (error_ga.ga_data == NULL)
	return;

    // avoid putting up a message box with blanks only
    for (p = (char *)error_ga.ga_data; *p; ++p)
	if (!isspace(*p))
	{
	    if (STRLEN(p) > 255)
		pError[0] = 255;
	    else
		pError[0] = STRLEN(p);

	    STRNCPY(&pError[1], p, pError[0]);
//	    ParamText(pError, nil, nil, nil);
//	    Alert(128, nil);
	    break;
	    // TODO: handled message longer than 256 chars
	    //	 use auto-sizeable alert
	    //	 or dialog with scrollbars (TextEdit zone)
	}
    ga_clear(&error_ga);
}
#endif

	void
gui_mch_getmouse(int *x, int *y)
{
	fprintf(stderr, "gui_mch_getmouse");

	/*int		rootx, rooty, winx, winy;
	  Window	root, child;
	  unsigned int mask;

	  if (gui.wid && XQueryPointer(gui.dpy, gui.wid, &root, &child,
	  &rootx, &rooty, &winx, &winy, &mask)) {
	 *x = winx;
	 *y = winy;
	 } else*/ {
		 *x = -1;
		 *y = -1;
	 }
}

	void
gui_mch_mousehide(int hide)
{
	fprintf(stderr, "gui_mch_getmouse");
	//  TODO
}

	static int
hex_digit(int c)
{
	if (isdigit(c))
		return c - '0';
	c = TOLOWER_ASC(c);
	if (c >= 'a' && c <= 'f')
		return c - 'a' + 10;
	return -1000;
}

/*
 * This function has been lifted from gui_w32.c and extended a bit.
 *
 * Return the Pixel value (color) for the given color name.
 * Return INVALCOLOR for error.
 */
guicolor_T
gui_mch_get_color(
		char_u	*name)
{
	typedef struct GuiColourTable
	{
		const char    *name;
		guicolor_T     colour;
	} GuiColourTable;

#define NSTATIC_COLOURS		50 // 32
#define NDYNAMIC_COLOURS	33
#define NCOLOURS		(NSTATIC_COLOURS + NDYNAMIC_COLOURS)

	static GuiColourTable table[NCOLOURS] =
	{
		{"Black",	    RGB(0x00, 0x00, 0x00)},
		{"DarkGray",	    RGB(0x80, 0x80, 0x80)},
		{"DarkGrey",	    RGB(0x80, 0x80, 0x80)},
		{"Gray",	    RGB(0xC0, 0xC0, 0xC0)},
		{"Grey",	    RGB(0xC0, 0xC0, 0xC0)},
		{"LightGray",	    RGB(0xD3, 0xD3, 0xD3)},
		{"LightGrey",	    RGB(0xD3, 0xD3, 0xD3)},
		{"Gray10",	    RGB(0x1A, 0x1A, 0x1A)},
		{"Grey10",	    RGB(0x1A, 0x1A, 0x1A)},
		{"Gray20",	    RGB(0x33, 0x33, 0x33)},
		{"Grey20",	    RGB(0x33, 0x33, 0x33)},
		{"Gray30",	    RGB(0x4D, 0x4D, 0x4D)},
		{"Grey30",	    RGB(0x4D, 0x4D, 0x4D)},
		{"Gray40",	    RGB(0x66, 0x66, 0x66)},
		{"Grey40",	    RGB(0x66, 0x66, 0x66)},
		{"Gray50",	    RGB(0x7F, 0x7F, 0x7F)},
		{"Grey50",	    RGB(0x7F, 0x7F, 0x7F)},
		{"Gray60",	    RGB(0x99, 0x99, 0x99)},
		{"Grey60",	    RGB(0x99, 0x99, 0x99)},
		{"Gray70",	    RGB(0xB3, 0xB3, 0xB3)},
		{"Grey70",	    RGB(0xB3, 0xB3, 0xB3)},
		{"Gray80",	    RGB(0xCC, 0xCC, 0xCC)},
		{"Grey80",	    RGB(0xCC, 0xCC, 0xCC)},
		{"Gray90",	    RGB(0xE5, 0xE5, 0xE5)},
		{"Grey90",	    RGB(0xE5, 0xE5, 0xE5)},
		{"White",	    RGB(0xFF, 0xFF, 0xFF)},
		{"DarkRed",	    RGB(0x80, 0x00, 0x00)},
		{"Red",		    RGB(0xFF, 0x00, 0x00)},
		{"LightRed",	    RGB(0xFF, 0xA0, 0xA0)},
		{"DarkBlue",	    RGB(0x00, 0x00, 0x80)},
		{"Blue",	    RGB(0x00, 0x00, 0xFF)},
		{"LightBlue",	    RGB(0xA0, 0xA0, 0xFF)},
		{"DarkGreen",	    RGB(0x00, 0x80, 0x00)},
		{"Green",	    RGB(0x00, 0xFF, 0x00)},
		{"LightGreen",	    RGB(0xA0, 0xFF, 0xA0)},
		{"DarkCyan",	    RGB(0x00, 0x80, 0x80)},
		{"Cyan",	    RGB(0x00, 0xFF, 0xFF)},
		{"LightCyan",	    RGB(0xA0, 0xFF, 0xFF)},
		{"DarkMagenta",	    RGB(0x80, 0x00, 0x80)},
		{"Magenta",	    RGB(0xFF, 0x00, 0xFF)},
		{"LightMagenta",    RGB(0xFF, 0xA0, 0xFF)},
		{"Brown",	    RGB(0x80, 0x40, 0x40)},
		{"Yellow",	    RGB(0xFF, 0xFF, 0x00)},
		{"LightYellow",	    RGB(0xFF, 0xFF, 0xA0)},
		{"DarkYellow",	    RGB(0xBB, 0xBB, 0x00)},
		{"SeaGreen",	    RGB(0x2E, 0x8B, 0x57)},
		{"Orange",	    RGB(0xFF, 0xA5, 0x00)},
		{"Purple",	    RGB(0xA0, 0x20, 0xF0)},
		{"SlateBlue",	    RGB(0x6A, 0x5A, 0xCD)},
		{"Violet",	    RGB(0xEE, 0x82, 0xEE)},
		//  NOTE: some entries are zero-allocated for NDDYNAMIC_COLORS
		//		 in this table!
	};

	static int endColour = NSTATIC_COLOURS;
	static int newColour = NSTATIC_COLOURS;

	int		    r, g, b;
	int		    i;

	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 INVALCOLOR;
		return RGB(r, g, b);
	}
	else
	{
		// Check if the name is one of the colours we know
		for (i = 0; i < endColour; i++)
			if (STRICMP(name, table[i].name) == 0)
				return table[i].colour;
	}

	/*
	 * Last attempt. Look in the file "$VIMRUNTIME/rgb.txt".
	 */
	{
#define LINE_LEN 100
		FILE	*fd;
		char	line[LINE_LEN];
		char_u	*fname;

		fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt");
		if (fname == NULL)
			return INVALCOLOR;

		fd = fopen((char *)fname, "rt");
		vim_free(fname);
		if (fd == NULL)
			return INVALCOLOR;

		while (!feof(fd))
		{
			int	    len;
			int	    pos;
			char    *colour;

			fgets(line, LINE_LEN, fd);
			len = strlen(line);

			if (len <= 1 || line[len-1] != '\n')
				continue;

			line[len-1] = '\0';

			i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
			if (i != 3)
				continue;

			colour = line + pos;

			if (STRICMP(colour, name) == 0)
			{
				fclose(fd);
				/*
				 * Now remember this colour in the table.
				 * A LRU scheme might be better but this is simpler.
				 * Or could use a growing array.
				 */
				guicolor_T gcolour = RGB(r,g,b);

				// NOTE: see note above in table allocation! We are working here with
				//		dynamically allocated names, not constant ones!
				vim_free((char*)table[newColour].name);
				table[newColour].name = (char *)vim_strsave((char_u *)colour);
				table[newColour].colour = gcolour;

				newColour++;
				if (newColour >= NCOLOURS)
					newColour = NSTATIC_COLOURS;
				if (endColour < NCOLOURS)
					endColour = newColour;

				return gcolour;
			}
		}

		fclose(fd);
	}

	return INVALCOLOR;
}

/*
 * Set the current text foreground color.
 */
void
gui_mch_set_fg_color(
		guicolor_T	color)
{
	rgb_color rgb = GUI_TO_RGB(color);
	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->SetHighColor(rgb);
		gui.vimWindow->Unlock();
	}
}

/*
 * Set the current text background color.
 */
void
gui_mch_set_bg_color(
		guicolor_T	color)
{
	rgb_color rgb = GUI_TO_RGB(color);
	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->SetLowColor(rgb);
		gui.vimWindow->Unlock();
	}
}

/*
 * Set the current text special color.
 */
	void
gui_mch_set_sp_color(guicolor_T	color)
{
	// prev_sp_color = color;
}

void
gui_mch_draw_string(
		int		row,
		int		col,
		char_u	*s,
		int		len,
		int		flags)
{
	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->mchDrawString(row, col, s, len, flags);
		gui.vimWindow->Unlock();
	}
}

        guicolor_T
gui_mch_get_rgb_color(int r, int g, int b)
{
    return gui_get_rgb_color_cmn(r, g, b);
}


// Return OK if the key with the termcap name "name" is supported.
int
gui_mch_haskey(
		char_u	*name)
{
	int i;

	for (i = 0; special_keys[i].BeKeys != 0; i++)
		if (name[0] == special_keys[i].vim_code0 &&
				name[1] == special_keys[i].vim_code1)
			return OK;
	return FAIL;
}

	void
gui_mch_beep()
{
	::beep();
}

	void
gui_mch_flash(int msec)
{
	// Do a visual beep by reversing the foreground and background colors

	if (gui.vimWindow->Lock()) {
		BRect rect = gui.vimTextArea->Bounds();

		gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
		gui.vimTextArea->FillRect(rect);
		gui.vimTextArea->Sync();
		snooze(msec * 1000);	 // wait for a few msec
		gui.vimTextArea->FillRect(rect);
		gui.vimTextArea->SetDrawingMode(B_OP_COPY);
		gui.vimTextArea->Flush();
		gui.vimWindow->Unlock();
	}
}

/*
 * Invert a rectangle from row r, column c, for nr rows and nc columns.
 */
void
gui_mch_invert_rectangle(
		int		r,
		int		c,
		int		nr,
		int		nc)
{
	BRect rect;
	rect.left = FILL_X(c);
	rect.top = FILL_Y(r);
	rect.right = rect.left + nc * gui.char_width - PEN_WIDTH;
	rect.bottom = rect.top + nr * gui.char_height - PEN_WIDTH;

	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
		gui.vimTextArea->FillRect(rect);
		gui.vimTextArea->SetDrawingMode(B_OP_COPY);
		gui.vimWindow->Unlock();
	}
}

/*
 * Iconify the GUI window.
 */
	void
gui_mch_iconify()
{
	if (gui.vimWindow->Lock()) {
		gui.vimWindow->Minimize(true);
		gui.vimWindow->Unlock();
	}
}

#if defined(FEAT_EVAL) || defined(PROTO)
/*
 * Bring the Vim window to the foreground.
 */
	void
gui_mch_set_foreground(void)
{
	// TODO
}
#endif

/*
 * Set the window title
 */
void
gui_mch_settitle(
		char_u	*title,
		char_u	*icon)
{
	if (gui.vimWindow->Lock()) {
		gui.vimWindow->SetTitle((char *)title);
		gui.vimWindow->Unlock();
	}
}

/*
 * Draw a cursor without focus.
 */
	void
gui_mch_draw_hollow_cursor(guicolor_T color)
{
	gui_mch_set_fg_color(color);

	BRect r;
	r.left = FILL_X(gui.col);
	r.top = FILL_Y(gui.row);
	int cells = utf_off2cells(LineOffset[gui.row] + gui.col, 100); // TODO-TODO
	if(cells>=4) cells = 1;
	r.right = r.left + cells*gui.char_width - PEN_WIDTH;
	r.bottom = r.top + gui.char_height - PEN_WIDTH;

	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->StrokeRect(r);
		gui.vimWindow->Unlock();
		// gui_mch_flush();
	}
}

/*
 * Draw part of a cursor, only w pixels wide, and h pixels high.
 */
void
gui_mch_draw_part_cursor(
		int		w,
		int		h,
		guicolor_T	color)
{
	gui_mch_set_fg_color(color);

	BRect r;
	r.left =
#ifdef FEAT_RIGHTLEFT
		// vertical line should be on the right of current point
		CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
#endif
		FILL_X(gui.col);
	r.right = r.left + w - PEN_WIDTH;
	r.bottom = FILL_Y(gui.row + 1) - PEN_WIDTH;
	r.top = r.bottom - h + PEN_WIDTH;

	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->FillRect(r);
		gui.vimWindow->Unlock();
		// gui_mch_flush();
	}
}

/*
 * 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()
{
	gui_mch_flush();
	while (port_count(gui.vdcmp) > 0 &&
			!vim_is_input_buf_full() &&
			gui_haiku_process_event(0) >= B_OK)
		/* nothing */ ;
}

/*
 * 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(
		int		wtime)
{
	int		    focus;
	bigtime_t	    until, timeout;
	status_t	    st;

	if (wtime >= 0) {
		timeout = wtime * 1000;
		until = system_time() + timeout;
	} else {
		timeout = B_INFINITE_TIMEOUT;
	}

	focus = gui.in_focus;
	for (;;)
	{
		// Stop or start blinking when focus changes
		if (gui.in_focus != focus)
		{
			if (gui.in_focus)
				gui_mch_start_blink();
			else
				gui_mch_stop_blink();
			focus = gui.in_focus;
		}

		gui_mch_flush();
		/*
		 * Don't use gui_mch_update() because then we will spin-lock until a
		 * char arrives, instead we use gui_haiku_process_event() 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.
		 */
		st = gui_haiku_process_event(timeout);

		if (input_available())
			return OK;
		if (st < B_OK)		    // includes B_TIMED_OUT
			return FAIL;

		/*
		 * Calculate how much longer we're willing to wait for the
		 * next event.
		 */
		if (wtime >= 0) {
			timeout = until - system_time();
			if (timeout < 0)
				break;
		}
	}
	return FAIL;

}

/*
 * Output routines.
 */

/*
 * Flush any output to the screen. This is typically called before
 * the app goes to sleep.
 */
	void
gui_mch_flush()
{
	//  does this need to lock the window? Apparently not but be safe.
	if (gui.vimWindow->Lock()) {
		gui.vimWindow->Flush();
		gui.vimWindow->Unlock();
	}
	return;
}

/*
 * Clear a rectangular region of the screen from text pos (row1, col1) to
 * (row2, col2) inclusive.
 */
void
gui_mch_clear_block(
		int		row1,
		int		col1,
		int		row2,
		int		col2)
{
	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->mchClearBlock(row1, col1, row2, col2);
		gui.vimWindow->Unlock();
	}
}

	void
gui_mch_clear_all()
{
	if (gui.vimWindow->Lock()) {
		gui.vimTextArea->mchClearAll();
		gui.vimWindow->Unlock();
	}
}

/*
 * 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(
		int		row,
		int		num_lines)
{
	gui.vimTextArea->mchDeleteLines(row, num_lines);
}

/*
 * Insert the given number of lines before the given row, scrolling down any
 * following text within the scroll region.
 */
void
gui_mch_insert_lines(
		int		row,
		int		num_lines)
{
	gui.vimTextArea->mchInsertLines(row, num_lines);
}

#if defined(FEAT_MENU) || defined(PROTO)
/*
 * Menu stuff.
 */

void
gui_mch_enable_menu(
		int		flag)
{
	if (gui.vimWindow->Lock())
	{
		BMenuBar *menubar = gui.vimForm->MenuBar();
		menubar->SetEnabled(flag);
		gui.vimWindow->Unlock();
	}
}

void
gui_mch_set_menu_pos(
		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(
		vimmenu_T	*menu,
		int		idx)
{
	vimmenu_T	*parent = menu->parent;

	//  popup menu - just create it unattached
	if (menu_is_popup(menu->name) && parent == NULL) {
		BPopUpMenu* popUpMenu = new BPopUpMenu((const char*)menu->name, false, false);
		menu->submenu_id = popUpMenu;
		menu->id = NULL;
		return;
	}

	if (!menu_is_menubar(menu->name)
			|| (parent != NULL && parent->submenu_id == NULL))
		return;

	if (gui.vimWindow->Lock())
	{
		// Major re-write of the menu code, it was failing with memory corruption when
		// we started loading multiple files (the Buffer menu)
		//
		// Note we don't use the preference values yet, all are inserted into the
		// menubar on a first come-first served basis...
		//
		// richard@whitequeen.com jul 99

		BMenu *tmp;

		if ( parent )
			tmp = parent->submenu_id;
		else
			tmp = gui.vimForm->MenuBar();
		//  make sure we don't try and add the same menu twice. The Buffers menu tries to
		//  do this and Be starts to crash...

		if ( ! tmp->FindItem((const char *) menu->dname)) {

			BMenu *bmenu = new BMenu((char *)menu->dname);

			menu->submenu_id = bmenu;

			//  when we add a BMenu to another Menu, it creates the interconnecting BMenuItem
			tmp->AddItem(bmenu);

			//  Now its safe to query the menu for the associated MenuItem....
			menu->id = tmp->FindItem((const char *) menu->dname);

		}
		gui.vimWindow->Unlock();
	}
}

	void
gui_mch_toggle_tearoffs(int enable)
{
	// no tearoff menus
}

	static BMessage *
MenuMessage(vimmenu_T *menu)
{
	BMessage *m = new BMessage('menu');
	m->AddPointer("VimMenu", (void *)menu);

	return m;
}

/*
 * Add a menu item to a menu
 */
void
gui_mch_add_menu_item(
		vimmenu_T	*menu,
		int		idx)
{
	int		mnemonic = 0;
	vimmenu_T	*parent = menu->parent;

	// TODO: use menu->actext
	// This is difficult, since on Be, an accelerator must be a single char
	// and a lot of Vim ones are the standard VI commands.
	//
	// Punt for Now...
	// richard@whiequeen.com jul 99
	if (gui.vimWindow->Lock())
	{
#ifdef FEAT_TOOLBAR
		if(menu_is_toolbar(parent->name)) {
			VimToolbar *toolbar = gui.vimForm->ToolBar();
			if(toolbar != NULL) {
				toolbar->AddButton(idx, menu);
			}
		} else
#endif

		if (parent->submenu_id != NULL || menu_is_popup(parent->name)) {
			if (menu_is_separator(menu->name)) {
				BSeparatorItem *item = new BSeparatorItem();
				parent->submenu_id->AddItem(item);
				menu->id = item;
				menu->submenu_id = NULL;
			}
			else {
				BMenuItem *item = new BMenuItem((char *)menu->dname,
						MenuMessage(menu));
				item->SetTarget(gui.vimTextArea);
				item->SetTrigger((char) menu->mnemonic);
				parent->submenu_id->AddItem(item);
				menu->id = item;
				menu->submenu_id = NULL;
			}
		}
		gui.vimWindow->Unlock();
	}
}

/*
 * Destroy the machine specific menu widget.
 */
void
gui_mch_destroy_menu(
		vimmenu_T	*menu)
{
	if (gui.vimWindow->Lock())
	{
#ifdef FEAT_TOOLBAR
		if(menu->parent && menu_is_toolbar(menu->parent->name)) {
			VimToolbar *toolbar = gui.vimForm->ToolBar();
			if(toolbar != NULL) {
				toolbar->RemoveButton(menu);
			}
		} else
#endif
		{
			assert(menu->submenu_id == NULL || menu->submenu_id->CountItems() == 0);
			/*
			 * Detach this menu from its parent, so that it is not deleted
			 * twice once we get to delete that parent.
			 * Deleting a BMenuItem also deletes the associated BMenu, if any
			 * (which does not have any items anymore since they were
			 * removed and deleted before).
			 */
			BMenu *bmenu = menu->id->Menu();
			if (bmenu)
			{
				bmenu->RemoveItem(menu->id);
				/*
				 * If we removed the last item from the menu bar,
				 * resize it out of sight.
				 */
				if (bmenu == gui.vimForm->MenuBar() && bmenu->CountItems() == 0)
				{
					bmenu->ResizeTo(-MENUBAR_MARGIN, -MENUBAR_MARGIN);
				}
			}
			delete menu->id;
			menu->id = NULL;
			menu->submenu_id = NULL;

			gui.menu_height = (int) gui.vimForm->MenuHeight();
		}
		gui.vimWindow->Unlock();
	}
}

/*
 * Make a menu either grey or not grey.
 */
void
gui_mch_menu_grey(
		vimmenu_T	*menu,
		int		grey)
{
#ifdef FEAT_TOOLBAR
	if(menu->parent && menu_is_toolbar(menu->parent->name)) {
		if (gui.vimWindow->Lock()) {
			VimToolbar *toolbar = gui.vimForm->ToolBar();
			if(toolbar != NULL) {
				toolbar->GrayButton(menu, grey);
			}
			gui.vimWindow->Unlock();
		}
	} else
#endif
	if (menu->id != NULL)
		menu->id->SetEnabled(!grey);
}

/*
 * Make menu item hidden or not hidden
 */
void
gui_mch_menu_hidden(
		vimmenu_T	*menu,
		int		hidden)
{
	if (menu->id != NULL)
		menu->id->SetEnabled(!hidden);
}

/*
 * This is called after setting all the menus to grey/hidden or not.
 */
	void
gui_mch_draw_menubar()
{
	// Nothing to do in BeOS
}

	void
gui_mch_show_popupmenu(vimmenu_T *menu)
{
	if (!menu_is_popup(menu->name) || menu->submenu_id == NULL)
		return;

	BPopUpMenu* popupMenu = dynamic_cast<BPopUpMenu*>(menu->submenu_id);
	if (popupMenu == NULL)
		return;

	BPoint point;
	if(gui.vimWindow->Lock()) {
		uint32 buttons = 0;
		gui.vimTextArea->GetMouse(&point, &buttons);
		gui.vimTextArea->ConvertToScreen(&point);
		gui.vimWindow->Unlock();
	}
	popupMenu->Go(point, true);
}

#endif // FEAT_MENU

// Mouse stuff

#ifdef FEAT_CLIPBOARD
/*
 * Clipboard stuff, for cutting and pasting text to other windows.
 */
char textplain[] = "text/plain";
char vimselectiontype[] = "application/x-vnd.Rhialto-Vim-selectiontype";

/*
 * Get the current selection and put it in the clipboard register.
 */
	void
clip_mch_request_selection(Clipboard_T *cbd)
{
	if (be_clipboard->Lock())
	{
		BMessage *m = be_clipboard->Data();
		// m->PrintToStream();

		char_u *string = NULL;
		ssize_t stringlen = -1;

		if (m->FindData(textplain, B_MIME_TYPE,
					(const void **)&string, &stringlen) == B_OK
				|| m->FindString("text", (const char **)&string) == B_OK)
		{
			if (stringlen == -1)
				stringlen = STRLEN(string);

			int type;
			char *seltype;
			ssize_t seltypelen;

			/*
			 * Try to get the special vim selection type first
			 */
			if (m->FindData(vimselectiontype, B_MIME_TYPE,
						(const void **)&seltype, &seltypelen) == B_OK)
			{
				switch (*seltype)
				{
					default:
					case 'L':	type = MLINE;	break;
					case 'C':	type = MCHAR;	break;
#ifdef FEAT_VISUAL
					case 'B':	type = MBLOCK;	break;
#endif
				}
			}
			else
			{
				// Otherwise use heuristic as documented
				type = memchr(string, stringlen, '\n') ? MLINE : MCHAR;
			}
			clip_yank_selection(type, string, (long)stringlen, cbd);
		}
		be_clipboard->Unlock();
	}
}
/*
 * Make vim the owner of the current selection.
 */
	void
clip_mch_lose_selection(Clipboard_T *cbd)
{
	// Nothing needs to be done here
}

/*
 * Make vim the owner of the current selection.  Return OK upon success.
 */
	int
clip_mch_own_selection(Clipboard_T *cbd)
{
	/*
	 * Never actually own the clipboard.  If another application sets the
	 * clipboard, we don't want to think that we still own it.
	 */
	return FAIL;
}

/*
 * Send the current selection to the clipboard.
 */
	void
clip_mch_set_selection(Clipboard_T *cbd)
{
	if (be_clipboard->Lock())
	{
		be_clipboard->Clear();
		BMessage *m = be_clipboard->Data();
		assert(m);

		// If the '*' register isn't already filled in, fill it in now
		cbd->owned = TRUE;
		clip_get_selection(cbd);
		cbd->owned = FALSE;

		char_u  *str = NULL;
		long_u  count;
		int	type;

		type = clip_convert_selection(&str, &count, cbd);

		if (type < 0)
			return;

		m->AddData(textplain, B_MIME_TYPE, (void *)str, count);

		// Add type of selection
		char    vtype;
		switch (type)
		{
			default:
			case MLINE:    vtype = 'L';    break;
			case MCHAR:    vtype = 'C';    break;
#ifdef FEAT_VISUAL
			case MBLOCK:   vtype = 'B';    break;
#endif
		}
		m->AddData(vimselectiontype, B_MIME_TYPE, (void *)&vtype, 1);

		vim_free(str);

		be_clipboard->Commit();
		be_clipboard->Unlock();
	}
}

#endif	// FEAT_CLIPBOARD

#ifdef FEAT_BROWSE
/*
 * Pop open a file browser and return the file selected, in allocated memory,
 * or NULL if Cancel is hit.
 *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
 *  title   - Title message for the file browser dialog.
 *  dflt    - Default name of file.
 *  ext     - Default extension to be added to files without extensions.
 *  initdir - directory in which to open the browser (NULL = current dir)
 *  filter  - Filter for matched files to choose from.
 *  Has a format like this:
 *  "C Files (*.c)\0*.c\0"
 *  "All Files\0*.*\0\0"
 *  If these two strings were concatenated, then a choice of two file
 *  filters will be selectable to the user.  Then only matching files will
 *  be shown in the browser.  If NULL, the default allows all files.
 *
 *  *NOTE* - the filter string must be terminated with TWO nulls.
 */
char_u *
gui_mch_browse(
		int saving,
		char_u *title,
		char_u *dflt,
		char_u *ext,
		char_u *initdir,
		char_u *filter)
{
	gui.vimApp->fFilePanel = new BFilePanel((saving == TRUE) ? B_SAVE_PANEL : B_OPEN_PANEL,
			NULL, NULL, 0, false,
			new BMessage((saving == TRUE) ? 'save' : 'open'), NULL, true);

	gui.vimApp->fBrowsedPath.Unset();

	gui.vimApp->fFilePanel->Window()->SetTitle((char*)title);
	gui.vimApp->fFilePanel->SetPanelDirectory((const char*)initdir);

	gui.vimApp->fFilePanel->Show();

	gui.vimApp->fFilePanelSem = create_sem(0, "FilePanelSem");

	while(acquire_sem(gui.vimApp->fFilePanelSem) == B_INTERRUPTED);

	char_u *fileName = NULL;
	status_t result = gui.vimApp->fBrowsedPath.InitCheck();
	if(result == B_OK) {
		fileName = vim_strsave((char_u*)gui.vimApp->fBrowsedPath.Path());
	} else
		if(result != B_NO_INIT) {
			fprintf(stderr, "gui_mch_browse: BPath error: %#08x (%s)\n",
					result, strerror(result));
		}

	delete gui.vimApp->fFilePanel;
	gui.vimApp->fFilePanel = NULL;

	return fileName;
}
#endif // FEAT_BROWSE


#if defined(FEAT_GUI_DIALOG)

/*
 * 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.
 */

int
gui_mch_dialog(
		int		 type,
		char_u	*title,
		char_u	*message,
		char_u	*buttons,
		int		 dfltbutton,
		char_u	*textfield,
		int ex_cmd)
{
	VimDialog *dialog = new VimDialog(type, (char*)title, (char*)message,
			(char*)buttons, dfltbutton, (char*)textfield, ex_cmd);
	return dialog->Go();
}

#endif // FEAT_GUI_DIALOG


/*
 * Return the RGB value of a pixel as long.
 */
    guicolor_T
gui_mch_get_rgb(guicolor_T pixel)
{
	rgb_color rgb = GUI_TO_RGB(pixel);

	return ((rgb.red & 0xff) << 16) + ((rgb.green & 0xff) << 8)
		+ (rgb.blue & 0xff);
}

	void
gui_mch_setmouse(int x, int y)
{
	TRACE();
	// TODO
}

#ifdef FEAT_MBYTE_IME
	void
im_set_position(int row, int col)
{
	if(gui.vimWindow->Lock())
	{
		gui.vimTextArea->DrawIMString();
		gui.vimWindow->Unlock();
	}
	return;
}
#endif

	void
gui_mch_show_toolbar(int showit)
{
	VimToolbar *toolbar = gui.vimForm->ToolBar();
	gui.toolbar_height = (toolbar && showit) ? toolbar->ToolbarHeight() : 0.;
}

	void
gui_mch_set_toolbar_pos(int x, int y, int w, int h)
{
	VimToolbar *toolbar = gui.vimForm->ToolBar();
	if(toolbar != NULL) {
		if (gui.vimWindow->Lock()) {
			toolbar->MoveTo(x, y);
			toolbar->ResizeTo(w - 1, h - 1);
			gui.vimWindow->Unlock();
		}
	}
}

#if defined(FEAT_GUI_TABLINE) || defined(PROTO)

/*
 * Show or hide the tabline.
 */
    void
gui_mch_show_tabline(int showit)
{
	VimTabLine *tabLine = gui.vimForm->TabLine();

    if (tabLine == NULL)
		return;

    if (!showit != !gui.vimForm->IsShowingTabLine()) {
		gui.vimForm->SetShowingTabLine(showit != 0);
		gui.tabline_height = gui.vimForm->TablineHeight();
    }
}

	void
gui_mch_set_tabline_pos(int x, int y, int w, int h)
{
	VimTabLine *tabLine = gui.vimForm->TabLine();
	if(tabLine != NULL) {
		if (gui.vimWindow->Lock()) {
			tabLine->MoveTo(x, y);
			tabLine->ResizeTo(w - 1, h - 1);
			gui.vimWindow->Unlock();
		}
	}
}

/*
 * Return TRUE when tabline is displayed.
 */
    int
gui_mch_showing_tabline()
{
	VimTabLine *tabLine = gui.vimForm->TabLine();
    return tabLine != NULL && gui.vimForm->IsShowingTabLine();
}

/*
 * Update the labels of the tabline.
 */
    void
gui_mch_update_tabline()
{
    tabpage_T	*tp;
    int		nr = 0;
    int		curtabidx = 0;

	VimTabLine *tabLine = gui.vimForm->TabLine();

    if (tabLine == NULL)
		return;

	gui.vimWindow->Lock();

    // Add a label for each tab page.  They all contain the same text area.
    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) {
		if (tp == curtab)
		    curtabidx = nr;

		BTab* tab = tabLine->TabAt(nr);

		if (tab == NULL) {
			tab = new VimTabLine::VimTab();
			tabLine->AddTab(NULL, tab);
		}

		get_tabline_label(tp, FALSE);
		tab->SetLabel((const char*)NameBuff);
		tabLine->Invalidate();
    }

    // Remove any old labels.
	while (nr < tabLine->CountTabs())
		tabLine->RemoveTab(nr);

	if(tabLine->Selection() != curtabidx)
		tabLine->Select(curtabidx);

	gui.vimWindow->Unlock();
}

/*
 * Set the current tab to "nr".  First tab is 1.
 */
    void
gui_mch_set_curtab(int nr)
{
	VimTabLine *tabLine = gui.vimForm->TabLine();
	if(tabLine == NULL)
		return;

	gui.vimWindow->Lock();

	if(tabLine->Selection() != nr -1)
		tabLine->Select(nr -1);

	gui.vimWindow->Unlock();
}

#endif // FEAT_GUI_TABLINE