Mercurial > vim
view src/gui_haiku.cc @ 32721:94f4a488412e v9.0.1683
Updated runtime files
Commit: https://github.com/vim/vim/commit/6efb1980336ff324e9c57a4e282530b952fca816
Author: Christian Brabandt <cb@256bit.org>
Date: Thu Aug 10 05:44:25 2023 +0200
Updated runtime files
This is a collection of various PRs from github that all require a minor
patch number:
1) https://github.com/vim/vim/pull/12612
Do not conflate dictionary key with end of block
2) https://github.com/vim/vim/pull/12729:
When saving and restoring 'undolevels', the constructs `&undolevels` and
`:set undolevels` are problematic.
The construct `&undolevels` reads an unpredictable value; it will be the
local option value (if one has been set), or the global option value
(otherwise), making it unsuitable for saving a value for later
restoration.
Similarly, if a local option value has been set for 'undolevels',
temporarily modifying the option via `:set undolevels` changes the local
value as well as the global value, requiring extra work to restore both
values.
Saving and restoring the option value in one step via the construct
`:let &undolevels = &undolevels` appears to make no changes to the
'undolevels' option, but if a local option has been set to a different
value than the global option, it has the unintended effect of changing
the global 'undolevels' value to the local value.
Update the documentation to explain these issues and recommend explicit
use of global and local option values when saving and restoring. Update
some unit tests to use `g:undolevels`.
3) https://github.com/vim/vim/pull/12702:
Problem: Pip requirements files are not recognized.
Solution: Add a pattern to match pip requirements files.
4) https://github.com/vim/vim/pull/12688:
Add indent file and tests for ABB Rapid
5) https://github.com/vim/vim/pull/12668:
Use Lua 5.1 numeric escapes in tests and add to CI
Only Lua 5.2+ and LuaJIT understand hexadecimal escapes in strings. Lua
5.1 only supports decimal escapes:
> A character in a string can also be specified by its numerical value
> using the escape sequence \ddd, where ddd is a sequence of up to three
> decimal digits. (Note that if a numerical escape is to be followed by a
> digit, it must be expressed using exactly three digits.) Strings in Lua
> can contain any 8-bit value, including embedded zeros, which can be
> specified as '\0'.
To make sure this works with Lua 5.4 and Lua 5.1 change the Vim CI to
run with Lua 5.1 as well as Lua 5.4
6) https://github.com/vim/vim/pull/12631:
Add hurl filetype detection
7) https://github.com/vim/vim/pull/12573:
Problem: Files for haskell persistent library are not recognized
Solution: Add pattern persistentmodels for haskell persistent library
closes: #12612
closes: #12729
closes: #12702
closes: #12688
closes: #12668
closes: #12631
closes: #12573
Co-authored-by: lacygoill <lacygoill@lacygoill.me>
Co-authored-by: Michael Henry <drmikehenry@drmikehenry.com>
Co-authored-by: ObserverOfTime <chronobserver@disroot.org>
Co-authored-by: KnoP-01 <knosowski@graeffrobotics.de>
Co-authored-by: James McCoy <jamessan@jamessan.com>
Co-authored-by: Jacob Pfeifer <jacob@pfeifer.dev>
Co-authored-by: Borys Lykah <lykahb@fastmail.com>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 10 Aug 2023 06:30:06 +0200 |
parents | 695b50472e85 |
children | 1629cc65d78d |
line wrap: on
line source
/* vi:set ts=8 sts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * BeBox GUI support Copyright 1998 by Olaf Seibert. * All Rights Reserved. * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. * * Based on "GUI support for the Buzzword Enhanced Operating System." * * Ported to R4 by Richard Offer <richard@whitequeen.com> Jul 99 * * Haiku support by Siarzhuk Zharski <imker@gmx.li> Apr-Mai 2009 * */ /* * Structure of the Haiku GUI code: * * There are 3 threads. * 1. The initial thread. In gui_mch_prepare() this gets to run the * BApplication message loop. But before it starts doing that, * it creates thread 2 * 2. The main() thread. This thread is created in gui_mch_prepare() * and its purpose in life is to call main(argc, argv) again. * This thread is doing the bulk of the work. * 3. Sooner or later, a window is opened by the main() thread. This * causes a second message loop to be created: the window thread. * * == alternatively === * * #if RUN_BAPPLICATION_IN_NEW_THREAD... * * 1. The initial thread. In gui_mch_prepare() this gets to spawn * thread 2. After doing that, it returns to main() to do the * bulk of the work, being the main() thread. * 2. Runs the BApplication. * 3. The window thread, just like in the first case. * * This second alternative is cleaner from Vim's viewpoint. However, * the BeBook seems to assume everywhere that the BApplication *must* * run in the initial thread. So perhaps doing otherwise is very wrong. * * However, from a B_SINGLE_LAUNCH viewpoint, the first is better. * If Vim is marked "Single Launch" in its application resources, * and a file is dropped on the Vim icon, and another Vim is already * running, the file is passed on to the earlier Vim. This happens * in BApplication::Run(). So we want Vim to terminate if * BApplication::Run() terminates. (See the BeBook, on BApplication. * However, it seems that the second copy of Vim isn't even started * in this case... which is for the better since I wouldn't know how * to detect this case.) * * Communication between these threads occurs mostly by translating * BMessages that come in and posting an appropriate translation on * the VDCMP (Vim Direct Communication Message Port). Therefore the * actions required for keypresses and window resizes, etc, are mostly * performed in the main() thread. * * A notable exception to this is the Draw() event. The redrawing of * the window contents is performed asynchronously from the window * thread. To make this work correctly, a locking protocol is used when * any thread is accessing the essential variables that are used by * the window thread. * * This locking protocol consists of locking Vim's window. This is both * convenient and necessary. */ extern "C" { #include <assert.h> #include <float.h> #include <syslog.h> #include "vim.h" #include "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) { (char_u)((g) >> 16), (char_u)((g) >> 8), (char_u)((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 ARRAY_LENGTH(special_keys) // ---------------- 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, deleted in 8.2.1422) // Update the screen display update_screen(UPD_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. */ mch_signal(SIGINT, SIG_IGN); mch_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. */ mch_signal(SIGINT, SIG_IGN); mch_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 truly 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(); } } int gui_mch_get_scrollbar_xpadding(void) { // TODO: Calculate the padding for adjust scrollbar position when the // Window is maximized. return 0; } int gui_mch_get_scrollbar_ypadding(void) { // TODO: Calculate the padding for adjust scrollbar position when the // Window is maximized. return 0; } 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_unknown_font_str), 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_unknown_font_str), 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; } 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) { return gui_get_color_cmn(name); } /* * 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(); #ifdef MESSAGE_QUEUE # ifdef FEAT_TIMERS did_add_timer = FALSE; # endif parse_queued_messages(); # ifdef FEAT_TIMERS if (did_add_timer) // Need to recompute the waiting time. break; # endif # ifdef FEAT_JOB_CHANNEL if (has_any_channel()) { if (wtime < 0 || timeout > 20000) timeout = 20000; } else if (wtime < 0) timeout = B_INFINITE_TIMEOUT; # endif #endif /* * 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 it's 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