text: /* 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;
  	}
      }
      return true;
  }
  
      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);
      }
      return 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(FEAT_GUI_HAIKU) || 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(int may_call_gui_update_cursor)
  {
      // 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 = (char_u*)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(TRUE);
  	    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
  