mirror of
https://github.com/vim/vim
synced 2025-07-16 09:12:00 +00:00
Problem: Cannot add color names. Solution: Add the v:colornames dictionary. (Drew Vogel, closes #8761)
5095 lines
120 KiB
C++
5095 lines
120 KiB
C++
/* vi:set ts=8 sts=4 sw=4:
|
|
*
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
* BeBox GUI support Copyright 1998 by Olaf Seibert.
|
|
* All Rights Reserved.
|
|
*
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
*
|
|
* Based on "GUI support for the Buzzword Enhanced Operating System."
|
|
*
|
|
* Ported to R4 by Richard Offer <richard@whitequeen.com> Jul 99
|
|
*
|
|
* Haiku support by Siarzhuk Zharski <imker@gmx.li> Apr-Mai 2009
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Structure of the Haiku GUI code:
|
|
*
|
|
* There are 3 threads.
|
|
* 1. The initial thread. In gui_mch_prepare() this gets to run the
|
|
* BApplication message loop. But before it starts doing that,
|
|
* it creates thread 2
|
|
* 2. The main() thread. This thread is created in gui_mch_prepare()
|
|
* and its purpose in life is to call main(argc, argv) again.
|
|
* This thread is doing the bulk of the work.
|
|
* 3. Sooner or later, a window is opened by the main() thread. This
|
|
* causes a second message loop to be created: the window thread.
|
|
*
|
|
* == alternatively ===
|
|
*
|
|
* #if RUN_BAPPLICATION_IN_NEW_THREAD...
|
|
*
|
|
* 1. The initial thread. In gui_mch_prepare() this gets to spawn
|
|
* thread 2. After doing that, it returns to main() to do the
|
|
* bulk of the work, being the main() thread.
|
|
* 2. Runs the BApplication.
|
|
* 3. The window thread, just like in the first case.
|
|
*
|
|
* This second alternative is cleaner from Vim's viewpoint. However,
|
|
* the BeBook seems to assume everywhere that the BApplication *must*
|
|
* run in the initial thread. So perhaps doing otherwise is very wrong.
|
|
*
|
|
* However, from a B_SINGLE_LAUNCH viewpoint, the first is better.
|
|
* If Vim is marked "Single Launch" in its application resources,
|
|
* and a file is dropped on the Vim icon, and another Vim is already
|
|
* running, the file is passed on to the earlier Vim. This happens
|
|
* in BApplication::Run(). So we want Vim to terminate if
|
|
* BApplication::Run() terminates. (See the BeBook, on BApplication.
|
|
* However, it seems that the second copy of Vim isn't even started
|
|
* in this case... which is for the better since I wouldn't know how
|
|
* to detect this case.)
|
|
*
|
|
* Communication between these threads occurs mostly by translating
|
|
* BMessages that come in and posting an appropriate translation on
|
|
* the VDCMP (Vim Direct Communication Message Port). Therefore the
|
|
* actions required for keypresses and window resizes, etc, are mostly
|
|
* performed in the main() thread.
|
|
*
|
|
* A notable exception to this is the Draw() event. The redrawing of
|
|
* the window contents is performed asynchronously from the window
|
|
* thread. To make this work correctly, a locking protocol is used when
|
|
* any thread is accessing the essential variables that are used by
|
|
* the window thread.
|
|
*
|
|
* This locking protocol consists of locking Vim's window. This is both
|
|
* convenient and necessary.
|
|
*/
|
|
|
|
extern "C" {
|
|
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
#include <syslog.h>
|
|
|
|
#include "vim.h"
|
|
#include "globals.h"
|
|
#include "proto.h"
|
|
#include "version.h"
|
|
|
|
} // extern "C"
|
|
|
|
// ---------------- start of header part ----------------
|
|
|
|
//#include <Alert.h>
|
|
#include <Application.h>
|
|
#include <Beep.h>
|
|
#include <Bitmap.h>
|
|
#include <Box.h>
|
|
#include <Button.h>
|
|
#include <Clipboard.h>
|
|
#include <Debug.h>
|
|
//#include <Directory.h>
|
|
//#include <Entry.h>
|
|
#include <File.h>
|
|
#include <FilePanel.h>
|
|
#include <FindDirectory.h>
|
|
//#include <Font.h>
|
|
#include <IconUtils.h>
|
|
#include <Input.h>
|
|
#include <ListView.h>
|
|
#include <MenuBar.h>
|
|
#include <MenuItem.h>
|
|
//#include <MessageQueue.h>
|
|
//#include <OS.h>
|
|
#include <Path.h>
|
|
#include <PictureButton.h>
|
|
#include <PopUpMenu.h>
|
|
//#include <Region.h>
|
|
#include <Resources.h>
|
|
//#include <Roster.h>
|
|
#include <Screen.h>
|
|
#include <ScrollBar.h>
|
|
#include <ScrollView.h>
|
|
#include <String.h>
|
|
#include <StringView.h>
|
|
//#include <SupportDefs.h>
|
|
#include <TabView.h>
|
|
#include <TextControl.h>
|
|
#include <TextView.h>
|
|
#include <TranslationUtils.h>
|
|
#include <TranslatorFormats.h>
|
|
#include <View.h>
|
|
#include <Window.h>
|
|
|
|
class VimApp;
|
|
class VimFormView;
|
|
class VimTextAreaView;
|
|
class VimWindow;
|
|
class VimToolbar;
|
|
class VimTabLine;
|
|
|
|
extern key_map *keyMap;
|
|
extern char *keyMapChars;
|
|
|
|
extern int main(int argc, char **argv);
|
|
|
|
#ifndef B_MAX_PORT_COUNT
|
|
#define B_MAX_PORT_COUNT 255
|
|
#endif
|
|
|
|
// VimApp seems comparable to the X "vimShell"
|
|
class VimApp: public BApplication
|
|
{
|
|
typedef BApplication Inherited;
|
|
public:
|
|
VimApp(const char *appsig);
|
|
~VimApp();
|
|
|
|
// callbacks:
|
|
#if 0
|
|
virtual void DispatchMessage(BMessage *m, BHandler *h)
|
|
{
|
|
m->PrintToStream();
|
|
Inherited::DispatchMessage(m, h);
|
|
}
|
|
#endif
|
|
virtual void ReadyToRun();
|
|
virtual void ArgvReceived(int32 argc, char **argv);
|
|
virtual void RefsReceived(BMessage *m);
|
|
virtual bool QuitRequested();
|
|
virtual void MessageReceived(BMessage *m);
|
|
|
|
static void SendRefs(BMessage *m, bool changedir);
|
|
|
|
sem_id fFilePanelSem;
|
|
BFilePanel* fFilePanel;
|
|
BPath fBrowsedPath;
|
|
private:
|
|
};
|
|
|
|
class VimWindow: public BWindow
|
|
{
|
|
typedef BWindow Inherited;
|
|
public:
|
|
VimWindow();
|
|
~VimWindow();
|
|
|
|
// virtual void DispatchMessage(BMessage *m, BHandler *h);
|
|
virtual void WindowActivated(bool active);
|
|
virtual bool QuitRequested();
|
|
|
|
VimFormView *formView;
|
|
|
|
private:
|
|
void init();
|
|
|
|
};
|
|
|
|
class VimFormView: public BView
|
|
{
|
|
typedef BView Inherited;
|
|
public:
|
|
VimFormView(BRect frame);
|
|
~VimFormView();
|
|
|
|
// callbacks:
|
|
virtual void AllAttached();
|
|
virtual void FrameResized(float new_width, float new_height);
|
|
|
|
#define MENUBAR_MARGIN 1
|
|
float MenuHeight() const
|
|
{ return menuBar ? menuBar->Frame().Height() + MENUBAR_MARGIN: 0; }
|
|
BMenuBar *MenuBar() const
|
|
{ return menuBar; }
|
|
|
|
private:
|
|
void init(BRect);
|
|
|
|
BMenuBar *menuBar;
|
|
VimTextAreaView *textArea;
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
public:
|
|
float ToolbarHeight() const;
|
|
VimToolbar *ToolBar() const
|
|
{ return toolBar; }
|
|
private:
|
|
VimToolbar *toolBar;
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
public:
|
|
VimTabLine *TabLine() const { return tabLine; }
|
|
bool IsShowingTabLine() const { return showingTabLine; }
|
|
void SetShowingTabLine(bool showing) { showingTabLine = showing; }
|
|
float TablineHeight() const;
|
|
private:
|
|
VimTabLine *tabLine;
|
|
int showingTabLine;
|
|
#endif
|
|
};
|
|
|
|
class VimTextAreaView: public BView
|
|
{
|
|
typedef BView Inherited;
|
|
public:
|
|
VimTextAreaView(BRect frame);
|
|
~VimTextAreaView();
|
|
|
|
// callbacks:
|
|
virtual void Draw(BRect updateRect);
|
|
virtual void KeyDown(const char *bytes, int32 numBytes);
|
|
virtual void MouseDown(BPoint point);
|
|
virtual void MouseUp(BPoint point);
|
|
virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
|
|
virtual void MessageReceived(BMessage *m);
|
|
|
|
// own functions:
|
|
int mchInitFont(char_u *name);
|
|
void mchDrawString(int row, int col, char_u *s, int len, int flags);
|
|
void mchClearBlock(int row1, int col1, int row2, int col2);
|
|
void mchClearAll();
|
|
void mchDeleteLines(int row, int num_lines);
|
|
void mchInsertLines(int row, int num_lines);
|
|
|
|
static void guiSendMouseEvent(int button, int x, int y, int repeated_click, int_u modifiers);
|
|
static void guiMouseMoved(int x, int y);
|
|
static void guiBlankMouse(bool should_hide);
|
|
static int_u mouseModifiersToVim(int32 beModifiers);
|
|
|
|
int32 mouseDragEventCount;
|
|
|
|
#ifdef FEAT_MBYTE_IME
|
|
void DrawIMString(void);
|
|
#endif
|
|
|
|
private:
|
|
void init(BRect);
|
|
|
|
int_u vimMouseButton;
|
|
int_u vimMouseModifiers;
|
|
|
|
#ifdef FEAT_MBYTE_IME
|
|
struct {
|
|
BMessenger* messenger;
|
|
BMessage* message;
|
|
BPoint location;
|
|
int row;
|
|
int col;
|
|
int count;
|
|
} IMData;
|
|
#endif
|
|
};
|
|
|
|
class VimScrollBar: public BScrollBar
|
|
{
|
|
typedef BScrollBar Inherited;
|
|
public:
|
|
VimScrollBar(scrollbar_T *gsb, orientation posture);
|
|
~VimScrollBar();
|
|
|
|
virtual void ValueChanged(float newValue);
|
|
virtual void MouseUp(BPoint where);
|
|
void SetValue(float newval);
|
|
scrollbar_T *getGsb()
|
|
{ return gsb; }
|
|
|
|
int32 scrollEventCount;
|
|
|
|
private:
|
|
scrollbar_T *gsb;
|
|
float ignoreValue;
|
|
};
|
|
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
|
|
class VimToolbar : public BBox
|
|
{
|
|
static BBitmap *normalButtonsBitmap;
|
|
static BBitmap *grayedButtonsBitmap;
|
|
|
|
BBitmap *LoadVimBitmap(const char* fileName);
|
|
bool GetPictureFromBitmap(BPicture *pictureTo, int32 index, BBitmap *bitmapFrom, bool pressed);
|
|
bool ModifyBitmapToGrayed(BBitmap *bitmap);
|
|
|
|
BList fButtonsList;
|
|
void InvalidateLayout();
|
|
|
|
public:
|
|
VimToolbar(BRect frame, const char * name);
|
|
~VimToolbar();
|
|
|
|
bool PrepareButtonBitmaps();
|
|
|
|
bool AddButton(int32 index, vimmenu_T *menu);
|
|
bool RemoveButton(vimmenu_T *menu);
|
|
bool GrayButton(vimmenu_T *menu, int grey);
|
|
|
|
float ToolbarHeight() const;
|
|
virtual void AttachedToWindow();
|
|
};
|
|
|
|
BBitmap *VimToolbar::normalButtonsBitmap = NULL;
|
|
BBitmap *VimToolbar::grayedButtonsBitmap = NULL;
|
|
|
|
const float ToolbarMargin = 3.;
|
|
const float ButtonMargin = 3.;
|
|
|
|
#endif //FEAT_TOOLBAR
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
|
|
class VimTabLine : public BTabView
|
|
{
|
|
public:
|
|
class VimTab : public BTab {
|
|
public:
|
|
VimTab() : BTab(new BView(BRect(), "-Empty-", 0, 0)) {}
|
|
|
|
virtual void Select(BView* owner);
|
|
};
|
|
|
|
VimTabLine(BRect r) : BTabView(r, "vimTabLine", B_WIDTH_FROM_LABEL,
|
|
B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT, B_WILL_DRAW | B_FRAME_EVENTS) {}
|
|
|
|
float TablineHeight() const;
|
|
virtual void MouseDown(BPoint point);
|
|
};
|
|
|
|
#endif //FEAT_GUI_TABLINE
|
|
|
|
|
|
// For caching the fonts that are used;
|
|
// Vim seems rather sloppy in this regard.
|
|
class VimFont: public BFont
|
|
{
|
|
typedef BFont Inherited;
|
|
public:
|
|
VimFont();
|
|
VimFont(const VimFont *rhs);
|
|
VimFont(const BFont *rhs);
|
|
VimFont(const VimFont &rhs);
|
|
~VimFont();
|
|
|
|
VimFont *next;
|
|
int refcount;
|
|
char_u *name;
|
|
|
|
private:
|
|
void init();
|
|
};
|
|
|
|
#if defined(FEAT_GUI_DIALOG)
|
|
|
|
class VimDialog : public BWindow
|
|
{
|
|
typedef BWindow Inherited;
|
|
|
|
BButton* _CreateButton(int32 which, const char* label);
|
|
|
|
public:
|
|
|
|
class View : public BView {
|
|
typedef BView Inherited;
|
|
|
|
public:
|
|
View(BRect frame);
|
|
~View();
|
|
|
|
virtual void Draw(BRect updateRect);
|
|
void InitIcon(int32 type);
|
|
|
|
private:
|
|
BBitmap* fIconBitmap;
|
|
};
|
|
|
|
VimDialog(int type, const char *title, const char *message,
|
|
const char *buttons, int dfltbutton, const char *textfield,
|
|
int ex_cmd);
|
|
~VimDialog();
|
|
|
|
int Go();
|
|
|
|
virtual void MessageReceived(BMessage *msg);
|
|
|
|
private:
|
|
sem_id fDialogSem;
|
|
int fDialogValue;
|
|
BList fButtonsList;
|
|
BTextView* fMessageView;
|
|
BTextControl* fInputControl;
|
|
const char* fInputValue;
|
|
};
|
|
|
|
class VimSelectFontDialog : public BWindow
|
|
{
|
|
typedef BWindow Inherited;
|
|
|
|
void _CleanList(BListView* list);
|
|
void _UpdateFontStyles();
|
|
void _UpdateSizeInputPreview();
|
|
void _UpdateFontPreview();
|
|
bool _UpdateFromListItem(BListView* list, char* text, int textSize);
|
|
public:
|
|
|
|
VimSelectFontDialog(font_family* family, font_style* style, float* size);
|
|
~VimSelectFontDialog();
|
|
|
|
bool Go();
|
|
|
|
virtual void MessageReceived(BMessage *msg);
|
|
|
|
private:
|
|
status_t fStatus;
|
|
sem_id fDialogSem;
|
|
bool fDialogValue;
|
|
font_family* fFamily;
|
|
font_style* fStyle;
|
|
float* fSize;
|
|
font_family fFontFamily;
|
|
font_style fFontStyle;
|
|
float fFontSize;
|
|
BStringView* fPreview;
|
|
BListView* fFamiliesList;
|
|
BListView* fStylesList;
|
|
BListView* fSizesList;
|
|
BTextControl* fSizesInput;
|
|
};
|
|
|
|
#endif // FEAT_GUI_DIALOG
|
|
|
|
// ---------------- end of GUI classes ----------------
|
|
|
|
struct MainArgs {
|
|
int argc;
|
|
char **argv;
|
|
};
|
|
|
|
// These messages are copied through the VDCMP.
|
|
// Therefore they ought not to have anything fancy.
|
|
// They must be of POD type (Plain Old Data)
|
|
// as the C++ standard calls them.
|
|
|
|
#define KEY_MSG_BUFSIZ 7
|
|
#if KEY_MSG_BUFSIZ < MAX_KEY_CODE_LEN
|
|
#error Increase KEY_MSG_BUFSIZ!
|
|
#endif
|
|
|
|
struct VimKeyMsg {
|
|
char_u length;
|
|
char_u chars[KEY_MSG_BUFSIZ]; // contains Vim encoding
|
|
bool csi_escape;
|
|
};
|
|
|
|
struct VimResizeMsg {
|
|
int width;
|
|
int height;
|
|
};
|
|
|
|
struct VimScrollBarMsg {
|
|
VimScrollBar *sb;
|
|
long value;
|
|
int stillDragging;
|
|
};
|
|
|
|
struct VimMenuMsg {
|
|
vimmenu_T *guiMenu;
|
|
};
|
|
|
|
struct VimMouseMsg {
|
|
int button;
|
|
int x;
|
|
int y;
|
|
int repeated_click;
|
|
int_u modifiers;
|
|
};
|
|
|
|
struct VimMouseMovedMsg {
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
struct VimFocusMsg {
|
|
bool active;
|
|
};
|
|
|
|
struct VimRefsMsg {
|
|
BMessage *message;
|
|
bool changedir;
|
|
};
|
|
|
|
struct VimTablineMsg {
|
|
int index;
|
|
};
|
|
|
|
struct VimTablineMenuMsg {
|
|
int index;
|
|
int event;
|
|
};
|
|
|
|
struct VimMsg {
|
|
enum VimMsgType {
|
|
Key, Resize, ScrollBar, Menu, Mouse, MouseMoved, Focus, Refs, Tabline, TablineMenu
|
|
};
|
|
|
|
union {
|
|
struct VimKeyMsg Key;
|
|
struct VimResizeMsg NewSize;
|
|
struct VimScrollBarMsg Scroll;
|
|
struct VimMenuMsg Menu;
|
|
struct VimMouseMsg Mouse;
|
|
struct VimMouseMovedMsg MouseMoved;
|
|
struct VimFocusMsg Focus;
|
|
struct VimRefsMsg Refs;
|
|
struct VimTablineMsg Tabline;
|
|
struct VimTablineMenuMsg TablineMenu;
|
|
} u;
|
|
};
|
|
|
|
#define RGB(r, g, b) ((char_u)(r) << 16 | (char_u)(g) << 8 | (char_u)(b) << 0)
|
|
#define GUI_TO_RGB(g) { (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(NOT_VALID);
|
|
}
|
|
|
|
// Really handle dropped files and folders.
|
|
static void
|
|
RefsReceived(BMessage *m, bool changedir)
|
|
{
|
|
uint32 type;
|
|
int32 count;
|
|
|
|
m->PrintToStream();
|
|
switch (m->what) {
|
|
case B_REFS_RECEIVED:
|
|
case B_SIMPLE_DATA:
|
|
m->GetInfo("refs", &type, &count);
|
|
if (type != B_REF_TYPE)
|
|
goto bad;
|
|
break;
|
|
case B_ARGV_RECEIVED:
|
|
m->GetInfo("argv", &type, &count);
|
|
if (type != B_STRING_TYPE)
|
|
goto bad;
|
|
if (changedir) {
|
|
char *dirname;
|
|
if (m->FindString("cwd", (const char **) &dirname) == B_OK) {
|
|
chdir(dirname);
|
|
do_cmdline_cmd((char_u *)"cd .");
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
bad:
|
|
/*fprintf(stderr, "bad!\n"); */
|
|
delete m;
|
|
return;
|
|
}
|
|
|
|
#ifdef FEAT_VISUAL
|
|
reset_VIsual();
|
|
#endif
|
|
|
|
char_u **fnames;
|
|
fnames = (char_u **) alloc(count * sizeof(char_u *));
|
|
int fname_index = 0;
|
|
|
|
switch (m->what) {
|
|
case B_REFS_RECEIVED:
|
|
case B_SIMPLE_DATA:
|
|
// fprintf(stderr, "case B_REFS_RECEIVED\n");
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
entry_ref ref;
|
|
if (m->FindRef("refs", i, &ref) == B_OK) {
|
|
BEntry entry(&ref, false);
|
|
BPath path;
|
|
entry.GetPath(&path);
|
|
|
|
// Change to parent directory?
|
|
if (changedir) {
|
|
BPath parentpath;
|
|
path.GetParent(&parentpath);
|
|
docd(parentpath);
|
|
}
|
|
|
|
// Is it a directory? If so, cd into it.
|
|
BDirectory bdir(&ref);
|
|
if (bdir.InitCheck() == B_OK) {
|
|
// don't cd if we already did it
|
|
if (!changedir)
|
|
docd(path);
|
|
} else {
|
|
mch_dirname(IObuff, IOSIZE);
|
|
char_u *fname = shorten_fname((char_u *)path.Path(), IObuff);
|
|
if (fname == NULL)
|
|
fname = (char_u *)path.Path();
|
|
fnames[fname_index++] = vim_strsave(fname);
|
|
// fprintf(stderr, "%s\n", fname);
|
|
}
|
|
|
|
// Only do it for the first file/dir
|
|
changedir = false;
|
|
}
|
|
}
|
|
break;
|
|
case B_ARGV_RECEIVED:
|
|
// fprintf(stderr, "case B_ARGV_RECEIVED\n");
|
|
for (int i = 1; i < count; ++i)
|
|
{
|
|
char *fname;
|
|
|
|
if (m->FindString("argv", i, (const char **) &fname) == B_OK) {
|
|
fnames[fname_index++] = vim_strsave((char_u *)fname);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
// fprintf(stderr, "case default\n");
|
|
break;
|
|
}
|
|
|
|
delete m;
|
|
|
|
// Handle the drop, :edit to get to the file
|
|
if (fname_index > 0) {
|
|
handle_drop(fname_index, fnames, FALSE, drop_callback, NULL);
|
|
|
|
setcursor();
|
|
out_flush();
|
|
} else {
|
|
vim_free(fnames);
|
|
}
|
|
}
|
|
|
|
VimApp::VimApp(const char *appsig):
|
|
BApplication(appsig),
|
|
fFilePanelSem(-1),
|
|
fFilePanel(NULL)
|
|
{
|
|
}
|
|
|
|
VimApp::~VimApp()
|
|
{
|
|
}
|
|
|
|
void
|
|
VimApp::ReadyToRun()
|
|
{
|
|
/*
|
|
* Apparently signals are inherited by the created thread -
|
|
* disable the most annoying ones.
|
|
*/
|
|
signal(SIGINT, SIG_IGN);
|
|
signal(SIGQUIT, SIG_IGN);
|
|
}
|
|
|
|
void
|
|
VimApp::ArgvReceived(int32 arg_argc, char **arg_argv)
|
|
{
|
|
if (!IsLaunching()) {
|
|
/*
|
|
* This can happen if we are set to Single or Exclusive
|
|
* Launch. Be nice and open the file(s).
|
|
*/
|
|
if (gui.vimWindow)
|
|
gui.vimWindow->Minimize(false);
|
|
BMessage *m = CurrentMessage();
|
|
DetachCurrentMessage();
|
|
SendRefs(m, true);
|
|
}
|
|
}
|
|
|
|
void
|
|
VimApp::RefsReceived(BMessage *m)
|
|
{
|
|
// Horrible hack!!! XXX XXX XXX
|
|
// The real problem is that b_start_ffc is set too late for
|
|
// the initial empty buffer. As a result the window will be
|
|
// split instead of abandoned.
|
|
int limit = 15;
|
|
while (--limit >= 0 && (curbuf == NULL || curbuf->b_start_ffc == 0))
|
|
snooze(100000); // 0.1 s
|
|
if (gui.vimWindow)
|
|
gui.vimWindow->Minimize(false);
|
|
DetachCurrentMessage();
|
|
SendRefs(m, true);
|
|
}
|
|
|
|
/*
|
|
* Pass a BMessage on to the main() thread.
|
|
* Caller must have detached the message.
|
|
*/
|
|
void
|
|
VimApp::SendRefs(BMessage *m, bool changedir)
|
|
{
|
|
VimRefsMsg rm;
|
|
rm.message = m;
|
|
rm.changedir = changedir;
|
|
|
|
write_port(gui.vdcmp, VimMsg::Refs, &rm, sizeof(rm));
|
|
// calls ::RefsReceived
|
|
}
|
|
|
|
void
|
|
VimApp::MessageReceived(BMessage *m)
|
|
{
|
|
switch (m->what) {
|
|
case 'save':
|
|
{
|
|
entry_ref refDirectory;
|
|
m->FindRef("directory", &refDirectory);
|
|
fBrowsedPath.SetTo(&refDirectory);
|
|
BString strName;
|
|
m->FindString("name", &strName);
|
|
fBrowsedPath.Append(strName.String());
|
|
}
|
|
break;
|
|
case 'open':
|
|
{
|
|
entry_ref ref;
|
|
m->FindRef("refs", &ref);
|
|
fBrowsedPath.SetTo(&ref);
|
|
}
|
|
break;
|
|
case B_CANCEL:
|
|
{
|
|
BFilePanel *panel;
|
|
m->FindPointer("source", (void**)&panel);
|
|
if (fFilePanelSem != -1 && panel == fFilePanel)
|
|
{
|
|
delete_sem(fFilePanelSem);
|
|
fFilePanelSem = -1;
|
|
}
|
|
|
|
}
|
|
break;
|
|
default:
|
|
Inherited::MessageReceived(m);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool
|
|
VimApp::QuitRequested()
|
|
{
|
|
(void)Inherited::QuitRequested();
|
|
return false;
|
|
}
|
|
|
|
// ---------------- VimWindow ----------------
|
|
|
|
VimWindow::VimWindow():
|
|
BWindow(BRect(40, 40, 150, 150),
|
|
"Vim",
|
|
B_TITLED_WINDOW,
|
|
0,
|
|
B_CURRENT_WORKSPACE)
|
|
|
|
{
|
|
init();
|
|
}
|
|
|
|
VimWindow::~VimWindow()
|
|
{
|
|
if (formView) {
|
|
RemoveChild(formView);
|
|
delete formView;
|
|
}
|
|
gui.vimWindow = NULL;
|
|
}
|
|
|
|
void
|
|
VimWindow::init()
|
|
{
|
|
// Attach the VimFormView
|
|
formView = new VimFormView(Bounds());
|
|
if (formView != NULL) {
|
|
AddChild(formView);
|
|
}
|
|
}
|
|
|
|
#if 0 // disabled in zeta patch
|
|
void
|
|
VimWindow::DispatchMessage(BMessage *m, BHandler *h)
|
|
{
|
|
/*
|
|
* Route B_MOUSE_UP messages to MouseUp(), in
|
|
* a manner that should be compatible with the
|
|
* intended future system behaviour.
|
|
*/
|
|
switch (m->what) {
|
|
case B_MOUSE_UP:
|
|
// if (!h) h = PreferredHandler();
|
|
// gcc isn't happy without this extra set of braces, complains about
|
|
// jump to case label crosses init of 'class BView * v'
|
|
// richard@whitequeen.com jul 99
|
|
{
|
|
BView *v = dynamic_cast<BView *>(h);
|
|
if (v) {
|
|
// m->PrintToStream();
|
|
BPoint where;
|
|
m->FindPoint("where", &where);
|
|
v->MouseUp(where);
|
|
} else {
|
|
Inherited::DispatchMessage(m, h);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Inherited::DispatchMessage(m, h);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void
|
|
VimWindow::WindowActivated(bool active)
|
|
{
|
|
Inherited::WindowActivated(active);
|
|
// the textArea gets the keyboard action
|
|
if (active && gui.vimTextArea)
|
|
gui.vimTextArea->MakeFocus(true);
|
|
|
|
struct VimFocusMsg fm;
|
|
fm.active = active;
|
|
|
|
write_port(gui.vdcmp, VimMsg::Focus, &fm, sizeof(fm));
|
|
}
|
|
|
|
bool
|
|
VimWindow::QuitRequested()
|
|
{
|
|
struct VimKeyMsg km;
|
|
km.length = 5;
|
|
memcpy((char *)km.chars, "\033:qa\r", km.length);
|
|
km.csi_escape = false;
|
|
write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
|
|
return false;
|
|
}
|
|
|
|
// ---------------- VimFormView ----------------
|
|
|
|
VimFormView::VimFormView(BRect frame):
|
|
BView(frame, "VimFormView", B_FOLLOW_ALL_SIDES,
|
|
B_WILL_DRAW | B_FRAME_EVENTS),
|
|
menuBar(NULL),
|
|
#ifdef FEAT_TOOLBAR
|
|
toolBar(NULL),
|
|
#endif
|
|
#ifdef FEAT_GUI_TABLINE
|
|
// showingTabLine(false),
|
|
tabLine(NULL),
|
|
#endif
|
|
textArea(NULL)
|
|
{
|
|
init(frame);
|
|
}
|
|
|
|
VimFormView::~VimFormView()
|
|
{
|
|
if (menuBar) {
|
|
RemoveChild(menuBar);
|
|
#ifdef never
|
|
// deleting the menuBar leads to SEGV on exit
|
|
// richard@whitequeen.com Jul 99
|
|
delete menuBar;
|
|
#endif
|
|
}
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
delete toolBar;
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
delete tabLine;
|
|
#endif
|
|
|
|
if (textArea) {
|
|
RemoveChild(textArea);
|
|
delete textArea;
|
|
}
|
|
gui.vimForm = NULL;
|
|
}
|
|
|
|
void
|
|
VimFormView::init(BRect frame)
|
|
{
|
|
menuBar = new BMenuBar(BRect(0,0,-MENUBAR_MARGIN,-MENUBAR_MARGIN),
|
|
"VimMenuBar");
|
|
|
|
AddChild(menuBar);
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
toolBar = new VimToolbar(BRect(0,0,0,0), "VimToolBar");
|
|
toolBar->PrepareButtonBitmaps();
|
|
AddChild(toolBar);
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
tabLine = new VimTabLine(BRect(0,0,0,0));
|
|
// tabLine->PrepareButtonBitmaps();
|
|
AddChild(tabLine);
|
|
#endif
|
|
|
|
BRect remaining = frame;
|
|
textArea = new VimTextAreaView(remaining);
|
|
AddChild(textArea);
|
|
// The textArea will be resized later when menus are added
|
|
|
|
gui.vimForm = this;
|
|
}
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
float
|
|
VimFormView::ToolbarHeight() const
|
|
{
|
|
return toolBar ? toolBar->ToolbarHeight() : 0.;
|
|
}
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
float
|
|
VimFormView::TablineHeight() const
|
|
{
|
|
return (tabLine && IsShowingTabLine()) ? tabLine->TablineHeight() : 0.;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
VimFormView::AllAttached()
|
|
{
|
|
/*
|
|
* Apparently signals are inherited by the created thread -
|
|
* disable the most annoying ones.
|
|
*/
|
|
signal(SIGINT, SIG_IGN);
|
|
signal(SIGQUIT, SIG_IGN);
|
|
|
|
if (menuBar && textArea) {
|
|
/*
|
|
* Resize the textArea to fill the space left over by the menu.
|
|
* This is somewhat futile since it will be done again once
|
|
* menus are added to the menu bar.
|
|
*/
|
|
BRect remaining = Bounds();
|
|
|
|
#ifdef FEAT_MENU
|
|
remaining.top += MenuHeight();
|
|
menuBar->ResizeTo(remaining.right, remaining.top);
|
|
gui.menu_height = (int) MenuHeight();
|
|
#endif
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
toolBar->MoveTo(remaining.left, remaining.top);
|
|
toolBar->ResizeTo(remaining.right, ToolbarHeight());
|
|
remaining.top += ToolbarHeight();
|
|
gui.toolbar_height = ToolbarHeight();
|
|
#endif
|
|
|
|
#ifdef FEAT_GUI_TABLINE
|
|
tabLine->MoveTo(remaining.left, remaining.top);
|
|
tabLine->ResizeTo(remaining.right + 1, TablineHeight());
|
|
remaining.top += TablineHeight();
|
|
gui.tabline_height = TablineHeight();
|
|
#endif
|
|
|
|
textArea->ResizeTo(remaining.Width(), remaining.Height());
|
|
textArea->MoveTo(remaining.left, remaining.top);
|
|
}
|
|
|
|
|
|
Inherited::AllAttached();
|
|
}
|
|
|
|
void
|
|
VimFormView::FrameResized(float new_width, float new_height)
|
|
{
|
|
struct VimResizeMsg sm;
|
|
int adjust_h, adjust_w;
|
|
|
|
new_width += 1; // adjust from width to number of pixels occupied
|
|
new_height += 1;
|
|
|
|
sm.width = (int) new_width;
|
|
sm.height = (int) new_height;
|
|
adjust_w = ((int)new_width - gui_get_base_width()) % gui.char_width;
|
|
adjust_h = ((int)new_height - gui_get_base_height()) % gui.char_height;
|
|
|
|
if (adjust_w > 0 || adjust_h > 0) {
|
|
sm.width -= adjust_w;
|
|
sm.height -= adjust_h;
|
|
}
|
|
|
|
write_port(gui.vdcmp, VimMsg::Resize, &sm, sizeof(sm));
|
|
// calls gui_resize_shell(new_width, new_height);
|
|
|
|
return;
|
|
|
|
/*
|
|
* The area below the vertical scrollbar is erased to the colour
|
|
* set with SetViewColor() automatically, because we had set
|
|
* B_WILL_DRAW. Resizing the window tight around the vertical
|
|
* scroll bar also helps to avoid debris.
|
|
*/
|
|
}
|
|
|
|
// ---------------- VimTextAreaView ----------------
|
|
|
|
VimTextAreaView::VimTextAreaView(BRect frame):
|
|
BView(frame, "VimTextAreaView", B_FOLLOW_ALL_SIDES,
|
|
#ifdef FEAT_MBYTE_IME
|
|
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_INPUT_METHOD_AWARE
|
|
#else
|
|
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
|
|
#endif
|
|
),
|
|
mouseDragEventCount(0)
|
|
{
|
|
#ifdef FEAT_MBYTE_IME
|
|
IMData.messenger = NULL;
|
|
IMData.message = NULL;
|
|
#endif
|
|
init(frame);
|
|
}
|
|
|
|
VimTextAreaView::~VimTextAreaView()
|
|
{
|
|
gui.vimTextArea = NULL;
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::init(BRect frame)
|
|
{
|
|
// set up global var for fast access
|
|
gui.vimTextArea = this;
|
|
|
|
/*
|
|
* Tell the app server not to erase the view: we will
|
|
* fill it in completely by ourselves.
|
|
* (Does this really work? Even if not, it won't harm either.)
|
|
*/
|
|
SetViewColor(B_TRANSPARENT_32_BIT);
|
|
#define PEN_WIDTH 1
|
|
SetPenSize(PEN_WIDTH);
|
|
#define W_WIDTH(curwin) 0
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::Draw(BRect updateRect)
|
|
{
|
|
/*
|
|
* XXX Other ports call here:
|
|
* out_flush(); * make sure all output has been processed *
|
|
* but we can't do that, since it involves too much information
|
|
* that is owned by other threads...
|
|
*/
|
|
|
|
/*
|
|
* No need to use gui.vimWindow->Lock(): we are locked already.
|
|
* However, it would not hurt.
|
|
*/
|
|
rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
|
|
SetLowColor(rgb);
|
|
FillRect(updateRect, B_SOLID_LOW);
|
|
gui_redraw((int) updateRect.left, (int) updateRect.top,
|
|
(int) (updateRect.Width() + PEN_WIDTH), (int) (updateRect.Height() + PEN_WIDTH));
|
|
|
|
// Clear the border areas if needed
|
|
SetLowColor(rgb);
|
|
|
|
if (updateRect.left < FILL_X(0)) // left border
|
|
FillRect(BRect(updateRect.left, updateRect.top,
|
|
FILL_X(0)-PEN_WIDTH, updateRect.bottom), B_SOLID_LOW);
|
|
if (updateRect.top < FILL_Y(0)) // top border
|
|
FillRect(BRect(updateRect.left, updateRect.top,
|
|
updateRect.right, FILL_Y(0)-PEN_WIDTH), B_SOLID_LOW);
|
|
if (updateRect.right >= FILL_X(Columns)) // right border
|
|
FillRect(BRect(FILL_X((int)Columns), updateRect.top,
|
|
updateRect.right, updateRect.bottom), B_SOLID_LOW);
|
|
if (updateRect.bottom >= FILL_Y(Rows)) // bottom border
|
|
FillRect(BRect(updateRect.left, FILL_Y((int)Rows),
|
|
updateRect.right, updateRect.bottom), B_SOLID_LOW);
|
|
|
|
#ifdef FEAT_MBYTE_IME
|
|
DrawIMString();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::KeyDown(const char *bytes, int32 numBytes)
|
|
{
|
|
struct VimKeyMsg km;
|
|
char_u *dest = km.chars;
|
|
|
|
bool canHaveVimModifiers = false;
|
|
|
|
BMessage *msg = Window()->CurrentMessage();
|
|
assert(msg);
|
|
// msg->PrintToStream();
|
|
|
|
/*
|
|
* Convert special keys to Vim codes.
|
|
* I think it is better to do it in the window thread
|
|
* so we use at least a little bit of the potential
|
|
* of our 2 CPUs. Besides, due to the fantastic mapping
|
|
* of special keys to UTF-8, we have quite some work to
|
|
* do...
|
|
* TODO: I'm not quite happy with detection of special
|
|
* keys. Perhaps I should use scan codes after all...
|
|
*/
|
|
if (numBytes > 1) {
|
|
// This cannot be a special key
|
|
if (numBytes > KEY_MSG_BUFSIZ)
|
|
numBytes = KEY_MSG_BUFSIZ; // should never happen... ???
|
|
km.length = numBytes;
|
|
memcpy((char *)dest, bytes, numBytes);
|
|
km.csi_escape = true;
|
|
} else {
|
|
int32 scancode = 0;
|
|
msg->FindInt32("key", &scancode);
|
|
|
|
int32 beModifiers = 0;
|
|
msg->FindInt32("modifiers", &beModifiers);
|
|
|
|
char_u string[3];
|
|
int len = 0;
|
|
km.length = 0;
|
|
|
|
/*
|
|
* For normal, printable ASCII characters, don't look them up
|
|
* to check if they might be a special key. They aren't.
|
|
*/
|
|
assert(B_BACKSPACE <= 0x20);
|
|
assert(B_DELETE == 0x7F);
|
|
if (((char_u)bytes[0] <= 0x20 || (char_u)bytes[0] == 0x7F) &&
|
|
numBytes == 1) {
|
|
/*
|
|
* Due to the great nature of Be's mapping of special keys,
|
|
* viz. into the range of the control characters,
|
|
* we can only be sure it is *really* a special key if
|
|
* if it is special without using ctrl. So, only if ctrl is
|
|
* used, we need to check it unmodified.
|
|
*/
|
|
if (beModifiers & B_CONTROL_KEY) {
|
|
int index = keyMap->normal_map[scancode];
|
|
int newNumBytes = keyMapChars[index];
|
|
char_u *newBytes = (char_u *)&keyMapChars[index + 1];
|
|
|
|
/*
|
|
* Check if still special without the control key.
|
|
* This is needed for BACKSPACE: that key does produce
|
|
* different values with modifiers (DEL).
|
|
* Otherwise we could simply have checked for equality.
|
|
*/
|
|
if (newNumBytes != 1 || (*newBytes > 0x20 &&
|
|
*newBytes != 0x7F )) {
|
|
goto notspecial;
|
|
}
|
|
bytes = (char *)newBytes;
|
|
}
|
|
canHaveVimModifiers = true;
|
|
|
|
uint16 beoskey;
|
|
int first, last;
|
|
|
|
/*
|
|
* If numBytes == 0 that probably always indicates a special key.
|
|
* (does not happen yet)
|
|
*/
|
|
if (numBytes == 0 || bytes[0] == B_FUNCTION_KEY) {
|
|
beoskey = F(scancode);
|
|
first = FIRST_FUNCTION_KEY;
|
|
last = NUM_SPECIAL_KEYS;
|
|
} else if (*bytes == '\n' && scancode == 0x47) {
|
|
// remap the (non-keypad) ENTER key from \n to \r.
|
|
string[0] = '\r';
|
|
len = 1;
|
|
first = last = 0;
|
|
} else {
|
|
beoskey = K(bytes[0]);
|
|
first = 0;
|
|
last = FIRST_FUNCTION_KEY;
|
|
}
|
|
|
|
for (int i = first; i < last; i++) {
|
|
if (special_keys[i].BeKeys == beoskey) {
|
|
string[0] = CSI;
|
|
string[1] = special_keys[i].vim_code0;
|
|
string[2] = special_keys[i].vim_code1;
|
|
len = 3;
|
|
}
|
|
}
|
|
}
|
|
notspecial:
|
|
if (len == 0) {
|
|
string[0] = bytes[0];
|
|
len = 1;
|
|
}
|
|
|
|
// Special keys (and a few others) may have modifiers
|
|
#if 0
|
|
if (len == 3 ||
|
|
bytes[0] == B_SPACE || bytes[0] == B_TAB ||
|
|
bytes[0] == B_RETURN || bytes[0] == '\r' ||
|
|
bytes[0] == B_ESCAPE)
|
|
#else
|
|
if (canHaveVimModifiers)
|
|
#endif
|
|
{
|
|
int modifiers;
|
|
modifiers = 0;
|
|
if (beModifiers & B_SHIFT_KEY)
|
|
modifiers |= MOD_MASK_SHIFT;
|
|
if (beModifiers & B_CONTROL_KEY)
|
|
modifiers |= MOD_MASK_CTRL;
|
|
if (beModifiers & B_OPTION_KEY)
|
|
modifiers |= MOD_MASK_ALT;
|
|
|
|
/*
|
|
* For some keys a shift modifier is translated into another key
|
|
* code. Do we need to handle the case where len != 1 and
|
|
* string[0] != CSI? (Not for BeOS, since len == 3 implies
|
|
* string[0] == CSI...)
|
|
*/
|
|
int key;
|
|
if (string[0] == CSI && len == 3)
|
|
key = TO_SPECIAL(string[1], string[2]);
|
|
else
|
|
key = string[0];
|
|
key = simplify_key(key, &modifiers);
|
|
if (IS_SPECIAL(key))
|
|
{
|
|
string[0] = CSI;
|
|
string[1] = K_SECOND(key);
|
|
string[2] = K_THIRD(key);
|
|
len = 3;
|
|
}
|
|
else
|
|
{
|
|
string[0] = key;
|
|
len = 1;
|
|
}
|
|
|
|
if (modifiers)
|
|
{
|
|
*dest++ = CSI;
|
|
*dest++ = KS_MODIFIER;
|
|
*dest++ = modifiers;
|
|
km.length = 3;
|
|
}
|
|
}
|
|
memcpy((char *)dest, string, len);
|
|
km.length += len;
|
|
km.csi_escape = false;
|
|
}
|
|
|
|
write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
|
|
|
|
/*
|
|
* blank out the pointer if necessary
|
|
*/
|
|
if (p_mh && !gui.pointer_hidden)
|
|
{
|
|
guiBlankMouse(true);
|
|
gui.pointer_hidden = TRUE;
|
|
}
|
|
}
|
|
void
|
|
VimTextAreaView::guiSendMouseEvent(
|
|
int button,
|
|
int x,
|
|
int y,
|
|
int repeated_click,
|
|
int_u modifiers)
|
|
{
|
|
VimMouseMsg mm;
|
|
|
|
mm.button = button;
|
|
mm.x = x;
|
|
mm.y = y;
|
|
mm.repeated_click = repeated_click;
|
|
mm.modifiers = modifiers;
|
|
|
|
write_port(gui.vdcmp, VimMsg::Mouse, &mm, sizeof(mm));
|
|
// calls gui_send_mouse_event()
|
|
|
|
/*
|
|
* if our pointer is currently hidden, then we should show it.
|
|
*/
|
|
if (gui.pointer_hidden)
|
|
{
|
|
guiBlankMouse(false);
|
|
gui.pointer_hidden = FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::guiMouseMoved(
|
|
int x,
|
|
int y)
|
|
{
|
|
VimMouseMovedMsg mm;
|
|
|
|
mm.x = x;
|
|
mm.y = y;
|
|
|
|
write_port(gui.vdcmp, VimMsg::MouseMoved, &mm, sizeof(mm));
|
|
|
|
if (gui.pointer_hidden)
|
|
{
|
|
guiBlankMouse(false);
|
|
gui.pointer_hidden = FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::guiBlankMouse(bool should_hide)
|
|
{
|
|
if (should_hide) {
|
|
// gui.vimApp->HideCursor();
|
|
gui.vimApp->ObscureCursor();
|
|
/*
|
|
* ObscureCursor() would even be easier, but then
|
|
* Vim's idea of mouse visibility does not necessarily
|
|
* correspond to reality.
|
|
*/
|
|
} else {
|
|
// gui.vimApp->ShowCursor();
|
|
}
|
|
}
|
|
|
|
int_u
|
|
VimTextAreaView::mouseModifiersToVim(int32 beModifiers)
|
|
{
|
|
int_u vim_modifiers = 0x0;
|
|
|
|
if (beModifiers & B_SHIFT_KEY)
|
|
vim_modifiers |= MOUSE_SHIFT;
|
|
if (beModifiers & B_CONTROL_KEY)
|
|
vim_modifiers |= MOUSE_CTRL;
|
|
if (beModifiers & B_OPTION_KEY) // Alt or Meta key
|
|
vim_modifiers |= MOUSE_ALT;
|
|
|
|
return vim_modifiers;
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::MouseDown(BPoint point)
|
|
{
|
|
BMessage *m = Window()->CurrentMessage();
|
|
assert(m);
|
|
|
|
int32 buttons = 0;
|
|
m->FindInt32("buttons", &buttons);
|
|
|
|
int vimButton;
|
|
|
|
if (buttons & B_PRIMARY_MOUSE_BUTTON)
|
|
vimButton = MOUSE_LEFT;
|
|
else if (buttons & B_SECONDARY_MOUSE_BUTTON)
|
|
vimButton = MOUSE_RIGHT;
|
|
else if (buttons & B_TERTIARY_MOUSE_BUTTON)
|
|
vimButton = MOUSE_MIDDLE;
|
|
else
|
|
return; // Unknown button
|
|
|
|
vimMouseButton = 1; // don't care which one
|
|
|
|
// Handle multiple clicks
|
|
int32 clicks = 0;
|
|
m->FindInt32("clicks", &clicks);
|
|
|
|
int32 modifiers = 0;
|
|
m->FindInt32("modifiers", &modifiers);
|
|
|
|
vimMouseModifiers = mouseModifiersToVim(modifiers);
|
|
|
|
guiSendMouseEvent(vimButton, point.x, point.y,
|
|
clicks > 1 /* = repeated_click*/, vimMouseModifiers);
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::MouseUp(BPoint point)
|
|
{
|
|
vimMouseButton = 0;
|
|
|
|
BMessage *m = Window()->CurrentMessage();
|
|
assert(m);
|
|
// m->PrintToStream();
|
|
|
|
int32 modifiers = 0;
|
|
m->FindInt32("modifiers", &modifiers);
|
|
|
|
vimMouseModifiers = mouseModifiersToVim(modifiers);
|
|
|
|
guiSendMouseEvent(MOUSE_RELEASE, point.x, point.y,
|
|
0 /* = repeated_click*/, vimMouseModifiers);
|
|
|
|
Inherited::MouseUp(point);
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
|
|
{
|
|
/*
|
|
* if our pointer is currently hidden, then we should show it.
|
|
*/
|
|
if (gui.pointer_hidden)
|
|
{
|
|
guiBlankMouse(false);
|
|
gui.pointer_hidden = FALSE;
|
|
}
|
|
|
|
if (!vimMouseButton) { // could also check m->"buttons"
|
|
guiMouseMoved(point.x, point.y);
|
|
return;
|
|
}
|
|
|
|
atomic_add(&mouseDragEventCount, 1);
|
|
|
|
// Don't care much about "transit"
|
|
guiSendMouseEvent(MOUSE_DRAG, point.x, point.y, 0, vimMouseModifiers);
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::MessageReceived(BMessage *m)
|
|
{
|
|
switch (m->what) {
|
|
case 'menu':
|
|
{
|
|
VimMenuMsg mm;
|
|
mm.guiMenu = NULL; // in case no pointer in msg
|
|
m->FindPointer("VimMenu", (void **)&mm.guiMenu);
|
|
write_port(gui.vdcmp, VimMsg::Menu, &mm, sizeof(mm));
|
|
}
|
|
break;
|
|
case B_MOUSE_WHEEL_CHANGED:
|
|
{
|
|
VimScrollBar* scb = curwin->w_scrollbars[1].id;
|
|
float small=0, big=0, dy=0;
|
|
m->FindFloat("be:wheel_delta_y", &dy);
|
|
scb->GetSteps(&small, &big);
|
|
scb->SetValue(scb->Value()+small*dy*3);
|
|
scb->ValueChanged(scb->Value());
|
|
#if 0
|
|
scb = curwin->w_scrollbars[0].id;
|
|
scb->GetSteps(&small, &big);
|
|
scb->SetValue(scb->Value()+small*dy);
|
|
scb->ValueChanged(scb->Value());
|
|
#endif
|
|
}
|
|
break;
|
|
#ifdef FEAT_MBYTE_IME
|
|
case B_INPUT_METHOD_EVENT:
|
|
{
|
|
int32 opcode;
|
|
m->FindInt32("be:opcode", &opcode);
|
|
switch(opcode)
|
|
{
|
|
case B_INPUT_METHOD_STARTED:
|
|
if (!IMData.messenger) delete IMData.messenger;
|
|
IMData.messenger = new BMessenger();
|
|
m->FindMessenger("be:reply_to", IMData.messenger);
|
|
break;
|
|
case B_INPUT_METHOD_CHANGED:
|
|
{
|
|
BString str;
|
|
bool confirmed;
|
|
if (IMData.message) *(IMData.message) = *m;
|
|
else IMData.message = new BMessage(*m);
|
|
DrawIMString();
|
|
m->FindBool("be:confirmed", &confirmed);
|
|
if (confirmed)
|
|
{
|
|
m->FindString("be:string", &str);
|
|
char_u *chars = (char_u*)str.String();
|
|
struct VimKeyMsg km;
|
|
km.csi_escape = true;
|
|
int clen;
|
|
int i = 0;
|
|
while (i < str.Length())
|
|
{
|
|
clen = utf_ptr2len(chars+i);
|
|
memcpy(km.chars, chars+i, clen);
|
|
km.length = clen;
|
|
write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
|
|
i += clen;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case B_INPUT_METHOD_LOCATION_REQUEST:
|
|
{
|
|
BMessage msg(B_INPUT_METHOD_EVENT);
|
|
msg.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST);
|
|
msg.AddPoint("be:location_reply", IMData.location);
|
|
msg.AddFloat("be:height_reply", FILL_Y(1));
|
|
IMData.messenger->SendMessage(&msg);
|
|
}
|
|
break;
|
|
case B_INPUT_METHOD_STOPPED:
|
|
delete IMData.messenger;
|
|
delete IMData.message;
|
|
IMData.messenger = NULL;
|
|
IMData.message = NULL;
|
|
break;
|
|
}
|
|
}
|
|
// TODO: sz: break here???
|
|
#endif
|
|
default:
|
|
if (m->WasDropped()) {
|
|
BWindow *w = Window();
|
|
w->DetachCurrentMessage();
|
|
w->Minimize(false);
|
|
VimApp::SendRefs(m, (modifiers() & B_SHIFT_KEY) != 0);
|
|
} else {
|
|
Inherited::MessageReceived(m);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
VimTextAreaView::mchInitFont(char_u *name)
|
|
{
|
|
VimFont *newFont = (VimFont *)gui_mch_get_font(name, 1);
|
|
if (newFont != NOFONT) {
|
|
gui.norm_font = (GuiFont)newFont;
|
|
gui_mch_set_font((GuiFont)newFont);
|
|
if (name && STRCMP(name, "*") != 0)
|
|
hl_set_font_name(name);
|
|
|
|
SetDrawingMode(B_OP_COPY);
|
|
|
|
/*
|
|
* Try to load other fonts for bold, italic, and bold-italic.
|
|
* We should also try to work out what font to use for these when they are
|
|
* not specified by X resources, but we don't yet.
|
|
*/
|
|
return OK;
|
|
}
|
|
return FAIL;
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::mchDrawString(int row, int col, char_u *s, int len, int flags)
|
|
{
|
|
/*
|
|
* First we must erase the area, because DrawString won't do
|
|
* that for us. XXX Most of the time this is a waste of effort
|
|
* since the bachground has been erased already... DRAW_TRANSP
|
|
* should be set when appropriate!!!
|
|
* (Rectangles include the bottom and right edge)
|
|
*/
|
|
if (!(flags & DRAW_TRANSP)) {
|
|
int cells;
|
|
cells = 0;
|
|
for (int i=0; i<len; i++) {
|
|
int cn = utf_ptr2cells((char_u *)(s+i));
|
|
if (cn<4) cells += cn;
|
|
}
|
|
|
|
BRect r(FILL_X(col), FILL_Y(row),
|
|
FILL_X(col + cells) - PEN_WIDTH, FILL_Y(row + 1) - PEN_WIDTH);
|
|
FillRect(r, B_SOLID_LOW);
|
|
}
|
|
|
|
BFont font;
|
|
this->GetFont(&font);
|
|
if (!font.IsFixed())
|
|
{
|
|
char* p = (char*)s;
|
|
int32 clen, lastpos = 0;
|
|
BPoint where;
|
|
int cells;
|
|
while ((p - (char*)s) < len) {
|
|
clen = utf_ptr2len((u_char*)p);
|
|
where.Set(TEXT_X(col+lastpos), TEXT_Y(row));
|
|
DrawString(p, clen, where);
|
|
if (flags & DRAW_BOLD) {
|
|
where.x += 1.0;
|
|
SetDrawingMode(B_OP_BLEND);
|
|
DrawString(p, clen, where);
|
|
SetDrawingMode(B_OP_COPY);
|
|
}
|
|
cells = utf_ptr2cells((char_u *)p);
|
|
if (cells<4) lastpos += cells;
|
|
else lastpos++;
|
|
p += clen;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BPoint where(TEXT_X(col), TEXT_Y(row));
|
|
DrawString((char*)s, len, where);
|
|
if (flags & DRAW_BOLD) {
|
|
where.x += 1.0;
|
|
SetDrawingMode(B_OP_BLEND);
|
|
DrawString((char*)s, len, where);
|
|
SetDrawingMode(B_OP_COPY);
|
|
}
|
|
}
|
|
|
|
if (flags & DRAW_UNDERL) {
|
|
int cells;
|
|
cells = 0;
|
|
for (int i=0; i<len; i++) {
|
|
int cn = utf_ptr2cells((char_u *)(s+i));
|
|
if (cn<4) cells += cn;
|
|
}
|
|
|
|
BPoint start(FILL_X(col), FILL_Y(row + 1) - PEN_WIDTH);
|
|
BPoint end(FILL_X(col + cells) - PEN_WIDTH, start.y);
|
|
|
|
StrokeLine(start, end);
|
|
}
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::mchClearBlock(
|
|
int row1,
|
|
int col1,
|
|
int row2,
|
|
int col2)
|
|
{
|
|
BRect r(FILL_X(col1), FILL_Y(row1),
|
|
FILL_X(col2 + 1) - PEN_WIDTH, FILL_Y(row2 + 1) - PEN_WIDTH);
|
|
gui_mch_set_bg_color(gui.back_pixel);
|
|
FillRect(r, B_SOLID_LOW);
|
|
}
|
|
|
|
void
|
|
VimTextAreaView::mchClearAll()
|
|
{
|
|
gui_mch_set_bg_color(gui.back_pixel);
|
|
FillRect(Bounds(), B_SOLID_LOW);
|
|
}
|
|
|
|
/*
|
|
* mchDeleteLines() Lock()s the window by itself.
|
|
*/
|
|
void
|
|
VimTextAreaView::mchDeleteLines(int row, int num_lines)
|
|
{
|
|
BRect source, dest;
|
|
source.left = FILL_X(gui.scroll_region_left);
|
|
source.top = FILL_Y(row + num_lines);
|
|
source.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
|
|
source.bottom = FILL_Y(gui.scroll_region_bot + 1) - PEN_WIDTH;
|
|
|
|
dest.left = FILL_X(gui.scroll_region_left);
|
|
dest.top = FILL_Y(row);
|
|
dest.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
|
|
dest.bottom = FILL_Y(gui.scroll_region_bot - num_lines + 1) - PEN_WIDTH;
|
|
|
|
if (gui.vimWindow->Lock()) {
|
|
// Clear one column more for when bold has spilled over
|
|
CopyBits(source, dest);
|
|
gui_clear_block(gui.scroll_region_bot - num_lines + 1,
|
|
gui.scroll_region_left,
|
|
gui.scroll_region_bot, gui.scroll_region_right);
|
|
|
|
|
|
gui.vimWindow->Unlock();
|
|
/*
|
|
* The Draw() callback will be called now if some of the source
|
|
* bits were not in the visible region.
|
|
*/
|
|
}
|
|
// gui_x11_check_copy_area();
|
|
// }
|
|
}
|
|
|
|
/*
|
|
* mchInsertLines() Lock()s the window by itself.
|
|
*/
|
|
void
|
|
VimTextAreaView::mchInsertLines(int row, int num_lines)
|
|
{
|
|
BRect source, dest;
|
|
|
|
// XXX Attempt at a hack:
|
|
gui.vimWindow->UpdateIfNeeded();
|
|
source.left = FILL_X(gui.scroll_region_left);
|
|
source.top = FILL_Y(row);
|
|
source.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
|
|
source.bottom = FILL_Y(gui.scroll_region_bot - num_lines + 1) - PEN_WIDTH;
|
|
|
|
dest.left = FILL_X(gui.scroll_region_left);
|
|
dest.top = FILL_Y(row + num_lines);
|
|
dest.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
|
|
dest.bottom = FILL_Y(gui.scroll_region_bot + 1) - PEN_WIDTH;
|
|
|
|
if (gui.vimWindow->Lock()) {
|
|
// Clear one column more for when bold has spilled over
|
|
CopyBits(source, dest);
|
|
gui_clear_block(row, gui.scroll_region_left,
|
|
row + num_lines - 1, gui.scroll_region_right);
|
|
|
|
gui.vimWindow->Unlock();
|
|
/*
|
|
* The Draw() callback will be called now if some of the source
|
|
* bits were not in the visible region.
|
|
* However, if we scroll too fast it can't keep up and the
|
|
* update region gets messed up. This seems to be because copying
|
|
* un-Draw()n bits does not generate Draw() calls for the copy...
|
|
* I moved the hack to before the CopyBits() to reduce the
|
|
* amount of additional waiting needed.
|
|
*/
|
|
|
|
// gui_x11_check_copy_area();
|
|
|
|
}
|
|
}
|
|
|
|
#ifdef FEAT_MBYTE_IME
|
|
/*
|
|
* DrawIMString draws string with IMData.message.
|
|
*/
|
|
void VimTextAreaView::DrawIMString(void)
|
|
{
|
|
static const rgb_color r_highlight = {255, 152, 152, 255},
|
|
b_highlight = {152, 203, 255, 255};
|
|
BString str;
|
|
const char* s;
|
|
int len;
|
|
BMessage* msg = IMData.message;
|
|
if (!msg)
|
|
return;
|
|
gui_redraw_block(IMData.row, 0,
|
|
IMData.row + IMData.count, W_WIDTH(curwin), GUI_MON_NOCLEAR);
|
|
bool confirmed = false;
|
|
msg->FindBool("be:confirmed", &confirmed);
|
|
if (confirmed)
|
|
return;
|
|
rgb_color hcolor = HighColor(), lcolor = LowColor();
|
|
msg->FindString("be:string", &str);
|
|
s = str.String();
|
|
len = str.Length();
|
|
SetHighColor(0, 0, 0);
|
|
IMData.row = gui.row;
|
|
IMData.col = gui.col;
|
|
int32 sel_start = 0, sel_end = 0;
|
|
msg->FindInt32("be:selection", 0, &sel_start);
|
|
msg->FindInt32("be:selection", 1, &sel_end);
|
|
int clen, cn;
|
|
BPoint pos(IMData.col, 0);
|
|
BRect r;
|
|
BPoint where;
|
|
IMData.location = ConvertToScreen(
|
|
BPoint(FILL_X(pos.x), FILL_Y(IMData.row + pos.y)));
|
|
for (int i=0; i<len; i+=clen)
|
|
{
|
|
cn = utf_ptr2cells((char_u *)(s+i));
|
|
clen = utf_ptr2len((char_u *)(s+i));
|
|
if (pos.x + cn > W_WIDTH(curwin))
|
|
{
|
|
pos.y++;
|
|
pos.x = 0;
|
|
}
|
|
if (sel_start<=i && i<sel_end)
|
|
{
|
|
SetLowColor(r_highlight);
|
|
IMData.location = ConvertToScreen(
|
|
BPoint(FILL_X(pos.x), FILL_Y(IMData.row + pos.y)));
|
|
}
|
|
else
|
|
{
|
|
SetLowColor(b_highlight);
|
|
}
|
|
r.Set(FILL_X(pos.x), FILL_Y(IMData.row + pos.y),
|
|
FILL_X(pos.x + cn) - PEN_WIDTH,
|
|
FILL_Y(IMData.row + pos.y + 1) - PEN_WIDTH);
|
|
FillRect(r, B_SOLID_LOW);
|
|
where.Set(TEXT_X(pos.x), TEXT_Y(IMData.row + pos.y));
|
|
DrawString((s+i), clen, where);
|
|
pos.x += cn;
|
|
}
|
|
IMData.count = (int)pos.y;
|
|
|
|
SetHighColor(hcolor);
|
|
SetLowColor(lcolor);
|
|
}
|
|
#endif
|
|
// ---------------- VimScrollBar ----------------
|
|
|
|
/*
|
|
* BUG: XXX
|
|
* It seems that BScrollBar determine their direction not from
|
|
* "posture" but from if they are "tall" or "wide" in shape...
|
|
*
|
|
* Also, place them out of sight, because Vim enables them before
|
|
* they are positioned.
|
|
*/
|
|
VimScrollBar::VimScrollBar(scrollbar_T *g, orientation posture):
|
|
BScrollBar(posture == B_HORIZONTAL ? BRect(-100,-100,-10,-90) :
|
|
BRect(-100,-100,-90,-10),
|
|
"vim scrollbar", (BView *)NULL,
|
|
0.0, 10.0, posture),
|
|
ignoreValue(-1),
|
|
scrollEventCount(0)
|
|
{
|
|
gsb = g;
|
|
SetResizingMode(B_FOLLOW_NONE);
|
|
}
|
|
|
|
VimScrollBar::~VimScrollBar()
|
|
{
|
|
}
|
|
|
|
void
|
|
VimScrollBar::ValueChanged(float newValue)
|
|
{
|
|
if (ignoreValue >= 0.0 && newValue == ignoreValue) {
|
|
ignoreValue = -1;
|
|
return;
|
|
}
|
|
ignoreValue = -1;
|
|
/*
|
|
* We want to throttle the amount of scroll messages generated.
|
|
* Normally I presume you won't get a new message before we've
|
|
* handled the previous one, but because we're passing them on this
|
|
* happens very quickly. So instead we keep a counter of how many
|
|
* scroll events there are (or will be) in the VDCMP, and the
|
|
* throttling happens at the receiving end.
|
|
*/
|
|
atomic_add(&scrollEventCount, 1);
|
|
|
|
struct VimScrollBarMsg sm;
|
|
|
|
sm.sb = this;
|
|
sm.value = (long) newValue;
|
|
sm.stillDragging = TRUE;
|
|
|
|
write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));
|
|
|
|
// calls gui_drag_scrollbar(sb, newValue, TRUE);
|
|
}
|
|
|
|
/*
|
|
* When the mouse goes up, report that scrolling has stopped.
|
|
* MouseUp() is NOT called when the mouse-up occurs outside
|
|
* the window, even though the thumb does move while the mouse
|
|
* is outside... This has some funny effects... XXX
|
|
* So we do special processing when the window de/activates.
|
|
*/
|
|
void
|
|
VimScrollBar::MouseUp(BPoint where)
|
|
{
|
|
// BMessage *m = Window()->CurrentMessage();
|
|
// m->PrintToStream();
|
|
|
|
atomic_add(&scrollEventCount, 1);
|
|
|
|
struct VimScrollBarMsg sm;
|
|
|
|
sm.sb = this;
|
|
sm.value = (long) Value();
|
|
sm.stillDragging = FALSE;
|
|
|
|
write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));
|
|
|
|
// calls gui_drag_scrollbar(sb, newValue, FALSE);
|
|
|
|
Inherited::MouseUp(where);
|
|
}
|
|
|
|
void
|
|
VimScrollBar::SetValue(float newValue)
|
|
{
|
|
if (newValue == Value())
|
|
return;
|
|
|
|
ignoreValue = newValue;
|
|
Inherited::SetValue(newValue);
|
|
}
|
|
|
|
// ---------------- VimFont ----------------
|
|
|
|
VimFont::VimFont(): BFont()
|
|
{
|
|
init();
|
|
}
|
|
|
|
VimFont::VimFont(const VimFont *rhs): BFont(rhs)
|
|
{
|
|
init();
|
|
}
|
|
|
|
VimFont::VimFont(const BFont *rhs): BFont(rhs)
|
|
{
|
|
init();
|
|
}
|
|
|
|
VimFont::VimFont(const VimFont &rhs): BFont(rhs)
|
|
{
|
|
init();
|
|
}
|
|
|
|
VimFont::~VimFont()
|
|
{
|
|
}
|
|
|
|
void
|
|
VimFont::init()
|
|
{
|
|
next = NULL;
|
|
refcount = 1;
|
|
name = NULL;
|
|
}
|
|
|
|
// ---------------- VimDialog ----------------
|
|
|
|
#if defined(FEAT_GUI_DIALOG)
|
|
|
|
const unsigned int kVimDialogButtonMsg = 'VMDB';
|
|
const unsigned int kVimDialogIconStripeWidth = 30;
|
|
const unsigned int kVimDialogButtonsSpacingX = 9;
|
|
const unsigned int kVimDialogButtonsSpacingY = 4;
|
|
const unsigned int kVimDialogSpacingX = 6;
|
|
const unsigned int kVimDialogSpacingY = 10;
|
|
const unsigned int kVimDialogMinimalWidth = 310;
|
|
const unsigned int kVimDialogMinimalHeight = 75;
|
|
const BRect kDefaultRect =
|
|
BRect(0, 0, kVimDialogMinimalWidth, kVimDialogMinimalHeight);
|
|
|
|
VimDialog::VimDialog(int type, const char *title, const char *message,
|
|
const char *buttons, int dfltbutton, const char *textfield, int ex_cmd)
|
|
: BWindow(kDefaultRect, title, B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
|
|
B_NOT_CLOSABLE | B_NOT_RESIZABLE |
|
|
B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS)
|
|
, fDialogSem(-1)
|
|
, fDialogValue(dfltbutton)
|
|
, fMessageView(NULL)
|
|
, fInputControl(NULL)
|
|
, fInputValue(textfield)
|
|
{
|
|
// master view
|
|
VimDialog::View* view = new VimDialog::View(Bounds());
|
|
if (view == NULL)
|
|
return;
|
|
|
|
if (title == NULL)
|
|
SetTitle("Vim " VIM_VERSION_MEDIUM);
|
|
|
|
AddChild(view);
|
|
|
|
// icon
|
|
view->InitIcon(type);
|
|
|
|
// buttons
|
|
int32 which = 1;
|
|
float maxButtonWidth = 0;
|
|
float maxButtonHeight = 0;
|
|
float buttonsWidth = 0;
|
|
float buttonsHeight = 0;
|
|
BString strButtons(buttons);
|
|
strButtons.RemoveAll("&");
|
|
do {
|
|
int32 end = strButtons.FindFirst('\n');
|
|
if (end != B_ERROR)
|
|
strButtons.SetByteAt(end, '\0');
|
|
|
|
BButton *button = _CreateButton(which++, strButtons.String());
|
|
view->AddChild(button);
|
|
fButtonsList.AddItem(button);
|
|
|
|
maxButtonWidth = max_c(maxButtonWidth, button->Bounds().Width());
|
|
maxButtonHeight = max_c(maxButtonHeight, button->Bounds().Height());
|
|
buttonsWidth += button->Bounds().Width();
|
|
buttonsHeight += button->Bounds().Height();
|
|
|
|
if (end == B_ERROR)
|
|
break;
|
|
|
|
strButtons.Remove(0, end + 1);
|
|
} while (true);
|
|
|
|
int32 buttonsCount = fButtonsList.CountItems();
|
|
buttonsWidth += kVimDialogButtonsSpacingX * (buttonsCount - 1);
|
|
buttonsHeight += kVimDialogButtonsSpacingY * (buttonsCount - 1);
|
|
float dialogWidth = buttonsWidth + kVimDialogIconStripeWidth +
|
|
kVimDialogSpacingX * 2;
|
|
float dialogHeight = maxButtonHeight + kVimDialogSpacingY * 3;
|
|
|
|
// Check 'v' flag in 'guioptions': vertical button placement.
|
|
bool vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL) ||
|
|
dialogWidth >= gui.vimWindow->Bounds().Width();
|
|
if (vertical) {
|
|
dialogWidth -= buttonsWidth;
|
|
dialogWidth += maxButtonWidth;
|
|
dialogHeight -= maxButtonHeight;
|
|
dialogHeight += buttonsHeight;
|
|
}
|
|
|
|
dialogWidth = max_c(dialogWidth, kVimDialogMinimalWidth);
|
|
|
|
// message view
|
|
BRect rect(0, 0, dialogWidth, 0);
|
|
rect.left += kVimDialogIconStripeWidth + 16 + kVimDialogSpacingX;
|
|
rect.top += kVimDialogSpacingY;
|
|
rect.right -= kVimDialogSpacingX;
|
|
rect.bottom = rect.top;
|
|
fMessageView = new BTextView(rect, "_tv_", rect.OffsetByCopy(B_ORIGIN),
|
|
B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
|
|
|
|
fMessageView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
|
|
fMessageView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
|
|
fMessageView->SetText(message);
|
|
fMessageView->MakeEditable(false);
|
|
fMessageView->MakeSelectable(false);
|
|
fMessageView->SetWordWrap(true);
|
|
AddChild(fMessageView);
|
|
|
|
float messageHeight = fMessageView->TextHeight(0, fMessageView->CountLines());
|
|
fMessageView->ResizeBy(0, messageHeight);
|
|
fMessageView->SetTextRect(BRect(0, 0, rect.Width(), messageHeight));
|
|
|
|
dialogHeight += messageHeight;
|
|
|
|
// input view
|
|
if (fInputValue != NULL) {
|
|
rect.top =
|
|
rect.bottom += messageHeight + kVimDialogSpacingY;
|
|
fInputControl = new BTextControl(rect, "_iv_", NULL, fInputValue, NULL,
|
|
B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE | B_PULSE_NEEDED);
|
|
fInputControl->TextView()->SetText(fInputValue);
|
|
fInputControl->TextView()->SetWordWrap(false);
|
|
AddChild(fInputControl);
|
|
|
|
float width = 0.f, height = 0.f;
|
|
fInputControl->GetPreferredSize(&width, &height);
|
|
fInputControl->MakeFocus(true);
|
|
|
|
dialogHeight += height + kVimDialogSpacingY * 1.5;
|
|
}
|
|
|
|
dialogHeight = max_c(dialogHeight, kVimDialogMinimalHeight);
|
|
|
|
ResizeTo(dialogWidth, dialogHeight);
|
|
MoveTo((gui.vimWindow->Bounds().Width() - dialogWidth) / 2,
|
|
(gui.vimWindow->Bounds().Height() - dialogHeight) / 2);
|
|
|
|
// adjust layout of buttons
|
|
float buttonWidth = max_c(maxButtonWidth, rect.Width() * 0.66);
|
|
BPoint origin(dialogWidth, dialogHeight);
|
|
origin.x -= kVimDialogSpacingX + (vertical ? buttonWidth : buttonsWidth);
|
|
origin.y -= kVimDialogSpacingY + (vertical ? buttonsHeight : maxButtonHeight);
|
|
|
|
for (int32 i = 0 ; i < buttonsCount; i++) {
|
|
BButton *button = (BButton*)fButtonsList.ItemAt(i);
|
|
button->MoveTo(origin);
|
|
if (vertical) {
|
|
origin.y += button->Frame().Height() + kVimDialogButtonsSpacingY;
|
|
button->ResizeTo(buttonWidth, button->Frame().Height());
|
|
} else
|
|
origin.x += button->Frame().Width() + kVimDialogButtonsSpacingX;
|
|
|
|
if (dfltbutton == i + 1) {
|
|
button->MakeDefault(true);
|
|
button->MakeFocus(fInputControl == NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
VimDialog::~VimDialog()
|
|
{
|
|
if (fDialogSem > B_OK)
|
|
delete_sem(fDialogSem);
|
|
}
|
|
|
|
int
|
|
VimDialog::Go()
|
|
{
|
|
fDialogSem = create_sem(0, "VimDialogSem");
|
|
if (fDialogSem < B_OK) {
|
|
Quit();
|
|
return fDialogValue;
|
|
}
|
|
|
|
Show();
|
|
|
|
while (acquire_sem(fDialogSem) == B_INTERRUPTED);
|
|
|
|
int retValue = fDialogValue;
|
|
if (fInputValue != NULL)
|
|
vim_strncpy((char_u*)fInputValue, (char_u*)fInputControl->Text(), IOSIZE - 1);
|
|
|
|
if (Lock())
|
|
Quit();
|
|
|
|
return retValue;
|
|
}
|
|
|
|
void VimDialog::MessageReceived(BMessage *msg)
|
|
{
|
|
int32 which = 0;
|
|
if (msg->what != kVimDialogButtonMsg ||
|
|
msg->FindInt32("which", &which) != B_OK)
|
|
return BWindow::MessageReceived(msg);
|
|
|
|
fDialogValue = which;
|
|
delete_sem(fDialogSem);
|
|
fDialogSem = -1;
|
|
}
|
|
|
|
BButton* VimDialog::_CreateButton(int32 which, const char* label)
|
|
{
|
|
BMessage *message = new BMessage(kVimDialogButtonMsg);
|
|
message->AddInt32("which", which);
|
|
|
|
BRect rect(0, 0, 0, 0);
|
|
BString name;
|
|
name << "_b" << which << "_";
|
|
|
|
BButton* button = new BButton(rect, name.String(), label, message,
|
|
B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
|
|
|
|
float width = 0.f, height = 0.f;
|
|
button->GetPreferredSize(&width, &height);
|
|
button->ResizeTo(width, height);
|
|
|
|
return button;
|
|
}
|
|
|
|
VimDialog::View::View(BRect frame)
|
|
: BView(frame, "VimDialogView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
|
|
fIconBitmap(NULL)
|
|
{
|
|
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
}
|
|
|
|
VimDialog::View::~View()
|
|
{
|
|
delete fIconBitmap;
|
|
}
|
|
|
|
void VimDialog::View::Draw(BRect updateRect)
|
|
{
|
|
BRect stripeRect = Bounds();
|
|
stripeRect.right = kVimDialogIconStripeWidth;
|
|
SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
|
|
FillRect(stripeRect);
|
|
|
|
if (fIconBitmap == NULL)
|
|
return;
|
|
|
|
SetDrawingMode(B_OP_ALPHA);
|
|
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
|
|
DrawBitmapAsync(fIconBitmap, BPoint(18, 6));
|
|
}
|
|
|
|
void VimDialog::View::InitIcon(int32 type)
|
|
{
|
|
if (type == VIM_GENERIC)
|
|
return;
|
|
|
|
BPath path;
|
|
status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
|
|
if (status != B_OK) {
|
|
fprintf(stderr, "Cannot retrieve app info:%s\n", strerror(status));
|
|
return;
|
|
}
|
|
|
|
path.Append("app_server");
|
|
|
|
BFile file(path.Path(), O_RDONLY);
|
|
if (file.InitCheck() != B_OK) {
|
|
fprintf(stderr, "App file assignment failed:%s\n",
|
|
strerror(file.InitCheck()));
|
|
return;
|
|
}
|
|
|
|
BResources resources(&file);
|
|
if (resources.InitCheck() != B_OK) {
|
|
fprintf(stderr, "App server resources assignment failed:%s\n",
|
|
strerror(resources.InitCheck()));
|
|
return;
|
|
}
|
|
|
|
const char *name = "";
|
|
switch(type) {
|
|
case VIM_ERROR: name = "stop"; break;
|
|
case VIM_WARNING: name = "warn"; break;
|
|
case VIM_INFO: name = "info"; break;
|
|
case VIM_QUESTION: name = "idea"; break;
|
|
default: return;
|
|
}
|
|
|
|
int32 iconSize = 32;
|
|
fIconBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
|
|
if (fIconBitmap == NULL || fIconBitmap->InitCheck() != B_OK) {
|
|
fprintf(stderr, "Icon bitmap allocation failed:%s\n",
|
|
(fIconBitmap == NULL) ? "null" : strerror(fIconBitmap->InitCheck()));
|
|
return;
|
|
}
|
|
|
|
size_t size = 0;
|
|
const uint8* iconData = NULL;
|
|
// try vector icon first?
|
|
iconData = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE, name, &size);
|
|
if (iconData != NULL && BIconUtils::GetVectorIcon(iconData, size, fIconBitmap) == B_OK)
|
|
return;
|
|
|
|
// try bitmap icon now
|
|
iconData = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE, name, &size);
|
|
if (iconData == NULL) {
|
|
fprintf(stderr, "Bitmap icon resource not found\n");
|
|
delete fIconBitmap;
|
|
fIconBitmap = NULL;
|
|
return;
|
|
}
|
|
|
|
if (fIconBitmap->ColorSpace() != B_CMAP8)
|
|
BIconUtils::ConvertFromCMAP8(iconData, iconSize, iconSize, iconSize, fIconBitmap);
|
|
}
|
|
|
|
const unsigned int kVimDialogOKButtonMsg = 'FDOK';
|
|
const unsigned int kVimDialogCancelButtonMsg = 'FDCN';
|
|
const unsigned int kVimDialogSizeInputMsg = 'SICH';
|
|
const unsigned int kVimDialogFamilySelectMsg = 'MSFM';
|
|
const unsigned int kVimDialogStyleSelectMsg = 'MSST';
|
|
const unsigned int kVimDialogSizeSelectMsg = 'MSSZ';
|
|
|
|
VimSelectFontDialog::VimSelectFontDialog(font_family* family, font_style* style, float* size)
|
|
: BWindow(kDefaultRect, "Font Selection", B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
|
|
B_NOT_CLOSABLE | B_NOT_RESIZABLE |
|
|
B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS)
|
|
, fStatus(B_NO_INIT)
|
|
, fDialogSem(-1)
|
|
, fDialogValue(false)
|
|
, fFamily(family)
|
|
, fStyle(style)
|
|
, fSize(size)
|
|
, fFontSize(*size)
|
|
, fPreview(0)
|
|
, fFamiliesList(0)
|
|
, fStylesList(0)
|
|
, fSizesList(0)
|
|
, fSizesInput(0)
|
|
{
|
|
strncpy(fFontFamily, *family, B_FONT_FAMILY_LENGTH);
|
|
strncpy(fFontStyle, *style, B_FONT_STYLE_LENGTH);
|
|
|
|
// "client" area view
|
|
BBox *clientBox = new BBox(Bounds(), B_EMPTY_STRING, B_FOLLOW_ALL_SIDES,
|
|
B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP | B_PULSE_NEEDED,
|
|
B_PLAIN_BORDER);
|
|
AddChild(clientBox);
|
|
|
|
// client view
|
|
BRect RC = clientBox->Bounds();
|
|
RC.InsetBy(kVimDialogSpacingX, kVimDialogSpacingY);
|
|
BRect rc(RC.LeftTop(), RC.LeftTop());
|
|
|
|
// at first create all controls
|
|
fPreview = new BStringView(rc, "preview", "DejaVu Sans Mono");
|
|
clientBox->AddChild(fPreview);
|
|
|
|
BBox* boxDivider = new BBox(rc, B_EMPTY_STRING,
|
|
B_FOLLOW_NONE, B_WILL_DRAW, B_FANCY_BORDER);
|
|
clientBox->AddChild(boxDivider);
|
|
|
|
BStringView *labelFamily = new BStringView(rc, "labelFamily", "Family:");
|
|
clientBox->AddChild(labelFamily);
|
|
labelFamily->ResizeToPreferred();
|
|
|
|
BStringView *labelStyle = new BStringView(rc, "labelStyle", "Style:");
|
|
clientBox->AddChild(labelStyle);
|
|
labelStyle->ResizeToPreferred();
|
|
|
|
BStringView *labelSize = new BStringView(rc, "labelSize", "Size:");
|
|
clientBox->AddChild(labelSize);
|
|
labelSize->ResizeToPreferred();
|
|
|
|
fFamiliesList = new BListView(rc, "listFamily",
|
|
B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
|
|
BScrollView *scrollFamilies = new BScrollView("scrollFamily",
|
|
fFamiliesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
|
|
clientBox->AddChild(scrollFamilies);
|
|
|
|
fStylesList= new BListView(rc, "listStyles",
|
|
B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
|
|
BScrollView *scrollStyles = new BScrollView("scrollStyle",
|
|
fStylesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
|
|
clientBox->AddChild(scrollStyles);
|
|
|
|
fSizesInput = new BTextControl(rc, "inputSize", NULL, "???",
|
|
new BMessage(kVimDialogSizeInputMsg));
|
|
clientBox->AddChild(fSizesInput);
|
|
fSizesInput->ResizeToPreferred();
|
|
|
|
fSizesList = new BListView(rc, "listSizes",
|
|
B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
|
|
BScrollView *scrollSizes = new BScrollView("scrollSize",
|
|
fSizesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
|
|
clientBox->AddChild(scrollSizes);
|
|
|
|
BButton *buttonOK = new BButton(rc, "buttonOK", "OK",
|
|
new BMessage(kVimDialogOKButtonMsg));
|
|
clientBox->AddChild(buttonOK);
|
|
buttonOK->ResizeToPreferred();
|
|
|
|
BButton *buttonCancel = new BButton(rc, "buttonCancel", "Cancel",
|
|
new BMessage(kVimDialogCancelButtonMsg));
|
|
clientBox->AddChild(buttonCancel);
|
|
buttonCancel->ResizeToPreferred();
|
|
|
|
// layout controls
|
|
float lineHeight = labelFamily->Bounds().Height();
|
|
float previewHeight = lineHeight * 3;
|
|
float offsetYLabels = previewHeight + kVimDialogSpacingY;
|
|
float offsetYLists = offsetYLabels + lineHeight + kVimDialogSpacingY / 2;
|
|
float offsetYSizes = offsetYLists + fSizesInput->Bounds().Height() + kVimDialogSpacingY / 2;
|
|
float listsHeight = lineHeight * 9;
|
|
float offsetYButtons = offsetYLists + listsHeight + kVimDialogSpacingY;
|
|
float maxControlsHeight = offsetYButtons + buttonOK->Bounds().Height();
|
|
float familiesWidth = labelFamily->Bounds().Width() * 5;
|
|
float offsetXStyles = familiesWidth + kVimDialogSpacingX;
|
|
float stylesWidth = labelStyle->Bounds().Width() * 4;
|
|
float offsetXSizes = offsetXStyles + stylesWidth + kVimDialogSpacingX;
|
|
float sizesWidth = labelSize->Bounds().Width() * 2;
|
|
float maxControlsWidth = offsetXSizes + sizesWidth;
|
|
|
|
ResizeTo(maxControlsWidth + kVimDialogSpacingX * 2,
|
|
maxControlsHeight + kVimDialogSpacingY * 2);
|
|
|
|
BRect rcVim = gui.vimWindow->Frame();
|
|
MoveTo(rcVim.left + (rcVim.Width() - Frame().Width()) / 2,
|
|
rcVim.top + (rcVim.Height() - Frame().Height()) / 2);
|
|
|
|
fPreview->ResizeTo(maxControlsWidth, previewHeight);
|
|
fPreview->SetAlignment(B_ALIGN_CENTER);
|
|
|
|
boxDivider->MoveBy(0.f, previewHeight + kVimDialogSpacingY / 2);
|
|
boxDivider->ResizeTo(maxControlsWidth, 1.f);
|
|
|
|
labelFamily->MoveBy(0.f, offsetYLabels);
|
|
labelStyle->MoveBy(offsetXStyles, offsetYLabels);
|
|
labelSize->MoveBy(offsetXSizes, offsetYLabels);
|
|
|
|
// text control alignment issues
|
|
float insetX = fSizesInput->TextView()->Bounds().Width() - fSizesInput->Bounds().Width();
|
|
float insetY = fSizesInput->TextView()->Bounds().Width() - fSizesInput->Bounds().Width();
|
|
|
|
scrollFamilies->MoveBy(0.f, offsetYLists);
|
|
scrollStyles->MoveBy(offsetXStyles, offsetYLists);
|
|
fSizesInput->MoveBy(offsetXSizes + insetX / 2, offsetYLists + insetY / 2);
|
|
scrollSizes->MoveBy(offsetXSizes, offsetYSizes);
|
|
|
|
fSizesInput->SetAlignment(B_ALIGN_CENTER, B_ALIGN_CENTER);
|
|
|
|
scrollFamilies->ResizeTo(familiesWidth, listsHeight);
|
|
scrollStyles->ResizeTo(stylesWidth, listsHeight);
|
|
fSizesInput->ResizeTo(sizesWidth, fSizesInput->Bounds().Height());
|
|
scrollSizes->ResizeTo(sizesWidth,
|
|
listsHeight - (offsetYSizes - offsetYLists));
|
|
|
|
buttonOK->MoveBy(maxControlsWidth - buttonOK->Bounds().Width(), offsetYButtons);
|
|
buttonCancel->MoveBy(maxControlsWidth - buttonOK->Bounds().Width()
|
|
- buttonCancel->Bounds().Width() - kVimDialogSpacingX, offsetYButtons);
|
|
|
|
// fill lists
|
|
int selIndex = -1;
|
|
int count = count_font_families();
|
|
for (int i = 0; i < count; i++) {
|
|
font_family family;
|
|
if (get_font_family(i, &family ) == B_OK) {
|
|
fFamiliesList->AddItem(new BStringItem((const char*)family));
|
|
if (strncmp(family, fFontFamily, B_FONT_FAMILY_LENGTH) == 0)
|
|
selIndex = i;
|
|
}
|
|
}
|
|
|
|
if (selIndex >= 0) {
|
|
fFamiliesList->Select(selIndex);
|
|
fFamiliesList->ScrollToSelection();
|
|
}
|
|
|
|
_UpdateFontStyles();
|
|
|
|
selIndex = -1;
|
|
for (int size = 8, index = 0; size <= 18; size++, index++) {
|
|
BString str;
|
|
str << size;
|
|
fSizesList->AddItem(new BStringItem(str));
|
|
if (size == fFontSize)
|
|
selIndex = index;
|
|
|
|
}
|
|
|
|
if (selIndex >= 0) {
|
|
fSizesList->Select(selIndex);
|
|
fSizesList->ScrollToSelection();
|
|
}
|
|
|
|
fFamiliesList->SetSelectionMessage(new BMessage(kVimDialogFamilySelectMsg));
|
|
fStylesList->SetSelectionMessage(new BMessage(kVimDialogStyleSelectMsg));
|
|
fSizesList->SetSelectionMessage(new BMessage(kVimDialogSizeSelectMsg));
|
|
fSizesInput->SetModificationMessage(new BMessage(kVimDialogSizeInputMsg));
|
|
|
|
_UpdateSizeInputPreview();
|
|
_UpdateFontPreview();
|
|
|
|
fStatus = B_OK;
|
|
}
|
|
|
|
VimSelectFontDialog::~VimSelectFontDialog()
|
|
{
|
|
_CleanList(fFamiliesList);
|
|
_CleanList(fStylesList);
|
|
_CleanList(fSizesList);
|
|
|
|
if (fDialogSem > B_OK)
|
|
delete_sem(fDialogSem);
|
|
}
|
|
|
|
void
|
|
VimSelectFontDialog::_CleanList(BListView* list)
|
|
{
|
|
while (0 < list->CountItems())
|
|
delete (dynamic_cast<BStringItem*>(list->RemoveItem((int32)0)));
|
|
}
|
|
|
|
bool
|
|
VimSelectFontDialog::Go()
|
|
{
|
|
if (fStatus != B_OK) {
|
|
Quit();
|
|
return NOFONT;
|
|
}
|
|
|
|
fDialogSem = create_sem(0, "VimFontSelectDialogSem");
|
|
if (fDialogSem < B_OK) {
|
|
Quit();
|
|
return fDialogValue;
|
|
}
|
|
|
|
Show();
|
|
|
|
while (acquire_sem(fDialogSem) == B_INTERRUPTED);
|
|
|
|
bool retValue = fDialogValue;
|
|
|
|
if (Lock())
|
|
Quit();
|
|
|
|
return retValue;
|
|
}
|
|
|
|
|
|
void VimSelectFontDialog::_UpdateFontStyles()
|
|
{
|
|
_CleanList(fStylesList);
|
|
|
|
int32 selIndex = -1;
|
|
int32 count = count_font_styles(fFontFamily);
|
|
for (int32 i = 0; i < count; i++) {
|
|
font_style style;
|
|
uint32 flags = 0;
|
|
if (get_font_style(fFontFamily, i, &style, &flags) == B_OK) {
|
|
fStylesList->AddItem(new BStringItem((const char*)style));
|
|
if (strncmp(style, fFontStyle, B_FONT_STYLE_LENGTH) == 0)
|
|
selIndex = i;
|
|
}
|
|
}
|
|
|
|
if (selIndex >= 0) {
|
|
fStylesList->Select(selIndex);
|
|
fStylesList->ScrollToSelection();
|
|
} else
|
|
fStylesList->Select(0);
|
|
}
|
|
|
|
|
|
void VimSelectFontDialog::_UpdateSizeInputPreview()
|
|
{
|
|
char buf[10] = {0};
|
|
vim_snprintf(buf, sizeof(buf), (char*)"%.0f", fFontSize);
|
|
fSizesInput->SetText(buf);
|
|
}
|
|
|
|
|
|
void VimSelectFontDialog::_UpdateFontPreview()
|
|
{
|
|
BFont font;
|
|
fPreview->GetFont(&font);
|
|
font.SetSize(fFontSize);
|
|
font.SetFamilyAndStyle(fFontFamily, fFontStyle);
|
|
fPreview->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE);
|
|
|
|
BString str;
|
|
str << fFontFamily << " " << fFontStyle << ", " << (int)fFontSize << " pt.";
|
|
fPreview->SetText(str);
|
|
}
|
|
|
|
|
|
bool
|
|
VimSelectFontDialog::_UpdateFromListItem(BListView* list, char* text, int textSize)
|
|
{
|
|
int32 index = list->CurrentSelection();
|
|
if (index < 0)
|
|
return false;
|
|
BStringItem* item = (BStringItem*)list->ItemAt(index);
|
|
if (item == NULL)
|
|
return false;
|
|
strncpy(text, item->Text(), textSize);
|
|
return true;
|
|
}
|
|
|
|
|
|
void VimSelectFontDialog::MessageReceived(BMessage *msg)
|
|
{
|
|
switch (msg->what) {
|
|
case kVimDialogOKButtonMsg:
|
|
strncpy(*fFamily, fFontFamily, B_FONT_FAMILY_LENGTH);
|
|
strncpy(*fStyle, fFontStyle, B_FONT_STYLE_LENGTH);
|
|
*fSize = fFontSize;
|
|
fDialogValue = true;
|
|
case kVimDialogCancelButtonMsg:
|
|
delete_sem(fDialogSem);
|
|
fDialogSem = -1;
|
|
return;
|
|
case B_KEY_UP:
|
|
{
|
|
int32 key = 0;
|
|
if (msg->FindInt32("raw_char", &key) == B_OK
|
|
&& key == B_ESCAPE) {
|
|
delete_sem(fDialogSem);
|
|
fDialogSem = -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kVimDialogFamilySelectMsg:
|
|
if (_UpdateFromListItem(fFamiliesList,
|
|
fFontFamily, B_FONT_FAMILY_LENGTH)) {
|
|
_UpdateFontStyles();
|
|
_UpdateFontPreview();
|
|
}
|
|
break;
|
|
case kVimDialogStyleSelectMsg:
|
|
if (_UpdateFromListItem(fStylesList,
|
|
fFontStyle, B_FONT_STYLE_LENGTH))
|
|
_UpdateFontPreview();
|
|
break;
|
|
case kVimDialogSizeSelectMsg:
|
|
{
|
|
char buf[10] = {0};
|
|
if (_UpdateFromListItem(fSizesList, buf, sizeof(buf))) {
|
|
float size = atof(buf);
|
|
if (size > 0.f) {
|
|
fFontSize = size;
|
|
_UpdateSizeInputPreview();
|
|
_UpdateFontPreview();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case kVimDialogSizeInputMsg:
|
|
{
|
|
float size = atof(fSizesInput->Text());
|
|
if (size > 0.f) {
|
|
fFontSize = size;
|
|
_UpdateFontPreview();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return BWindow::MessageReceived(msg);
|
|
}
|
|
|
|
#endif // FEAT_GUI_DIALOG
|
|
|
|
#ifdef FEAT_TOOLBAR
|
|
|
|
// some forward declaration required by toolbar functions...
|
|
static BMessage * MenuMessage(vimmenu_T *menu);
|
|
|
|
VimToolbar::VimToolbar(BRect frame, const char *name) :
|
|
BBox(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER)
|
|
{
|
|
}
|
|
|
|
VimToolbar::~VimToolbar()
|
|
{
|
|
int32 count = fButtonsList.CountItems();
|
|
for (int32 i = 0; i < count; i++)
|
|
delete (BPictureButton*)fButtonsList.ItemAt(i);
|
|
fButtonsList.MakeEmpty();
|
|
|
|
delete normalButtonsBitmap;
|
|
delete grayedButtonsBitmap;
|
|
normalButtonsBitmap = NULL;
|
|
grayedButtonsBitmap = NULL;
|
|
}
|
|
|
|
void
|
|
VimToolbar::AttachedToWindow()
|
|
{
|
|
BBox::AttachedToWindow();
|
|
|
|
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
}
|
|
|
|
float
|
|
VimToolbar::ToolbarHeight() const
|
|
{
|
|
float size = NULL == normalButtonsBitmap ? 18. : normalButtonsBitmap->Bounds().Height();
|
|
return size + ToolbarMargin * 2 + ButtonMargin * 2 + 1;
|
|
}
|
|
|
|
bool
|
|
VimToolbar::ModifyBitmapToGrayed(BBitmap *bitmap)
|
|
{
|
|
float height = bitmap->Bounds().Height();
|
|
float width = bitmap->Bounds().Width();
|
|
|
|
rgb_color *bits = (rgb_color*)bitmap->Bits();
|
|
int32 pixels = bitmap->BitsLength() / 4;
|
|
for (int32 i = 0; i < pixels; i++) {
|
|
bits[i].red = bits[i].green =
|
|
bits[i].blue = ((uint32)bits[i].red + bits[i].green + bits[i].blue) / 3;
|
|
bits[i].alpha /= 4;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
VimToolbar::PrepareButtonBitmaps()
|
|
{
|
|
// first try to load potentially customized $VIRUNTIME/bitmaps/builtin-tools.png
|
|
normalButtonsBitmap = LoadVimBitmap("builtin-tools.png");
|
|
if (normalButtonsBitmap == NULL)
|
|
// customized not found? dig application resources for "builtin-tools" one
|
|
normalButtonsBitmap = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "builtin-tools");
|
|
|
|
if (normalButtonsBitmap == NULL)
|
|
return false;
|
|
|
|
BMessage archive;
|
|
normalButtonsBitmap->Archive(&archive);
|
|
|
|
grayedButtonsBitmap = new BBitmap(&archive);
|
|
if (grayedButtonsBitmap == NULL)
|
|
return false;
|
|
|
|
// modify grayed bitmap
|
|
ModifyBitmapToGrayed(grayedButtonsBitmap);
|
|
|
|
return true;
|
|
}
|
|
|
|
BBitmap *VimToolbar::LoadVimBitmap(const char* fileName)
|
|
{
|
|
BBitmap *bitmap = NULL;
|
|
|
|
int mustfree = 0;
|
|
char_u* runtimePath = vim_getenv((char_u*)"VIMRUNTIME", &mustfree);
|
|
if (runtimePath != NULL && fileName != NULL) {
|
|
BString strPath((char*)runtimePath);
|
|
strPath << "/bitmaps/" << fileName;
|
|
bitmap = BTranslationUtils::GetBitmap(strPath.String());
|
|
}
|
|
|
|
if (mustfree)
|
|
vim_free(runtimePath);
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
bool
|
|
VimToolbar::GetPictureFromBitmap(BPicture *pictureTo, int32 index, BBitmap *bitmapFrom, bool pressed)
|
|
{
|
|
float size = bitmapFrom->Bounds().Height() + 1.;
|
|
|
|
BView view(BRect(0, 0, size, size), "", 0, 0);
|
|
|
|
AddChild(&view);
|
|
view.BeginPicture(pictureTo);
|
|
|
|
view.SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
|
view.FillRect(view.Bounds());
|
|
view.SetDrawingMode(B_OP_OVER);
|
|
|
|
BRect source(0, 0, size - 1, size - 1);
|
|
BRect destination(source);
|
|
|
|
source.OffsetBy(size * index, 0);
|
|
destination.OffsetBy(ButtonMargin, ButtonMargin);
|
|
|
|
view.DrawBitmap(bitmapFrom, source, destination);
|
|
|
|
if (pressed) {
|
|
rgb_color shineColor = ui_color(B_SHINE_COLOR);
|
|
rgb_color shadowColor = ui_color(B_SHADOW_COLOR);
|
|
size += ButtonMargin * 2 - 1;
|
|
view.BeginLineArray(4);
|
|
view.AddLine(BPoint(0, 0), BPoint(size, 0), shadowColor);
|
|
view.AddLine(BPoint(size, 0), BPoint(size, size), shineColor);
|
|
view.AddLine(BPoint(size, size), BPoint(0, size), shineColor);
|
|
view.AddLine(BPoint(0, size), BPoint(0, 0), shadowColor);
|
|
view.EndLineArray();
|
|
}
|
|
|
|
view.EndPicture();
|
|
RemoveChild(&view);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
VimToolbar::AddButton(int32 index, vimmenu_T *menu)
|
|
{
|
|
BPictureButton *button = NULL;
|
|
if (!menu_is_separator(menu->name)) {
|
|
float size = normalButtonsBitmap ?
|
|
normalButtonsBitmap->Bounds().Height() + 1. + ButtonMargin * 2 : 18.;
|
|
BRect frame(0, 0, size, size);
|
|
BPicture pictureOn;
|
|
BPicture pictureOff;
|
|
BPicture pictureGray;
|
|
|
|
if (menu->iconfile == NULL && menu->iconidx >= 0 && normalButtonsBitmap) {
|
|
GetPictureFromBitmap(&pictureOn, menu->iconidx, normalButtonsBitmap, true);
|
|
GetPictureFromBitmap(&pictureOff, menu->iconidx, normalButtonsBitmap, false);
|
|
GetPictureFromBitmap(&pictureGray, menu->iconidx, grayedButtonsBitmap, false);
|
|
} else {
|
|
|
|
char_u buffer[MAXPATHL] = {0};
|
|
BBitmap *bitmap = NULL;
|
|
|
|
if (menu->iconfile) {
|
|
gui_find_iconfile(menu->iconfile, buffer, (char*)"png");
|
|
bitmap = BTranslationUtils::GetBitmap((char*)buffer);
|
|
}
|
|
|
|
if (bitmap == NULL && gui_find_bitmap(menu->name, buffer, (char*)"png") == OK)
|
|
bitmap = BTranslationUtils::GetBitmap((char*)buffer);
|
|
|
|
if (bitmap == NULL)
|
|
bitmap = new BBitmap(BRect(0, 0, size, size), B_RGB32);
|
|
|
|
GetPictureFromBitmap(&pictureOn, 0, bitmap, true);
|
|
GetPictureFromBitmap(&pictureOff, 0, bitmap, false);
|
|
ModifyBitmapToGrayed(bitmap);
|
|
GetPictureFromBitmap(&pictureGray, 0, bitmap, false);
|
|
|
|
delete bitmap;
|
|
}
|
|
|
|
button = new BPictureButton(frame, (char*)menu->name,
|
|
&pictureOff, &pictureOn, MenuMessage(menu));
|
|
|
|
button->SetDisabledOn(&pictureGray);
|
|
button->SetDisabledOff(&pictureGray);
|
|
|
|
button->SetTarget(gui.vimTextArea);
|
|
|
|
AddChild(button);
|
|
|
|
menu->button = button;
|
|
}
|
|
|
|
bool result = fButtonsList.AddItem(button, index);
|
|
InvalidateLayout();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
VimToolbar::RemoveButton(vimmenu_T *menu)
|
|
{
|
|
if (menu->button) {
|
|
if (fButtonsList.RemoveItem(menu->button)) {
|
|
delete menu->button;
|
|
menu->button = NULL;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
VimToolbar::GrayButton(vimmenu_T *menu, int grey)
|
|
{
|
|
if (menu->button) {
|
|
int32 index = fButtonsList.IndexOf(menu->button);
|
|
if (index >= 0)
|
|
menu->button->SetEnabled(grey ? false : true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
VimToolbar::InvalidateLayout()
|
|
{
|
|
int32 offset = ToolbarMargin;
|
|
int32 count = fButtonsList.CountItems();
|
|
for (int32 i = 0; i < count; i++) {
|
|
BPictureButton *button = (BPictureButton *)fButtonsList.ItemAt(i);
|
|
if (button) {
|
|
button->MoveTo(offset, ToolbarMargin);
|
|
offset += button->Bounds().Width() + ToolbarMargin;
|
|
} else
|
|
offset += ToolbarMargin * 3;
|
|
}
|
|
}
|
|
|
|
#endif /*FEAT_TOOLBAR*/
|
|
|
|
#if defined(FEAT_GUI_TABLINE)
|
|
|
|
float
|
|
VimTabLine::TablineHeight() const
|
|
{
|
|
// float size = NULL == normalButtonsBitmap ? 18. : normalButtonsBitmap->Bounds().Height();
|
|
// return size + ToolbarMargin * 2 + ButtonMargin * 2 + 1;
|
|
return TabHeight(); // + ToolbarMargin;
|
|
}
|
|
|
|
void
|
|
VimTabLine::MouseDown(BPoint point)
|
|
{
|
|
if (!gui_mch_showing_tabline())
|
|
return;
|
|
|
|
BMessage *m = Window()->CurrentMessage();
|
|
assert(m);
|
|
|
|
int32 buttons = 0;
|
|
m->FindInt32("buttons", &buttons);
|
|
|
|
int32 clicks = 0;
|
|
m->FindInt32("clicks", &clicks);
|
|
|
|
int index = 0; // 0 means here - no tab found
|
|
for (int i = 0; i < CountTabs(); i++) {
|
|
if (TabFrame(i).Contains(point)) {
|
|
index = i + 1; // indexes are 1-based
|
|
break;
|
|
}
|
|
}
|
|
|
|
int event = -1;
|
|
|
|
if ((buttons & B_PRIMARY_MOUSE_BUTTON) && clicks > 1)
|
|
// left button double click on - create new tab
|
|
event = TABLINE_MENU_NEW;
|
|
|
|
else if (buttons & B_TERTIARY_MOUSE_BUTTON)
|
|
// middle button click - close the pointed tab
|
|
// or create new one in case empty space
|
|
event = index > 0 ? TABLINE_MENU_CLOSE : TABLINE_MENU_NEW;
|
|
|
|
else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
|
|
// right button click - show context menu
|
|
BPopUpMenu* popUpMenu = new BPopUpMenu("tabLineContextMenu", false, false);
|
|
popUpMenu->AddItem(new BMenuItem(_("Close tabi R"), new BMessage(TABLINE_MENU_CLOSE)));
|
|
popUpMenu->AddItem(new BMenuItem(_("New tab T"), new BMessage(TABLINE_MENU_NEW)));
|
|
popUpMenu->AddItem(new BMenuItem(_("Open tab..."), new BMessage(TABLINE_MENU_OPEN)));
|
|
|
|
ConvertToScreen(&point);
|
|
BMenuItem* item = popUpMenu->Go(point);
|
|
if (item != NULL) {
|
|
event = item->Command();
|
|
}
|
|
|
|
delete popUpMenu;
|
|
|
|
} else {
|
|
// default processing
|
|
BTabView::MouseDown(point);
|
|
return;
|
|
}
|
|
|
|
if (event < 0)
|
|
return;
|
|
|
|
VimTablineMenuMsg tmm;
|
|
tmm.index = index;
|
|
tmm.event = event;
|
|
write_port(gui.vdcmp, VimMsg::TablineMenu, &tmm, sizeof(tmm));
|
|
}
|
|
|
|
void
|
|
VimTabLine::VimTab::Select(BView* owner)
|
|
{
|
|
BTab::Select(owner);
|
|
|
|
VimTabLine *tabLine = gui.vimForm->TabLine();
|
|
if (tabLine != NULL) {
|
|
|
|
int32 i = 0;
|
|
for (; i < tabLine->CountTabs(); i++)
|
|
if (this == tabLine->TabAt(i))
|
|
break;
|
|
|
|
// printf("%d:%d:%s\n", i, tabLine->CountTabs(), tabLine->TabAt(i)->Label());
|
|
if (i < tabLine->CountTabs()) {
|
|
VimTablineMsg tm;
|
|
tm.index = i + 1;
|
|
write_port(gui.vdcmp, VimMsg::Tabline, &tm, sizeof(tm));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // defined(FEAT_GUI_TABLINE)
|
|
|
|
// ---------------- ----------------
|
|
|
|
// some global variables
|
|
static char appsig[] = "application/x-vnd.Haiku-Vim-8";
|
|
key_map *keyMap;
|
|
char *keyMapChars;
|
|
int main_exitcode = 127;
|
|
|
|
status_t
|
|
gui_haiku_process_event(bigtime_t timeout)
|
|
{
|
|
struct VimMsg vm;
|
|
int32 what;
|
|
ssize_t size;
|
|
|
|
size = read_port_etc(gui.vdcmp, &what, &vm, sizeof(vm),
|
|
B_TIMEOUT, timeout);
|
|
|
|
if (size >= 0) {
|
|
switch (what) {
|
|
case VimMsg::Key:
|
|
{
|
|
char_u *string = vm.u.Key.chars;
|
|
int len = vm.u.Key.length;
|
|
if (len == 1 && string[0] == Ctrl_chr('C')) {
|
|
trash_input_buf();
|
|
got_int = TRUE;
|
|
}
|
|
|
|
if (vm.u.Key.csi_escape)
|
|
#ifndef FEAT_MBYTE_IME
|
|
{
|
|
int i;
|
|
char_u buf[2];
|
|
|
|
for (i = 0; i < len; ++i)
|
|
{
|
|
add_to_input_buf(string + i, 1);
|
|
if (string[i] == CSI)
|
|
{
|
|
// Turn CSI into K_CSI.
|
|
buf[0] = KS_EXTRA;
|
|
buf[1] = (int)KE_CSI;
|
|
add_to_input_buf(buf, 2);
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
add_to_input_buf_csi(string, len);
|
|
#endif
|
|
else
|
|
add_to_input_buf(string, len);
|
|
}
|
|
break;
|
|
case VimMsg::Resize:
|
|
gui_resize_shell(vm.u.NewSize.width, vm.u.NewSize.height);
|
|
break;
|
|
case VimMsg::ScrollBar:
|
|
{
|
|
/*
|
|
* If loads of scroll messages queue up, use only the last
|
|
* one. Always report when the scrollbar stops dragging.
|
|
* This is not perfect yet anyway: these events are queued
|
|
* yet again, this time in the keyboard input buffer.
|
|
*/
|
|
int32 oldCount =
|
|
atomic_add(&vm.u.Scroll.sb->scrollEventCount, -1);
|
|
if (oldCount <= 1 || !vm.u.Scroll.stillDragging)
|
|
gui_drag_scrollbar(vm.u.Scroll.sb->getGsb(),
|
|
vm.u.Scroll.value, vm.u.Scroll.stillDragging);
|
|
}
|
|
break;
|
|
#if defined(FEAT_MENU)
|
|
case VimMsg::Menu:
|
|
gui_menu_cb(vm.u.Menu.guiMenu);
|
|
break;
|
|
#endif
|
|
case VimMsg::Mouse:
|
|
{
|
|
int32 oldCount;
|
|
if (vm.u.Mouse.button == MOUSE_DRAG)
|
|
oldCount =
|
|
atomic_add(&gui.vimTextArea->mouseDragEventCount, -1);
|
|
else
|
|
oldCount = 0;
|
|
if (oldCount <= 1)
|
|
gui_send_mouse_event(vm.u.Mouse.button, vm.u.Mouse.x,
|
|
vm.u.Mouse.y, vm.u.Mouse.repeated_click,
|
|
vm.u.Mouse.modifiers);
|
|
}
|
|
break;
|
|
case VimMsg::MouseMoved:
|
|
{
|
|
gui_mouse_moved(vm.u.MouseMoved.x, vm.u.MouseMoved.y);
|
|
}
|
|
break;
|
|
case VimMsg::Focus:
|
|
gui.in_focus = vm.u.Focus.active;
|
|
// XXX Signal that scrollbar dragging has stopped?
|
|
// This is needed because we don't get a MouseUp if
|
|
// that happens while outside the window... :-(
|
|
if (gui.dragged_sb) {
|
|
gui.dragged_sb = SBAR_NONE;
|
|
}
|
|
// gui_update_cursor(TRUE, FALSE);
|
|
break;
|
|
case VimMsg::Refs:
|
|
::RefsReceived(vm.u.Refs.message, vm.u.Refs.changedir);
|
|
break;
|
|
case VimMsg::Tabline:
|
|
send_tabline_event(vm.u.Tabline.index);
|
|
break;
|
|
case VimMsg::TablineMenu:
|
|
send_tabline_menu_event(vm.u.TablineMenu.index, vm.u.TablineMenu.event);
|
|
break;
|
|
default:
|
|
// unrecognised message, ignore it
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If size < B_OK, it is an error code.
|
|
*/
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* Here are some functions to protect access to ScreenLines[] and
|
|
* LineOffset[]. These are used from the window thread to respond
|
|
* to a Draw() callback. When that occurs, the window is already
|
|
* locked by the system.
|
|
*
|
|
* Other code that needs to lock is any code that changes these
|
|
* variables. Other read-only access, or access merely to the
|
|
* contents of the screen buffer, need not be locked.
|
|
*
|
|
* If there is no window, don't call Lock() but do succeed.
|
|
*/
|
|
|
|
int
|
|
vim_lock_screen()
|
|
{
|
|
return !gui.vimWindow || gui.vimWindow->Lock();
|
|
}
|
|
|
|
void
|
|
vim_unlock_screen()
|
|
{
|
|
if (gui.vimWindow)
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
|
|
#define RUN_BAPPLICATION_IN_NEW_THREAD 0
|
|
|
|
#if RUN_BAPPLICATION_IN_NEW_THREAD
|
|
|
|
int32
|
|
run_vimapp(void *args)
|
|
{
|
|
VimApp app(appsig);
|
|
|
|
gui.vimApp = &app;
|
|
app.Run(); // Run until Quit() called
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
int32
|
|
call_main(void *args)
|
|
{
|
|
struct MainArgs *ma = (MainArgs *)args;
|
|
|
|
return main(ma->argc, ma->argv);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Parse the GUI related command-line arguments. Any arguments used are
|
|
* deleted from argv, and *argc is decremented accordingly. This is called
|
|
* when vim is started, whether or not the GUI has been started.
|
|
*/
|
|
void
|
|
gui_mch_prepare(
|
|
int *argc,
|
|
char **argv)
|
|
{
|
|
/*
|
|
* We don't have any command line arguments for the BeOS GUI yet,
|
|
* but this is an excellent place to create our Application object.
|
|
*/
|
|
if (!gui.vimApp) {
|
|
thread_info tinfo;
|
|
get_thread_info(find_thread(NULL), &tinfo);
|
|
|
|
// May need the port very early on to process RefsReceived()
|
|
gui.vdcmp = create_port(B_MAX_PORT_COUNT, "vim VDCMP");
|
|
|
|
#if RUN_BAPPLICATION_IN_NEW_THREAD
|
|
thread_id tid = spawn_thread(run_vimapp, "vim VimApp",
|
|
tinfo.priority, NULL);
|
|
if (tid >= B_OK) {
|
|
resume_thread(tid);
|
|
} else {
|
|
getout(1);
|
|
}
|
|
#else
|
|
MainArgs ma = { *argc, argv };
|
|
thread_id tid = spawn_thread(call_main, "vim main()",
|
|
tinfo.priority, &ma);
|
|
if (tid >= B_OK) {
|
|
VimApp app(appsig);
|
|
|
|
gui.vimApp = &app;
|
|
resume_thread(tid);
|
|
/*
|
|
* This is rather horrible.
|
|
* call_main will call main() again...
|
|
* There will be no infinite recursion since
|
|
* gui.vimApp is set now.
|
|
*/
|
|
app.Run(); // Run until Quit() called
|
|
// fprintf(stderr, "app.Run() returned...\n");
|
|
status_t dummy_exitcode;
|
|
(void)wait_for_thread(tid, &dummy_exitcode);
|
|
|
|
/*
|
|
* This path should be the normal one taken to exit Vim.
|
|
* The main() thread calls mch_exit() which calls
|
|
* gui_mch_exit() which terminates its thread.
|
|
*/
|
|
exit(main_exitcode);
|
|
}
|
|
#endif
|
|
}
|
|
// Don't fork() when starting the GUI. Spawned threads are not
|
|
// duplicated with a fork(). The result is a mess.
|
|
gui.dofork = FALSE;
|
|
/*
|
|
* XXX Try to determine whether we were started from
|
|
* the Tracker or the terminal.
|
|
* It would be nice to have this work, because the Tracker
|
|
* follows symlinks, so even if you double-click on gvim,
|
|
* when it is a link to vim it will still pass a command name
|
|
* of vim...
|
|
* We try here to see if stdin comes from /dev/null. If so,
|
|
* (or if there is an error, which should never happen) start the GUI.
|
|
* This does the wrong thing for vim - </dev/null, and we're
|
|
* too early to see the command line parsing. Tough.
|
|
* On the other hand, it starts the gui for vim file & which is nice.
|
|
*/
|
|
if (!isatty(0)) {
|
|
struct stat stat_stdin, stat_dev_null;
|
|
|
|
if (fstat(0, &stat_stdin) == -1 ||
|
|
stat("/dev/null", &stat_dev_null) == -1 ||
|
|
(stat_stdin.st_dev == stat_dev_null.st_dev &&
|
|
stat_stdin.st_ino == stat_dev_null.st_ino))
|
|
gui.starting = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the GUI can be started. Called before gvimrc is sourced.
|
|
* Return OK or FAIL.
|
|
*/
|
|
int
|
|
gui_mch_init_check(void)
|
|
{
|
|
return OK; // TODO: GUI can always be started?
|
|
}
|
|
|
|
/*
|
|
* Initialise the GUI. Create all the windows, set up all the call-backs
|
|
* etc.
|
|
*/
|
|
int
|
|
gui_mch_init()
|
|
{
|
|
display_errors();
|
|
gui.def_norm_pixel = RGB(0x00, 0x00, 0x00); // black
|
|
gui.def_back_pixel = RGB(0xFF, 0xFF, 0xFF); // white
|
|
gui.norm_pixel = gui.def_norm_pixel;
|
|
gui.back_pixel = gui.def_back_pixel;
|
|
|
|
gui.scrollbar_width = (int) B_V_SCROLL_BAR_WIDTH;
|
|
gui.scrollbar_height = (int) B_H_SCROLL_BAR_HEIGHT;
|
|
#ifdef FEAT_MENU
|
|
gui.menu_height = 19; // initial guess -
|
|
// correct for my default settings
|
|
#endif
|
|
gui.border_offset = 3; // coordinates are inside window borders
|
|
|
|
if (gui.vdcmp < B_OK)
|
|
return FAIL;
|
|
get_key_map(&keyMap, &keyMapChars);
|
|
|
|
gui.vimWindow = new VimWindow(); // hidden and locked
|
|
if (!gui.vimWindow)
|
|
return FAIL;
|
|
|
|
gui.vimWindow->Run(); // Run() unlocks but does not show
|
|
|
|
// Get the colors from the "Normal" group (set in syntax.c or in a vimrc
|
|
// file)
|
|
set_normal_colors();
|
|
|
|
/*
|
|
* Check that none of the colors are the same as the background color
|
|
*/
|
|
gui_check_colors();
|
|
|
|
// Get the colors for the highlight groups (gui_check_colors() might have
|
|
// changed them)
|
|
highlight_gui_started(); // re-init colors and fonts
|
|
|
|
gui_mch_new_colors(); // window must exist for this
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Called when the foreground or background color has been changed.
|
|
*/
|
|
void
|
|
gui_mch_new_colors()
|
|
{
|
|
rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
|
|
|
|
if (gui.vimWindow->Lock()) {
|
|
gui.vimForm->SetViewColor(rgb);
|
|
// Does this not have too much effect for those small rectangles?
|
|
gui.vimForm->Invalidate();
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open the GUI window which was created by a call to gui_mch_init().
|
|
*/
|
|
int
|
|
gui_mch_open()
|
|
{
|
|
if (gui_win_x != -1 && gui_win_y != -1)
|
|
gui_mch_set_winpos(gui_win_x, gui_win_y);
|
|
|
|
// Actually open the window
|
|
if (gui.vimWindow->Lock()) {
|
|
gui.vimWindow->Show();
|
|
gui.vimWindow->Unlock();
|
|
return OK;
|
|
}
|
|
|
|
return FAIL;
|
|
}
|
|
|
|
void
|
|
gui_mch_exit(int vim_exitcode)
|
|
{
|
|
if (gui.vimWindow) {
|
|
thread_id tid = gui.vimWindow->Thread();
|
|
gui.vimWindow->Lock();
|
|
gui.vimWindow->Quit();
|
|
// Wait until it is 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_font), name);
|
|
return NOFONT;
|
|
}
|
|
|
|
bool useSelectGUI = false;
|
|
if (name != NULL)
|
|
if (STRCMP(name, "*") == 0) {
|
|
useSelectGUI = true;
|
|
STRNCPY(font_name, hl_get_font_name(), buff_size);
|
|
} else
|
|
STRNCPY(font_name, name, buff_size);
|
|
|
|
if (font_name[0] == 0) {
|
|
be_fixed_font->GetFamilyAndStyle(&family, &style);
|
|
size = be_fixed_font->Size();
|
|
vim_snprintf(font_name, buff_size,
|
|
(char*)"%s/%s/%.0f", family, style, size);
|
|
}
|
|
|
|
// replace underscores with spaces
|
|
char* end = 0;
|
|
while (end = strchr((char *)font_name, '_'))
|
|
*end = ' ';
|
|
|
|
// store the name before strtok corrupt the buffer ;-)
|
|
static char buff[buff_size] = {0};
|
|
STRNCPY(buff, font_name, buff_size);
|
|
STRNCPY(family, strtok(buff, "/\0"), B_FONT_FAMILY_LENGTH);
|
|
char* style_s = strtok(0, "/\0");
|
|
if (style_s != 0)
|
|
STRNCPY(style, style_s, B_FONT_STYLE_LENGTH);
|
|
size = atof((style_s != 0) ? strtok(0, "/\0") : "0");
|
|
|
|
if (useSelectGUI) {
|
|
if (gui_mch_font_dialog(&family, &style, &size) == NOFONT)
|
|
return FAIL;
|
|
// compose for further processing
|
|
vim_snprintf(font_name, buff_size,
|
|
(char*)"%s/%s/%.0f", family, style, size);
|
|
hl_set_font_name((char_u*)font_name);
|
|
|
|
// Set guifont to the name of the selected font.
|
|
char_u* new_p_guifont = (char_u*)alloc(STRLEN(font_name) + 1);
|
|
if (new_p_guifont != NULL) {
|
|
STRCPY(new_p_guifont, font_name);
|
|
vim_free(p_guifont);
|
|
p_guifont = new_p_guifont;
|
|
// Replace spaces in the font name with underscores.
|
|
for ( ; *new_p_guifont; ++new_p_guifont)
|
|
if (*new_p_guifont == ' ')
|
|
*new_p_guifont = '_';
|
|
}
|
|
}
|
|
|
|
VimFont *flp;
|
|
for (flp = fontList; flp; flp = flp->next) {
|
|
if (STRCMP(font_name, flp->name) == 0) {
|
|
flp->refcount++;
|
|
return (GuiFont)flp;
|
|
}
|
|
}
|
|
|
|
VimFont *font = new VimFont();
|
|
font->name = vim_strsave((char_u*)font_name);
|
|
|
|
if (count_font_styles(family) <= 0) {
|
|
if (giveErrorIfMissing)
|
|
semsg(_(e_font), font->name);
|
|
delete font;
|
|
return NOFONT;
|
|
}
|
|
|
|
// Remember font in the static list for later use
|
|
font->next = fontList;
|
|
fontList = font;
|
|
|
|
font->SetFamilyAndStyle(family, style);
|
|
if (size > 0.f)
|
|
font->SetSize(size);
|
|
|
|
font->SetSpacing(B_FIXED_SPACING);
|
|
font->SetEncoding(B_UNICODE_UTF8);
|
|
|
|
return (GuiFont)font;
|
|
}
|
|
|
|
/*
|
|
* Set the current text font.
|
|
*/
|
|
void
|
|
gui_mch_set_font(
|
|
GuiFont font)
|
|
{
|
|
if (gui.vimWindow->Lock()) {
|
|
VimFont *vf = (VimFont *)font;
|
|
|
|
gui.vimTextArea->SetFont(vf);
|
|
|
|
gui.char_width = (int) vf->StringWidth("n");
|
|
font_height fh;
|
|
vf->GetHeight(&fh);
|
|
gui.char_height = (int)(fh.ascent + 0.9999)
|
|
+ (int)(fh.descent + 0.9999) + (int)(fh.leading + 0.9999);
|
|
gui.char_ascent = (int)(fh.ascent + 0.9999);
|
|
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
|
|
// XXX TODO This is apparently never called...
|
|
void
|
|
gui_mch_free_font(
|
|
GuiFont font)
|
|
{
|
|
if (font == NOFONT)
|
|
return;
|
|
VimFont *f = (VimFont *)font;
|
|
if (--f->refcount <= 0) {
|
|
if (f->refcount < 0)
|
|
fprintf(stderr, "VimFont: refcount < 0\n");
|
|
delete f;
|
|
}
|
|
}
|
|
|
|
char_u *
|
|
gui_mch_get_fontname(GuiFont font, char_u *name)
|
|
{
|
|
if (name == NULL)
|
|
return NULL;
|
|
return vim_strsave(name);
|
|
}
|
|
|
|
/*
|
|
* Adjust gui.char_height (after 'linespace' was changed).
|
|
*/
|
|
int
|
|
gui_mch_adjust_charheight()
|
|
{
|
|
|
|
// TODO: linespace support?
|
|
|
|
// #ifdef FEAT_XFONTSET
|
|
// if (gui.fontset != NOFONTSET)
|
|
// {
|
|
// gui.char_height = fontset_height((XFontSet)gui.fontset) + p_linespace;
|
|
// gui.char_ascent = fontset_ascent((XFontSet)gui.fontset)
|
|
// + p_linespace / 2;
|
|
// }
|
|
// else
|
|
// #endif
|
|
{
|
|
VimFont *font = (VimFont *)gui.norm_font;
|
|
font_height fh = {0};
|
|
font->GetHeight(&fh);
|
|
gui.char_height = (int)(fh.ascent + fh.descent + 0.5) + p_linespace;
|
|
gui.char_ascent = (int)(fh.ascent + 0.5) + p_linespace / 2;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
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 its safe to query the menu for the associated MenuItem....
|
|
menu->id = tmp->FindItem((const char *) menu->dname);
|
|
|
|
}
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
|
|
void
|
|
gui_mch_toggle_tearoffs(int enable)
|
|
{
|
|
// no tearoff menus
|
|
}
|
|
|
|
static BMessage *
|
|
MenuMessage(vimmenu_T *menu)
|
|
{
|
|
BMessage *m = new BMessage('menu');
|
|
m->AddPointer("VimMenu", (void *)menu);
|
|
|
|
return m;
|
|
}
|
|
|
|
/*
|
|
* Add a menu item to a menu
|
|
*/
|
|
void
|
|
gui_mch_add_menu_item(
|
|
vimmenu_T *menu,
|
|
int idx)
|
|
{
|
|
int mnemonic = 0;
|
|
vimmenu_T *parent = menu->parent;
|
|
|
|
// TODO: use menu->actext
|
|
// This is difficult, since on Be, an accelerator must be a single char
|
|
// and a lot of Vim ones are the standard VI commands.
|
|
//
|
|
// Punt for Now...
|
|
// richard@whiequeen.com jul 99
|
|
if (gui.vimWindow->Lock())
|
|
{
|
|
#ifdef FEAT_TOOLBAR
|
|
if (menu_is_toolbar(parent->name)) {
|
|
VimToolbar *toolbar = gui.vimForm->ToolBar();
|
|
if (toolbar != NULL) {
|
|
toolbar->AddButton(idx, menu);
|
|
}
|
|
} else
|
|
#endif
|
|
|
|
if (parent->submenu_id != NULL || menu_is_popup(parent->name)) {
|
|
if (menu_is_separator(menu->name)) {
|
|
BSeparatorItem *item = new BSeparatorItem();
|
|
parent->submenu_id->AddItem(item);
|
|
menu->id = item;
|
|
menu->submenu_id = NULL;
|
|
}
|
|
else {
|
|
BMenuItem *item = new BMenuItem((char *)menu->dname,
|
|
MenuMessage(menu));
|
|
item->SetTarget(gui.vimTextArea);
|
|
item->SetTrigger((char) menu->mnemonic);
|
|
parent->submenu_id->AddItem(item);
|
|
menu->id = item;
|
|
menu->submenu_id = NULL;
|
|
}
|
|
}
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Destroy the machine specific menu widget.
|
|
*/
|
|
void
|
|
gui_mch_destroy_menu(
|
|
vimmenu_T *menu)
|
|
{
|
|
if (gui.vimWindow->Lock())
|
|
{
|
|
#ifdef FEAT_TOOLBAR
|
|
if (menu->parent && menu_is_toolbar(menu->parent->name)) {
|
|
VimToolbar *toolbar = gui.vimForm->ToolBar();
|
|
if (toolbar != NULL) {
|
|
toolbar->RemoveButton(menu);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
assert(menu->submenu_id == NULL || menu->submenu_id->CountItems() == 0);
|
|
/*
|
|
* Detach this menu from its parent, so that it is not deleted
|
|
* twice once we get to delete that parent.
|
|
* Deleting a BMenuItem also deletes the associated BMenu, if any
|
|
* (which does not have any items anymore since they were
|
|
* removed and deleted before).
|
|
*/
|
|
BMenu *bmenu = menu->id->Menu();
|
|
if (bmenu)
|
|
{
|
|
bmenu->RemoveItem(menu->id);
|
|
/*
|
|
* If we removed the last item from the menu bar,
|
|
* resize it out of sight.
|
|
*/
|
|
if (bmenu == gui.vimForm->MenuBar() && bmenu->CountItems() == 0)
|
|
{
|
|
bmenu->ResizeTo(-MENUBAR_MARGIN, -MENUBAR_MARGIN);
|
|
}
|
|
}
|
|
delete menu->id;
|
|
menu->id = NULL;
|
|
menu->submenu_id = NULL;
|
|
|
|
gui.menu_height = (int) gui.vimForm->MenuHeight();
|
|
}
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make a menu either grey or not grey.
|
|
*/
|
|
void
|
|
gui_mch_menu_grey(
|
|
vimmenu_T *menu,
|
|
int grey)
|
|
{
|
|
#ifdef FEAT_TOOLBAR
|
|
if (menu->parent && menu_is_toolbar(menu->parent->name)) {
|
|
if (gui.vimWindow->Lock()) {
|
|
VimToolbar *toolbar = gui.vimForm->ToolBar();
|
|
if (toolbar != NULL) {
|
|
toolbar->GrayButton(menu, grey);
|
|
}
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
} else
|
|
#endif
|
|
if (menu->id != NULL)
|
|
menu->id->SetEnabled(!grey);
|
|
}
|
|
|
|
/*
|
|
* Make menu item hidden or not hidden
|
|
*/
|
|
void
|
|
gui_mch_menu_hidden(
|
|
vimmenu_T *menu,
|
|
int hidden)
|
|
{
|
|
if (menu->id != NULL)
|
|
menu->id->SetEnabled(!hidden);
|
|
}
|
|
|
|
/*
|
|
* This is called after setting all the menus to grey/hidden or not.
|
|
*/
|
|
void
|
|
gui_mch_draw_menubar()
|
|
{
|
|
// Nothing to do in BeOS
|
|
}
|
|
|
|
void
|
|
gui_mch_show_popupmenu(vimmenu_T *menu)
|
|
{
|
|
if (!menu_is_popup(menu->name) || menu->submenu_id == NULL)
|
|
return;
|
|
|
|
BPopUpMenu* popupMenu = dynamic_cast<BPopUpMenu*>(menu->submenu_id);
|
|
if (popupMenu == NULL)
|
|
return;
|
|
|
|
BPoint point;
|
|
if (gui.vimWindow->Lock()) {
|
|
uint32 buttons = 0;
|
|
gui.vimTextArea->GetMouse(&point, &buttons);
|
|
gui.vimTextArea->ConvertToScreen(&point);
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
popupMenu->Go(point, true);
|
|
}
|
|
|
|
#endif // FEAT_MENU
|
|
|
|
// Mouse stuff
|
|
|
|
#ifdef FEAT_CLIPBOARD
|
|
/*
|
|
* Clipboard stuff, for cutting and pasting text to other windows.
|
|
*/
|
|
char textplain[] = "text/plain";
|
|
char vimselectiontype[] = "application/x-vnd.Rhialto-Vim-selectiontype";
|
|
|
|
/*
|
|
* Get the current selection and put it in the clipboard register.
|
|
*/
|
|
void
|
|
clip_mch_request_selection(Clipboard_T *cbd)
|
|
{
|
|
if (be_clipboard->Lock())
|
|
{
|
|
BMessage *m = be_clipboard->Data();
|
|
// m->PrintToStream();
|
|
|
|
char_u *string = NULL;
|
|
ssize_t stringlen = -1;
|
|
|
|
if (m->FindData(textplain, B_MIME_TYPE,
|
|
(const void **)&string, &stringlen) == B_OK
|
|
|| m->FindString("text", (const char **)&string) == B_OK)
|
|
{
|
|
if (stringlen == -1)
|
|
stringlen = STRLEN(string);
|
|
|
|
int type;
|
|
char *seltype;
|
|
ssize_t seltypelen;
|
|
|
|
/*
|
|
* Try to get the special vim selection type first
|
|
*/
|
|
if (m->FindData(vimselectiontype, B_MIME_TYPE,
|
|
(const void **)&seltype, &seltypelen) == B_OK)
|
|
{
|
|
switch (*seltype)
|
|
{
|
|
default:
|
|
case 'L': type = MLINE; break;
|
|
case 'C': type = MCHAR; break;
|
|
#ifdef FEAT_VISUAL
|
|
case 'B': type = MBLOCK; break;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise use heuristic as documented
|
|
type = memchr(string, stringlen, '\n') ? MLINE : MCHAR;
|
|
}
|
|
clip_yank_selection(type, string, (long)stringlen, cbd);
|
|
}
|
|
be_clipboard->Unlock();
|
|
}
|
|
}
|
|
/*
|
|
* Make vim the owner of the current selection.
|
|
*/
|
|
void
|
|
clip_mch_lose_selection(Clipboard_T *cbd)
|
|
{
|
|
// Nothing needs to be done here
|
|
}
|
|
|
|
/*
|
|
* Make vim the owner of the current selection. Return OK upon success.
|
|
*/
|
|
int
|
|
clip_mch_own_selection(Clipboard_T *cbd)
|
|
{
|
|
/*
|
|
* Never actually own the clipboard. If another application sets the
|
|
* clipboard, we don't want to think that we still own it.
|
|
*/
|
|
return FAIL;
|
|
}
|
|
|
|
/*
|
|
* Send the current selection to the clipboard.
|
|
*/
|
|
void
|
|
clip_mch_set_selection(Clipboard_T *cbd)
|
|
{
|
|
if (be_clipboard->Lock())
|
|
{
|
|
be_clipboard->Clear();
|
|
BMessage *m = be_clipboard->Data();
|
|
assert(m);
|
|
|
|
// If the '*' register isn't already filled in, fill it in now
|
|
cbd->owned = TRUE;
|
|
clip_get_selection(cbd);
|
|
cbd->owned = FALSE;
|
|
|
|
char_u *str = NULL;
|
|
long_u count;
|
|
int type;
|
|
|
|
type = clip_convert_selection(&str, &count, cbd);
|
|
|
|
if (type < 0)
|
|
return;
|
|
|
|
m->AddData(textplain, B_MIME_TYPE, (void *)str, count);
|
|
|
|
// Add type of selection
|
|
char vtype;
|
|
switch (type)
|
|
{
|
|
default:
|
|
case MLINE: vtype = 'L'; break;
|
|
case MCHAR: vtype = 'C'; break;
|
|
#ifdef FEAT_VISUAL
|
|
case MBLOCK: vtype = 'B'; break;
|
|
#endif
|
|
}
|
|
m->AddData(vimselectiontype, B_MIME_TYPE, (void *)&vtype, 1);
|
|
|
|
vim_free(str);
|
|
|
|
be_clipboard->Commit();
|
|
be_clipboard->Unlock();
|
|
}
|
|
}
|
|
|
|
#endif // FEAT_CLIPBOARD
|
|
|
|
#ifdef FEAT_BROWSE
|
|
/*
|
|
* Pop open a file browser and return the file selected, in allocated memory,
|
|
* or NULL if Cancel is hit.
|
|
* saving - TRUE if the file will be saved to, FALSE if it will be opened.
|
|
* title - Title message for the file browser dialog.
|
|
* dflt - Default name of file.
|
|
* ext - Default extension to be added to files without extensions.
|
|
* initdir - directory in which to open the browser (NULL = current dir)
|
|
* filter - Filter for matched files to choose from.
|
|
* Has a format like this:
|
|
* "C Files (*.c)\0*.c\0"
|
|
* "All Files\0*.*\0\0"
|
|
* If these two strings were concatenated, then a choice of two file
|
|
* filters will be selectable to the user. Then only matching files will
|
|
* be shown in the browser. If NULL, the default allows all files.
|
|
*
|
|
* *NOTE* - the filter string must be terminated with TWO nulls.
|
|
*/
|
|
char_u *
|
|
gui_mch_browse(
|
|
int saving,
|
|
char_u *title,
|
|
char_u *dflt,
|
|
char_u *ext,
|
|
char_u *initdir,
|
|
char_u *filter)
|
|
{
|
|
gui.vimApp->fFilePanel = new BFilePanel((saving == TRUE) ? B_SAVE_PANEL : B_OPEN_PANEL,
|
|
NULL, NULL, 0, false,
|
|
new BMessage((saving == TRUE) ? 'save' : 'open'), NULL, true);
|
|
|
|
gui.vimApp->fBrowsedPath.Unset();
|
|
|
|
gui.vimApp->fFilePanel->Window()->SetTitle((char*)title);
|
|
gui.vimApp->fFilePanel->SetPanelDirectory((const char*)initdir);
|
|
|
|
gui.vimApp->fFilePanel->Show();
|
|
|
|
gui.vimApp->fFilePanelSem = create_sem(0, "FilePanelSem");
|
|
|
|
while (acquire_sem(gui.vimApp->fFilePanelSem) == B_INTERRUPTED);
|
|
|
|
char_u *fileName = NULL;
|
|
status_t result = gui.vimApp->fBrowsedPath.InitCheck();
|
|
if (result == B_OK) {
|
|
fileName = vim_strsave((char_u*)gui.vimApp->fBrowsedPath.Path());
|
|
} else
|
|
if (result != B_NO_INIT) {
|
|
fprintf(stderr, "gui_mch_browse: BPath error: %#08x (%s)\n",
|
|
result, strerror(result));
|
|
}
|
|
|
|
delete gui.vimApp->fFilePanel;
|
|
gui.vimApp->fFilePanel = NULL;
|
|
|
|
return fileName;
|
|
}
|
|
#endif // FEAT_BROWSE
|
|
|
|
|
|
#if defined(FEAT_GUI_DIALOG)
|
|
|
|
/*
|
|
* Create a dialog dynamically from the parameter strings.
|
|
* type = type of dialog (question, alert, etc.)
|
|
* title = dialog title. may be NULL for default title.
|
|
* message = text to display. Dialog sizes to accommodate it.
|
|
* buttons = '\n' separated list of button captions, default first.
|
|
* dfltbutton = number of default button.
|
|
*
|
|
* This routine returns 1 if the first button is pressed,
|
|
* 2 for the second, etc.
|
|
*
|
|
* 0 indicates Esc was pressed.
|
|
* -1 for unexpected error
|
|
*
|
|
* If stubbing out this fn, return 1.
|
|
*/
|
|
|
|
int
|
|
gui_mch_dialog(
|
|
int type,
|
|
char_u *title,
|
|
char_u *message,
|
|
char_u *buttons,
|
|
int dfltbutton,
|
|
char_u *textfield,
|
|
int ex_cmd)
|
|
{
|
|
VimDialog *dialog = new VimDialog(type, (char*)title, (char*)message,
|
|
(char*)buttons, dfltbutton, (char*)textfield, ex_cmd);
|
|
return dialog->Go();
|
|
}
|
|
|
|
#endif // FEAT_GUI_DIALOG
|
|
|
|
|
|
/*
|
|
* Return the RGB value of a pixel as long.
|
|
*/
|
|
guicolor_T
|
|
gui_mch_get_rgb(guicolor_T pixel)
|
|
{
|
|
rgb_color rgb = GUI_TO_RGB(pixel);
|
|
|
|
return ((rgb.red & 0xff) << 16) + ((rgb.green & 0xff) << 8)
|
|
+ (rgb.blue & 0xff);
|
|
}
|
|
|
|
void
|
|
gui_mch_setmouse(int x, int y)
|
|
{
|
|
TRACE();
|
|
// TODO
|
|
}
|
|
|
|
#ifdef FEAT_MBYTE_IME
|
|
void
|
|
im_set_position(int row, int col)
|
|
{
|
|
if (gui.vimWindow->Lock())
|
|
{
|
|
gui.vimTextArea->DrawIMString();
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
gui_mch_show_toolbar(int showit)
|
|
{
|
|
VimToolbar *toolbar = gui.vimForm->ToolBar();
|
|
gui.toolbar_height = (toolbar && showit) ? toolbar->ToolbarHeight() : 0.;
|
|
}
|
|
|
|
void
|
|
gui_mch_set_toolbar_pos(int x, int y, int w, int h)
|
|
{
|
|
VimToolbar *toolbar = gui.vimForm->ToolBar();
|
|
if (toolbar != NULL) {
|
|
if (gui.vimWindow->Lock()) {
|
|
toolbar->MoveTo(x, y);
|
|
toolbar->ResizeTo(w - 1, h - 1);
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
|
|
|
|
/*
|
|
* Show or hide the tabline.
|
|
*/
|
|
void
|
|
gui_mch_show_tabline(int showit)
|
|
{
|
|
VimTabLine *tabLine = gui.vimForm->TabLine();
|
|
|
|
if (tabLine == NULL)
|
|
return;
|
|
|
|
if (!showit != !gui.vimForm->IsShowingTabLine()) {
|
|
gui.vimForm->SetShowingTabLine(showit != 0);
|
|
gui.tabline_height = gui.vimForm->TablineHeight();
|
|
}
|
|
}
|
|
|
|
void
|
|
gui_mch_set_tabline_pos(int x, int y, int w, int h)
|
|
{
|
|
VimTabLine *tabLine = gui.vimForm->TabLine();
|
|
if (tabLine != NULL) {
|
|
if (gui.vimWindow->Lock()) {
|
|
tabLine->MoveTo(x, y);
|
|
tabLine->ResizeTo(w - 1, h - 1);
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return TRUE when tabline is displayed.
|
|
*/
|
|
int
|
|
gui_mch_showing_tabline()
|
|
{
|
|
VimTabLine *tabLine = gui.vimForm->TabLine();
|
|
return tabLine != NULL && gui.vimForm->IsShowingTabLine();
|
|
}
|
|
|
|
/*
|
|
* Update the labels of the tabline.
|
|
*/
|
|
void
|
|
gui_mch_update_tabline()
|
|
{
|
|
tabpage_T *tp;
|
|
int nr = 0;
|
|
int curtabidx = 0;
|
|
|
|
VimTabLine *tabLine = gui.vimForm->TabLine();
|
|
|
|
if (tabLine == NULL)
|
|
return;
|
|
|
|
gui.vimWindow->Lock();
|
|
|
|
// Add a label for each tab page. They all contain the same text area.
|
|
for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) {
|
|
if (tp == curtab)
|
|
curtabidx = nr;
|
|
|
|
BTab* tab = tabLine->TabAt(nr);
|
|
|
|
if (tab == NULL) {
|
|
tab = new VimTabLine::VimTab();
|
|
tabLine->AddTab(NULL, tab);
|
|
}
|
|
|
|
get_tabline_label(tp, FALSE);
|
|
tab->SetLabel((const char*)NameBuff);
|
|
tabLine->Invalidate();
|
|
}
|
|
|
|
// Remove any old labels.
|
|
while (nr < tabLine->CountTabs())
|
|
tabLine->RemoveTab(nr);
|
|
|
|
if (tabLine->Selection() != curtabidx)
|
|
tabLine->Select(curtabidx);
|
|
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
|
|
/*
|
|
* Set the current tab to "nr". First tab is 1.
|
|
*/
|
|
void
|
|
gui_mch_set_curtab(int nr)
|
|
{
|
|
VimTabLine *tabLine = gui.vimForm->TabLine();
|
|
if (tabLine == NULL)
|
|
return;
|
|
|
|
gui.vimWindow->Lock();
|
|
|
|
if (tabLine->Selection() != nr -1)
|
|
tabLine->Select(nr -1);
|
|
|
|
gui.vimWindow->Unlock();
|
|
}
|
|
|
|
#endif // FEAT_GUI_TABLINE
|