summaryrefslogtreecommitdiffstats
path: root/src/gui_haiku.cc
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-02-26 16:16:53 +0100
committerBram Moolenaar <Bram@vim.org>2020-02-26 16:16:53 +0100
commitb3f740695a0199d22cd97aee314f06c7ae32d2ea (patch)
tree8221662bb578d80a7a044f8a20aef09394a5a1a8 /src/gui_haiku.cc
parentd672dde584effd55702ee15efec4cb2a8c77bf85 (diff)
patch 8.2.0320: no Haiku supportv8.2.0320
Problem: No Haiku support. Solution: Add support for Haiku. (Emir Sari, closes #5605)
Diffstat (limited to 'src/gui_haiku.cc')
-rw-r--r--src/gui_haiku.cc5242
1 files changed, 5242 insertions, 0 deletions
diff --git a/src/gui_haiku.cc b/src/gui_haiku.cc
new file mode 100644
index 0000000000..b7ecf64402
--- /dev/null
+++ b/src/gui_haiku.cc
@@ -0,0 +1,5242 @@
+/* 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);