diff options
Diffstat (limited to 'src/gui_haiku.cc')
-rw-r--r-- | src/gui_haiku.cc | 5242 |
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); + + 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, |