xref: /vim-8.2.3635/src/gui_haiku.cc (revision e30d1025)
1 /* vi:set ts=8 sts=4 sw=4:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *    BeBox GUI support Copyright 1998 by Olaf Seibert.
5  *		    All Rights Reserved.
6  *
7  * Do ":help uganda"  in Vim to read copying and usage conditions.
8  * Do ":help credits" in Vim to see a list of people who contributed.
9  *
10  * Based on "GUI support for the Buzzword Enhanced Operating System."
11  *
12  * Ported to R4 by Richard Offer <[email protected]> Jul 99
13  *
14  * Haiku support by Siarzhuk Zharski <[email protected]> Apr-Mai 2009
15  *
16  */
17 
18 /*
19  * Structure of the Haiku GUI code:
20  *
21  * There are 3 threads.
22  * 1. The initial thread. In gui_mch_prepare() this gets to run the
23  *    BApplication message loop. But before it starts doing that,
24  *    it creates thread 2
25  * 2. The main() thread. This thread is created in gui_mch_prepare()
26  *    and its purpose in life is to call main(argc, argv) again.
27  *    This thread is doing the bulk of the work.
28  * 3. Sooner or later, a window is opened by the main() thread. This
29  *    causes a second message loop to be created: the window thread.
30  *
31  * == alternatively ===
32  *
33  * #if RUN_BAPPLICATION_IN_NEW_THREAD...
34  *
35  * 1. The initial thread. In gui_mch_prepare() this gets to spawn
36  *    thread 2. After doing that, it returns to main() to do the
37  *    bulk of the work, being the main() thread.
38  * 2. Runs the BApplication.
39  * 3. The window thread, just like in the first case.
40  *
41  * This second alternative is cleaner from Vim's viewpoint. However,
42  * the BeBook seems to assume everywhere that the BApplication *must*
43  * run in the initial thread. So perhaps doing otherwise is very wrong.
44  *
45  * However, from a B_SINGLE_LAUNCH viewpoint, the first is better.
46  * If Vim is marked "Single Launch" in its application resources,
47  * and a file is dropped on the Vim icon, and another Vim is already
48  * running, the file is passed on to the earlier Vim. This happens
49  * in BApplication::Run(). So we want Vim to terminate if
50  * BApplication::Run() terminates. (See the BeBook, on BApplication.
51  * However, it seems that the second copy of Vim isn't even started
52  * in this case... which is for the better since I wouldn't know how
53  * to detect this case.)
54  *
55  * Communication between these threads occurs mostly by translating
56  * BMessages that come in and posting an appropriate translation on
57  * the VDCMP (Vim Direct Communication Message Port). Therefore the
58  * actions required for keypresses and window resizes, etc, are mostly
59  * performed in the main() thread.
60  *
61  * A notable exception to this is the Draw() event. The redrawing of
62  * the window contents is performed asynchronously from the window
63  * thread. To make this work correctly, a locking protocol is used when
64  * any thread is accessing the essential variables that are used by
65  * the window thread.
66  *
67  * This locking protocol consists of locking Vim's window. This is both
68  * convenient and necessary.
69  */
70 
71 extern "C" {
72 
73 #include <assert.h>
74 #include <float.h>
75 #include <syslog.h>
76 
77 #include "vim.h"
78 #include "globals.h"
79 #include "proto.h"
80 #include "version.h"
81 
82 }   // extern "C"
83 
84 // ---------------- start of header part ----------------
85 
86 //#include <Alert.h>
87 #include <Application.h>
88 #include <Beep.h>
89 #include <Bitmap.h>
90 #include <Box.h>
91 #include <Button.h>
92 #include <Clipboard.h>
93 #include <Debug.h>
94 //#include <Directory.h>
95 //#include <Entry.h>
96 #include <File.h>
97 #include <FilePanel.h>
98 #include <FindDirectory.h>
99 //#include <Font.h>
100 #include <IconUtils.h>
101 #include <Input.h>
102 #include <ListView.h>
103 #include <MenuBar.h>
104 #include <MenuItem.h>
105 //#include <MessageQueue.h>
106 //#include <OS.h>
107 #include <Path.h>
108 #include <PictureButton.h>
109 #include <PopUpMenu.h>
110 //#include <Region.h>
111 #include <Resources.h>
112 //#include <Roster.h>
113 #include <Screen.h>
114 #include <ScrollBar.h>
115 #include <ScrollView.h>
116 #include <String.h>
117 #include <StringView.h>
118 //#include <SupportDefs.h>
119 #include <TabView.h>
120 #include <TextControl.h>
121 #include <TextView.h>
122 #include <TranslationUtils.h>
123 #include <TranslatorFormats.h>
124 #include <View.h>
125 #include <Window.h>
126 
127 class VimApp;
128 class VimFormView;
129 class VimTextAreaView;
130 class VimWindow;
131 class VimToolbar;
132 class VimTabLine;
133 
134 extern key_map *keyMap;
135 extern char *keyMapChars;
136 
137 extern int main(int argc, char **argv);
138 
139 #ifndef B_MAX_PORT_COUNT
140 #define B_MAX_PORT_COUNT    255
141 #endif
142 
143 // VimApp seems comparable to the X "vimShell"
144 class VimApp: public BApplication
145 {
146     typedef BApplication Inherited;
147     public:
148     VimApp(const char *appsig);
149     ~VimApp();
150 
151     // callbacks:
152 #if 0
153     virtual void DispatchMessage(BMessage *m, BHandler *h)
154     {
155 	m->PrintToStream();
156 	Inherited::DispatchMessage(m, h);
157     }
158 #endif
159     virtual void ReadyToRun();
160     virtual void ArgvReceived(int32 argc, char **argv);
161     virtual void RefsReceived(BMessage *m);
162     virtual bool QuitRequested();
163     virtual void MessageReceived(BMessage *m);
164 
165     static void SendRefs(BMessage *m, bool changedir);
166 
167     sem_id	fFilePanelSem;
168     BFilePanel*	fFilePanel;
169     BPath	fBrowsedPath;
170     private:
171 };
172 
173 class VimWindow: public BWindow
174 {
175     typedef BWindow Inherited;
176     public:
177     VimWindow();
178     ~VimWindow();
179 
180     //	  virtual void DispatchMessage(BMessage *m, BHandler *h);
181     virtual void WindowActivated(bool active);
182     virtual bool QuitRequested();
183 
184     VimFormView	    *formView;
185 
186     private:
187     void init();
188 
189 };
190 
191 class VimFormView: public BView
192 {
193     typedef BView Inherited;
194     public:
195     VimFormView(BRect frame);
196     ~VimFormView();
197 
198     // callbacks:
199     virtual void AllAttached();
200     virtual void FrameResized(float new_width, float new_height);
201 
202 #define MENUBAR_MARGIN	1
MenuHeight() const203     float MenuHeight() const
204     { return menuBar ? menuBar->Frame().Height() + MENUBAR_MARGIN: 0; }
MenuBar() const205     BMenuBar *MenuBar() const
206     { return menuBar; }
207 
208     private:
209     void init(BRect);
210 
211     BMenuBar	    *menuBar;
212     VimTextAreaView *textArea;
213 
214 #ifdef FEAT_TOOLBAR
215     public:
216     float ToolbarHeight() const;
ToolBar() const217     VimToolbar *ToolBar() const
218     { return toolBar; }
219     private:
220     VimToolbar	    *toolBar;
221 #endif
222 
223 #ifdef FEAT_GUI_TABLINE
224     public:
TabLine() const225     VimTabLine *TabLine() const	{ return tabLine; }
IsShowingTabLine() const226     bool IsShowingTabLine() const { return showingTabLine; }
SetShowingTabLine(bool showing)227     void SetShowingTabLine(bool showing) { showingTabLine = showing;	}
228     float TablineHeight() const;
229     private:
230     VimTabLine	*tabLine;
231     int	showingTabLine;
232 #endif
233 };
234 
235 class VimTextAreaView: public BView
236 {
237     typedef BView Inherited;
238     public:
239     VimTextAreaView(BRect frame);
240     ~VimTextAreaView();
241 
242     // callbacks:
243     virtual void Draw(BRect updateRect);
244     virtual void KeyDown(const char *bytes, int32 numBytes);
245     virtual void MouseDown(BPoint point);
246     virtual void MouseUp(BPoint point);
247     virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
248     virtual void MessageReceived(BMessage *m);
249 
250     // own functions:
251     int mchInitFont(char_u *name);
252     void mchDrawString(int row, int col, char_u *s, int len, int flags);
253     void mchClearBlock(int row1, int col1, int row2, int col2);
254     void mchClearAll();
255     void mchDeleteLines(int row, int num_lines);
256     void mchInsertLines(int row, int num_lines);
257 
258     static void guiSendMouseEvent(int button, int x, int y, int repeated_click, int_u modifiers);
259     static void guiMouseMoved(int x, int y);
260     static void guiBlankMouse(bool should_hide);
261     static int_u mouseModifiersToVim(int32 beModifiers);
262 
263     int32 mouseDragEventCount;
264 
265 #ifdef FEAT_MBYTE_IME
266     void DrawIMString(void);
267 #endif
268 
269     private:
270     void init(BRect);
271 
272     int_u	vimMouseButton;
273     int_u	vimMouseModifiers;
274 
275 #ifdef FEAT_MBYTE_IME
276     struct {
277 	BMessenger* messenger;
278 	BMessage* message;
279 	BPoint location;
280 	int row;
281 	int col;
282 	int count;
283     } IMData;
284 #endif
285 };
286 
287 class VimScrollBar: public BScrollBar
288 {
289     typedef BScrollBar Inherited;
290     public:
291     VimScrollBar(scrollbar_T *gsb, orientation posture);
292     ~VimScrollBar();
293 
294     virtual void ValueChanged(float newValue);
295     virtual void MouseUp(BPoint where);
296     void SetValue(float newval);
getGsb()297     scrollbar_T *getGsb()
298     { return gsb; }
299 
300     int32	scrollEventCount;
301 
302     private:
303     scrollbar_T *gsb;
304     float   ignoreValue;
305 };
306 
307 
308 #ifdef FEAT_TOOLBAR
309 
310 class VimToolbar : public BBox
311 {
312     static BBitmap *normalButtonsBitmap;
313     static BBitmap *grayedButtonsBitmap;
314 
315     BBitmap *LoadVimBitmap(const char* fileName);
316     bool GetPictureFromBitmap(BPicture *pictureTo, int32 index, BBitmap *bitmapFrom, bool pressed);
317     bool ModifyBitmapToGrayed(BBitmap *bitmap);
318 
319     BList fButtonsList;
320     void InvalidateLayout();
321 
322     public:
323     VimToolbar(BRect frame, const char * name);
324     ~VimToolbar();
325 
326     bool PrepareButtonBitmaps();
327 
328     bool AddButton(int32 index, vimmenu_T *menu);
329     bool RemoveButton(vimmenu_T *menu);
330     bool GrayButton(vimmenu_T *menu, int grey);
331 
332     float ToolbarHeight() const;
333     virtual void AttachedToWindow();
334 };
335 
336 BBitmap *VimToolbar::normalButtonsBitmap  = NULL;
337 BBitmap *VimToolbar::grayedButtonsBitmap  = NULL;
338 
339 const float ToolbarMargin = 3.;
340 const float ButtonMargin  = 3.;
341 
342 #endif //FEAT_TOOLBAR
343 
344 #ifdef FEAT_GUI_TABLINE
345 
346 class VimTabLine : public BTabView
347 {
348     public:
349 	class VimTab : public BTab {
350 	    public:
VimTab()351 		VimTab() : BTab(new BView(BRect(), "-Empty-", 0, 0)) {}
352 
353 	    virtual void Select(BView* owner);
354 	};
355 
VimTabLine(BRect r)356 	VimTabLine(BRect r) : BTabView(r, "vimTabLine", B_WIDTH_FROM_LABEL,
357 	       B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT, B_WILL_DRAW | B_FRAME_EVENTS) {}
358 
359     float TablineHeight() const;
360     virtual void MouseDown(BPoint point);
361 };
362 
363 #endif //FEAT_GUI_TABLINE
364 
365 
366 // For caching the fonts that are used;
367 // Vim seems rather sloppy in this regard.
368 class VimFont: public BFont
369 {
370     typedef BFont Inherited;
371     public:
372     VimFont();
373     VimFont(const VimFont *rhs);
374     VimFont(const BFont *rhs);
375     VimFont(const VimFont &rhs);
376     ~VimFont();
377 
378     VimFont *next;
379     int refcount;
380     char_u *name;
381 
382     private:
383     void init();
384 };
385 
386 #if defined(FEAT_GUI_DIALOG)
387 
388 class VimDialog : public BWindow
389 {
390     typedef BWindow Inherited;
391 
392     BButton* _CreateButton(int32 which, const char* label);
393 
394     public:
395 
396     class View : public BView {
397 	typedef BView Inherited;
398 
399 	public:
400 	View(BRect frame);
401 	~View();
402 
403 	virtual void Draw(BRect updateRect);
404 	void InitIcon(int32 type);
405 
406 	private:
407 	BBitmap*    fIconBitmap;
408     };
409 
410     VimDialog(int type, const char *title, const char *message,
411 	    const char *buttons, int dfltbutton, const char *textfield,
412 	    int ex_cmd);
413     ~VimDialog();
414 
415     int Go();
416 
417     virtual void MessageReceived(BMessage *msg);
418 
419     private:
420     sem_id	    fDialogSem;
421     int		    fDialogValue;
422     BList	    fButtonsList;
423     BTextView*	    fMessageView;
424     BTextControl*   fInputControl;
425     const char*	    fInputValue;
426 };
427 
428 class VimSelectFontDialog : public BWindow
429 {
430     typedef BWindow Inherited;
431 
432     void _CleanList(BListView* list);
433     void _UpdateFontStyles();
434     void _UpdateSizeInputPreview();
435     void _UpdateFontPreview();
436     bool _UpdateFromListItem(BListView* list, char* text, int textSize);
437     public:
438 
439     VimSelectFontDialog(font_family* family, font_style* style, float* size);
440     ~VimSelectFontDialog();
441 
442     bool Go();
443 
444     virtual void MessageReceived(BMessage *msg);
445 
446     private:
447     status_t	    fStatus;
448     sem_id	    fDialogSem;
449     bool	    fDialogValue;
450     font_family*    fFamily;
451     font_style*	    fStyle;
452     float*	    fSize;
453     font_family	    fFontFamily;
454     font_style	    fFontStyle;
455     float	    fFontSize;
456     BStringView*    fPreview;
457     BListView*	    fFamiliesList;
458     BListView*	    fStylesList;
459     BListView*	    fSizesList;
460     BTextControl*   fSizesInput;
461 };
462 
463 #endif // FEAT_GUI_DIALOG
464 
465 // ---------------- end of GUI classes ----------------
466 
467 struct MainArgs {
468     int	     argc;
469     char    **argv;
470 };
471 
472 // These messages are copied through the VDCMP.
473 // Therefore they ought not to have anything fancy.
474 // They must be of POD type (Plain Old Data)
475 // as the C++ standard calls them.
476 
477 #define	KEY_MSG_BUFSIZ	7
478 #if KEY_MSG_BUFSIZ < MAX_KEY_CODE_LEN
479 #error Increase KEY_MSG_BUFSIZ!
480 #endif
481 
482 struct VimKeyMsg {
483     char_u  length;
484     char_u  chars[KEY_MSG_BUFSIZ];  // contains Vim encoding
485     bool    csi_escape;
486 };
487 
488 struct VimResizeMsg {
489     int	    width;
490     int	    height;
491 };
492 
493 struct VimScrollBarMsg {
494     VimScrollBar *sb;
495     long    value;
496     int	    stillDragging;
497 };
498 
499 struct VimMenuMsg {
500     vimmenu_T	*guiMenu;
501 };
502 
503 struct VimMouseMsg {
504     int	    button;
505     int	    x;
506     int	    y;
507     int	    repeated_click;
508     int_u   modifiers;
509 };
510 
511 struct VimMouseMovedMsg {
512     int	    x;
513     int	    y;
514 };
515 
516 struct VimFocusMsg {
517     bool    active;
518 };
519 
520 struct VimRefsMsg {
521     BMessage   *message;
522     bool    changedir;
523 };
524 
525 struct VimTablineMsg {
526     int	    index;
527 };
528 
529 struct VimTablineMenuMsg {
530     int	    index;
531     int	    event;
532 };
533 
534 struct VimMsg {
535     enum VimMsgType {
536 	Key, Resize, ScrollBar, Menu, Mouse, MouseMoved, Focus, Refs, Tabline, TablineMenu
537     };
538 
539     union {
540 	struct VimKeyMsg    Key;
541 	struct VimResizeMsg NewSize;
542 	struct VimScrollBarMsg	Scroll;
543 	struct VimMenuMsg   Menu;
544 	struct VimMouseMsg  Mouse;
545 	struct VimMouseMovedMsg	MouseMoved;
546 	struct VimFocusMsg  Focus;
547 	struct VimRefsMsg   Refs;
548 	struct VimTablineMsg	Tabline;
549 	struct VimTablineMenuMsg    TablineMenu;
550     } u;
551 };
552 
553 #define RGB(r, g, b)	((char_u)(r) << 16 | (char_u)(g) << 8 | (char_u)(b) << 0)
554 #define GUI_TO_RGB(g)	{ (char_u)((g) >> 16), (char_u)((g) >> 8), (char_u)((g) >> 0), 255 }
555 
556 // ---------------- end of header part ----------------
557 
558 static struct specialkey
559 {
560     uint16  BeKeys;
561 #define KEY(a,b)    ((a)<<8|(b))
562 #define K(a)	    KEY(0,a)		// for ASCII codes
563 #define F(b)	    KEY(1,b)		// for scancodes
564     char_u  vim_code0;
565     char_u  vim_code1;
566 } special_keys[] =
567 {
568     {K(B_UP_ARROW),	'k', 'u'},
569     {K(B_DOWN_ARROW),	    'k', 'd'},
570     {K(B_LEFT_ARROW),	    'k', 'l'},
571     {K(B_RIGHT_ARROW),	    'k', 'r'},
572     {K(B_BACKSPACE),	    'k', 'b'},
573     {K(B_INSERT),	'k', 'I'},
574     {K(B_DELETE),	'k', 'D'},
575     {K(B_HOME),		'k', 'h'},
576     {K(B_END),		'@', '7'},
577     {K(B_PAGE_UP),	'k', 'P'},	// XK_Prior
578     {K(B_PAGE_DOWN),	    'k', 'N'},	    // XK_Next,
579 
580 #define FIRST_FUNCTION_KEY  11
581     {F(B_F1_KEY),	'k', '1'},
582     {F(B_F2_KEY),	'k', '2'},
583     {F(B_F3_KEY),	'k', '3'},
584     {F(B_F4_KEY),	'k', '4'},
585     {F(B_F5_KEY),	'k', '5'},
586     {F(B_F6_KEY),	'k', '6'},
587     {F(B_F7_KEY),	'k', '7'},
588     {F(B_F8_KEY),	'k', '8'},
589     {F(B_F9_KEY),	'k', '9'},
590     {F(B_F10_KEY),	'k', ';'},
591 
592     {F(B_F11_KEY),	'F', '1'},
593     {F(B_F12_KEY),	'F', '2'},
594     //	{XK_F13,	    'F', '3'},	// would be print screen
595     // sysreq
596     {F(0x0F),		'F', '4'},	// scroll lock
597     {F(0x10),		'F', '5'},	// pause/break
598     //	{XK_F16,	'F', '6'},
599     //	{XK_F17,	'F', '7'},
600     //	{XK_F18,	'F', '8'},
601     //	{XK_F19,	'F', '9'},
602     //	 {XK_F20,	'F', 'A'},
603     //	{XK_F21,	'F', 'B'},
604     //	{XK_F22,	'F', 'C'},
605     //	{XK_F23,	'F', 'D'},
606     //	{XK_F24,	'F', 'E'},
607     //	{XK_F25,	'F', 'F'},
608     //	{XK_F26,	'F', 'G'},
609     //	{XK_F27,	'F', 'H'},
610     //	{XK_F28,	'F', 'I'},
611     //	{XK_F29,	'F', 'J'},
612     //	{XK_F30,	'F', 'K'},
613     //	{XK_F31,	'F', 'L'},
614     //	{XK_F32,	'F', 'M'},
615     //	{XK_F33,	'F', 'N'},
616     //	{XK_F34,	'F', 'O'},
617     //	{XK_F35,	'F', 'P'},	// keysymdef.h defines up to F35
618 
619     //	{XK_Help,	'%', '1'},	// XK_Help
620     {F(B_PRINT_KEY),	    '%', '9'},
621 
622 #if 0
623     // Keypad keys:
624     {F(0x48),	    'k', 'l'},	    // XK_KP_Left
625     {F(0x4A),	    'k', 'r'},	    // XK_KP_Right
626     {F(0x38),	    'k', 'u'},	    // XK_KP_Up
627     {F(0x59),	    'k', 'd'},	    // XK_KP_Down
628     {F(0x64),	    'k', 'I'},	    // XK_KP_Insert
629     {F(0x65),	    'k', 'D'},	    // XK_KP_Delete
630     {F(0x37),	    'k', 'h'},	    // XK_KP_Home
631     {F(0x58),	    '@', '7'},	    // XK_KP_End
632     {F(0x39),	    'k', 'P'},	    // XK_KP_Prior
633     {F(0x60),	    'k', 'N'},	    // XK_KP_Next
634     {F(0x49),	    '&', '8'},	    // XK_Undo, keypad 5
635 #endif
636 
637     // End of list marker:
638     {0,		    0, 0}
639 };
640 
641 #define NUM_SPECIAL_KEYS    ARRAY_LENGTH(special_keys)
642 
643 // ---------------- VimApp ----------------
644 
645     static void
docd(BPath & path)646 docd(BPath &path)
647 {
648     mch_chdir((char *)path.Path());
649     // Do this to get the side effects of a :cd command
650     do_cmdline_cmd((char_u *)"cd .");
651 }
652 
653 	static void
drop_callback(void * cookie)654 drop_callback(void *cookie)
655 {
656     // TODO here we could handle going to a specific position in the dropped
657     // file (see src/gui_mac.c, deleted in 8.2.1422)
658     // Update the screen display
659     update_screen(NOT_VALID);
660 }
661 
662     // Really handle dropped files and folders.
663 	static void
RefsReceived(BMessage * m,bool changedir)664 RefsReceived(BMessage *m, bool changedir)
665 {
666     uint32 type;
667     int32 count;
668 
669     m->PrintToStream();
670     switch (m->what) {
671 	case B_REFS_RECEIVED:
672 	case B_SIMPLE_DATA:
673 	    m->GetInfo("refs", &type, &count);
674 	    if (type != B_REF_TYPE)
675 		goto bad;
676 	    break;
677 	case B_ARGV_RECEIVED:
678 	    m->GetInfo("argv", &type, &count);
679 	    if (type != B_STRING_TYPE)
680 		goto bad;
681 	    if (changedir) {
682 		char *dirname;
683 		if (m->FindString("cwd", (const char **) &dirname) == B_OK) {
684 		    chdir(dirname);
685 		    do_cmdline_cmd((char_u *)"cd .");
686 		}
687 	    }
688 	    break;
689 	default:
690 bad:
691 	    /*fprintf(stderr, "bad!\n"); */
692 	    delete m;
693 	    return;
694     }
695 
696 #ifdef FEAT_VISUAL
697     reset_VIsual();
698 #endif
699 
700     char_u  **fnames;
701     fnames = (char_u **) alloc(count * sizeof(char_u *));
702     int fname_index = 0;
703 
704     switch (m->what) {
705 	case B_REFS_RECEIVED:
706 	case B_SIMPLE_DATA:
707 	    // fprintf(stderr, "case B_REFS_RECEIVED\n");
708 	    for (int i = 0; i < count; ++i)
709 	    {
710 		entry_ref ref;
711 		if (m->FindRef("refs", i, &ref) == B_OK) {
712 		    BEntry entry(&ref, false);
713 		    BPath path;
714 		    entry.GetPath(&path);
715 
716 		    // Change to parent directory?
717 		    if (changedir) {
718 			BPath parentpath;
719 			path.GetParent(&parentpath);
720 			docd(parentpath);
721 		    }
722 
723 		    // Is it a directory? If so, cd into it.
724 		    BDirectory bdir(&ref);
725 		    if (bdir.InitCheck() == B_OK) {
726 			// don't cd if we already did it
727 			if (!changedir)
728 			    docd(path);
729 		    } else {
730 			mch_dirname(IObuff, IOSIZE);
731 			char_u *fname = shorten_fname((char_u *)path.Path(), IObuff);
732 			if (fname == NULL)
733 			    fname = (char_u *)path.Path();
734 			fnames[fname_index++] = vim_strsave(fname);
735 			// fprintf(stderr, "%s\n", fname);
736 		    }
737 
738 		    // Only do it for the first file/dir
739 		    changedir = false;
740 		}
741 	    }
742 	    break;
743 	case B_ARGV_RECEIVED:
744 	    // fprintf(stderr, "case B_ARGV_RECEIVED\n");
745 	    for (int i = 1; i < count; ++i)
746 	    {
747 		char *fname;
748 
749 		if (m->FindString("argv", i, (const char **) &fname) == B_OK) {
750 		    fnames[fname_index++] = vim_strsave((char_u *)fname);
751 		}
752 	    }
753 	    break;
754 	default:
755 	    // fprintf(stderr, "case default\n");
756 	    break;
757     }
758 
759     delete m;
760 
761     // Handle the drop, :edit to get to the file
762     if (fname_index > 0) {
763 	handle_drop(fname_index, fnames, FALSE, drop_callback, NULL);
764 
765 	setcursor();
766 	out_flush();
767     } else {
768 	vim_free(fnames);
769     }
770 }
771 
VimApp(const char * appsig)772 VimApp::VimApp(const char *appsig):
773     BApplication(appsig),
774     fFilePanelSem(-1),
775     fFilePanel(NULL)
776 {
777 }
778 
~VimApp()779 VimApp::~VimApp()
780 {
781 }
782 
783     void
ReadyToRun()784 VimApp::ReadyToRun()
785 {
786     /*
787      * Apparently signals are inherited by the created thread -
788      * disable the most annoying ones.
789      */
790     signal(SIGINT, SIG_IGN);
791     signal(SIGQUIT, SIG_IGN);
792 }
793 
794     void
ArgvReceived(int32 arg_argc,char ** arg_argv)795 VimApp::ArgvReceived(int32 arg_argc, char **arg_argv)
796 {
797     if (!IsLaunching()) {
798 	/*
799 	 * This can happen if we are set to Single or Exclusive
800 	 * Launch. Be nice and open the file(s).
801 	 */
802 	if (gui.vimWindow)
803 	    gui.vimWindow->Minimize(false);
804 	BMessage *m = CurrentMessage();
805 	DetachCurrentMessage();
806 	SendRefs(m, true);
807     }
808 }
809 
810     void
RefsReceived(BMessage * m)811 VimApp::RefsReceived(BMessage *m)
812 {
813     // Horrible hack!!! XXX XXX XXX
814     // The real problem is that b_start_ffc is set too late for
815     // the initial empty buffer. As a result the window will be
816     // split instead of abandoned.
817     int limit = 15;
818     while (--limit >= 0 && (curbuf == NULL || curbuf->b_start_ffc == 0))
819 	snooze(100000);    //  0.1 s
820     if (gui.vimWindow)
821 	gui.vimWindow->Minimize(false);
822     DetachCurrentMessage();
823     SendRefs(m, true);
824 }
825 
826 /*
827  * Pass a BMessage on to the main() thread.
828  * Caller must have detached the message.
829  */
830     void
SendRefs(BMessage * m,bool changedir)831 VimApp::SendRefs(BMessage *m, bool changedir)
832 {
833     VimRefsMsg rm;
834     rm.message = m;
835     rm.changedir = changedir;
836 
837     write_port(gui.vdcmp, VimMsg::Refs, &rm, sizeof(rm));
838     //	calls ::RefsReceived
839 }
840 
841     void
MessageReceived(BMessage * m)842 VimApp::MessageReceived(BMessage *m)
843 {
844     switch (m->what) {
845 	case 'save':
846 	    {
847 		entry_ref refDirectory;
848 		m->FindRef("directory", &refDirectory);
849 		fBrowsedPath.SetTo(&refDirectory);
850 		BString strName;
851 		m->FindString("name", &strName);
852 		fBrowsedPath.Append(strName.String());
853 	    }
854 	    break;
855 	case 'open':
856 	    {
857 		entry_ref ref;
858 		m->FindRef("refs", &ref);
859 		fBrowsedPath.SetTo(&ref);
860 	    }
861 	    break;
862 	case B_CANCEL:
863 	    {
864 		BFilePanel *panel;
865 		m->FindPointer("source", (void**)&panel);
866 		if (fFilePanelSem != -1 && panel == fFilePanel)
867 		{
868 		    delete_sem(fFilePanelSem);
869 		    fFilePanelSem = -1;
870 		}
871 
872 	    }
873 	    break;
874 	default:
875 	    Inherited::MessageReceived(m);
876 	    break;
877     }
878 }
879 
880     bool
QuitRequested()881 VimApp::QuitRequested()
882 {
883     (void)Inherited::QuitRequested();
884     return false;
885 }
886 
887 // ---------------- VimWindow ----------------
888 
VimWindow()889 VimWindow::VimWindow():
890     BWindow(BRect(40, 40, 150, 150),
891 	    "Vim",
892 	    B_TITLED_WINDOW,
893 	    0,
894 	    B_CURRENT_WORKSPACE)
895 
896 {
897     init();
898 }
899 
~VimWindow()900 VimWindow::~VimWindow()
901 {
902     if (formView) {
903 	RemoveChild(formView);
904 	delete formView;
905     }
906     gui.vimWindow = NULL;
907 }
908 
909     void
init()910 VimWindow::init()
911 {
912     // Attach the VimFormView
913     formView = new VimFormView(Bounds());
914     if (formView != NULL) {
915 	AddChild(formView);
916     }
917 }
918 
919 #if 0  //  disabled in zeta patch
920     void
921 VimWindow::DispatchMessage(BMessage *m, BHandler *h)
922 {
923     /*
924      * Route B_MOUSE_UP messages to MouseUp(), in
925      * a manner that should be compatible with the
926      * intended future system behaviour.
927      */
928     switch (m->what) {
929 	case B_MOUSE_UP:
930 	    //	if (!h) h = PreferredHandler();
931 	    //	gcc isn't happy without this extra set of braces, complains about
932 	    //	jump to case label crosses init of 'class BView * v'
933 	    //	[email protected] jul 99
934 	    {
935 		BView *v = dynamic_cast<BView *>(h);
936 		if (v) {
937 		    // m->PrintToStream();
938 		    BPoint where;
939 		    m->FindPoint("where", &where);
940 		    v->MouseUp(where);
941 		} else {
942 		    Inherited::DispatchMessage(m, h);
943 		}
944 	    }
945 	    break;
946 	default:
947 	    Inherited::DispatchMessage(m, h);
948     }
949 }
950 #endif
951 
952     void
WindowActivated(bool active)953 VimWindow::WindowActivated(bool active)
954 {
955     Inherited::WindowActivated(active);
956     // the textArea gets the keyboard action
957     if (active && gui.vimTextArea)
958 	gui.vimTextArea->MakeFocus(true);
959 
960     struct VimFocusMsg fm;
961     fm.active = active;
962 
963     write_port(gui.vdcmp, VimMsg::Focus, &fm, sizeof(fm));
964 }
965 
966     bool
QuitRequested()967 VimWindow::QuitRequested()
968 {
969     struct VimKeyMsg km;
970     km.length = 5;
971     memcpy((char *)km.chars, "\033:qa\r", km.length);
972     km.csi_escape = false;
973     write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
974     return false;
975 }
976 
977 // ---------------- VimFormView ----------------
978 
VimFormView(BRect frame)979 VimFormView::VimFormView(BRect frame):
980     BView(frame, "VimFormView", B_FOLLOW_ALL_SIDES,
981 	    B_WILL_DRAW | B_FRAME_EVENTS),
982     menuBar(NULL),
983 #ifdef FEAT_TOOLBAR
984     toolBar(NULL),
985 #endif
986 #ifdef FEAT_GUI_TABLINE
987 //  showingTabLine(false),
988     tabLine(NULL),
989 #endif
990     textArea(NULL)
991 {
992     init(frame);
993 }
994 
~VimFormView()995 VimFormView::~VimFormView()
996 {
997     if (menuBar) {
998 	RemoveChild(menuBar);
999 #ifdef never
1000 	//  deleting the menuBar leads to SEGV on exit
1001 	//  [email protected] Jul 99
1002 	delete menuBar;
1003 #endif
1004     }
1005 
1006 #ifdef FEAT_TOOLBAR
1007     delete toolBar;
1008 #endif
1009 
1010 #ifdef FEAT_GUI_TABLINE
1011     delete tabLine;
1012 #endif
1013 
1014     if (textArea) {
1015 	RemoveChild(textArea);
1016 	delete textArea;
1017     }
1018     gui.vimForm = NULL;
1019 }
1020 
1021     void
init(BRect frame)1022 VimFormView::init(BRect frame)
1023 {
1024     menuBar = new BMenuBar(BRect(0,0,-MENUBAR_MARGIN,-MENUBAR_MARGIN),
1025 	    "VimMenuBar");
1026 
1027     AddChild(menuBar);
1028 
1029 #ifdef FEAT_TOOLBAR
1030     toolBar = new VimToolbar(BRect(0,0,0,0), "VimToolBar");
1031     toolBar->PrepareButtonBitmaps();
1032     AddChild(toolBar);
1033 #endif
1034 
1035 #ifdef FEAT_GUI_TABLINE
1036     tabLine = new VimTabLine(BRect(0,0,0,0));
1037 //  tabLine->PrepareButtonBitmaps();
1038     AddChild(tabLine);
1039 #endif
1040 
1041     BRect remaining = frame;
1042     textArea = new VimTextAreaView(remaining);
1043     AddChild(textArea);
1044     // The textArea will be resized later when menus are added
1045 
1046     gui.vimForm = this;
1047 }
1048 
1049 #ifdef FEAT_TOOLBAR
1050     float
ToolbarHeight() const1051 VimFormView::ToolbarHeight() const
1052 {
1053     return toolBar ? toolBar->ToolbarHeight() : 0.;
1054 }
1055 #endif
1056 
1057 #ifdef FEAT_GUI_TABLINE
1058     float
TablineHeight() const1059 VimFormView::TablineHeight() const
1060 {
1061     return (tabLine && IsShowingTabLine()) ? tabLine->TablineHeight() : 0.;
1062 }
1063 #endif
1064 
1065     void
AllAttached()1066 VimFormView::AllAttached()
1067 {
1068     /*
1069      * Apparently signals are inherited by the created thread -
1070      * disable the most annoying ones.
1071      */
1072     signal(SIGINT, SIG_IGN);
1073     signal(SIGQUIT, SIG_IGN);
1074 
1075     if (menuBar && textArea) {
1076 	/*
1077 	 * Resize the textArea to fill the space left over by the menu.
1078 	 * This is somewhat futile since it will be done again once
1079 	 * menus are added to the menu bar.
1080 	 */
1081 	BRect remaining = Bounds();
1082 
1083 #ifdef FEAT_MENU
1084 	remaining.top += MenuHeight();
1085 	menuBar->ResizeTo(remaining.right, remaining.top);
1086 	gui.menu_height = (int) MenuHeight();
1087 #endif
1088 
1089 #ifdef FEAT_TOOLBAR
1090 	toolBar->MoveTo(remaining.left, remaining.top);
1091 	toolBar->ResizeTo(remaining.right, ToolbarHeight());
1092 	remaining.top += ToolbarHeight();
1093 	gui.toolbar_height = ToolbarHeight();
1094 #endif
1095 
1096 #ifdef FEAT_GUI_TABLINE
1097 	tabLine->MoveTo(remaining.left, remaining.top);
1098 	tabLine->ResizeTo(remaining.right + 1, TablineHeight());
1099 	remaining.top += TablineHeight();
1100 	gui.tabline_height = TablineHeight();
1101 #endif
1102 
1103 	textArea->ResizeTo(remaining.Width(), remaining.Height());
1104 	textArea->MoveTo(remaining.left, remaining.top);
1105     }
1106 
1107 
1108     Inherited::AllAttached();
1109 }
1110 
1111     void
FrameResized(float new_width,float new_height)1112 VimFormView::FrameResized(float new_width, float new_height)
1113 {
1114     struct VimResizeMsg sm;
1115     int adjust_h, adjust_w;
1116 
1117     new_width += 1;	//  adjust from width to number of pixels occupied
1118     new_height += 1;
1119 
1120     sm.width = (int) new_width;
1121     sm.height = (int) new_height;
1122     adjust_w = ((int)new_width - gui_get_base_width()) % gui.char_width;
1123     adjust_h = ((int)new_height - gui_get_base_height()) % gui.char_height;
1124 
1125     if (adjust_w > 0 || adjust_h > 0) {
1126 	sm.width  -= adjust_w;
1127 	sm.height -= adjust_h;
1128     }
1129 
1130     write_port(gui.vdcmp, VimMsg::Resize, &sm, sizeof(sm));
1131     //	calls gui_resize_shell(new_width, new_height);
1132 
1133     return;
1134 
1135     /*
1136      * The area below the vertical scrollbar is erased to the colour
1137      * set with SetViewColor() automatically, because we had set
1138      * B_WILL_DRAW. Resizing the window tight around the vertical
1139      * scroll bar also helps to avoid debris.
1140      */
1141 }
1142 
1143 // ---------------- VimTextAreaView ----------------
1144 
VimTextAreaView(BRect frame)1145 VimTextAreaView::VimTextAreaView(BRect frame):
1146     BView(frame, "VimTextAreaView", B_FOLLOW_ALL_SIDES,
1147 #ifdef FEAT_MBYTE_IME
1148 	B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_INPUT_METHOD_AWARE
1149 #else
1150 	B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
1151 #endif
1152 	),
1153     mouseDragEventCount(0)
1154 {
1155 #ifdef FEAT_MBYTE_IME
1156     IMData.messenger = NULL;
1157     IMData.message = NULL;
1158 #endif
1159     init(frame);
1160 }
1161 
~VimTextAreaView()1162 VimTextAreaView::~VimTextAreaView()
1163 {
1164     gui.vimTextArea = NULL;
1165 }
1166 
1167     void
init(BRect frame)1168 VimTextAreaView::init(BRect frame)
1169 {
1170     // set up global var for fast access
1171     gui.vimTextArea = this;
1172 
1173     /*
1174      * Tell the app server not to erase the view: we will
1175      * fill it in completely by ourselves.
1176      * (Does this really work? Even if not, it won't harm either.)
1177      */
1178     SetViewColor(B_TRANSPARENT_32_BIT);
1179 #define PEN_WIDTH   1
1180     SetPenSize(PEN_WIDTH);
1181 #define W_WIDTH(curwin)   0
1182 }
1183 
1184     void
Draw(BRect updateRect)1185 VimTextAreaView::Draw(BRect updateRect)
1186 {
1187     /*
1188      * XXX Other ports call here:
1189      * out_flush();	 * make sure all output has been processed *
1190      * but we can't do that, since it involves too much information
1191      * that is owned by other threads...
1192      */
1193 
1194     /*
1195      *	No need to use gui.vimWindow->Lock(): we are locked already.
1196      *	However, it would not hurt.
1197      */
1198     rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
1199     SetLowColor(rgb);
1200     FillRect(updateRect, B_SOLID_LOW);
1201     gui_redraw((int) updateRect.left, (int) updateRect.top,
1202 	    (int) (updateRect.Width() + PEN_WIDTH), (int) (updateRect.Height() + PEN_WIDTH));
1203 
1204     // Clear the border areas if needed
1205     SetLowColor(rgb);
1206 
1207     if (updateRect.left < FILL_X(0))	//  left border
1208 	FillRect(BRect(updateRect.left, updateRect.top,
1209 		    FILL_X(0)-PEN_WIDTH, updateRect.bottom), B_SOLID_LOW);
1210     if (updateRect.top < FILL_Y(0)) //	top border
1211 	FillRect(BRect(updateRect.left, updateRect.top,
1212 		    updateRect.right, FILL_Y(0)-PEN_WIDTH), B_SOLID_LOW);
1213     if (updateRect.right >= FILL_X(Columns)) //  right border
1214 	FillRect(BRect(FILL_X((int)Columns), updateRect.top,
1215 		    updateRect.right, updateRect.bottom), B_SOLID_LOW);
1216     if (updateRect.bottom >= FILL_Y(Rows))   //  bottom border
1217 	FillRect(BRect(updateRect.left, FILL_Y((int)Rows),
1218 		    updateRect.right, updateRect.bottom), B_SOLID_LOW);
1219 
1220 #ifdef FEAT_MBYTE_IME
1221     DrawIMString();
1222 #endif
1223 }
1224 
1225     void
KeyDown(const char * bytes,int32 numBytes)1226 VimTextAreaView::KeyDown(const char *bytes, int32 numBytes)
1227 {
1228     struct VimKeyMsg km;
1229     char_u *dest = km.chars;
1230 
1231     bool canHaveVimModifiers = false;
1232 
1233     BMessage *msg = Window()->CurrentMessage();
1234     assert(msg);
1235     // msg->PrintToStream();
1236 
1237     /*
1238      * Convert special keys to Vim codes.
1239      * I think it is better to do it in the window thread
1240      * so we use at least a little bit of the potential
1241      * of our 2 CPUs. Besides, due to the fantastic mapping
1242      * of special keys to UTF-8, we have quite some work to
1243      * do...
1244      * TODO: I'm not quite happy with detection of special
1245      * keys. Perhaps I should use scan codes after all...
1246      */
1247     if (numBytes > 1) {
1248 	// This cannot be a special key
1249 	if (numBytes > KEY_MSG_BUFSIZ)
1250 	    numBytes = KEY_MSG_BUFSIZ;	    //	should never happen... ???
1251 	km.length = numBytes;
1252 	memcpy((char *)dest, bytes, numBytes);
1253 	km.csi_escape = true;
1254     } else {
1255 	int32 scancode = 0;
1256 	msg->FindInt32("key", &scancode);
1257 
1258 	int32 beModifiers = 0;
1259 	msg->FindInt32("modifiers", &beModifiers);
1260 
1261 	char_u string[3];
1262 	int len = 0;
1263 	km.length = 0;
1264 
1265 	/*
1266 	 * For normal, printable ASCII characters, don't look them up
1267 	 * to check if they might be a special key. They aren't.
1268 	 */
1269 	assert(B_BACKSPACE <= 0x20);
1270 	assert(B_DELETE == 0x7F);
1271 	if (((char_u)bytes[0] <= 0x20 || (char_u)bytes[0] == 0x7F) &&
1272 		numBytes == 1) {
1273 	    /*
1274 	     * Due to the great nature of Be's mapping of special keys,
1275 	     * viz. into the range of the control characters,
1276 	     * we can only be sure it is *really* a special key if
1277 	     * if it is special without using ctrl. So, only if ctrl is
1278 	     * used, we need to check it unmodified.
1279 	     */
1280 	    if (beModifiers & B_CONTROL_KEY) {
1281 		int index = keyMap->normal_map[scancode];
1282 		int newNumBytes = keyMapChars[index];
1283 		char_u *newBytes = (char_u *)&keyMapChars[index + 1];
1284 
1285 		/*
1286 		 * Check if still special without the control key.
1287 		 * This is needed for BACKSPACE: that key does produce
1288 		 * different values with modifiers (DEL).
1289 		 * Otherwise we could simply have checked for equality.
1290 		 */
1291 		if (newNumBytes != 1 || (*newBytes > 0x20 &&
1292 			    *newBytes != 0x7F )) {
1293 		    goto notspecial;
1294 		}
1295 		bytes = (char *)newBytes;
1296 	    }
1297 	    canHaveVimModifiers = true;
1298 
1299 	    uint16 beoskey;
1300 	    int first, last;
1301 
1302 	    /*
1303 	     * If numBytes == 0 that probably always indicates a special key.
1304 	     * (does not happen yet)
1305 	     */
1306 	    if (numBytes == 0 || bytes[0] == B_FUNCTION_KEY) {
1307 		beoskey = F(scancode);
1308 		first = FIRST_FUNCTION_KEY;
1309 		last = NUM_SPECIAL_KEYS;
1310 	    } else if (*bytes == '\n' && scancode == 0x47) {
1311 		// remap the (non-keypad) ENTER key from \n to \r.
1312 		string[0] = '\r';
1313 		len = 1;
1314 		first = last = 0;
1315 	    } else {
1316 		beoskey = K(bytes[0]);
1317 		first = 0;
1318 		last = FIRST_FUNCTION_KEY;
1319 	    }
1320 
1321 	    for (int i = first; i < last; i++) {
1322 		if (special_keys[i].BeKeys == beoskey) {
1323 		    string[0] = CSI;
1324 		    string[1] = special_keys[i].vim_code0;
1325 		    string[2] = special_keys[i].vim_code1;
1326 		    len = 3;
1327 		}
1328 	    }
1329 	}
1330 notspecial:
1331 	if (len == 0) {
1332 	    string[0] = bytes[0];
1333 	    len = 1;
1334 	}
1335 
1336 	// Special keys (and a few others) may have modifiers
1337 #if 0
1338 	if (len == 3 ||
1339 		bytes[0] == B_SPACE || bytes[0] == B_TAB ||
1340 		bytes[0] == B_RETURN || bytes[0] == '\r' ||
1341 		bytes[0] == B_ESCAPE)
1342 #else
1343 	    if (canHaveVimModifiers)
1344 #endif
1345 	    {
1346 		int modifiers;
1347 		modifiers = 0;
1348 		if (beModifiers & B_SHIFT_KEY)
1349 		    modifiers |= MOD_MASK_SHIFT;
1350 		if (beModifiers & B_CONTROL_KEY)
1351 		    modifiers |= MOD_MASK_CTRL;
1352 		if (beModifiers & B_OPTION_KEY)
1353 		    modifiers |= MOD_MASK_ALT;
1354 
1355 		/*
1356 		 * For some keys a shift modifier is translated into another key
1357 		 * code.  Do we need to handle the case where len != 1 and
1358 		 * string[0] != CSI? (Not for BeOS, since len == 3 implies
1359 		 * string[0] == CSI...)
1360 		 */
1361 		int key;
1362 		if (string[0] == CSI && len == 3)
1363 		    key = TO_SPECIAL(string[1], string[2]);
1364 		else
1365 		    key = string[0];
1366 		key = simplify_key(key, &modifiers);
1367 		if (IS_SPECIAL(key))
1368 		{
1369 		    string[0] = CSI;
1370 		    string[1] = K_SECOND(key);
1371 		    string[2] = K_THIRD(key);
1372 		    len = 3;
1373 		}
1374 		else
1375 		{
1376 		    string[0] = key;
1377 		    len = 1;
1378 		}
1379 
1380 		if (modifiers)
1381 		{
1382 		    *dest++ = CSI;
1383 		    *dest++ = KS_MODIFIER;
1384 		    *dest++ = modifiers;
1385 		    km.length = 3;
1386 		}
1387 	    }
1388 	memcpy((char *)dest, string, len);
1389 	km.length += len;
1390 	km.csi_escape = false;
1391     }
1392 
1393     write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
1394 
1395     /*
1396      * blank out the pointer if necessary
1397      */
1398     if (p_mh && !gui.pointer_hidden)
1399     {
1400 	guiBlankMouse(true);
1401 	gui.pointer_hidden = TRUE;
1402     }
1403 }
1404 void
guiSendMouseEvent(int button,int x,int y,int repeated_click,int_u modifiers)1405 VimTextAreaView::guiSendMouseEvent(
1406 	int	button,
1407 	int	x,
1408 	int	y,
1409 	int	repeated_click,
1410 	int_u	modifiers)
1411 {
1412     VimMouseMsg mm;
1413 
1414     mm.button = button;
1415     mm.x = x;
1416     mm.y = y;
1417     mm.repeated_click = repeated_click;
1418     mm.modifiers = modifiers;
1419 
1420     write_port(gui.vdcmp, VimMsg::Mouse, &mm, sizeof(mm));
1421     //	calls gui_send_mouse_event()
1422 
1423     /*
1424      * if our pointer is currently hidden, then we should show it.
1425      */
1426     if (gui.pointer_hidden)
1427     {
1428 	guiBlankMouse(false);
1429 	gui.pointer_hidden = FALSE;
1430     }
1431 }
1432 
1433 void
guiMouseMoved(int x,int y)1434 VimTextAreaView::guiMouseMoved(
1435 	int	x,
1436 	int	y)
1437 {
1438     VimMouseMovedMsg mm;
1439 
1440     mm.x = x;
1441     mm.y = y;
1442 
1443     write_port(gui.vdcmp, VimMsg::MouseMoved, &mm, sizeof(mm));
1444 
1445     if (gui.pointer_hidden)
1446     {
1447 	guiBlankMouse(false);
1448 	gui.pointer_hidden = FALSE;
1449     }
1450 }
1451 
1452     void
guiBlankMouse(bool should_hide)1453 VimTextAreaView::guiBlankMouse(bool should_hide)
1454 {
1455     if (should_hide) {
1456 	// gui.vimApp->HideCursor();
1457 	gui.vimApp->ObscureCursor();
1458 	/*
1459 	 * ObscureCursor() would even be easier, but then
1460 	 * Vim's idea of mouse visibility does not necessarily
1461 	 * correspond to reality.
1462 	 */
1463     } else {
1464 	// gui.vimApp->ShowCursor();
1465     }
1466 }
1467 
1468     int_u
mouseModifiersToVim(int32 beModifiers)1469 VimTextAreaView::mouseModifiersToVim(int32 beModifiers)
1470 {
1471     int_u vim_modifiers = 0x0;
1472 
1473     if (beModifiers & B_SHIFT_KEY)
1474 	vim_modifiers |= MOUSE_SHIFT;
1475     if (beModifiers & B_CONTROL_KEY)
1476 	vim_modifiers |= MOUSE_CTRL;
1477     if (beModifiers & B_OPTION_KEY)	// Alt or Meta key
1478 	vim_modifiers |= MOUSE_ALT;
1479 
1480     return vim_modifiers;
1481 }
1482 
1483     void
MouseDown(BPoint point)1484 VimTextAreaView::MouseDown(BPoint point)
1485 {
1486     BMessage *m = Window()->CurrentMessage();
1487     assert(m);
1488 
1489     int32 buttons = 0;
1490     m->FindInt32("buttons", &buttons);
1491 
1492     int vimButton;
1493 
1494     if (buttons & B_PRIMARY_MOUSE_BUTTON)
1495 	vimButton = MOUSE_LEFT;
1496     else if (buttons & B_SECONDARY_MOUSE_BUTTON)
1497 	vimButton = MOUSE_RIGHT;
1498     else if (buttons & B_TERTIARY_MOUSE_BUTTON)
1499 	vimButton = MOUSE_MIDDLE;
1500     else
1501 	return;		// Unknown button
1502 
1503     vimMouseButton = 1;	    // don't care which one
1504 
1505     // Handle multiple clicks
1506     int32 clicks = 0;
1507     m->FindInt32("clicks", &clicks);
1508 
1509     int32 modifiers = 0;
1510     m->FindInt32("modifiers", &modifiers);
1511 
1512     vimMouseModifiers = mouseModifiersToVim(modifiers);
1513 
1514     guiSendMouseEvent(vimButton, point.x, point.y,
1515 	    clicks > 1 /* = repeated_click*/, vimMouseModifiers);
1516 }
1517 
1518     void
MouseUp(BPoint point)1519 VimTextAreaView::MouseUp(BPoint point)
1520 {
1521     vimMouseButton = 0;
1522 
1523     BMessage *m = Window()->CurrentMessage();
1524     assert(m);
1525     // m->PrintToStream();
1526 
1527     int32 modifiers = 0;
1528     m->FindInt32("modifiers", &modifiers);
1529 
1530     vimMouseModifiers = mouseModifiersToVim(modifiers);
1531 
1532     guiSendMouseEvent(MOUSE_RELEASE, point.x, point.y,
1533 	    0 /* = repeated_click*/, vimMouseModifiers);
1534 
1535     Inherited::MouseUp(point);
1536 }
1537 
1538     void
MouseMoved(BPoint point,uint32 transit,const BMessage * message)1539 VimTextAreaView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
1540 {
1541     /*
1542      * if our pointer is currently hidden, then we should show it.
1543      */
1544     if (gui.pointer_hidden)
1545     {
1546 	guiBlankMouse(false);
1547 	gui.pointer_hidden = FALSE;
1548     }
1549 
1550     if (!vimMouseButton) {    // could also check m->"buttons"
1551 	guiMouseMoved(point.x, point.y);
1552 	return;
1553     }
1554 
1555     atomic_add(&mouseDragEventCount, 1);
1556 
1557     // Don't care much about "transit"
1558     guiSendMouseEvent(MOUSE_DRAG, point.x, point.y, 0, vimMouseModifiers);
1559 }
1560 
1561     void
MessageReceived(BMessage * m)1562 VimTextAreaView::MessageReceived(BMessage *m)
1563 {
1564     switch (m->what) {
1565 	case 'menu':
1566 	    {
1567 		VimMenuMsg mm;
1568 		mm.guiMenu = NULL;  // in case no pointer in msg
1569 		m->FindPointer("VimMenu", (void **)&mm.guiMenu);
1570 		write_port(gui.vdcmp, VimMsg::Menu, &mm, sizeof(mm));
1571 	    }
1572 	    break;
1573 	case B_MOUSE_WHEEL_CHANGED:
1574 	    {
1575 		VimScrollBar* scb = curwin->w_scrollbars[1].id;
1576 		float small=0, big=0, dy=0;
1577 		m->FindFloat("be:wheel_delta_y", &dy);
1578 		scb->GetSteps(&small, &big);
1579 		scb->SetValue(scb->Value()+small*dy*3);
1580 		scb->ValueChanged(scb->Value());
1581 #if 0
1582 		scb = curwin->w_scrollbars[0].id;
1583 		scb->GetSteps(&small, &big);
1584 		scb->SetValue(scb->Value()+small*dy);
1585 		scb->ValueChanged(scb->Value());
1586 #endif
1587 	    }
1588 	    break;
1589 #ifdef FEAT_MBYTE_IME
1590 	case B_INPUT_METHOD_EVENT:
1591 	    {
1592 		int32 opcode;
1593 		m->FindInt32("be:opcode", &opcode);
1594 		switch(opcode)
1595 		{
1596 		    case B_INPUT_METHOD_STARTED:
1597 			if (!IMData.messenger) delete IMData.messenger;
1598 			IMData.messenger = new BMessenger();
1599 			m->FindMessenger("be:reply_to", IMData.messenger);
1600 			break;
1601 		    case B_INPUT_METHOD_CHANGED:
1602 			{
1603 			    BString str;
1604 			    bool confirmed;
1605 			    if (IMData.message) *(IMData.message) = *m;
1606 			    else	       IMData.message = new BMessage(*m);
1607 			    DrawIMString();
1608 			    m->FindBool("be:confirmed", &confirmed);
1609 			    if (confirmed)
1610 			    {
1611 				m->FindString("be:string", &str);
1612 				char_u *chars = (char_u*)str.String();
1613 				struct VimKeyMsg km;
1614 				km.csi_escape = true;
1615 				int clen;
1616 				int i = 0;
1617 				while (i < str.Length())
1618 				{
1619 				    clen = utf_ptr2len(chars+i);
1620 				    memcpy(km.chars, chars+i, clen);
1621 				    km.length = clen;
1622 				    write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
1623 				    i += clen;
1624 				}
1625 			    }
1626 			}
1627 			break;
1628 		    case B_INPUT_METHOD_LOCATION_REQUEST:
1629 			{
1630 			    BMessage msg(B_INPUT_METHOD_EVENT);
1631 			    msg.AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST);
1632 			    msg.AddPoint("be:location_reply", IMData.location);
1633 			    msg.AddFloat("be:height_reply", FILL_Y(1));
1634 			    IMData.messenger->SendMessage(&msg);
1635 			}
1636 			break;
1637 		    case B_INPUT_METHOD_STOPPED:
1638 			delete IMData.messenger;
1639 			delete IMData.message;
1640 			IMData.messenger = NULL;
1641 			IMData.message = NULL;
1642 			break;
1643 		}
1644 	    }
1645 	    // TODO: sz: break here???
1646 #endif
1647 	default:
1648 	    if (m->WasDropped()) {
1649 		BWindow *w = Window();
1650 		w->DetachCurrentMessage();
1651 		w->Minimize(false);
1652 		VimApp::SendRefs(m, (modifiers() & B_SHIFT_KEY) != 0);
1653 	    } else {
1654 		Inherited::MessageReceived(m);
1655 	    }
1656 	    break;
1657     }
1658 }
1659 
1660     int
mchInitFont(char_u * name)1661 VimTextAreaView::mchInitFont(char_u *name)
1662 {
1663     VimFont *newFont = (VimFont *)gui_mch_get_font(name, 1);
1664     if (newFont != NOFONT) {
1665 	gui.norm_font = (GuiFont)newFont;
1666 	gui_mch_set_font((GuiFont)newFont);
1667 	if (name && STRCMP(name, "*") != 0)
1668 	    hl_set_font_name(name);
1669 
1670 	SetDrawingMode(B_OP_COPY);
1671 
1672 	/*
1673 	 * Try to load other fonts for bold, italic, and bold-italic.
1674 	 * We should also try to work out what font to use for these when they are
1675 	 * not specified by X resources, but we don't yet.
1676 	 */
1677 	return OK;
1678     }
1679     return FAIL;
1680 }
1681 
1682     void
mchDrawString(int row,int col,char_u * s,int len,int flags)1683 VimTextAreaView::mchDrawString(int row, int col, char_u *s, int len, int flags)
1684 {
1685     /*
1686      * First we must erase the area, because DrawString won't do
1687      * that for us. XXX Most of the time this is a waste of effort
1688      * since the bachground has been erased already... DRAW_TRANSP
1689      * should be set when appropriate!!!
1690      * (Rectangles include the bottom and right edge)
1691      */
1692     if (!(flags & DRAW_TRANSP)) {
1693 	int cells;
1694 	cells = 0;
1695 	for (int i=0; i<len; i++) {
1696 	    int cn = utf_ptr2cells((char_u *)(s+i));
1697 	    if (cn<4) cells += cn;
1698 	}
1699 
1700 	BRect r(FILL_X(col), FILL_Y(row),
1701 		FILL_X(col + cells) - PEN_WIDTH, FILL_Y(row + 1) - PEN_WIDTH);
1702 	FillRect(r, B_SOLID_LOW);
1703     }
1704 
1705     BFont font;
1706     this->GetFont(&font);
1707     if (!font.IsFixed())
1708     {
1709 	char* p = (char*)s;
1710 	int32 clen, lastpos = 0;
1711 	BPoint where;
1712 	int cells;
1713 	while ((p - (char*)s) < len) {
1714 	    clen = utf_ptr2len((u_char*)p);
1715 	    where.Set(TEXT_X(col+lastpos), TEXT_Y(row));
1716 	    DrawString(p, clen, where);
1717 	    if (flags & DRAW_BOLD) {
1718 		where.x += 1.0;
1719 		SetDrawingMode(B_OP_BLEND);
1720 		DrawString(p, clen, where);
1721 		SetDrawingMode(B_OP_COPY);
1722 	    }
1723 	    cells = utf_ptr2cells((char_u *)p);
1724 	    if (cells<4) lastpos += cells;
1725 	    else	lastpos++;
1726 	    p += clen;
1727 	}
1728     }
1729     else
1730     {
1731 	BPoint where(TEXT_X(col), TEXT_Y(row));
1732 	DrawString((char*)s, len, where);
1733 	if (flags & DRAW_BOLD) {
1734 	    where.x += 1.0;
1735 	    SetDrawingMode(B_OP_BLEND);
1736 	    DrawString((char*)s, len, where);
1737 	    SetDrawingMode(B_OP_COPY);
1738 	}
1739     }
1740 
1741     if (flags & DRAW_UNDERL) {
1742 	int cells;
1743 	cells = 0;
1744 	for (int i=0; i<len; i++) {
1745 	    int cn = utf_ptr2cells((char_u *)(s+i));
1746 	    if (cn<4) cells += cn;
1747 	}
1748 
1749 	BPoint start(FILL_X(col), FILL_Y(row + 1) - PEN_WIDTH);
1750 	BPoint end(FILL_X(col + cells) - PEN_WIDTH, start.y);
1751 
1752 	StrokeLine(start, end);
1753     }
1754 }
1755 
1756 void
mchClearBlock(int row1,int col1,int row2,int col2)1757 VimTextAreaView::mchClearBlock(
1758 	int	row1,
1759 	int	col1,
1760 	int	row2,
1761 	int	col2)
1762 {
1763     BRect r(FILL_X(col1), FILL_Y(row1),
1764 	    FILL_X(col2 + 1) - PEN_WIDTH, FILL_Y(row2 + 1) - PEN_WIDTH);
1765     gui_mch_set_bg_color(gui.back_pixel);
1766     FillRect(r, B_SOLID_LOW);
1767 }
1768 
1769     void
mchClearAll()1770 VimTextAreaView::mchClearAll()
1771 {
1772     gui_mch_set_bg_color(gui.back_pixel);
1773     FillRect(Bounds(), B_SOLID_LOW);
1774 }
1775 
1776 /*
1777  * mchDeleteLines() Lock()s the window by itself.
1778  */
1779     void
mchDeleteLines(int row,int num_lines)1780 VimTextAreaView::mchDeleteLines(int row, int num_lines)
1781 {
1782     BRect source, dest;
1783     source.left = FILL_X(gui.scroll_region_left);
1784     source.top = FILL_Y(row + num_lines);
1785     source.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
1786     source.bottom = FILL_Y(gui.scroll_region_bot + 1) - PEN_WIDTH;
1787 
1788     dest.left = FILL_X(gui.scroll_region_left);
1789     dest.top = FILL_Y(row);
1790     dest.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
1791     dest.bottom = FILL_Y(gui.scroll_region_bot - num_lines + 1) - PEN_WIDTH;
1792 
1793     if (gui.vimWindow->Lock()) {
1794 	// Clear one column more for when bold has spilled over
1795 	CopyBits(source, dest);
1796 	gui_clear_block(gui.scroll_region_bot - num_lines + 1,
1797 		gui.scroll_region_left,
1798 		gui.scroll_region_bot, gui.scroll_region_right);
1799 
1800 
1801 	gui.vimWindow->Unlock();
1802 	/*
1803 	 * The Draw() callback will be called now if some of the source
1804 	 * bits were not in the visible region.
1805 	 */
1806     }
1807     // gui_x11_check_copy_area();
1808     //	}
1809 }
1810 
1811 /*
1812  * mchInsertLines() Lock()s the window by itself.
1813  */
1814     void
mchInsertLines(int row,int num_lines)1815 VimTextAreaView::mchInsertLines(int row, int num_lines)
1816 {
1817     BRect source, dest;
1818 
1819     // XXX Attempt at a hack:
1820     gui.vimWindow->UpdateIfNeeded();
1821     source.left = FILL_X(gui.scroll_region_left);
1822     source.top = FILL_Y(row);
1823     source.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
1824     source.bottom = FILL_Y(gui.scroll_region_bot - num_lines + 1) - PEN_WIDTH;
1825 
1826     dest.left = FILL_X(gui.scroll_region_left);
1827     dest.top = FILL_Y(row + num_lines);
1828     dest.right = FILL_X(gui.scroll_region_right + 1) - PEN_WIDTH;
1829     dest.bottom = FILL_Y(gui.scroll_region_bot + 1) - PEN_WIDTH;
1830 
1831     if (gui.vimWindow->Lock()) {
1832 	// Clear one column more for when bold has spilled over
1833 	CopyBits(source, dest);
1834 	gui_clear_block(row, gui.scroll_region_left,
1835 		row + num_lines - 1, gui.scroll_region_right);
1836 
1837 	gui.vimWindow->Unlock();
1838 	/*
1839 	 * The Draw() callback will be called now if some of the source
1840 	 * bits were not in the visible region.
1841 	 * However, if we scroll too fast it can't keep up and the
1842 	 * update region gets messed up. This seems to be because copying
1843 	 * un-Draw()n bits does not generate Draw() calls for the copy...
1844 	 * I moved the hack to before the CopyBits() to reduce the
1845 	 * amount of additional waiting needed.
1846 	 */
1847 
1848 	// gui_x11_check_copy_area();
1849 
1850     }
1851 }
1852 
1853 #ifdef FEAT_MBYTE_IME
1854 /*
1855  * DrawIMString draws string with IMData.message.
1856  */
DrawIMString(void)1857 void VimTextAreaView::DrawIMString(void)
1858 {
1859     static const rgb_color r_highlight = {255, 152, 152, 255},
1860 		 b_highlight = {152, 203, 255, 255};
1861     BString str;
1862     const char* s;
1863     int len;
1864     BMessage* msg = IMData.message;
1865     if (!msg)
1866 	return;
1867     gui_redraw_block(IMData.row, 0,
1868 	    IMData.row + IMData.count, W_WIDTH(curwin), GUI_MON_NOCLEAR);
1869     bool confirmed = false;
1870     msg->FindBool("be:confirmed", &confirmed);
1871     if (confirmed)
1872 	return;
1873     rgb_color hcolor = HighColor(), lcolor = LowColor();
1874     msg->FindString("be:string", &str);
1875     s = str.String();
1876     len = str.Length();
1877     SetHighColor(0, 0, 0);
1878     IMData.row = gui.row;
1879     IMData.col = gui.col;
1880     int32 sel_start = 0, sel_end = 0;
1881     msg->FindInt32("be:selection", 0, &sel_start);
1882     msg->FindInt32("be:selection", 1, &sel_end);
1883     int clen, cn;
1884     BPoint pos(IMData.col, 0);
1885     BRect r;
1886     BPoint where;
1887     IMData.location = ConvertToScreen(
1888 	    BPoint(FILL_X(pos.x), FILL_Y(IMData.row + pos.y)));
1889     for (int i=0; i<len; i+=clen)
1890     {
1891 	cn = utf_ptr2cells((char_u *)(s+i));
1892 	clen = utf_ptr2len((char_u *)(s+i));
1893 	if (pos.x + cn > W_WIDTH(curwin))
1894 	{
1895 	    pos.y++;
1896 	    pos.x = 0;
1897 	}
1898 	if (sel_start<=i && i<sel_end)
1899 	{
1900 	    SetLowColor(r_highlight);
1901 	    IMData.location = ConvertToScreen(
1902 		    BPoint(FILL_X(pos.x), FILL_Y(IMData.row + pos.y)));
1903 	}
1904 	else
1905 	{
1906 	    SetLowColor(b_highlight);
1907 	}
1908 	r.Set(FILL_X(pos.x), FILL_Y(IMData.row + pos.y),
1909 		FILL_X(pos.x + cn) - PEN_WIDTH,
1910 		FILL_Y(IMData.row + pos.y + 1) - PEN_WIDTH);
1911 	FillRect(r, B_SOLID_LOW);
1912 	where.Set(TEXT_X(pos.x), TEXT_Y(IMData.row + pos.y));
1913 	DrawString((s+i), clen, where);
1914 	pos.x += cn;
1915     }
1916     IMData.count = (int)pos.y;
1917 
1918     SetHighColor(hcolor);
1919     SetLowColor(lcolor);
1920 }
1921 #endif
1922 // ---------------- VimScrollBar ----------------
1923 
1924 /*
1925  * BUG: XXX
1926  * It seems that BScrollBar determine their direction not from
1927  * "posture" but from if they are "tall" or "wide" in shape...
1928  *
1929  * Also, place them out of sight, because Vim enables them before
1930  * they are positioned.
1931  */
VimScrollBar(scrollbar_T * g,orientation posture)1932 VimScrollBar::VimScrollBar(scrollbar_T *g, orientation posture):
1933     BScrollBar(posture == B_HORIZONTAL ?  BRect(-100,-100,-10,-90) :
1934 	    BRect(-100,-100,-90,-10),
1935 	    "vim scrollbar", (BView *)NULL,
1936 	    0.0, 10.0, posture),
1937     ignoreValue(-1),
1938     scrollEventCount(0)
1939 {
1940     gsb = g;
1941     SetResizingMode(B_FOLLOW_NONE);
1942 }
1943 
~VimScrollBar()1944 VimScrollBar::~VimScrollBar()
1945 {
1946 }
1947 
1948     void
ValueChanged(float newValue)1949 VimScrollBar::ValueChanged(float newValue)
1950 {
1951     if (ignoreValue >= 0.0 && newValue == ignoreValue) {
1952 	ignoreValue = -1;
1953 	return;
1954     }
1955     ignoreValue = -1;
1956     /*
1957      * We want to throttle the amount of scroll messages generated.
1958      * Normally I presume you won't get a new message before we've
1959      * handled the previous one, but because we're passing them on this
1960      * happens very quickly. So instead we keep a counter of how many
1961      * scroll events there are (or will be) in the VDCMP, and the
1962      * throttling happens at the receiving end.
1963      */
1964     atomic_add(&scrollEventCount, 1);
1965 
1966     struct VimScrollBarMsg sm;
1967 
1968     sm.sb = this;
1969     sm.value = (long) newValue;
1970     sm.stillDragging = TRUE;
1971 
1972     write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));
1973 
1974     //	calls gui_drag_scrollbar(sb, newValue, TRUE);
1975 }
1976 
1977 /*
1978  * When the mouse goes up, report that scrolling has stopped.
1979  * MouseUp() is NOT called when the mouse-up occurs outside
1980  * the window, even though the thumb does move while the mouse
1981  * is outside... This has some funny effects... XXX
1982  * So we do special processing when the window de/activates.
1983  */
1984     void
MouseUp(BPoint where)1985 VimScrollBar::MouseUp(BPoint where)
1986 {
1987     // BMessage *m = Window()->CurrentMessage();
1988     // m->PrintToStream();
1989 
1990     atomic_add(&scrollEventCount, 1);
1991 
1992     struct VimScrollBarMsg sm;
1993 
1994     sm.sb = this;
1995     sm.value = (long) Value();
1996     sm.stillDragging = FALSE;
1997 
1998     write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));
1999 
2000     //	calls gui_drag_scrollbar(sb, newValue, FALSE);
2001 
2002     Inherited::MouseUp(where);
2003 }
2004 
2005     void
SetValue(float newValue)2006 VimScrollBar::SetValue(float newValue)
2007 {
2008     if (newValue == Value())
2009 	return;
2010 
2011     ignoreValue = newValue;
2012     Inherited::SetValue(newValue);
2013 }
2014 
2015 // ---------------- VimFont ----------------
2016 
VimFont()2017 VimFont::VimFont(): BFont()
2018 {
2019     init();
2020 }
2021 
VimFont(const VimFont * rhs)2022 VimFont::VimFont(const VimFont *rhs): BFont(rhs)
2023 {
2024     init();
2025 }
2026 
VimFont(const BFont * rhs)2027 VimFont::VimFont(const BFont *rhs): BFont(rhs)
2028 {
2029     init();
2030 }
2031 
VimFont(const VimFont & rhs)2032 VimFont::VimFont(const VimFont &rhs): BFont(rhs)
2033 {
2034     init();
2035 }
2036 
~VimFont()2037 VimFont::~VimFont()
2038 {
2039 }
2040 
2041     void
init()2042 VimFont::init()
2043 {
2044     next = NULL;
2045     refcount = 1;
2046     name = NULL;
2047 }
2048 
2049 // ---------------- VimDialog ----------------
2050 
2051 #if defined(FEAT_GUI_DIALOG)
2052 
2053 const unsigned int  kVimDialogButtonMsg = 'VMDB';
2054 const unsigned int  kVimDialogIconStripeWidth = 30;
2055 const unsigned int  kVimDialogButtonsSpacingX = 9;
2056 const unsigned int  kVimDialogButtonsSpacingY = 4;
2057 const unsigned int  kVimDialogSpacingX = 6;
2058 const unsigned int  kVimDialogSpacingY = 10;
2059 const unsigned int  kVimDialogMinimalWidth  = 310;
2060 const unsigned int  kVimDialogMinimalHeight = 75;
2061 const BRect	    kDefaultRect =
2062 BRect(0, 0, kVimDialogMinimalWidth, kVimDialogMinimalHeight);
2063 
VimDialog(int type,const char * title,const char * message,const char * buttons,int dfltbutton,const char * textfield,int ex_cmd)2064 VimDialog::VimDialog(int type, const char *title, const char *message,
2065 	const char *buttons, int dfltbutton, const char *textfield, int ex_cmd)
2066 : BWindow(kDefaultRect, title, B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
2067 	B_NOT_CLOSABLE | B_NOT_RESIZABLE |
2068 	B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS)
2069     , fDialogSem(-1)
2070     , fDialogValue(dfltbutton)
2071     , fMessageView(NULL)
2072     , fInputControl(NULL)
2073     , fInputValue(textfield)
2074 {
2075     //	master view
2076     VimDialog::View* view = new VimDialog::View(Bounds());
2077     if (view == NULL)
2078 	return;
2079 
2080     if (title == NULL)
2081 	SetTitle("Vim " VIM_VERSION_MEDIUM);
2082 
2083     AddChild(view);
2084 
2085     //	icon
2086     view->InitIcon(type);
2087 
2088     //	buttons
2089     int32 which = 1;
2090     float maxButtonWidth  = 0;
2091     float maxButtonHeight = 0;
2092     float buttonsWidth	  = 0;
2093     float buttonsHeight   = 0;
2094     BString strButtons(buttons);
2095     strButtons.RemoveAll("&");
2096     do {
2097 	int32 end = strButtons.FindFirst('\n');
2098 	if (end != B_ERROR)
2099 	    strButtons.SetByteAt(end, '\0');
2100 
2101 	BButton *button = _CreateButton(which++, strButtons.String());
2102 	view->AddChild(button);
2103 	fButtonsList.AddItem(button);
2104 
2105 	maxButtonWidth	= max_c(maxButtonWidth,  button->Bounds().Width());
2106 	maxButtonHeight = max_c(maxButtonHeight, button->Bounds().Height());
2107 	buttonsWidth   += button->Bounds().Width();
2108 	buttonsHeight  += button->Bounds().Height();
2109 
2110 	if (end == B_ERROR)
2111 	    break;
2112 
2113 	strButtons.Remove(0, end + 1);
2114     } while (true);
2115 
2116     int32 buttonsCount = fButtonsList.CountItems();
2117     buttonsWidth      += kVimDialogButtonsSpacingX * (buttonsCount - 1);
2118     buttonsHeight     += kVimDialogButtonsSpacingY * (buttonsCount - 1);
2119     float dialogWidth  = buttonsWidth + kVimDialogIconStripeWidth +
2120 	kVimDialogSpacingX * 2;
2121     float dialogHeight = maxButtonHeight + kVimDialogSpacingY * 3;
2122 
2123     // Check 'v' flag in 'guioptions': vertical button placement.
2124     bool vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL) ||
2125 	dialogWidth >= gui.vimWindow->Bounds().Width();
2126     if (vertical) {
2127 	dialogWidth  -= buttonsWidth;
2128 	dialogWidth  += maxButtonWidth;
2129 	dialogHeight -= maxButtonHeight;
2130 	dialogHeight += buttonsHeight;
2131     }
2132 
2133     dialogWidth  = max_c(dialogWidth,  kVimDialogMinimalWidth);
2134 
2135     //	message view
2136     BRect rect(0, 0, dialogWidth, 0);
2137     rect.left  += kVimDialogIconStripeWidth + 16 + kVimDialogSpacingX;
2138     rect.top   += kVimDialogSpacingY;
2139     rect.right -= kVimDialogSpacingX;
2140     rect.bottom = rect.top;
2141     fMessageView = new BTextView(rect, "_tv_", rect.OffsetByCopy(B_ORIGIN),
2142 	    B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
2143 
2144     fMessageView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2145     rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
2146     fMessageView->SetFontAndColor(be_plain_font, B_FONT_ALL, &textColor);
2147     fMessageView->SetText(message);
2148     fMessageView->MakeEditable(false);
2149     fMessageView->MakeSelectable(false);
2150     fMessageView->SetWordWrap(true);
2151     AddChild(fMessageView);
2152 
2153     float messageHeight = fMessageView->TextHeight(0, fMessageView->CountLines());
2154     fMessageView->ResizeBy(0, messageHeight);
2155     fMessageView->SetTextRect(BRect(0, 0, rect.Width(), messageHeight));
2156 
2157     dialogHeight += messageHeight;
2158 
2159     //	input view
2160     if (fInputValue != NULL) {
2161 	rect.top     =
2162 	    rect.bottom += messageHeight + kVimDialogSpacingY;
2163 	fInputControl = new BTextControl(rect, "_iv_", NULL, fInputValue, NULL,
2164 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_NAVIGABLE |  B_PULSE_NEEDED);
2165 	fInputControl->TextView()->SetText(fInputValue);
2166 	fInputControl->TextView()->SetWordWrap(false);
2167 	AddChild(fInputControl);
2168 
2169 	float width = 0.f, height = 0.f;
2170 	fInputControl->GetPreferredSize(&width, &height);
2171 	fInputControl->MakeFocus(true);
2172 
2173 	dialogHeight += height + kVimDialogSpacingY * 1.5;
2174     }
2175 
2176     dialogHeight = max_c(dialogHeight, kVimDialogMinimalHeight);
2177 
2178     ResizeTo(dialogWidth, dialogHeight);
2179     MoveTo((gui.vimWindow->Bounds().Width() - dialogWidth) / 2,
2180 	    (gui.vimWindow->Bounds().Height() - dialogHeight) / 2);
2181 
2182     //	adjust layout of buttons
2183     float buttonWidth = max_c(maxButtonWidth, rect.Width() * 0.66);
2184     BPoint origin(dialogWidth, dialogHeight);
2185     origin.x -= kVimDialogSpacingX + (vertical ? buttonWidth : buttonsWidth);
2186     origin.y -= kVimDialogSpacingY + (vertical ? buttonsHeight	: maxButtonHeight);
2187 
2188     for (int32 i = 0 ; i < buttonsCount; i++) {
2189 	BButton *button = (BButton*)fButtonsList.ItemAt(i);
2190 	button->MoveTo(origin);
2191 	if (vertical) {
2192 	    origin.y += button->Frame().Height() + kVimDialogButtonsSpacingY;
2193 	    button->ResizeTo(buttonWidth, button->Frame().Height());
2194 	} else
2195 	    origin.x += button->Frame().Width() + kVimDialogButtonsSpacingX;
2196 
2197 	if (dfltbutton == i + 1) {
2198 	    button->MakeDefault(true);
2199 	    button->MakeFocus(fInputControl == NULL);
2200 	}
2201     }
2202 }
2203 
~VimDialog()2204 VimDialog::~VimDialog()
2205 {
2206     if (fDialogSem > B_OK)
2207 	delete_sem(fDialogSem);
2208 }
2209 
2210     int
Go()2211 VimDialog::Go()
2212 {
2213     fDialogSem = create_sem(0, "VimDialogSem");
2214     if (fDialogSem < B_OK) {
2215 	Quit();
2216 	return fDialogValue;
2217     }
2218 
2219     Show();
2220 
2221     while (acquire_sem(fDialogSem) == B_INTERRUPTED);
2222 
2223     int retValue = fDialogValue;
2224     if (fInputValue != NULL)
2225 	vim_strncpy((char_u*)fInputValue, (char_u*)fInputControl->Text(), IOSIZE - 1);
2226 
2227     if (Lock())
2228 	Quit();
2229 
2230     return retValue;
2231 }
2232 
MessageReceived(BMessage * msg)2233 void VimDialog::MessageReceived(BMessage *msg)
2234 {
2235     int32 which = 0;
2236     if (msg->what != kVimDialogButtonMsg ||
2237 	    msg->FindInt32("which", &which) != B_OK)
2238 	return BWindow::MessageReceived(msg);
2239 
2240     fDialogValue = which;
2241     delete_sem(fDialogSem);
2242     fDialogSem = -1;
2243 }
2244 
_CreateButton(int32 which,const char * label)2245 BButton* VimDialog::_CreateButton(int32 which, const char* label)
2246 {
2247     BMessage *message = new BMessage(kVimDialogButtonMsg);
2248     message->AddInt32("which", which);
2249 
2250     BRect rect(0, 0, 0, 0);
2251     BString name;
2252     name << "_b" << which << "_";
2253 
2254     BButton* button = new BButton(rect, name.String(), label, message,
2255 	    B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
2256 
2257     float width = 0.f, height = 0.f;
2258     button->GetPreferredSize(&width, &height);
2259     button->ResizeTo(width, height);
2260 
2261     return button;
2262 }
2263 
View(BRect frame)2264 VimDialog::View::View(BRect frame)
2265     :	BView(frame, "VimDialogView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW),
2266     fIconBitmap(NULL)
2267 {
2268     SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2269 }
2270 
~View()2271 VimDialog::View::~View()
2272 {
2273     delete fIconBitmap;
2274 }
2275 
Draw(BRect updateRect)2276 void VimDialog::View::Draw(BRect updateRect)
2277 {
2278     BRect stripeRect = Bounds();
2279     stripeRect.right = kVimDialogIconStripeWidth;
2280     SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT));
2281     FillRect(stripeRect);
2282 
2283     if (fIconBitmap == NULL)
2284 	return;
2285 
2286     SetDrawingMode(B_OP_ALPHA);
2287     SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
2288     DrawBitmapAsync(fIconBitmap, BPoint(18, 6));
2289 }
2290 
InitIcon(int32 type)2291 void VimDialog::View::InitIcon(int32 type)
2292 {
2293     if (type == VIM_GENERIC)
2294 	return;
2295 
2296     BPath path;
2297     status_t status = find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
2298     if (status != B_OK) {
2299 	fprintf(stderr, "Cannot retrieve app info:%s\n", strerror(status));
2300 	return;
2301     }
2302 
2303     path.Append("app_server");
2304 
2305     BFile file(path.Path(), O_RDONLY);
2306     if (file.InitCheck() != B_OK) {
2307 	fprintf(stderr, "App file assignment failed:%s\n",
2308 		strerror(file.InitCheck()));
2309 	return;
2310     }
2311 
2312     BResources resources(&file);
2313     if (resources.InitCheck() != B_OK) {
2314 	fprintf(stderr, "App server resources assignment failed:%s\n",
2315 		strerror(resources.InitCheck()));
2316 	return;
2317     }
2318 
2319     const char *name = "";
2320     switch(type) {
2321 	case VIM_ERROR:	    name = "stop"; break;
2322 	case VIM_WARNING:   name = "warn"; break;
2323 	case VIM_INFO:	    name = "info"; break;
2324 	case VIM_QUESTION:  name = "idea"; break;
2325 	default: return;
2326     }
2327 
2328     int32 iconSize = 32;
2329     fIconBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
2330     if (fIconBitmap == NULL || fIconBitmap->InitCheck() != B_OK) {
2331 	fprintf(stderr, "Icon bitmap allocation failed:%s\n",
2332 		(fIconBitmap == NULL) ? "null" : strerror(fIconBitmap->InitCheck()));
2333 	return;
2334     }
2335 
2336     size_t size = 0;
2337     const uint8* iconData = NULL;
2338     //	try vector icon first?
2339     iconData = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE, name, &size);
2340     if (iconData != NULL && BIconUtils::GetVectorIcon(iconData, size, fIconBitmap) == B_OK)
2341 	return;
2342 
2343     //	try bitmap icon now
2344     iconData = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE, name, &size);
2345     if (iconData == NULL) {
2346 	fprintf(stderr, "Bitmap icon resource not found\n");
2347 	delete fIconBitmap;
2348 	fIconBitmap = NULL;
2349 	return;
2350     }
2351 
2352     if (fIconBitmap->ColorSpace() != B_CMAP8)
2353 	BIconUtils::ConvertFromCMAP8(iconData, iconSize, iconSize, iconSize, fIconBitmap);
2354 }
2355 
2356 const unsigned int  kVimDialogOKButtonMsg = 'FDOK';
2357 const unsigned int  kVimDialogCancelButtonMsg = 'FDCN';
2358 const unsigned int  kVimDialogSizeInputMsg = 'SICH';
2359 const unsigned int  kVimDialogFamilySelectMsg = 'MSFM';
2360 const unsigned int  kVimDialogStyleSelectMsg = 'MSST';
2361 const unsigned int  kVimDialogSizeSelectMsg = 'MSSZ';
2362 
VimSelectFontDialog(font_family * family,font_style * style,float * size)2363 VimSelectFontDialog::VimSelectFontDialog(font_family* family, font_style* style, float* size)
2364 : BWindow(kDefaultRect, "Font Selection", B_TITLED_WINDOW_LOOK, B_MODAL_APP_WINDOW_FEEL,
2365 	B_NOT_CLOSABLE | B_NOT_RESIZABLE |
2366 	B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_ASYNCHRONOUS_CONTROLS)
2367     , fStatus(B_NO_INIT)
2368     , fDialogSem(-1)
2369     , fDialogValue(false)
2370     , fFamily(family)
2371     , fStyle(style)
2372     , fSize(size)
2373     , fFontSize(*size)
2374     , fPreview(0)
2375     , fFamiliesList(0)
2376     , fStylesList(0)
2377     , fSizesList(0)
2378     , fSizesInput(0)
2379 {
2380     strncpy(fFontFamily, *family, B_FONT_FAMILY_LENGTH);
2381     strncpy(fFontStyle, *style, B_FONT_STYLE_LENGTH);
2382 
2383     //	"client" area view
2384     BBox *clientBox = new BBox(Bounds(), B_EMPTY_STRING, B_FOLLOW_ALL_SIDES,
2385 		    B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE_JUMP | B_PULSE_NEEDED,
2386 		    B_PLAIN_BORDER);
2387     AddChild(clientBox);
2388 
2389     //	client view
2390     BRect RC = clientBox->Bounds();
2391     RC.InsetBy(kVimDialogSpacingX, kVimDialogSpacingY);
2392     BRect rc(RC.LeftTop(), RC.LeftTop());
2393 
2394     //	at first create all controls
2395     fPreview = new BStringView(rc, "preview", "DejaVu Sans Mono");
2396     clientBox->AddChild(fPreview);
2397 
2398     BBox* boxDivider = new BBox(rc, B_EMPTY_STRING,
2399 	    B_FOLLOW_NONE, B_WILL_DRAW, B_FANCY_BORDER);
2400     clientBox->AddChild(boxDivider);
2401 
2402     BStringView *labelFamily = new BStringView(rc, "labelFamily", "Family:");
2403     clientBox->AddChild(labelFamily);
2404     labelFamily->ResizeToPreferred();
2405 
2406     BStringView *labelStyle = new BStringView(rc, "labelStyle", "Style:");
2407     clientBox->AddChild(labelStyle);
2408     labelStyle->ResizeToPreferred();
2409 
2410     BStringView *labelSize = new BStringView(rc, "labelSize", "Size:");
2411     clientBox->AddChild(labelSize);
2412     labelSize->ResizeToPreferred();
2413 
2414     fFamiliesList = new BListView(rc, "listFamily",
2415 	    B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
2416     BScrollView *scrollFamilies = new BScrollView("scrollFamily",
2417 	    fFamiliesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
2418     clientBox->AddChild(scrollFamilies);
2419 
2420     fStylesList= new BListView(rc, "listStyles",
2421 	    B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
2422     BScrollView *scrollStyles = new BScrollView("scrollStyle",
2423 	    fStylesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
2424     clientBox->AddChild(scrollStyles);
2425 
2426     fSizesInput = new BTextControl(rc, "inputSize", NULL, "???",
2427 	    new BMessage(kVimDialogSizeInputMsg));
2428     clientBox->AddChild(fSizesInput);
2429     fSizesInput->ResizeToPreferred();
2430 
2431     fSizesList = new BListView(rc, "listSizes",
2432 	    B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES);
2433     BScrollView *scrollSizes = new BScrollView("scrollSize",
2434 	    fSizesList, B_FOLLOW_LEFT_RIGHT, 0, false, true);
2435     clientBox->AddChild(scrollSizes);
2436 
2437     BButton *buttonOK = new BButton(rc, "buttonOK", "OK",
2438 			new BMessage(kVimDialogOKButtonMsg));
2439     clientBox->AddChild(buttonOK);
2440     buttonOK->ResizeToPreferred();
2441 
2442     BButton *buttonCancel = new BButton(rc, "buttonCancel", "Cancel",
2443 			new BMessage(kVimDialogCancelButtonMsg));
2444     clientBox->AddChild(buttonCancel);
2445     buttonCancel->ResizeToPreferred();
2446 
2447     //	layout controls
2448     float lineHeight = labelFamily->Bounds().Height();
2449     float previewHeight = lineHeight * 3;
2450     float offsetYLabels = previewHeight + kVimDialogSpacingY;
2451     float offsetYLists = offsetYLabels + lineHeight + kVimDialogSpacingY / 2;
2452     float offsetYSizes = offsetYLists + fSizesInput->Bounds().Height() + kVimDialogSpacingY / 2;
2453     float listsHeight = lineHeight * 9;
2454     float offsetYButtons = offsetYLists + listsHeight +  kVimDialogSpacingY;
2455     float maxControlsHeight = offsetYButtons + buttonOK->Bounds().Height();
2456     float familiesWidth = labelFamily->Bounds().Width() * 5;
2457     float offsetXStyles = familiesWidth + kVimDialogSpacingX;
2458     float stylesWidth = labelStyle->Bounds().Width() * 4;
2459     float offsetXSizes = offsetXStyles + stylesWidth + kVimDialogSpacingX;
2460     float sizesWidth = labelSize->Bounds().Width() * 2;
2461     float maxControlsWidth = offsetXSizes + sizesWidth;
2462 
2463     ResizeTo(maxControlsWidth + kVimDialogSpacingX * 2,
2464 	maxControlsHeight + kVimDialogSpacingY * 2);
2465 
2466     BRect rcVim = gui.vimWindow->Frame();
2467     MoveTo(rcVim.left + (rcVim.Width() - Frame().Width()) / 2,
2468 	    rcVim.top + (rcVim.Height() - Frame().Height()) / 2);
2469 
2470     fPreview->ResizeTo(maxControlsWidth, previewHeight);
2471     fPreview->SetAlignment(B_ALIGN_CENTER);
2472 
2473     boxDivider->MoveBy(0.f, previewHeight + kVimDialogSpacingY / 2);
2474     boxDivider->ResizeTo(maxControlsWidth, 1.f);
2475 
2476     labelFamily->MoveBy(0.f, offsetYLabels);
2477     labelStyle->MoveBy(offsetXStyles, offsetYLabels);
2478     labelSize->MoveBy(offsetXSizes, offsetYLabels);
2479 
2480     //	text control alignment issues
2481     float insetX = fSizesInput->TextView()->Bounds().Width() - fSizesInput->Bounds().Width();
2482     float insetY = fSizesInput->TextView()->Bounds().Width() - fSizesInput->Bounds().Width();
2483 
2484     scrollFamilies->MoveBy(0.f, offsetYLists);
2485     scrollStyles->MoveBy(offsetXStyles, offsetYLists);
2486     fSizesInput->MoveBy(offsetXSizes + insetX / 2, offsetYLists + insetY / 2);
2487     scrollSizes->MoveBy(offsetXSizes, offsetYSizes);
2488 
2489     fSizesInput->SetAlignment(B_ALIGN_CENTER, B_ALIGN_CENTER);
2490 
2491     scrollFamilies->ResizeTo(familiesWidth, listsHeight);
2492     scrollStyles->ResizeTo(stylesWidth, listsHeight);
2493     fSizesInput->ResizeTo(sizesWidth, fSizesInput->Bounds().Height());
2494     scrollSizes->ResizeTo(sizesWidth,
2495 	    listsHeight - (offsetYSizes - offsetYLists));
2496 
2497     buttonOK->MoveBy(maxControlsWidth - buttonOK->Bounds().Width(), offsetYButtons);
2498     buttonCancel->MoveBy(maxControlsWidth - buttonOK->Bounds().Width()
2499 	    - buttonCancel->Bounds().Width() - kVimDialogSpacingX, offsetYButtons);
2500 
2501     //	fill lists
2502     int selIndex = -1;
2503     int count = count_font_families();
2504     for (int i = 0; i < count; i++) {
2505 	font_family family;
2506 	if (get_font_family(i, &family ) == B_OK) {
2507 	    fFamiliesList->AddItem(new BStringItem((const char*)family));
2508 	    if (strncmp(family, fFontFamily, B_FONT_FAMILY_LENGTH) == 0)
2509 		selIndex = i;
2510 	}
2511     }
2512 
2513     if (selIndex >= 0) {
2514 	fFamiliesList->Select(selIndex);
2515 	fFamiliesList->ScrollToSelection();
2516     }
2517 
2518     _UpdateFontStyles();
2519 
2520     selIndex = -1;
2521     for (int size = 8, index = 0; size <= 18; size++, index++) {
2522 	BString str;
2523 	str << size;
2524 	fSizesList->AddItem(new BStringItem(str));
2525 	if (size == fFontSize)
2526 	    selIndex = index;
2527 
2528     }
2529 
2530     if (selIndex >= 0) {
2531 	fSizesList->Select(selIndex);
2532 	fSizesList->ScrollToSelection();
2533     }
2534 
2535     fFamiliesList->SetSelectionMessage(new BMessage(kVimDialogFamilySelectMsg));
2536     fStylesList->SetSelectionMessage(new BMessage(kVimDialogStyleSelectMsg));
2537     fSizesList->SetSelectionMessage(new BMessage(kVimDialogSizeSelectMsg));
2538     fSizesInput->SetModificationMessage(new BMessage(kVimDialogSizeInputMsg));
2539 
2540     _UpdateSizeInputPreview();
2541     _UpdateFontPreview();
2542 
2543     fStatus = B_OK;
2544 }
2545 
~VimSelectFontDialog()2546 VimSelectFontDialog::~VimSelectFontDialog()
2547 {
2548     _CleanList(fFamiliesList);
2549     _CleanList(fStylesList);
2550     _CleanList(fSizesList);
2551 
2552     if (fDialogSem > B_OK)
2553 	delete_sem(fDialogSem);
2554 }
2555 
2556     void
_CleanList(BListView * list)2557 VimSelectFontDialog::_CleanList(BListView* list)
2558 {
2559     while (0 < list->CountItems())
2560 	delete (dynamic_cast<BStringItem*>(list->RemoveItem((int32)0)));
2561 }
2562 
2563     bool
Go()2564 VimSelectFontDialog::Go()
2565 {
2566     if (fStatus != B_OK) {
2567 	Quit();
2568 	return NOFONT;
2569     }
2570 
2571     fDialogSem = create_sem(0, "VimFontSelectDialogSem");
2572     if (fDialogSem < B_OK) {
2573 	Quit();
2574 	return fDialogValue;
2575     }
2576 
2577     Show();
2578 
2579     while (acquire_sem(fDialogSem) == B_INTERRUPTED);
2580 
2581     bool retValue = fDialogValue;
2582 
2583     if (Lock())
2584 	Quit();
2585 
2586     return retValue;
2587 }
2588 
2589 
_UpdateFontStyles()2590 void VimSelectFontDialog::_UpdateFontStyles()
2591 {
2592     _CleanList(fStylesList);
2593 
2594     int32 selIndex = -1;
2595     int32 count = count_font_styles(fFontFamily);
2596     for (int32 i = 0; i < count; i++) {
2597 	font_style style;
2598 	uint32 flags = 0;
2599 	if (get_font_style(fFontFamily, i, &style, &flags) == B_OK) {
2600 	    fStylesList->AddItem(new BStringItem((const char*)style));
2601 	    if (strncmp(style, fFontStyle, B_FONT_STYLE_LENGTH) == 0)
2602 		selIndex = i;
2603 	}
2604     }
2605 
2606     if (selIndex >= 0) {
2607 	fStylesList->Select(selIndex);
2608 	fStylesList->ScrollToSelection();
2609     } else
2610 	fStylesList->Select(0);
2611 }
2612 
2613 
_UpdateSizeInputPreview()2614 void VimSelectFontDialog::_UpdateSizeInputPreview()
2615 {
2616     char buf[10] = {0};
2617     vim_snprintf(buf, sizeof(buf), (char*)"%.0f", fFontSize);
2618     fSizesInput->SetText(buf);
2619 }
2620 
2621 
_UpdateFontPreview()2622 void VimSelectFontDialog::_UpdateFontPreview()
2623 {
2624     BFont font;
2625     fPreview->GetFont(&font);
2626     font.SetSize(fFontSize);
2627     font.SetFamilyAndStyle(fFontFamily, fFontStyle);
2628     fPreview->SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE);
2629 
2630     BString str;
2631     str << fFontFamily << " " << fFontStyle << ", " << (int)fFontSize << " pt.";
2632     fPreview->SetText(str);
2633 }
2634 
2635 
2636     bool
_UpdateFromListItem(BListView * list,char * text,int textSize)2637 VimSelectFontDialog::_UpdateFromListItem(BListView* list, char* text, int textSize)
2638 {
2639     int32 index = list->CurrentSelection();
2640     if (index < 0)
2641 	return false;
2642     BStringItem* item = (BStringItem*)list->ItemAt(index);
2643     if (item == NULL)
2644 	return false;
2645     strncpy(text, item->Text(), textSize);
2646     return true;
2647 }
2648 
2649 
MessageReceived(BMessage * msg)2650 void VimSelectFontDialog::MessageReceived(BMessage *msg)
2651 {
2652     switch (msg->what) {
2653 	case kVimDialogOKButtonMsg:
2654 	    strncpy(*fFamily, fFontFamily, B_FONT_FAMILY_LENGTH);
2655 	    strncpy(*fStyle, fFontStyle, B_FONT_STYLE_LENGTH);
2656 	    *fSize = fFontSize;
2657 	    fDialogValue = true;
2658 	case kVimDialogCancelButtonMsg:
2659 	    delete_sem(fDialogSem);
2660 	    fDialogSem = -1;
2661 	    return;
2662 	case B_KEY_UP:
2663 	    {
2664 		int32 key = 0;
2665 		if (msg->FindInt32("raw_char", &key) == B_OK
2666 			&& key == B_ESCAPE) {
2667 		    delete_sem(fDialogSem);
2668 		    fDialogSem = -1;
2669 		}
2670 	    }
2671 	    break;
2672 
2673 	case kVimDialogFamilySelectMsg:
2674 	    if (_UpdateFromListItem(fFamiliesList,
2675 		    fFontFamily, B_FONT_FAMILY_LENGTH)) {
2676 		_UpdateFontStyles();
2677 		_UpdateFontPreview();
2678 	    }
2679 	    break;
2680 	case kVimDialogStyleSelectMsg:
2681 	    if (_UpdateFromListItem(fStylesList,
2682 		    fFontStyle, B_FONT_STYLE_LENGTH))
2683 		_UpdateFontPreview();
2684 	    break;
2685 	case kVimDialogSizeSelectMsg:
2686 	    {
2687 		char buf[10] = {0};
2688 		if (_UpdateFromListItem(fSizesList, buf, sizeof(buf))) {
2689 		    float size = atof(buf);
2690 		    if (size > 0.f) {
2691 			fFontSize = size;
2692 			_UpdateSizeInputPreview();
2693 			_UpdateFontPreview();
2694 		    }
2695 		}
2696 	    }
2697 	    break;
2698 	case kVimDialogSizeInputMsg:
2699 	    {
2700 		float size = atof(fSizesInput->Text());
2701 		if (size > 0.f) {
2702 		    fFontSize = size;
2703 		    _UpdateFontPreview();
2704 		}
2705 	    }
2706 	    break;
2707 	default:
2708 	    break;
2709     }
2710     return BWindow::MessageReceived(msg);
2711 }
2712 
2713 #endif // FEAT_GUI_DIALOG
2714 
2715 #ifdef FEAT_TOOLBAR
2716 
2717 //  some forward declaration required by toolbar functions...
2718 static BMessage * MenuMessage(vimmenu_T *menu);
2719 
VimToolbar(BRect frame,const char * name)2720 VimToolbar::VimToolbar(BRect frame, const char *name) :
2721     BBox(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS, B_PLAIN_BORDER)
2722 {
2723 }
2724 
~VimToolbar()2725 VimToolbar::~VimToolbar()
2726 {
2727     int32 count = fButtonsList.CountItems();
2728     for (int32 i = 0; i < count; i++)
2729 	delete (BPictureButton*)fButtonsList.ItemAt(i);
2730     fButtonsList.MakeEmpty();
2731 
2732     delete normalButtonsBitmap;
2733     delete grayedButtonsBitmap;
2734     normalButtonsBitmap    = NULL;
2735     grayedButtonsBitmap  = NULL;
2736 }
2737 
2738     void
AttachedToWindow()2739 VimToolbar::AttachedToWindow()
2740 {
2741     BBox::AttachedToWindow();
2742 
2743     SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2744 }
2745 
2746     float
ToolbarHeight() const2747 VimToolbar::ToolbarHeight() const
2748 {
2749     float size = NULL == normalButtonsBitmap ? 18. : normalButtonsBitmap->Bounds().Height();
2750     return size + ToolbarMargin * 2 + ButtonMargin * 2 + 1;
2751 }
2752 
2753     bool
ModifyBitmapToGrayed(BBitmap * bitmap)2754 VimToolbar::ModifyBitmapToGrayed(BBitmap *bitmap)
2755 {
2756     float height = bitmap->Bounds().Height();
2757     float width  = bitmap->Bounds().Width();
2758 
2759     rgb_color *bits = (rgb_color*)bitmap->Bits();
2760     int32 pixels = bitmap->BitsLength() / 4;
2761     for (int32 i = 0; i < pixels; i++) {
2762 	bits[i].red = bits[i].green =
2763 	bits[i].blue = ((uint32)bits[i].red + bits[i].green + bits[i].blue) / 3;
2764 	bits[i].alpha /= 4;
2765     }
2766 
2767     return true;
2768 }
2769 
2770     bool
PrepareButtonBitmaps()2771 VimToolbar::PrepareButtonBitmaps()
2772 {
2773     //	first try to load potentially customized $VIRUNTIME/bitmaps/builtin-tools.png
2774     normalButtonsBitmap = LoadVimBitmap("builtin-tools.png");
2775     if (normalButtonsBitmap == NULL)
2776 	//  customized not found? dig application resources for "builtin-tools" one
2777 	normalButtonsBitmap = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "builtin-tools");
2778 
2779     if (normalButtonsBitmap == NULL)
2780 	return false;
2781 
2782     BMessage archive;
2783     normalButtonsBitmap->Archive(&archive);
2784 
2785     grayedButtonsBitmap = new BBitmap(&archive);
2786     if (grayedButtonsBitmap == NULL)
2787 	return false;
2788 
2789     //	modify grayed bitmap
2790     ModifyBitmapToGrayed(grayedButtonsBitmap);
2791 
2792     return true;
2793 }
2794 
LoadVimBitmap(const char * fileName)2795 BBitmap *VimToolbar::LoadVimBitmap(const char* fileName)
2796 {
2797     BBitmap *bitmap = NULL;
2798 
2799     int mustfree = 0;
2800     char_u* runtimePath = vim_getenv((char_u*)"VIMRUNTIME", &mustfree);
2801     if (runtimePath != NULL && fileName != NULL) {
2802 	BString strPath((char*)runtimePath);
2803 	strPath << "/bitmaps/" << fileName;
2804 	bitmap = BTranslationUtils::GetBitmap(strPath.String());
2805     }
2806 
2807     if (mustfree)
2808 	vim_free(runtimePath);
2809 
2810     return bitmap;
2811 }
2812 
2813     bool
GetPictureFromBitmap(BPicture * pictureTo,int32 index,BBitmap * bitmapFrom,bool pressed)2814 VimToolbar::GetPictureFromBitmap(BPicture *pictureTo, int32 index, BBitmap *bitmapFrom, bool pressed)
2815 {
2816     float size = bitmapFrom->Bounds().Height() + 1.;
2817 
2818     BView view(BRect(0, 0, size, size), "", 0, 0);
2819 
2820     AddChild(&view);
2821     view.BeginPicture(pictureTo);
2822 
2823     view.SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2824     view.FillRect(view.Bounds());
2825     view.SetDrawingMode(B_OP_OVER);
2826 
2827     BRect source(0, 0, size - 1, size - 1);
2828     BRect destination(source);
2829 
2830     source.OffsetBy(size * index, 0);
2831     destination.OffsetBy(ButtonMargin, ButtonMargin);
2832 
2833     view.DrawBitmap(bitmapFrom, source, destination);
2834 
2835     if (pressed)	{
2836 	rgb_color shineColor  = ui_color(B_SHINE_COLOR);
2837 	rgb_color shadowColor = ui_color(B_SHADOW_COLOR);
2838 	size += ButtonMargin * 2 - 1;
2839 	view.BeginLineArray(4);
2840 	view.AddLine(BPoint(0, 0),	 BPoint(size, 0),    shadowColor);
2841 	view.AddLine(BPoint(size, 0),	 BPoint(size, size), shineColor);
2842 	view.AddLine(BPoint(size, size), BPoint(0, size),    shineColor);
2843 	view.AddLine(BPoint(0, size),	 BPoint(0, 0),	     shadowColor);
2844 	view.EndLineArray();
2845     }
2846 
2847     view.EndPicture();
2848     RemoveChild(&view);
2849 
2850     return true;
2851 }
2852 
2853     bool
AddButton(int32 index,vimmenu_T * menu)2854 VimToolbar::AddButton(int32 index, vimmenu_T *menu)
2855 {
2856     BPictureButton *button = NULL;
2857     if (!menu_is_separator(menu->name)) {
2858 	float size = normalButtonsBitmap ?
2859 	    normalButtonsBitmap->Bounds().Height() + 1. + ButtonMargin * 2 : 18.;
2860 	BRect frame(0, 0, size, size);
2861 	BPicture pictureOn;
2862 	BPicture pictureOff;
2863 	BPicture pictureGray;
2864 
2865 	if (menu->iconfile == NULL && menu->iconidx >= 0 && normalButtonsBitmap) {
2866 	    GetPictureFromBitmap(&pictureOn,  menu->iconidx, normalButtonsBitmap, true);
2867 	    GetPictureFromBitmap(&pictureOff, menu->iconidx, normalButtonsBitmap, false);
2868 	    GetPictureFromBitmap(&pictureGray, menu->iconidx, grayedButtonsBitmap, false);
2869 	} else {
2870 
2871 	    char_u buffer[MAXPATHL] = {0};
2872 	    BBitmap *bitmap = NULL;
2873 
2874 	    if (menu->iconfile) {
2875 		gui_find_iconfile(menu->iconfile, buffer, (char*)"png");
2876 		bitmap = BTranslationUtils::GetBitmap((char*)buffer);
2877 	    }
2878 
2879 	    if (bitmap == NULL && gui_find_bitmap(menu->name, buffer, (char*)"png") == OK)
2880 		bitmap = BTranslationUtils::GetBitmap((char*)buffer);
2881 
2882 	    if (bitmap == NULL)
2883 		bitmap = new BBitmap(BRect(0, 0, size, size), B_RGB32);
2884 
2885 	    GetPictureFromBitmap(&pictureOn,   0, bitmap, true);
2886 	    GetPictureFromBitmap(&pictureOff,  0, bitmap, false);
2887 	    ModifyBitmapToGrayed(bitmap);
2888 	    GetPictureFromBitmap(&pictureGray, 0, bitmap, false);
2889 
2890 	    delete bitmap;
2891 	}
2892 
2893 	button = new BPictureButton(frame, (char*)menu->name,
2894 		    &pictureOff, &pictureOn, MenuMessage(menu));
2895 
2896 	button->SetDisabledOn(&pictureGray);
2897 	button->SetDisabledOff(&pictureGray);
2898 
2899 	button->SetTarget(gui.vimTextArea);
2900 
2901 	AddChild(button);
2902 
2903 	menu->button = button;
2904     }
2905 
2906     bool result = fButtonsList.AddItem(button, index);
2907     InvalidateLayout();
2908     return result;
2909 }
2910 
2911     bool
RemoveButton(vimmenu_T * menu)2912 VimToolbar::RemoveButton(vimmenu_T *menu)
2913 {
2914     if (menu->button) {
2915 	if (fButtonsList.RemoveItem(menu->button)) {
2916 	    delete menu->button;
2917 	    menu->button = NULL;
2918 	}
2919     }
2920     return true;
2921 }
2922 
2923     bool
GrayButton(vimmenu_T * menu,int grey)2924 VimToolbar::GrayButton(vimmenu_T *menu, int grey)
2925 {
2926     if (menu->button) {
2927 	int32 index = fButtonsList.IndexOf(menu->button);
2928 	if (index >= 0)
2929 	    menu->button->SetEnabled(grey ? false : true);
2930     }
2931     return true;
2932 }
2933 
2934     void
InvalidateLayout()2935 VimToolbar::InvalidateLayout()
2936 {
2937     int32 offset = ToolbarMargin;
2938     int32 count = fButtonsList.CountItems();
2939     for (int32 i = 0; i < count; i++) {
2940 	BPictureButton *button = (BPictureButton *)fButtonsList.ItemAt(i);
2941 	if (button) {
2942 	    button->MoveTo(offset, ToolbarMargin);
2943 	    offset += button->Bounds().Width() + ToolbarMargin;
2944 	} else
2945 	    offset += ToolbarMargin * 3;
2946     }
2947 }
2948 
2949 #endif /*FEAT_TOOLBAR*/
2950 
2951 #if defined(FEAT_GUI_TABLINE)
2952 
2953     float
TablineHeight() const2954 VimTabLine::TablineHeight() const
2955 {
2956 //  float size = NULL == normalButtonsBitmap ? 18. : normalButtonsBitmap->Bounds().Height();
2957 //  return size + ToolbarMargin * 2 + ButtonMargin * 2 + 1;
2958     return TabHeight(); //  + ToolbarMargin;
2959 }
2960 
2961 void
MouseDown(BPoint point)2962 VimTabLine::MouseDown(BPoint point)
2963 {
2964     if (!gui_mch_showing_tabline())
2965 	return;
2966 
2967     BMessage *m = Window()->CurrentMessage();
2968     assert(m);
2969 
2970     int32 buttons = 0;
2971     m->FindInt32("buttons", &buttons);
2972 
2973     int32 clicks = 0;
2974     m->FindInt32("clicks", &clicks);
2975 
2976     int index = 0; //  0 means here - no tab found
2977     for (int i = 0; i < CountTabs(); i++) {
2978 	if (TabFrame(i).Contains(point)) {
2979 	    index = i + 1; //  indexes are 1-based
2980 	    break;
2981 	}
2982     }
2983 
2984     int event = -1;
2985 
2986     if ((buttons & B_PRIMARY_MOUSE_BUTTON) && clicks > 1)
2987 	//  left button double click on - create new tab
2988 	event = TABLINE_MENU_NEW;
2989 
2990     else if (buttons & B_TERTIARY_MOUSE_BUTTON)
2991 	//  middle button click - close the pointed tab
2992 	//  or create new one in case empty space
2993 	event = index > 0 ? TABLINE_MENU_CLOSE : TABLINE_MENU_NEW;
2994 
2995     else if (buttons & B_SECONDARY_MOUSE_BUTTON) {
2996 	//  right button click - show context menu
2997 	BPopUpMenu* popUpMenu = new BPopUpMenu("tabLineContextMenu", false, false);
2998 	popUpMenu->AddItem(new BMenuItem(_("Close tabi R"), new BMessage(TABLINE_MENU_CLOSE)));
2999 	popUpMenu->AddItem(new BMenuItem(_("New tab    T"), new BMessage(TABLINE_MENU_NEW)));
3000 	popUpMenu->AddItem(new BMenuItem(_("Open tab..."), new BMessage(TABLINE_MENU_OPEN)));
3001 
3002 	ConvertToScreen(&point);
3003 	BMenuItem* item = popUpMenu->Go(point);
3004 	if (item != NULL) {
3005 	    event = item->Command();
3006 	}
3007 
3008 	delete popUpMenu;
3009 
3010     } else {
3011 	//  default processing
3012 	BTabView::MouseDown(point);
3013 	return;
3014     }
3015 
3016     if (event < 0)
3017 	return;
3018 
3019     VimTablineMenuMsg tmm;
3020     tmm.index = index;
3021     tmm.event = event;
3022     write_port(gui.vdcmp, VimMsg::TablineMenu, &tmm, sizeof(tmm));
3023 }
3024 
3025 void
Select(BView * owner)3026 VimTabLine::VimTab::Select(BView* owner)
3027 {
3028     BTab::Select(owner);
3029 
3030     VimTabLine *tabLine = gui.vimForm->TabLine();
3031     if (tabLine != NULL) {
3032 
3033 	int32 i = 0;
3034 	for (; i < tabLine->CountTabs(); i++)
3035 	    if (this == tabLine->TabAt(i))
3036 		break;
3037 
3038 //	printf("%d:%d:%s\n", i, tabLine->CountTabs(), tabLine->TabAt(i)->Label());
3039 	if (i < tabLine->CountTabs()) {
3040 	    VimTablineMsg tm;
3041 	    tm.index = i + 1;
3042 	    write_port(gui.vdcmp, VimMsg::Tabline, &tm, sizeof(tm));
3043 	}
3044     }
3045 }
3046 
3047 #endif //  defined(FEAT_GUI_TABLINE)
3048 
3049 // ---------------- ----------------
3050 
3051 //  some global variables
3052 static char appsig[] = "application/x-vnd.Haiku-Vim-8";
3053 key_map *keyMap;
3054 char *keyMapChars;
3055 int main_exitcode = 127;
3056 
3057     status_t
gui_haiku_process_event(bigtime_t timeout)3058 gui_haiku_process_event(bigtime_t timeout)
3059 {
3060     struct VimMsg vm;
3061     int32 what;
3062     ssize_t size;
3063 
3064     size = read_port_etc(gui.vdcmp, &what, &vm, sizeof(vm),
3065 	    B_TIMEOUT, timeout);
3066 
3067     if (size >= 0) {
3068 	switch (what) {
3069 	    case VimMsg::Key:
3070 		{
3071 		    char_u *string = vm.u.Key.chars;
3072 		    int len = vm.u.Key.length;
3073 		    if (len == 1 && string[0] == Ctrl_chr('C')) {
3074 			trash_input_buf();
3075 			got_int = TRUE;
3076 		    }
3077 
3078 		    if (vm.u.Key.csi_escape)
3079 #ifndef FEAT_MBYTE_IME
3080 		    {
3081 			int	i;
3082 			char_u	buf[2];
3083 
3084 			for (i = 0; i < len; ++i)
3085 			{
3086 			    add_to_input_buf(string + i, 1);
3087 			    if (string[i] == CSI)
3088 			    {
3089 				// Turn CSI into K_CSI.
3090 				buf[0] = KS_EXTRA;
3091 				buf[1] = (int)KE_CSI;
3092 				add_to_input_buf(buf, 2);
3093 			    }
3094 			}
3095 		    }
3096 #else
3097 			add_to_input_buf_csi(string, len);
3098 #endif
3099 		    else
3100 			add_to_input_buf(string, len);
3101 		}
3102 		break;
3103 	    case VimMsg::Resize:
3104 		gui_resize_shell(vm.u.NewSize.width, vm.u.NewSize.height);
3105 		break;
3106 	    case VimMsg::ScrollBar:
3107 		{
3108 		    /*
3109 		     * If loads of scroll messages queue up, use only the last
3110 		     * one. Always report when the scrollbar stops dragging.
3111 		     * This is not perfect yet anyway: these events are queued
3112 		     * yet again, this time in the keyboard input buffer.
3113 		     */
3114 		    int32 oldCount =
3115 			atomic_add(&vm.u.Scroll.sb->scrollEventCount, -1);
3116 		    if (oldCount <= 1 || !vm.u.Scroll.stillDragging)
3117 			gui_drag_scrollbar(vm.u.Scroll.sb->getGsb(),
3118 				vm.u.Scroll.value, vm.u.Scroll.stillDragging);
3119 		}
3120 		break;
3121 #if defined(FEAT_MENU)
3122 	    case VimMsg::Menu:
3123 		gui_menu_cb(vm.u.Menu.guiMenu);
3124 		break;
3125 #endif
3126 	    case VimMsg::Mouse:
3127 		{
3128 		    int32 oldCount;
3129 		    if (vm.u.Mouse.button == MOUSE_DRAG)
3130 			oldCount =
3131 			    atomic_add(&gui.vimTextArea->mouseDragEventCount, -1);
3132 		    else
3133 			oldCount = 0;
3134 		    if (oldCount <= 1)
3135 			gui_send_mouse_event(vm.u.Mouse.button, vm.u.Mouse.x,
3136 				vm.u.Mouse.y, vm.u.Mouse.repeated_click,
3137 				vm.u.Mouse.modifiers);
3138 		}
3139 		break;
3140 	    case VimMsg::MouseMoved:
3141 		{
3142 		    gui_mouse_moved(vm.u.MouseMoved.x, vm.u.MouseMoved.y);
3143 		}
3144 		break;
3145 	    case VimMsg::Focus:
3146 		gui.in_focus = vm.u.Focus.active;
3147 		// XXX Signal that scrollbar dragging has stopped?
3148 		// This is needed because we don't get a MouseUp if
3149 		// that happens while outside the window... :-(
3150 		if (gui.dragged_sb) {
3151 		    gui.dragged_sb = SBAR_NONE;
3152 		}
3153 		//  gui_update_cursor(TRUE, FALSE);
3154 		break;
3155 	    case VimMsg::Refs:
3156 		::RefsReceived(vm.u.Refs.message, vm.u.Refs.changedir);
3157 		break;
3158 	    case VimMsg::Tabline:
3159 		send_tabline_event(vm.u.Tabline.index);
3160 		break;
3161 	    case VimMsg::TablineMenu:
3162 		send_tabline_menu_event(vm.u.TablineMenu.index, vm.u.TablineMenu.event);
3163 		break;
3164 	    default:
3165 		//  unrecognised message, ignore it
3166 		break;
3167 	}
3168     }
3169 
3170     /*
3171      * If size < B_OK, it is an error code.
3172      */
3173     return size;
3174 }
3175 
3176 /*
3177  * Here are some functions to protect access to ScreenLines[] and
3178  * LineOffset[]. These are used from the window thread to respond
3179  * to a Draw() callback. When that occurs, the window is already
3180  * locked by the system.
3181  *
3182  * Other code that needs to lock is any code that changes these
3183  * variables. Other read-only access, or access merely to the
3184  * contents of the screen buffer, need not be locked.
3185  *
3186  * If there is no window, don't call Lock() but do succeed.
3187  */
3188 
3189     int
vim_lock_screen()3190 vim_lock_screen()
3191 {
3192     return !gui.vimWindow || gui.vimWindow->Lock();
3193 }
3194 
3195     void
vim_unlock_screen()3196 vim_unlock_screen()
3197 {
3198     if (gui.vimWindow)
3199 	gui.vimWindow->Unlock();
3200 }
3201 
3202 #define RUN_BAPPLICATION_IN_NEW_THREAD	0
3203 
3204 #if RUN_BAPPLICATION_IN_NEW_THREAD
3205 
3206     int32
run_vimapp(void * args)3207 run_vimapp(void *args)
3208 {
3209     VimApp app(appsig);
3210 
3211     gui.vimApp = &app;
3212     app.Run();		    // Run until Quit() called
3213 
3214     return 0;
3215 }
3216 
3217 #else
3218 
3219     int32
call_main(void * args)3220 call_main(void *args)
3221 {
3222     struct MainArgs *ma = (MainArgs *)args;
3223 
3224     return main(ma->argc, ma->argv);
3225 }
3226 #endif
3227 
3228 /*
3229  * Parse the GUI related command-line arguments.  Any arguments used are
3230  * deleted from argv, and *argc is decremented accordingly.  This is called
3231  * when vim is started, whether or not the GUI has been started.
3232  */
3233     void
gui_mch_prepare(int * argc,char ** argv)3234 gui_mch_prepare(
3235 	int	*argc,
3236 	char	**argv)
3237 {
3238     /*
3239      * We don't have any command line arguments for the BeOS GUI yet,
3240      * but this is an excellent place to create our Application object.
3241      */
3242     if (!gui.vimApp) {
3243 	thread_info tinfo;
3244 	get_thread_info(find_thread(NULL), &tinfo);
3245 
3246 	// May need the port very early on to process RefsReceived()
3247 	gui.vdcmp = create_port(B_MAX_PORT_COUNT, "vim VDCMP");
3248 
3249 #if RUN_BAPPLICATION_IN_NEW_THREAD
3250 	thread_id tid = spawn_thread(run_vimapp, "vim VimApp",
3251 		tinfo.priority, NULL);
3252 	if (tid >= B_OK) {
3253 	    resume_thread(tid);
3254 	} else {
3255 	    getout(1);
3256 	}
3257 #else
3258 	MainArgs ma = { *argc, argv };
3259 	thread_id tid = spawn_thread(call_main, "vim main()",
3260 		tinfo.priority, &ma);
3261 	if (tid >= B_OK) {
3262 	    VimApp app(appsig);
3263 
3264 	    gui.vimApp = &app;
3265 	    resume_thread(tid);
3266 	    /*
3267 	     * This is rather horrible.
3268 	     * call_main will call main() again...
3269 	     * There will be no infinite recursion since
3270 	     * gui.vimApp is set now.
3271 	     */
3272 	    app.Run();		    // Run until Quit() called
3273 	    // fprintf(stderr, "app.Run() returned...\n");
3274 	    status_t dummy_exitcode;
3275 	    (void)wait_for_thread(tid, &dummy_exitcode);
3276 
3277 	    /*
3278 	     * This path should be the normal one taken to exit Vim.
3279 	     * The main() thread calls mch_exit() which calls
3280 	     * gui_mch_exit() which terminates its thread.
3281 	     */
3282 	    exit(main_exitcode);
3283 	}
3284 #endif
3285     }
3286     // Don't fork() when starting the GUI. Spawned threads are not
3287     // duplicated with a fork(). The result is a mess.
3288     gui.dofork = FALSE;
3289     /*
3290      * XXX Try to determine whether we were started from
3291      * the Tracker or the terminal.
3292      * It would be nice to have this work, because the Tracker
3293      * follows symlinks, so even if you double-click on gvim,
3294      * when it is a link to vim it will still pass a command name
3295      * of vim...
3296      * We try here to see if stdin comes from /dev/null. If so,
3297      * (or if there is an error, which should never happen) start the GUI.
3298      * This does the wrong thing for vim - </dev/null, and we're
3299      * too early to see the command line parsing. Tough.
3300      * On the other hand, it starts the gui for vim file & which is nice.
3301      */
3302     if (!isatty(0)) {
3303 	struct stat stat_stdin, stat_dev_null;
3304 
3305 	if (fstat(0, &stat_stdin) == -1 ||
3306 		stat("/dev/null", &stat_dev_null) == -1 ||
3307 		(stat_stdin.st_dev == stat_dev_null.st_dev &&
3308 		 stat_stdin.st_ino == stat_dev_null.st_ino))
3309 	    gui.starting = TRUE;
3310     }
3311 }
3312 
3313 /*
3314  * Check if the GUI can be started.  Called before gvimrc is sourced.
3315  * Return OK or FAIL.
3316  */
3317     int
gui_mch_init_check(void)3318 gui_mch_init_check(void)
3319 {
3320     return OK;	    // TODO: GUI can always be started?
3321 }
3322 
3323 /*
3324  * Initialise the GUI.	Create all the windows, set up all the call-backs
3325  * etc.
3326  */
3327     int
gui_mch_init()3328 gui_mch_init()
3329 {
3330     display_errors();
3331     gui.def_norm_pixel = RGB(0x00, 0x00, 0x00);	//  black
3332     gui.def_back_pixel = RGB(0xFF, 0xFF, 0xFF);	//  white
3333     gui.norm_pixel = gui.def_norm_pixel;
3334     gui.back_pixel = gui.def_back_pixel;
3335 
3336     gui.scrollbar_width = (int) B_V_SCROLL_BAR_WIDTH;
3337     gui.scrollbar_height = (int) B_H_SCROLL_BAR_HEIGHT;
3338 #ifdef FEAT_MENU
3339     gui.menu_height = 19;   //	initial guess -
3340     //	correct for my default settings
3341 #endif
3342     gui.border_offset = 3;  //	coordinates are inside window borders
3343 
3344     if (gui.vdcmp < B_OK)
3345 	return FAIL;
3346     get_key_map(&keyMap, &keyMapChars);
3347 
3348     gui.vimWindow = new VimWindow();	// hidden and locked
3349     if (!gui.vimWindow)
3350 	return FAIL;
3351 
3352     gui.vimWindow->Run();	// Run() unlocks but does not show
3353 
3354     // Get the colors from the "Normal" group (set in syntax.c or in a vimrc
3355     // file)
3356     set_normal_colors();
3357 
3358     /*
3359      * Check that none of the colors are the same as the background color
3360      */
3361     gui_check_colors();
3362 
3363     // Get the colors for the highlight groups (gui_check_colors() might have
3364     // changed them)
3365     highlight_gui_started();	    // re-init colors and fonts
3366 
3367     gui_mch_new_colors();	// window must exist for this
3368 
3369     return OK;
3370 }
3371 
3372 /*
3373  * Called when the foreground or background color has been changed.
3374  */
3375     void
gui_mch_new_colors()3376 gui_mch_new_colors()
3377 {
3378     rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
3379 
3380     if (gui.vimWindow->Lock()) {
3381 	gui.vimForm->SetViewColor(rgb);
3382 	//  Does this not have too much effect for those small rectangles?
3383 	gui.vimForm->Invalidate();
3384 	gui.vimWindow->Unlock();
3385     }
3386 }
3387 
3388 /*
3389  * Open the GUI window which was created by a call to gui_mch_init().
3390  */
3391     int
gui_mch_open()3392 gui_mch_open()
3393 {
3394     if (gui_win_x != -1 && gui_win_y != -1)
3395 	gui_mch_set_winpos(gui_win_x, gui_win_y);
3396 
3397     // Actually open the window
3398     if (gui.vimWindow->Lock()) {
3399 	gui.vimWindow->Show();
3400 	gui.vimWindow->Unlock();
3401 	return OK;
3402     }
3403 
3404     return FAIL;
3405 }
3406 
3407     void
gui_mch_exit(int vim_exitcode)3408 gui_mch_exit(int vim_exitcode)
3409 {
3410     if (gui.vimWindow) {
3411 	thread_id tid = gui.vimWindow->Thread();
3412 	gui.vimWindow->Lock();
3413 	gui.vimWindow->Quit();
3414 	// Wait until it is truly gone
3415 	int32 exitcode;
3416 	wait_for_thread(tid, &exitcode);
3417     }
3418     delete_port(gui.vdcmp);
3419 #if !RUN_BAPPLICATION_IN_NEW_THREAD
3420     /*
3421      * We are in the main() thread - quit the App thread and
3422      * quit ourselves (passing on the exitcode). Use a global since the
3423      * value from exit_thread() is only used if wait_for_thread() is
3424      * called in time (race condition).
3425      */
3426 #endif
3427     if (gui.vimApp) {
3428 	VimTextAreaView::guiBlankMouse(false);
3429 
3430 	main_exitcode = vim_exitcode;
3431 #if RUN_BAPPLICATION_IN_NEW_THREAD
3432 	thread_id tid = gui.vimApp->Thread();
3433 	int32 exitcode;
3434 	gui.vimApp->Lock();
3435 	gui.vimApp->Quit();
3436 	gui.vimApp->Unlock();
3437 	wait_for_thread(tid, &exitcode);
3438 #else
3439 	gui.vimApp->Lock();
3440 	gui.vimApp->Quit();
3441 	gui.vimApp->Unlock();
3442 	// suicide
3443 	exit_thread(vim_exitcode);
3444 #endif
3445     }
3446     // If we are somehow still here, let mch_exit() handle things.
3447 }
3448 
3449 /*
3450  * Get the position of the top left corner of the window.
3451  */
3452     int
gui_mch_get_winpos(int * x,int * y)3453 gui_mch_get_winpos(int *x, int *y)
3454 {
3455     if (gui.vimWindow->Lock()) {
3456 	BRect r;
3457 	r = gui.vimWindow->Frame();
3458 	gui.vimWindow->Unlock();
3459 	*x = (int)r.left;
3460 	*y = (int)r.top;
3461 	return OK;
3462     }
3463     else
3464 	return FAIL;
3465 }
3466 
3467 /*
3468  * Set the position of the top left corner of the window to the given
3469  * coordinates.
3470  */
3471     void
gui_mch_set_winpos(int x,int y)3472 gui_mch_set_winpos(int x, int y)
3473 {
3474     if (gui.vimWindow->Lock()) {
3475 	gui.vimWindow->MoveTo(x, y);
3476 	gui.vimWindow->Unlock();
3477     }
3478 }
3479 
3480 /*
3481  * Set the size of the window to the given width and height in pixels.
3482  */
3483 void
gui_mch_set_shellsize(int width,int height,int min_width,int min_height,int base_width,int base_height,int direction)3484 gui_mch_set_shellsize(
3485 	int	width,
3486 	int	height,
3487 	int	min_width,
3488 	int	min_height,
3489 	int	base_width,
3490 	int	base_height,
3491 	int	direction) // TODO: utilize?
3492 {
3493     /*
3494      * We are basically given the size of the VimForm, if I understand
3495      * correctly. Since it fills the window completely, this will also
3496      * be the size of the window.
3497      */
3498     if (gui.vimWindow->Lock()) {
3499 	gui.vimWindow->ResizeTo(width - PEN_WIDTH, height - PEN_WIDTH);
3500 
3501 	// set size limits
3502 	float minWidth, maxWidth, minHeight, maxHeight;
3503 
3504 	gui.vimWindow->GetSizeLimits(&minWidth, &maxWidth,
3505 		&minHeight, &maxHeight);
3506 	gui.vimWindow->SetSizeLimits(min_width, maxWidth,
3507 		min_height, maxHeight);
3508 
3509 	/*
3510 	 * Set the resizing alignment depending on font size.
3511 	 */
3512 	gui.vimWindow->SetWindowAlignment(
3513 		B_PIXEL_ALIGNMENT,	//  window_alignment mode,
3514 		1,		//  int32 h,
3515 		0,		//  int32 hOffset = 0,
3516 		gui.char_width,	    //	int32 width = 0,
3517 		base_width,	    //	int32 widthOffset = 0,
3518 		1,		//  int32 v = 0,
3519 		0,		//  int32 vOffset = 0,
3520 		gui.char_height,	//  int32 height = 0,
3521 		base_height	    //	int32 heightOffset = 0
3522 		);
3523 
3524 	gui.vimWindow->Unlock();
3525     }
3526 }
3527 
3528 void
gui_mch_get_screen_dimensions(int * screen_w,int * screen_h)3529 gui_mch_get_screen_dimensions(
3530 	int	*screen_w,
3531 	int	*screen_h)
3532 {
3533     BRect frame;
3534 
3535     {
3536 	BScreen screen(gui.vimWindow);
3537 
3538 	if (screen.IsValid()) {
3539 	    frame = screen.Frame();
3540 	} else {
3541 	    frame.right = 640;
3542 	    frame.bottom = 480;
3543 	}
3544     }
3545 
3546     // XXX approximations...
3547     *screen_w = (int) frame.right - 2 * gui.scrollbar_width - 20;
3548     *screen_h = (int) frame.bottom - gui.scrollbar_height
3549 #ifdef FEAT_MENU
3550 	- gui.menu_height
3551 #endif
3552 	- 30;
3553 }
3554 
3555 void
gui_mch_set_text_area_pos(int x,int y,int w,int h)3556 gui_mch_set_text_area_pos(
3557 	int	x,
3558 	int	y,
3559 	int	w,
3560 	int	h)
3561 {
3562     if (!gui.vimTextArea)
3563 	return;
3564 
3565     if (gui.vimWindow->Lock()) {
3566 	gui.vimTextArea->MoveTo(x, y);
3567 	gui.vimTextArea->ResizeTo(w - PEN_WIDTH, h - PEN_WIDTH);
3568 
3569 #ifdef FEAT_GUI_TABLINE
3570 	if (gui.vimForm->TabLine() != NULL) {
3571 	    gui.vimForm->TabLine()->ResizeTo(w, gui.vimForm->TablineHeight());
3572 	}
3573 #endif // FEAT_GUI_TABLINE
3574 
3575 	gui.vimWindow->Unlock();
3576     }
3577 }
3578 
3579 
3580 /*
3581  * Scrollbar stuff:
3582  */
3583 
3584 void
gui_mch_enable_scrollbar(scrollbar_T * sb,int flag)3585 gui_mch_enable_scrollbar(
3586 	scrollbar_T *sb,
3587 	int	flag)
3588 {
3589     VimScrollBar *vsb = sb->id;
3590     if (gui.vimWindow->Lock()) {
3591 	/*
3592 	 * This function is supposed to be idempotent, but Show()/Hide()
3593 	 * is not. Therefore we test if they are needed.
3594 	 */
3595 	if (flag) {
3596 	    if (vsb->IsHidden()) {
3597 		vsb->Show();
3598 	    }
3599 	} else {
3600 	    if (!vsb->IsHidden()) {
3601 		vsb->Hide();
3602 	    }
3603 	}
3604 	gui.vimWindow->Unlock();
3605     }
3606 }
3607 
3608 void
gui_mch_set_scrollbar_thumb(scrollbar_T * sb,int val,int size,int max)3609 gui_mch_set_scrollbar_thumb(
3610 	scrollbar_T *sb,
3611 	int	val,
3612 	int	size,
3613 	int	max)
3614 {
3615     if (gui.vimWindow->Lock()) {
3616 	VimScrollBar *s = sb->id;
3617 	if (max == 0) {
3618 	    s->SetValue(0);
3619 	    s->SetRange(0.0, 0.0);
3620 	} else {
3621 	    s->SetProportion((float)size / (max + 1.0));
3622 	    s->SetSteps(1.0, size > 5 ? size - 2 : size);
3623 #ifndef SCROLL_PAST_END	    //	really only defined in gui.c...
3624 	    max = max + 1 - size;
3625 #endif
3626 	    if (max < s->Value()) {
3627 		/*
3628 		 * If the new maximum is lower than the current value,
3629 		 * setting it would cause the value to be clipped and
3630 		 * therefore a ValueChanged() call.
3631 		 * We avoid this by setting the value first, because
3632 		 * it presumably is <= max.
3633 		 */
3634 		s->SetValue(val);
3635 		s->SetRange(0.0, max);
3636 	    } else {
3637 		/*
3638 		 * In the other case, set the range first, since the
3639 		 * new value might be higher than the current max.
3640 		 */
3641 		s->SetRange(0.0, max);
3642 		s->SetValue(val);
3643 	    }
3644 	}
3645 	gui.vimWindow->Unlock();
3646     }
3647 }
3648 
3649 void
gui_mch_set_scrollbar_pos(scrollbar_T * sb,int x,int y,int w,int h)3650 gui_mch_set_scrollbar_pos(
3651 	scrollbar_T *sb,
3652 	int	x,
3653 	int	y,
3654 	int	w,
3655 	int	h)
3656 {
3657     if (gui.vimWindow->Lock()) {
3658 	BRect winb = gui.vimWindow->Bounds();
3659 	float vsbx = x, vsby = y;
3660 	VimScrollBar *vsb = sb->id;
3661 	vsb->ResizeTo(w - PEN_WIDTH, h - PEN_WIDTH);
3662 	if (winb.right-(x+w)<w) vsbx = winb.right - (w - PEN_WIDTH);
3663 	vsb->MoveTo(vsbx, vsby);
3664 	gui.vimWindow->Unlock();
3665     }
3666 }
3667 
3668     int
gui_mch_get_scrollbar_xpadding(void)3669 gui_mch_get_scrollbar_xpadding(void)
3670 {
3671     // TODO: Calculate the padding for adjust scrollbar position when the
3672     // Window is maximized.
3673     return 0;
3674 }
3675 
3676     int
gui_mch_get_scrollbar_ypadding(void)3677 gui_mch_get_scrollbar_ypadding(void)
3678 {
3679     // TODO: Calculate the padding for adjust scrollbar position when the
3680     // Window is maximized.
3681     return 0;
3682 }
3683 
3684 void
gui_mch_create_scrollbar(scrollbar_T * sb,int orient)3685 gui_mch_create_scrollbar(
3686 	scrollbar_T *sb,
3687 	int	orient)	    // SBAR_VERT or SBAR_HORIZ
3688 {
3689     orientation posture =
3690 	(orient == SBAR_HORIZ) ? B_HORIZONTAL : B_VERTICAL;
3691 
3692     VimScrollBar *vsb = sb->id = new VimScrollBar(sb, posture);
3693     if (gui.vimWindow->Lock()) {
3694 	vsb->SetTarget(gui.vimTextArea);
3695 	vsb->Hide();
3696 	gui.vimForm->AddChild(vsb);
3697 	gui.vimWindow->Unlock();
3698     }
3699 }
3700 
3701 #if defined(FEAT_WINDOWS) || defined(FEAT_GUI_HAIKU) || defined(PROTO)
3702 void
gui_mch_destroy_scrollbar(scrollbar_T * sb)3703 gui_mch_destroy_scrollbar(
3704 	scrollbar_T *sb)
3705 {
3706     if (gui.vimWindow->Lock()) {
3707 	sb->id->RemoveSelf();
3708 	delete sb->id;
3709 	gui.vimWindow->Unlock();
3710     }
3711 }
3712 #endif
3713 
3714 /*
3715  * Cursor does not flash
3716  */
3717     int
gui_mch_is_blink_off(void)3718 gui_mch_is_blink_off(void)
3719 {
3720     return FALSE;
3721 }
3722 
3723 /*
3724  * Cursor blink functions.
3725  *
3726  * This is a simple state machine:
3727  * BLINK_NONE	not blinking at all
3728  * BLINK_OFF	blinking, cursor is not shown
3729  * BLINK_ON blinking, cursor is shown
3730  */
3731 
3732 #define BLINK_NONE  0
3733 #define BLINK_OFF   1
3734 #define BLINK_ON    2
3735 
3736 static int	blink_state = BLINK_NONE;
3737 static long_u	    blink_waittime = 700;
3738 static long_u	    blink_ontime = 400;
3739 static long_u	    blink_offtime = 250;
3740 static int  blink_timer = 0;
3741 
3742 void
gui_mch_set_blinking(long waittime,long on,long off)3743 gui_mch_set_blinking(
3744 	long	waittime,
3745 	long	on,
3746 	long	off)
3747 {
3748     // TODO
3749     blink_waittime = waittime;
3750     blink_ontime = on;
3751     blink_offtime = off;
3752 }
3753 
3754 /*
3755  * Stop the cursor blinking.  Show the cursor if it wasn't shown.
3756  */
3757     void
gui_mch_stop_blink(int may_call_gui_update_cursor)3758 gui_mch_stop_blink(int may_call_gui_update_cursor)
3759 {
3760     // TODO
3761     if (blink_timer != 0)
3762     {
3763 	// XtRemoveTimeOut(blink_timer);
3764 	blink_timer = 0;
3765     }
3766     if (blink_state == BLINK_OFF)
3767 	gui_update_cursor(TRUE, FALSE);
3768     blink_state = BLINK_NONE;
3769 }
3770 
3771 /*
3772  * Start the cursor blinking.  If it was already blinking, this restarts the
3773  * waiting time and shows the cursor.
3774  */
3775     void
gui_mch_start_blink()3776 gui_mch_start_blink()
3777 {
3778     // TODO
3779     if (blink_timer != 0)
3780 	;// XtRemoveTimeOut(blink_timer);
3781     // Only switch blinking on if none of the times is zero
3782     if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
3783     {
3784 	blink_timer = 1; // XtAppAddTimeOut(app_context, blink_waittime,
3785 	blink_state = BLINK_ON;
3786 	gui_update_cursor(TRUE, FALSE);
3787     }
3788 }
3789 
3790 /*
3791  * Initialise vim to use the font with the given name.	Return FAIL if the font
3792  * could not be loaded, OK otherwise.
3793  */
3794 int
gui_mch_init_font(char_u * font_name,int fontset)3795 gui_mch_init_font(
3796 	char_u	    *font_name,
3797 	int	    fontset)
3798 {
3799     if (gui.vimWindow->Lock())
3800     {
3801 	int rc = gui.vimTextArea->mchInitFont(font_name);
3802 	gui.vimWindow->Unlock();
3803 
3804 	return rc;
3805     }
3806 
3807     return FAIL;
3808 }
3809 
3810 
3811     int
gui_mch_adjust_charsize()3812 gui_mch_adjust_charsize()
3813 {
3814     return FAIL;
3815 }
3816 
3817 
3818     int
gui_mch_font_dialog(font_family * family,font_style * style,float * size)3819 gui_mch_font_dialog(font_family* family, font_style* style, float* size)
3820 {
3821 #if defined(FEAT_GUI_DIALOG)
3822 	// gui.vimWindow->Unlock();
3823     VimSelectFontDialog *dialog = new VimSelectFontDialog(family, style, size);
3824     return dialog->Go();
3825 #else
3826     return NOFONT;
3827 #endif // FEAT_GUI_DIALOG
3828 }
3829 
3830 
3831 GuiFont
gui_mch_get_font(char_u * name,int giveErrorIfMissing)3832 gui_mch_get_font(
3833 	char_u	    *name,
3834 	int	    giveErrorIfMissing)
3835 {
3836     static VimFont *fontList = NULL;
3837 
3838     if (!gui.in_use)	//  can't do this when GUI not running
3839 	return NOFONT;
3840 
3841     //	storage for locally modified name;
3842     const int buff_size = B_FONT_FAMILY_LENGTH + B_FONT_STYLE_LENGTH + 20;
3843     static char font_name[buff_size] = {0};
3844     font_family family = {0};
3845     font_style	style  = {0};
3846     float size = 0.f;
3847 
3848     if (name == 0 && be_fixed_font == 0) {
3849 	if (giveErrorIfMissing)
3850 			semsg(_(e_font), name);
3851 	return NOFONT;
3852     }
3853 
3854     bool useSelectGUI = false;
3855     if (name != NULL)
3856 	if (STRCMP(name, "*") == 0) {
3857 	    useSelectGUI = true;
3858 	    STRNCPY(font_name, hl_get_font_name(), buff_size);
3859 	} else
3860 	    STRNCPY(font_name, name, buff_size);
3861 
3862     if (font_name[0] == 0) {
3863 	be_fixed_font->GetFamilyAndStyle(&family, &style);
3864 	size = be_fixed_font->Size();
3865 	vim_snprintf(font_name, buff_size,
3866 	    (char*)"%s/%s/%.0f", family, style, size);
3867     }
3868 
3869     //	replace underscores with spaces
3870     char* end = 0;
3871     while (end = strchr((char *)font_name, '_'))
3872 	*end = ' ';
3873 
3874     //	store the name before strtok corrupt the buffer ;-)
3875     static char buff[buff_size] = {0};
3876     STRNCPY(buff, font_name, buff_size);
3877     STRNCPY(family, strtok(buff, "/\0"), B_FONT_FAMILY_LENGTH);
3878     char* style_s = strtok(0, "/\0");
3879     if (style_s != 0)
3880 	STRNCPY(style, style_s, B_FONT_STYLE_LENGTH);
3881     size = atof((style_s != 0) ? strtok(0, "/\0") : "0");
3882 
3883     if (useSelectGUI) {
3884 	if (gui_mch_font_dialog(&family, &style, &size) == NOFONT)
3885 	    return FAIL;
3886 	//  compose for further processing
3887 	vim_snprintf(font_name, buff_size,
3888 		(char*)"%s/%s/%.0f", family, style, size);
3889 	hl_set_font_name((char_u*)font_name);
3890 
3891 	//  Set guifont to the name of the selected font.
3892 	char_u* new_p_guifont = (char_u*)alloc(STRLEN(font_name) + 1);
3893 	if (new_p_guifont != NULL) {
3894 	    STRCPY(new_p_guifont, font_name);
3895 	    vim_free(p_guifont);
3896 	    p_guifont = new_p_guifont;
3897 	    //	Replace spaces in the font name with underscores.
3898 	    for ( ; *new_p_guifont; ++new_p_guifont)
3899 		if (*new_p_guifont == ' ')
3900 		    *new_p_guifont = '_';
3901 	}
3902     }
3903 
3904     VimFont *flp;
3905     for (flp = fontList; flp; flp = flp->next) {
3906 	if (STRCMP(font_name, flp->name) == 0) {
3907 	    flp->refcount++;
3908 	    return (GuiFont)flp;
3909 	}
3910     }
3911 
3912     VimFont *font = new VimFont();
3913     font->name = vim_strsave((char_u*)font_name);
3914 
3915     if (count_font_styles(family) <= 0) {
3916 	if (giveErrorIfMissing)
3917 			semsg(_(e_font), font->name);
3918 	delete font;
3919 	return NOFONT;
3920     }
3921 
3922     //	Remember font in the static list for later use
3923     font->next = fontList;
3924     fontList = font;
3925 
3926     font->SetFamilyAndStyle(family, style);
3927     if (size > 0.f)
3928 	font->SetSize(size);
3929 
3930     font->SetSpacing(B_FIXED_SPACING);
3931     font->SetEncoding(B_UNICODE_UTF8);
3932 
3933     return (GuiFont)font;
3934 }
3935 
3936 /*
3937  * Set the current text font.
3938  */
3939 void
gui_mch_set_font(GuiFont font)3940 gui_mch_set_font(
3941 	GuiFont	font)
3942 {
3943     if (gui.vimWindow->Lock()) {
3944 	VimFont *vf = (VimFont *)font;
3945 
3946 	gui.vimTextArea->SetFont(vf);
3947 
3948 	gui.char_width = (int) vf->StringWidth("n");
3949 	font_height fh;
3950 	vf->GetHeight(&fh);
3951 	gui.char_height = (int)(fh.ascent + 0.9999)
3952 	    + (int)(fh.descent + 0.9999) + (int)(fh.leading + 0.9999);
3953 	gui.char_ascent = (int)(fh.ascent + 0.9999);
3954 
3955 	gui.vimWindow->Unlock();
3956     }
3957 }
3958 
3959 // XXX TODO This is apparently never called...
3960 void
gui_mch_free_font(GuiFont font)3961 gui_mch_free_font(
3962 	GuiFont	font)
3963 {
3964     if (font == NOFONT)
3965 	return;
3966     VimFont *f = (VimFont *)font;
3967     if (--f->refcount <= 0) {
3968 	if (f->refcount < 0)
3969 	    fprintf(stderr, "VimFont: refcount < 0\n");
3970 	delete f;
3971     }
3972 }
3973 
3974     char_u *
gui_mch_get_fontname(GuiFont font,char_u * name)3975 gui_mch_get_fontname(GuiFont font, char_u *name)
3976 {
3977     if (name == NULL)
3978 	return NULL;
3979     return vim_strsave(name);
3980 }
3981 
3982 /*
3983  * Adjust gui.char_height (after 'linespace' was changed).
3984  */
3985     int
gui_mch_adjust_charheight()3986 gui_mch_adjust_charheight()
3987 {
3988 
3989     // TODO: linespace support?
3990 
3991 // #ifdef FEAT_XFONTSET
3992 //  if (gui.fontset != NOFONTSET)
3993 //  {
3994 //  gui.char_height = fontset_height((XFontSet)gui.fontset) + p_linespace;
3995 //  gui.char_ascent = fontset_ascent((XFontSet)gui.fontset)
3996 //  + p_linespace / 2;
3997 //  }
3998 //  else
3999 // #endif
4000     {
4001 	VimFont *font = (VimFont *)gui.norm_font;
4002 	font_height fh = {0};
4003 	font->GetHeight(&fh);
4004 	gui.char_height = (int)(fh.ascent + fh.descent + 0.5) + p_linespace;
4005 	gui.char_ascent = (int)(fh.ascent + 0.5) + p_linespace / 2;
4006     }
4007     return OK;
4008 }
4009 
4010     void
gui_mch_getmouse(int * x,int * y)4011 gui_mch_getmouse(int *x, int *y)
4012 {
4013     fprintf(stderr, "gui_mch_getmouse");
4014 
4015     /*int	rootx, rooty, winx, winy;
4016       Window	root, child;
4017       unsigned int mask;
4018 
4019       if (gui.wid && XQueryPointer(gui.dpy, gui.wid, &root, &child,
4020       &rootx, &rooty, &winx, &winy, &mask)) {
4021      *x = winx;
4022      *y = winy;
4023      } else*/ {
4024 	 *x = -1;
4025 	 *y = -1;
4026      }
4027 }
4028 
4029     void
gui_mch_mousehide(int hide)4030 gui_mch_mousehide(int hide)
4031 {
4032     fprintf(stderr, "gui_mch_getmouse");
4033     //	TODO
4034 }
4035 
4036     static int
hex_digit(int c)4037 hex_digit(int c)
4038 {
4039     if (isdigit(c))
4040 	return c - '0';
4041     c = TOLOWER_ASC(c);
4042     if (c >= 'a' && c <= 'f')
4043 	return c - 'a' + 10;
4044     return -1000;
4045 }
4046 
4047 /*
4048  * This function has been lifted from gui_w32.c and extended a bit.
4049  *
4050  * Return the Pixel value (color) for the given color name.
4051  * Return INVALCOLOR for error.
4052  */
4053 guicolor_T
gui_mch_get_color(char_u * name)4054 gui_mch_get_color(
4055 	char_u	*name)
4056 {
4057     return gui_get_color_cmn(name);
4058 }
4059 
4060 /*
4061  * Set the current text foreground color.
4062  */
4063 void
gui_mch_set_fg_color(guicolor_T color)4064 gui_mch_set_fg_color(
4065 	guicolor_T  color)
4066 {
4067     rgb_color rgb = GUI_TO_RGB(color);
4068     if (gui.vimWindow->Lock()) {
4069 	gui.vimTextArea->SetHighColor(rgb);
4070 	gui.vimWindow->Unlock();
4071     }
4072 }
4073 
4074 /*
4075  * Set the current text background color.
4076  */
4077 void
gui_mch_set_bg_color(guicolor_T color)4078 gui_mch_set_bg_color(
4079 	guicolor_T  color)
4080 {
4081     rgb_color rgb = GUI_TO_RGB(color);
4082     if (gui.vimWindow->Lock()) {
4083 	gui.vimTextArea->SetLowColor(rgb);
4084 	gui.vimWindow->Unlock();
4085     }
4086 }
4087 
4088 /*
4089  * Set the current text special color.
4090  */
4091     void
gui_mch_set_sp_color(guicolor_T color)4092 gui_mch_set_sp_color(guicolor_T	color)
4093 {
4094     // prev_sp_color = color;
4095 }
4096 
4097 void
gui_mch_draw_string(int row,int col,char_u * s,int len,int flags)4098 gui_mch_draw_string(
4099 	int	row,
4100 	int	col,
4101 	char_u	*s,
4102 	int	len,
4103 	int	flags)
4104 {
4105     if (gui.vimWindow->Lock()) {
4106 	gui.vimTextArea->mchDrawString(row, col, s, len, flags);
4107 	gui.vimWindow->Unlock();
4108     }
4109 }
4110 
4111 	guicolor_T
gui_mch_get_rgb_color(int r,int g,int b)4112 gui_mch_get_rgb_color(int r, int g, int b)
4113 {
4114     return gui_get_rgb_color_cmn(r, g, b);
4115 }
4116 
4117 
4118 // Return OK if the key with the termcap name "name" is supported.
4119 int
gui_mch_haskey(char_u * name)4120 gui_mch_haskey(
4121 	char_u	*name)
4122 {
4123     int i;
4124 
4125     for (i = 0; special_keys[i].BeKeys != 0; i++)
4126 	if (name[0] == special_keys[i].vim_code0 &&
4127 		name[1] == special_keys[i].vim_code1)
4128 	    return OK;
4129     return FAIL;
4130 }
4131 
4132     void
gui_mch_beep()4133 gui_mch_beep()
4134 {
4135     ::beep();
4136 }
4137 
4138     void
gui_mch_flash(int msec)4139 gui_mch_flash(int msec)
4140 {
4141     // Do a visual beep by reversing the foreground and background colors
4142 
4143     if (gui.vimWindow->Lock()) {
4144 	BRect rect = gui.vimTextArea->Bounds();
4145 
4146 	gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
4147 	gui.vimTextArea->FillRect(rect);
4148 	gui.vimTextArea->Sync();
4149 	snooze(msec * 1000);	 // wait for a few msec
4150 	gui.vimTextArea->FillRect(rect);
4151 	gui.vimTextArea->SetDrawingMode(B_OP_COPY);
4152 	gui.vimTextArea->Flush();
4153 	gui.vimWindow->Unlock();
4154     }
4155 }
4156 
4157 /*
4158  * Invert a rectangle from row r, column c, for nr rows and nc columns.
4159  */
4160 void
gui_mch_invert_rectangle(int r,int c,int nr,int nc)4161 gui_mch_invert_rectangle(
4162 	int	r,
4163 	int	c,
4164 	int	nr,
4165 	int	nc)
4166 {
4167     BRect rect;
4168     rect.left = FILL_X(c);
4169     rect.top = FILL_Y(r);
4170     rect.right = rect.left + nc * gui.char_width - PEN_WIDTH;
4171     rect.bottom = rect.top + nr * gui.char_height - PEN_WIDTH;
4172 
4173     if (gui.vimWindow->Lock()) {
4174 	gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
4175 	gui.vimTextArea->FillRect(rect);
4176 	gui.vimTextArea->SetDrawingMode(B_OP_COPY);
4177 	gui.vimWindow->Unlock();
4178     }
4179 }
4180 
4181 /*
4182  * Iconify the GUI window.
4183  */
4184     void
gui_mch_iconify()4185 gui_mch_iconify()
4186 {
4187     if (gui.vimWindow->Lock()) {
4188 	gui.vimWindow->Minimize(true);
4189 	gui.vimWindow->Unlock();
4190     }
4191 }
4192 
4193 #if defined(FEAT_EVAL) || defined(PROTO)
4194 /*
4195  * Bring the Vim window to the foreground.
4196  */
4197     void
gui_mch_set_foreground(void)4198 gui_mch_set_foreground(void)
4199 {
4200     // TODO
4201 }
4202 #endif
4203 
4204 /*
4205  * Set the window title
4206  */
4207 void
gui_mch_settitle(char_u * title,char_u * icon)4208 gui_mch_settitle(
4209 	char_u	*title,
4210 	char_u	*icon)
4211 {
4212     if (gui.vimWindow->Lock()) {
4213 	gui.vimWindow->SetTitle((char *)title);
4214 	gui.vimWindow->Unlock();
4215     }
4216 }
4217 
4218 /*
4219  * Draw a cursor without focus.
4220  */
4221     void
gui_mch_draw_hollow_cursor(guicolor_T color)4222 gui_mch_draw_hollow_cursor(guicolor_T color)
4223 {
4224     gui_mch_set_fg_color(color);
4225 
4226     BRect r;
4227     r.left = FILL_X(gui.col);
4228     r.top = FILL_Y(gui.row);
4229     int cells = utf_off2cells(LineOffset[gui.row] + gui.col, 100); // TODO-TODO
4230     if (cells>=4) cells = 1;
4231     r.right = r.left + cells*gui.char_width - PEN_WIDTH;
4232     r.bottom = r.top + gui.char_height - PEN_WIDTH;
4233 
4234     if (gui.vimWindow->Lock()) {
4235 	gui.vimTextArea->StrokeRect(r);
4236 	gui.vimWindow->Unlock();
4237 	// gui_mch_flush();
4238     }
4239 }
4240 
4241 /*
4242  * Draw part of a cursor, only w pixels wide, and h pixels high.
4243  */
4244 void
gui_mch_draw_part_cursor(int w,int h,guicolor_T color)4245 gui_mch_draw_part_cursor(
4246 	int	w,
4247 	int	h,
4248 	guicolor_T  color)
4249 {
4250     gui_mch_set_fg_color(color);
4251 
4252     BRect r;
4253     r.left =
4254 #ifdef FEAT_RIGHTLEFT
4255 	// vertical line should be on the right of current point
4256 	CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
4257 #endif
4258 	FILL_X(gui.col);
4259     r.right = r.left + w - PEN_WIDTH;
4260     r.bottom = FILL_Y(gui.row + 1) - PEN_WIDTH;
4261     r.top = r.bottom - h + PEN_WIDTH;
4262 
4263     if (gui.vimWindow->Lock()) {
4264 	gui.vimTextArea->FillRect(r);
4265 	gui.vimWindow->Unlock();
4266 	// gui_mch_flush();
4267     }
4268 }
4269 
4270 /*
4271  * Catch up with any queued events.  This may put keyboard input into the
4272  * input buffer, call resize call-backs, trigger timers etc.  If there is
4273  * nothing in the event queue (& no timers pending), then we return
4274  * immediately.
4275  */
4276     void
gui_mch_update()4277 gui_mch_update()
4278 {
4279     gui_mch_flush();
4280     while (port_count(gui.vdcmp) > 0 &&
4281 	    !vim_is_input_buf_full() &&
4282 	    gui_haiku_process_event(0) >= B_OK)
4283 	/* nothing */ ;
4284 }
4285 
4286 /*
4287  * GUI input routine called by gui_wait_for_chars().  Waits for a character
4288  * from the keyboard.
4289  *  wtime == -1	    Wait forever.
4290  *  wtime == 0	    This should never happen.
4291  *  wtime > 0	    Wait wtime milliseconds for a character.
4292  * Returns OK if a character was found to be available within the given time,
4293  * or FAIL otherwise.
4294  */
4295 int
gui_mch_wait_for_chars(int wtime)4296 gui_mch_wait_for_chars(
4297 	int	wtime)
4298 {
4299     int		focus;
4300     bigtime_t	until, timeout;
4301     status_t	st;
4302 
4303     if (wtime >= 0)
4304     {
4305 	timeout = wtime * 1000;
4306 	until = system_time() + timeout;
4307     }
4308     else
4309 	timeout = B_INFINITE_TIMEOUT;
4310 
4311     focus = gui.in_focus;
4312     for (;;)
4313     {
4314 	// Stop or start blinking when focus changes
4315 	if (gui.in_focus != focus)
4316 	{
4317 	    if (gui.in_focus)
4318 		gui_mch_start_blink();
4319 	    else
4320 		gui_mch_stop_blink(TRUE);
4321 	    focus = gui.in_focus;
4322 	}
4323 
4324 	gui_mch_flush();
4325 
4326 #ifdef MESSAGE_QUEUE
4327 # ifdef FEAT_TIMERS
4328 	did_add_timer = FALSE;
4329 # endif
4330 	parse_queued_messages();
4331 # ifdef FEAT_TIMERS
4332 	if (did_add_timer)
4333 	    // Need to recompute the waiting time.
4334 	    break;
4335 # endif
4336 # ifdef FEAT_JOB_CHANNEL
4337 	if (has_any_channel())
4338 	{
4339 	    if (wtime < 0 || timeout > 20000)
4340 		timeout = 20000;
4341 	}
4342 	else if (wtime < 0)
4343 	    timeout = B_INFINITE_TIMEOUT;
4344 # endif
4345 #endif
4346 
4347 	/*
4348 	 * Don't use gui_mch_update() because then we will spin-lock until a
4349 	 * char arrives, instead we use gui_haiku_process_event() to hang until
4350 	 * an event arrives.  No need to check for input_buf_full because we
4351 	 * are returning as soon as it contains a single char.
4352 	 */
4353 	st = gui_haiku_process_event(timeout);
4354 
4355 	if (input_available())
4356 	    return OK;
4357 	if (st < B_OK)		// includes B_TIMED_OUT
4358 	    return FAIL;
4359 
4360 	/*
4361 	 * Calculate how much longer we're willing to wait for the
4362 	 * next event.
4363 	 */
4364 	if (wtime >= 0)
4365 	{
4366 	    timeout = until - system_time();
4367 	    if (timeout < 0)
4368 		break;
4369 	}
4370     }
4371     return FAIL;
4372 
4373 }
4374 
4375 /*
4376  * Output routines.
4377  */
4378 
4379 /*
4380  * Flush any output to the screen. This is typically called before
4381  * the app goes to sleep.
4382  */
4383     void
gui_mch_flush()4384 gui_mch_flush()
4385 {
4386     //	does this need to lock the window? Apparently not but be safe.
4387     if (gui.vimWindow->Lock()) {
4388 	gui.vimWindow->Flush();
4389 	gui.vimWindow->Unlock();
4390     }
4391     return;
4392 }
4393 
4394 /*
4395  * Clear a rectangular region of the screen from text pos (row1, col1) to
4396  * (row2, col2) inclusive.
4397  */
4398 void
gui_mch_clear_block(int row1,int col1,int row2,int col2)4399 gui_mch_clear_block(
4400 	int	row1,
4401 	int	col1,
4402 	int	row2,
4403 	int	col2)
4404 {
4405     if (gui.vimWindow->Lock()) {
4406 	gui.vimTextArea->mchClearBlock(row1, col1, row2, col2);
4407 	gui.vimWindow->Unlock();
4408     }
4409 }
4410 
4411     void
gui_mch_clear_all()4412 gui_mch_clear_all()
4413 {
4414     if (gui.vimWindow->Lock()) {
4415 	gui.vimTextArea->mchClearAll();
4416 	gui.vimWindow->Unlock();
4417     }
4418 }
4419 
4420 /*
4421  * Delete the given number of lines from the given row, scrolling up any
4422  * text further down within the scroll region.
4423  */
4424 void
gui_mch_delete_lines(int row,int num_lines)4425 gui_mch_delete_lines(
4426 	int	row,
4427 	int	num_lines)
4428 {
4429     gui.vimTextArea->mchDeleteLines(row, num_lines);
4430 }
4431 
4432 /*
4433  * Insert the given number of lines before the given row, scrolling down any
4434  * following text within the scroll region.
4435  */
4436 void
gui_mch_insert_lines(int row,int num_lines)4437 gui_mch_insert_lines(
4438 	int	row,
4439 	int	num_lines)
4440 {
4441     gui.vimTextArea->mchInsertLines(row, num_lines);
4442 }
4443 
4444 #if defined(FEAT_MENU) || defined(PROTO)
4445 /*
4446  * Menu stuff.
4447  */
4448 
4449 void
gui_mch_enable_menu(int flag)4450 gui_mch_enable_menu(
4451 	int	flag)
4452 {
4453     if (gui.vimWindow->Lock())
4454     {
4455 	BMenuBar *menubar = gui.vimForm->MenuBar();
4456 	menubar->SetEnabled(flag);
4457 	gui.vimWindow->Unlock();
4458     }
4459 }
4460 
4461 void
gui_mch_set_menu_pos(int x,int y,int w,int h)4462 gui_mch_set_menu_pos(
4463 	int	x,
4464 	int	y,
4465 	int	w,
4466 	int	h)
4467 {
4468     // It will be in the right place anyway
4469 }
4470 
4471 /*
4472  * Add a sub menu to the menu bar.
4473  */
4474 void
gui_mch_add_menu(vimmenu_T * menu,int idx)4475 gui_mch_add_menu(
4476 	vimmenu_T   *menu,
4477 	int	idx)
4478 {
4479     vimmenu_T	*parent = menu->parent;
4480 
4481     //	popup menu - just create it unattached
4482     if (menu_is_popup(menu->name) && parent == NULL) {
4483 	BPopUpMenu* popUpMenu = new BPopUpMenu((const char*)menu->name, false, false);
4484 	menu->submenu_id = popUpMenu;
4485 	menu->id = NULL;
4486 	return;
4487     }
4488 
4489     if (!menu_is_menubar(menu->name)
4490 	    || (parent != NULL && parent->submenu_id == NULL))
4491 	return;
4492 
4493     if (gui.vimWindow->Lock())
4494     {
4495 	// Major re-write of the menu code, it was failing with memory corruption when
4496 	// we started loading multiple files (the Buffer menu)
4497 	//
4498 	// Note we don't use the preference values yet, all are inserted into the
4499 	// menubar on a first come-first served basis...
4500 	//
4501 	// [email protected] jul 99
4502 
4503 	BMenu *tmp;
4504 
4505 	if ( parent )
4506 	    tmp = parent->submenu_id;
4507 	else
4508 	    tmp = gui.vimForm->MenuBar();
4509 	//  make sure we don't try and add the same menu twice. The Buffers menu tries to
4510 	//  do this and Be starts to crash...
4511 
4512 	if ( ! tmp->FindItem((const char *) menu->dname)) {
4513 
4514 	    BMenu *bmenu = new BMenu((char *)menu->dname);
4515 
4516 	    menu->submenu_id = bmenu;
4517 
4518 	    //	when we add a BMenu to another Menu, it creates the interconnecting BMenuItem
4519 	    tmp->AddItem(bmenu);
4520 
4521 	    //	Now its safe to query the menu for the associated MenuItem....
4522 	    menu->id = tmp->FindItem((const char *) menu->dname);
4523 
4524 	}
4525 	gui.vimWindow->Unlock();
4526     }
4527 }
4528 
4529     void
gui_mch_toggle_tearoffs(int enable)4530 gui_mch_toggle_tearoffs(int enable)
4531 {
4532     // no tearoff menus
4533 }
4534 
4535     static BMessage *
MenuMessage(vimmenu_T * menu)4536 MenuMessage(vimmenu_T *menu)
4537 {
4538     BMessage *m = new BMessage('menu');
4539     m->AddPointer("VimMenu", (void *)menu);
4540 
4541     return m;
4542 }
4543 
4544 /*
4545  * Add a menu item to a menu
4546  */
4547 void
gui_mch_add_menu_item(vimmenu_T * menu,int idx)4548 gui_mch_add_menu_item(
4549 	vimmenu_T   *menu,
4550 	int	idx)
4551 {
4552     int	    mnemonic = 0;
4553     vimmenu_T	*parent = menu->parent;
4554 
4555     // TODO: use menu->actext
4556     // This is difficult, since on Be, an accelerator must be a single char
4557     // and a lot of Vim ones are the standard VI commands.
4558     //
4559     // Punt for Now...
4560     // [email protected] jul 99
4561     if (gui.vimWindow->Lock())
4562     {
4563 #ifdef FEAT_TOOLBAR
4564 	if (menu_is_toolbar(parent->name)) {
4565 	    VimToolbar *toolbar = gui.vimForm->ToolBar();
4566 	    if (toolbar != NULL) {
4567 		toolbar->AddButton(idx, menu);
4568 	    }
4569 	} else
4570 #endif
4571 
4572 	if (parent->submenu_id != NULL || menu_is_popup(parent->name)) {
4573 	    if (menu_is_separator(menu->name)) {
4574 		BSeparatorItem *item = new BSeparatorItem();
4575 		parent->submenu_id->AddItem(item);
4576 		menu->id = item;
4577 		menu->submenu_id = NULL;
4578 	    }
4579 	    else {
4580 		BMenuItem *item = new BMenuItem((char *)menu->dname,
4581 			MenuMessage(menu));
4582 		item->SetTarget(gui.vimTextArea);
4583 		item->SetTrigger((char) menu->mnemonic);
4584 		parent->submenu_id->AddItem(item);
4585 		menu->id = item;
4586 		menu->submenu_id = NULL;
4587 	    }
4588 	}
4589 	gui.vimWindow->Unlock();
4590     }
4591 }
4592 
4593 /*
4594  * Destroy the machine specific menu widget.
4595  */
4596 void
gui_mch_destroy_menu(vimmenu_T * menu)4597 gui_mch_destroy_menu(
4598 	vimmenu_T   *menu)
4599 {
4600     if (gui.vimWindow->Lock())
4601     {
4602 #ifdef FEAT_TOOLBAR
4603 	if (menu->parent && menu_is_toolbar(menu->parent->name)) {
4604 	    VimToolbar *toolbar = gui.vimForm->ToolBar();
4605 	    if (toolbar != NULL) {
4606 		toolbar->RemoveButton(menu);
4607 	    }
4608 	} else
4609 #endif
4610 	{
4611 	    assert(menu->submenu_id == NULL || menu->submenu_id->CountItems() == 0);
4612 	    /*
4613 	     * Detach this menu from its parent, so that it is not deleted
4614 	     * twice once we get to delete that parent.
4615 	     * Deleting a BMenuItem also deletes the associated BMenu, if any
4616 	     * (which does not have any items anymore since they were
4617 	     * removed and deleted before).
4618 	     */
4619 	    BMenu *bmenu = menu->id->Menu();
4620 	    if (bmenu)
4621 	    {
4622 		bmenu->RemoveItem(menu->id);
4623 		/*
4624 		 * If we removed the last item from the menu bar,
4625 		 * resize it out of sight.
4626 		 */
4627 		if (bmenu == gui.vimForm->MenuBar() && bmenu->CountItems() == 0)
4628 		{
4629 		    bmenu->ResizeTo(-MENUBAR_MARGIN, -MENUBAR_MARGIN);
4630 		}
4631 	    }
4632 	    delete menu->id;
4633 	    menu->id = NULL;
4634 	    menu->submenu_id = NULL;
4635 
4636 	    gui.menu_height = (int) gui.vimForm->MenuHeight();
4637 	}
4638 	gui.vimWindow->Unlock();
4639     }
4640 }
4641 
4642 /*
4643  * Make a menu either grey or not grey.
4644  */
4645 void
gui_mch_menu_grey(vimmenu_T * menu,int grey)4646 gui_mch_menu_grey(
4647 	vimmenu_T   *menu,
4648 	int	grey)
4649 {
4650 #ifdef FEAT_TOOLBAR
4651     if (menu->parent && menu_is_toolbar(menu->parent->name)) {
4652 	if (gui.vimWindow->Lock()) {
4653 	    VimToolbar *toolbar = gui.vimForm->ToolBar();
4654 	    if (toolbar != NULL) {
4655 		toolbar->GrayButton(menu, grey);
4656 	    }
4657 	    gui.vimWindow->Unlock();
4658 	}
4659     } else
4660 #endif
4661     if (menu->id != NULL)
4662 	menu->id->SetEnabled(!grey);
4663 }
4664 
4665 /*
4666  * Make menu item hidden or not hidden
4667  */
4668 void
gui_mch_menu_hidden(vimmenu_T * menu,int hidden)4669 gui_mch_menu_hidden(
4670 	vimmenu_T   *menu,
4671 	int	hidden)
4672 {
4673     if (menu->id != NULL)
4674 	menu->id->SetEnabled(!hidden);
4675 }
4676 
4677 /*
4678  * This is called after setting all the menus to grey/hidden or not.
4679  */
4680     void
gui_mch_draw_menubar()4681 gui_mch_draw_menubar()
4682 {
4683     // Nothing to do in BeOS
4684 }
4685 
4686     void
gui_mch_show_popupmenu(vimmenu_T * menu)4687 gui_mch_show_popupmenu(vimmenu_T *menu)
4688 {
4689     if (!menu_is_popup(menu->name) || menu->submenu_id == NULL)
4690 	return;
4691 
4692     BPopUpMenu* popupMenu = dynamic_cast<BPopUpMenu*>(menu->submenu_id);
4693     if (popupMenu == NULL)
4694 	return;
4695 
4696     BPoint point;
4697     if (gui.vimWindow->Lock()) {
4698 	uint32 buttons = 0;
4699 	gui.vimTextArea->GetMouse(&point, &buttons);
4700 	gui.vimTextArea->ConvertToScreen(&point);
4701 	gui.vimWindow->Unlock();
4702     }
4703     popupMenu->Go(point, true);
4704 }
4705 
4706 #endif // FEAT_MENU
4707 
4708 // Mouse stuff
4709 
4710 #ifdef FEAT_CLIPBOARD
4711 /*
4712  * Clipboard stuff, for cutting and pasting text to other windows.
4713  */
4714 char textplain[] = "text/plain";
4715 char vimselectiontype[] = "application/x-vnd.Rhialto-Vim-selectiontype";
4716 
4717 /*
4718  * Get the current selection and put it in the clipboard register.
4719  */
4720     void
clip_mch_request_selection(Clipboard_T * cbd)4721 clip_mch_request_selection(Clipboard_T *cbd)
4722 {
4723     if (be_clipboard->Lock())
4724     {
4725 	BMessage *m = be_clipboard->Data();
4726 	// m->PrintToStream();
4727 
4728 	char_u *string = NULL;
4729 	ssize_t stringlen = -1;
4730 
4731 	if (m->FindData(textplain, B_MIME_TYPE,
4732 		    (const void **)&string, &stringlen) == B_OK
4733 		|| m->FindString("text", (const char **)&string) == B_OK)
4734 	{
4735 	    if (stringlen == -1)
4736 		stringlen = STRLEN(string);
4737 
4738 	    int type;
4739 	    char *seltype;
4740 	    ssize_t seltypelen;
4741 
4742 	    /*
4743 	     * Try to get the special vim selection type first
4744 	     */
4745 	    if (m->FindData(vimselectiontype, B_MIME_TYPE,
4746 			(const void **)&seltype, &seltypelen) == B_OK)
4747 	    {
4748 		switch (*seltype)
4749 		{
4750 		    default:
4751 		    case 'L':	type = MLINE;	break;
4752 		    case 'C':	type = MCHAR;	break;
4753 #ifdef FEAT_VISUAL
4754 		    case 'B':	type = MBLOCK;	break;
4755 #endif
4756 		}
4757 	    }
4758 	    else
4759 	    {
4760 		// Otherwise use heuristic as documented
4761 		type = memchr(string, stringlen, '\n') ? MLINE : MCHAR;
4762 	    }
4763 	    clip_yank_selection(type, string, (long)stringlen, cbd);
4764 	}
4765 	be_clipboard->Unlock();
4766     }
4767 }
4768 /*
4769  * Make vim the owner of the current selection.
4770  */
4771     void
clip_mch_lose_selection(Clipboard_T * cbd)4772 clip_mch_lose_selection(Clipboard_T *cbd)
4773 {
4774     // Nothing needs to be done here
4775 }
4776 
4777 /*
4778  * Make vim the owner of the current selection.  Return OK upon success.
4779  */
4780     int
clip_mch_own_selection(Clipboard_T * cbd)4781 clip_mch_own_selection(Clipboard_T *cbd)
4782 {
4783     /*
4784      * Never actually own the clipboard.  If another application sets the
4785      * clipboard, we don't want to think that we still own it.
4786      */
4787     return FAIL;
4788 }
4789 
4790 /*
4791  * Send the current selection to the clipboard.
4792  */
4793     void
clip_mch_set_selection(Clipboard_T * cbd)4794 clip_mch_set_selection(Clipboard_T *cbd)
4795 {
4796     if (be_clipboard->Lock())
4797     {
4798 	be_clipboard->Clear();
4799 	BMessage *m = be_clipboard->Data();
4800 	assert(m);
4801 
4802 	// If the '*' register isn't already filled in, fill it in now
4803 	cbd->owned = TRUE;
4804 	clip_get_selection(cbd);
4805 	cbd->owned = FALSE;
4806 
4807 	char_u	*str = NULL;
4808 	long_u	count;
4809 	int type;
4810 
4811 	type = clip_convert_selection(&str, &count, cbd);
4812 
4813 	if (type < 0)
4814 	    return;
4815 
4816 	m->AddData(textplain, B_MIME_TYPE, (void *)str, count);
4817 
4818 	// Add type of selection
4819 	char	vtype;
4820 	switch (type)
4821 	{
4822 	    default:
4823 	    case MLINE:    vtype = 'L';    break;
4824 	    case MCHAR:    vtype = 'C';    break;
4825 #ifdef FEAT_VISUAL
4826 	    case MBLOCK:   vtype = 'B';    break;
4827 #endif
4828 	}
4829 	m->AddData(vimselectiontype, B_MIME_TYPE, (void *)&vtype, 1);
4830 
4831 	vim_free(str);
4832 
4833 	be_clipboard->Commit();
4834 	be_clipboard->Unlock();
4835     }
4836 }
4837 
4838 #endif	// FEAT_CLIPBOARD
4839 
4840 #ifdef FEAT_BROWSE
4841 /*
4842  * Pop open a file browser and return the file selected, in allocated memory,
4843  * or NULL if Cancel is hit.
4844  *  saving  - TRUE if the file will be saved to, FALSE if it will be opened.
4845  *  title   - Title message for the file browser dialog.
4846  *  dflt    - Default name of file.
4847  *  ext     - Default extension to be added to files without extensions.
4848  *  initdir - directory in which to open the browser (NULL = current dir)
4849  *  filter  - Filter for matched files to choose from.
4850  *  Has a format like this:
4851  *  "C Files (*.c)\0*.c\0"
4852  *  "All Files\0*.*\0\0"
4853  *  If these two strings were concatenated, then a choice of two file
4854  *  filters will be selectable to the user.  Then only matching files will
4855  *  be shown in the browser.  If NULL, the default allows all files.
4856  *
4857  *  *NOTE* - the filter string must be terminated with TWO nulls.
4858  */
4859 char_u *
gui_mch_browse(int saving,char_u * title,char_u * dflt,char_u * ext,char_u * initdir,char_u * filter)4860 gui_mch_browse(
4861 	int saving,
4862 	char_u *title,
4863 	char_u *dflt,
4864 	char_u *ext,
4865 	char_u *initdir,
4866 	char_u *filter)
4867 {
4868     gui.vimApp->fFilePanel = new BFilePanel((saving == TRUE) ? B_SAVE_PANEL : B_OPEN_PANEL,
4869 	    NULL, NULL, 0, false,
4870 	    new BMessage((saving == TRUE) ? 'save' : 'open'), NULL, true);
4871 
4872     gui.vimApp->fBrowsedPath.Unset();
4873 
4874     gui.vimApp->fFilePanel->Window()->SetTitle((char*)title);
4875     gui.vimApp->fFilePanel->SetPanelDirectory((const char*)initdir);
4876 
4877     gui.vimApp->fFilePanel->Show();
4878 
4879     gui.vimApp->fFilePanelSem = create_sem(0, "FilePanelSem");
4880 
4881     while (acquire_sem(gui.vimApp->fFilePanelSem) == B_INTERRUPTED);
4882 
4883     char_u *fileName = NULL;
4884     status_t result = gui.vimApp->fBrowsedPath.InitCheck();
4885     if (result == B_OK) {
4886 	fileName = vim_strsave((char_u*)gui.vimApp->fBrowsedPath.Path());
4887     } else
4888 	if (result != B_NO_INIT) {
4889 	    fprintf(stderr, "gui_mch_browse: BPath error: %#08x (%s)\n",
4890 		    result, strerror(result));
4891 	}
4892 
4893     delete gui.vimApp->fFilePanel;
4894     gui.vimApp->fFilePanel = NULL;
4895 
4896     return fileName;
4897 }
4898 #endif // FEAT_BROWSE
4899 
4900 
4901 #if defined(FEAT_GUI_DIALOG)
4902 
4903 /*
4904  * Create a dialog dynamically from the parameter strings.
4905  * type	    = type of dialog (question, alert, etc.)
4906  * title    = dialog title. may be NULL for default title.
4907  * message  = text to display. Dialog sizes to accommodate it.
4908  * buttons  = '\n' separated list of button captions, default first.
4909  * dfltbutton	= number of default button.
4910  *
4911  * This routine returns 1 if the first button is pressed,
4912  *	    2 for the second, etc.
4913  *
4914  *	    0 indicates Esc was pressed.
4915  *	    -1 for unexpected error
4916  *
4917  * If stubbing out this fn, return 1.
4918  */
4919 
4920 int
gui_mch_dialog(int type,char_u * title,char_u * message,char_u * buttons,int dfltbutton,char_u * textfield,int ex_cmd)4921 gui_mch_dialog(
4922 	int	 type,
4923 	char_u	*title,
4924 	char_u	*message,
4925 	char_u	*buttons,
4926 	int	 dfltbutton,
4927 	char_u	*textfield,
4928 	int ex_cmd)
4929 {
4930     VimDialog *dialog = new VimDialog(type, (char*)title, (char*)message,
4931 	    (char*)buttons, dfltbutton, (char*)textfield, ex_cmd);
4932     return dialog->Go();
4933 }
4934 
4935 #endif // FEAT_GUI_DIALOG
4936 
4937 
4938 /*
4939  * Return the RGB value of a pixel as long.
4940  */
4941     guicolor_T
gui_mch_get_rgb(guicolor_T pixel)4942 gui_mch_get_rgb(guicolor_T pixel)
4943 {
4944     rgb_color rgb = GUI_TO_RGB(pixel);
4945 
4946     return ((rgb.red & 0xff) << 16) + ((rgb.green & 0xff) << 8)
4947 	+ (rgb.blue & 0xff);
4948 }
4949 
4950     void
gui_mch_setmouse(int x,int y)4951 gui_mch_setmouse(int x, int y)
4952 {
4953     TRACE();
4954     // TODO
4955 }
4956 
4957 #ifdef FEAT_MBYTE_IME
4958     void
im_set_position(int row,int col)4959 im_set_position(int row, int col)
4960 {
4961     if (gui.vimWindow->Lock())
4962     {
4963 	gui.vimTextArea->DrawIMString();
4964 	gui.vimWindow->Unlock();
4965     }
4966     return;
4967 }
4968 #endif
4969 
4970     void
gui_mch_show_toolbar(int showit)4971 gui_mch_show_toolbar(int showit)
4972 {
4973     VimToolbar *toolbar = gui.vimForm->ToolBar();
4974     gui.toolbar_height = (toolbar && showit) ? toolbar->ToolbarHeight() : 0.;
4975 }
4976 
4977     void
gui_mch_set_toolbar_pos(int x,int y,int w,int h)4978 gui_mch_set_toolbar_pos(int x, int y, int w, int h)
4979 {
4980     VimToolbar *toolbar = gui.vimForm->ToolBar();
4981     if (toolbar != NULL) {
4982 	if (gui.vimWindow->Lock()) {
4983 	    toolbar->MoveTo(x, y);
4984 	    toolbar->ResizeTo(w - 1, h - 1);
4985 	    gui.vimWindow->Unlock();
4986 	}
4987     }
4988 }
4989 
4990 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
4991 
4992 /*
4993  * Show or hide the tabline.
4994  */
4995     void
gui_mch_show_tabline(int showit)4996 gui_mch_show_tabline(int showit)
4997 {
4998     VimTabLine *tabLine = gui.vimForm->TabLine();
4999 
5000     if (tabLine == NULL)
5001 	return;
5002 
5003     if (!showit != !gui.vimForm->IsShowingTabLine()) {
5004 	gui.vimForm->SetShowingTabLine(showit != 0);
5005 	gui.tabline_height = gui.vimForm->TablineHeight();
5006     }
5007 }
5008 
5009     void
gui_mch_set_tabline_pos(int x,int y,int w,int h)5010 gui_mch_set_tabline_pos(int x, int y, int w, int h)
5011 {
5012     VimTabLine *tabLine = gui.vimForm->TabLine();
5013     if (tabLine != NULL) {
5014 	if (gui.vimWindow->Lock()) {
5015 	    tabLine->MoveTo(x, y);
5016 	    tabLine->ResizeTo(w - 1, h - 1);
5017 	    gui.vimWindow->Unlock();
5018 	}
5019     }
5020 }
5021 
5022 /*
5023  * Return TRUE when tabline is displayed.
5024  */
5025     int
gui_mch_showing_tabline()5026 gui_mch_showing_tabline()
5027 {
5028     VimTabLine *tabLine = gui.vimForm->TabLine();
5029     return tabLine != NULL && gui.vimForm->IsShowingTabLine();
5030 }
5031 
5032 /*
5033  * Update the labels of the tabline.
5034  */
5035     void
gui_mch_update_tabline()5036 gui_mch_update_tabline()
5037 {
5038     tabpage_T	*tp;
5039     int	    nr = 0;
5040     int	    curtabidx = 0;
5041 
5042     VimTabLine *tabLine = gui.vimForm->TabLine();
5043 
5044     if (tabLine == NULL)
5045 	return;
5046 
5047     gui.vimWindow->Lock();
5048 
5049     // Add a label for each tab page.  They all contain the same text area.
5050     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) {
5051 	if (tp == curtab)
5052 	    curtabidx = nr;
5053 
5054 	BTab* tab = tabLine->TabAt(nr);
5055 
5056 	if (tab == NULL) {
5057 	    tab = new VimTabLine::VimTab();
5058 	    tabLine->AddTab(NULL, tab);
5059 	}
5060 
5061 	get_tabline_label(tp, FALSE);
5062 	tab->SetLabel((const char*)NameBuff);
5063 	tabLine->Invalidate();
5064     }
5065 
5066     // Remove any old labels.
5067     while (nr < tabLine->CountTabs())
5068 	tabLine->RemoveTab(nr);
5069 
5070     if (tabLine->Selection() != curtabidx)
5071 	tabLine->Select(curtabidx);
5072 
5073     gui.vimWindow->Unlock();
5074 }
5075 
5076 /*
5077  * Set the current tab to "nr".  First tab is 1.
5078  */
5079     void
gui_mch_set_curtab(int nr)5080 gui_mch_set_curtab(int nr)
5081 {
5082     VimTabLine *tabLine = gui.vimForm->TabLine();
5083     if (tabLine == NULL)
5084 	return;
5085 
5086     gui.vimWindow->Lock();
5087 
5088     if (tabLine->Selection() != nr -1)
5089 	tabLine->Select(nr -1);
5090 
5091     gui.vimWindow->Unlock();
5092 }
5093 
5094 #endif // FEAT_GUI_TABLINE
5095