blob: e2c659848b854c742bf6c71ffe587a1c7303b0f3 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* 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 * BeOS GUI.
11 *
12 * GUI support for the Buzzword Enhanced Operating System.
13 *
14 * Ported to R4 by Richard Offer <richard@whitequeen.com> Jul 99
15 *
16 */
17
18/*
19 * Structure of the BeOS 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 */
70extern "C" {
71
72#define new xxx_new_xxx
73
74#include <float.h>
75#include <assert.h>
76#include "vim.h"
77#include "globals.h"
78#include "proto.h"
79#include "option.h"
80
81#undef new
82
83} /* extern "C" */
84
85/* ---------------- start of header part ---------------- */
86
87#include <be/app/MessageQueue.h>
88#include <be/app/Clipboard.h>
89#include <be/kernel/OS.h>
90#include <be/support/Beep.h>
91#include <be/interface/View.h>
92#include <be/interface/Window.h>
93#include <be/interface/MenuBar.h>
94#include <be/interface/MenuItem.h>
95#include <be/interface/ScrollBar.h>
96#include <be/interface/Region.h>
97#include <be/interface/Screen.h>
98#include <be/storage/Path.h>
99#include <be/storage/Directory.h>
100#include <be/storage/Entry.h>
101#include <be/app/Application.h>
102#include <be/support/Debug.h>
103
104/*
105 * The macro B_PIXEL_ALIGNMENT shows us which version
106 * of the header files we're using.
107 */
108#if defined(B_PIXEL_ALIGNMENT)
109#define HAVE_R3_OR_LATER 1
110#else
111#define HAVE_R3_OR_LATER 0
112#endif
113
114class VimApp;
115class VimFormView;
116class VimTextAreaView;
117class VimWindow;
118
119extern key_map *keyMap;
120extern char *keyMapChars;
121
122extern int main(int argc, char **argv);
123
124#ifndef B_MAX_PORT_COUNT
125#define B_MAX_PORT_COUNT 100
126#endif
127
128/*
129 * VimApp seems comparable to the X "vimShell"
130 */
131class VimApp: public BApplication
132{
133 typedef BApplication Inherited;
134public:
135 VimApp(const char *appsig);
136 ~VimApp();
137
138 // callbacks:
139#if 0
140 virtual void DispatchMessage(BMessage *m, BHandler *h)
141 {
142 m->PrintToStream();
143 Inherited::DispatchMessage(m, h);
144 }
145#endif
146 virtual void ReadyToRun();
147 virtual void ArgvReceived(int32 argc, char **argv);
148 virtual void RefsReceived(BMessage *m);
149 virtual bool QuitRequested();
150
151 static void SendRefs(BMessage *m, bool changedir);
152private:
153};
154
155class VimWindow: public BWindow
156{
157 typedef BWindow Inherited;
158public:
159 VimWindow();
160 ~VimWindow();
161
162 virtual void DispatchMessage(BMessage *m, BHandler *h);
163 virtual void WindowActivated(bool active);
164 virtual bool QuitRequested();
165
166 VimFormView *formView;
167
168private:
169 void init();
170
171};
172
173class VimFormView: public BView
174{
175 typedef BView Inherited;
176public:
177 VimFormView(BRect frame);
178 ~VimFormView();
179
180 // callbacks:
181 virtual void AllAttached();
182 virtual void FrameResized(float new_width, float new_height);
183
184#define MENUBAR_MARGIN 1
185 float MenuHeight() const
186 { return menuBar ? menuBar->Frame().Height() + MENUBAR_MARGIN: 0; }
187 BMenuBar *MenuBar() const
188 { return menuBar; }
189
190private:
191 void init(BRect);
192
193 BMenuBar *menuBar;
194 VimTextAreaView *textArea;
195};
196
197class VimTextAreaView: public BView
198{
199 typedef BView Inherited;
200public:
201 VimTextAreaView(BRect frame);
202 ~VimTextAreaView();
203
204 // callbacks:
205 virtual void Draw(BRect updateRect);
206 virtual void KeyDown(const char *bytes, int32 numBytes);
207 virtual void MouseDown(BPoint point);
208 virtual void MouseUp(BPoint point);
209 virtual void MouseMoved(BPoint point, uint32 transit, const BMessage *message);
210 virtual void MessageReceived(BMessage *m);
211
212 // own functions:
213 int mchInitFont(char_u *name);
214 void mchDrawString(int row, int col, char_u *s, int len, int flags);
215 void mchClearBlock(int row1, int col1, int row2, int col2);
216 void mchClearAll();
217 void mchDeleteLines(int row, int num_lines);
218 void mchInsertLines(int row, int num_lines);
219
220 static void guiSendMouseEvent(int button, int x, int y, int repeated_click, int_u modifiers);
221 static void guiBlankMouse(bool should_hide);
222 static int_u mouseModifiersToVim(int32 beModifiers);
223
224 int32 mouseDragEventCount;
225
226private:
227 void init(BRect);
228
229 int_u vimMouseButton;
230 int_u vimMouseModifiers;
231};
232
233class VimScrollBar: public BScrollBar
234{
235 typedef BScrollBar Inherited;
236public:
237 VimScrollBar(scrollbar_T *gsb, orientation posture);
238 ~VimScrollBar();
239
240 virtual void ValueChanged(float newValue);
241 virtual void MouseUp(BPoint where);
242 void SetValue(float newval);
243 scrollbar_T *getGsb()
244 { return gsb; }
245
246 int32 scrollEventCount;
247
248private:
249 scrollbar_T *gsb;
250 float ignoreValue;
251};
252
253
254/*
255 * For caching the fonts that are used;
256 * Vim seems rather sloppy in this regard.
257 */
258class VimFont: public BFont
259{
260 typedef BFont Inherited;
261public:
262 VimFont();
263 VimFont(const VimFont *rhs);
264 VimFont(const BFont *rhs);
265 VimFont(const VimFont &rhs);
266 ~VimFont();
267
268 VimFont *next;
269 int refcount;
270 char_u *name;
271
272private:
273 void init();
274};
275
276/* ---------------- end of GUI classes ---------------- */
277
278struct MainArgs {
279 int argc;
280 char **argv;
281};
282
283/*
284 * These messages are copied through the VDCMP.
285 * Therefore they ought not to have anything fancy.
286 * They must be of POD type (Plain Old Data)
287 * as the C++ standard calls them.
288 */
289
290#define KEY_MSG_BUFSIZ 7
291#if KEY_MSG_BUFSIZ < MAX_KEY_CODE_LEN
292#error Increase KEY_MSG_BUFSIZ!
293#endif
294
295struct VimKeyMsg {
296 char_u length;
297 char_u chars[KEY_MSG_BUFSIZ]; /* contains Vim encoding */
298};
299
300struct VimResizeMsg {
301 int width;
302 int height;
303};
304
305struct VimScrollBarMsg {
306 VimScrollBar *sb;
307 long value;
308 int stillDragging;
309};
310
311struct VimMenuMsg {
312 vimmenu_T *guiMenu;
313};
314
315struct VimMouseMsg {
316 int button;
317 int x;
318 int y;
319 int repeated_click;
320 int_u modifiers;
321};
322
323struct VimFocusMsg {
324 bool active;
325};
326
327struct VimRefsMsg {
328 BMessage *message;
329 bool changedir;
330};
331
332struct VimMsg {
333 enum VimMsgType {
334 Key, Resize, ScrollBar, Menu, Mouse, Focus, Refs
335 };
336
337 union {
338 struct VimKeyMsg Key;
339 struct VimResizeMsg NewSize;
340 struct VimScrollBarMsg Scroll;
341 struct VimMenuMsg Menu;
342 struct VimMouseMsg Mouse;
343 struct VimFocusMsg Focus;
344 struct VimRefsMsg Refs;
345 } u;
346};
347
348#define RGB(r, g, b) ((char_u)(r) << 16 | (char_u)(g) << 8 | (char_u)(b) << 0)
349#define GUI_TO_RGB(g) { (g) >> 16, (g) >> 8, (g) >> 0, 255 }
350
351/* ---------------- end of header part ---------------- */
352
353static struct specialkey
354{
355 uint16 BeKeys;
356#define KEY(a,b) ((a)<<8|(b))
357#define K(a) KEY(0,a) // for ASCII codes
358#define F(b) KEY(1,b) // for scancodes
359 char_u vim_code0;
360 char_u vim_code1;
361} special_keys[] =
362{
363 {K(B_UP_ARROW), 'k', 'u'},
364 {K(B_DOWN_ARROW), 'k', 'd'},
365 {K(B_LEFT_ARROW), 'k', 'l'},
366 {K(B_RIGHT_ARROW), 'k', 'r'},
367 {K(B_BACKSPACE), 'k', 'b'},
368 {K(B_INSERT), 'k', 'I'},
369 {K(B_DELETE), 'k', 'D'},
370 {K(B_HOME), 'k', 'h'},
371 {K(B_END), '@', '7'},
372 {K(B_PAGE_UP), 'k', 'P'}, /* XK_Prior */
373 {K(B_PAGE_DOWN), 'k', 'N'}, /* XK_Next, */
374
375#define FIRST_FUNCTION_KEY 11
376 {F(B_F1_KEY), 'k', '1'},
377 {F(B_F2_KEY), 'k', '2'},
378 {F(B_F3_KEY), 'k', '3'},
379 {F(B_F4_KEY), 'k', '4'},
380 {F(B_F5_KEY), 'k', '5'},
381 {F(B_F6_KEY), 'k', '6'},
382 {F(B_F7_KEY), 'k', '7'},
383 {F(B_F8_KEY), 'k', '8'},
384 {F(B_F9_KEY), 'k', '9'},
385 {F(B_F10_KEY), 'k', ';'},
386
387 {F(B_F11_KEY), 'F', '1'},
388 {F(B_F12_KEY), 'F', '2'},
389// {XK_F13, 'F', '3'}, /* would be print screen/ */
390 /* sysreq */
391 {F(0x0F), 'F', '4'}, /* scroll lock */
392 {F(0x10), 'F', '5'}, /* pause/break */
393// {XK_F16, 'F', '6'},
394// {XK_F17, 'F', '7'},
395// {XK_F18, 'F', '8'},
396// {XK_F19, 'F', '9'},
397// {XK_F20, 'F', 'A'},
398//
399// {XK_F21, 'F', 'B'},
400// {XK_F22, 'F', 'C'},
401// {XK_F23, 'F', 'D'},
402// {XK_F24, 'F', 'E'},
403// {XK_F25, 'F', 'F'},
404// {XK_F26, 'F', 'G'},
405// {XK_F27, 'F', 'H'},
406// {XK_F28, 'F', 'I'},
407// {XK_F29, 'F', 'J'},
408// {XK_F30, 'F', 'K'},
409//
410// {XK_F31, 'F', 'L'},
411// {XK_F32, 'F', 'M'},
412// {XK_F33, 'F', 'N'},
413// {XK_F34, 'F', 'O'},
414// {XK_F35, 'F', 'P'}, /* keysymdef.h defines up to F35 */
415
416// {XK_Help, '%', '1'}, /* XK_Help */
417 {F(B_PRINT_KEY), '%', '9'},
418
419#if 0
420 /* Keypad keys: */
421 {F(0x48), 'k', 'l'}, /* XK_KP_Left */
422 {F(0x4A), 'k', 'r'}, /* XK_KP_Right */
423 {F(0x38), 'k', 'u'}, /* XK_KP_Up */
424 {F(0x59), 'k', 'd'}, /* XK_KP_Down */
425 {F(0x64), 'k', 'I'}, /* XK_KP_Insert */
426 {F(0x65), 'k', 'D'}, /* XK_KP_Delete */
427 {F(0x37), 'k', 'h'}, /* XK_KP_Home */
428 {F(0x58), '@', '7'}, /* XK_KP_End */
429 {F(0x39), 'k', 'P'}, /* XK_KP_Prior */
430 {F(0x60), 'k', 'N'}, /* XK_KP_Next */
431 {F(0x49), '&', '8'}, /* XK_Undo, keypad 5 */
432#endif
433
434 /* End of list marker: */
435 {0, 0, 0}
436};
437
438#define NUM_SPECIAL_KEYS (sizeof(special_keys)/sizeof(special_keys[0]))
439
440/* ---------------- VimApp ---------------- */
441
442 static void
443docd(BPath &path)
444{
445 mch_chdir(path.Path());
446 /* Do this to get the side effects of a :cd command */
447 do_cmdline_cmd((char_u *)"cd .");
448}
449
450/*
451 * Really handle dropped files and folders.
452 */
453 static void
454RefsReceived(BMessage *m, bool changedir)
455{
456 uint32 type;
457 int32 count;
458
459 //m->PrintToStream();
460 switch (m->what) {
461 case B_REFS_RECEIVED:
462 case B_SIMPLE_DATA:
463 m->GetInfo("refs", &type, &count);
464 if (type != B_REF_TYPE)
465 goto bad;
466 break;
467 case B_ARGV_RECEIVED:
468 m->GetInfo("argv", &type, &count);
469 if (type != B_STRING_TYPE)
470 goto bad;
471 if (changedir) {
472 char *dirname;
473 if (m->FindString("cwd", (const char **) &dirname) == B_OK) {
474 chdir(dirname);
475 do_cmdline_cmd((char_u *)"cd .");
476 }
477 }
478 break;
479 default:
480 bad:
481 //fprintf(stderr, "bad!\n");
482 delete m;
483 return;
484 }
485
486#ifdef FEAT_VISUAL
487 reset_VIsual();
488#endif
489
490 char_u **fnames;
491 fnames = (char_u **) alloc(count * sizeof(char_u *));
492 int fname_index = 0;
493
494 switch (m->what) {
495 case B_REFS_RECEIVED:
496 case B_SIMPLE_DATA:
497 //fprintf(stderr, "case B_REFS_RECEIVED\n");
498 for (int i = 0; i < count; ++i)
499 {
500 entry_ref ref;
501 if (m->FindRef("refs", i, &ref) == B_OK) {
502 BEntry entry(&ref, false);
503 BPath path;
504 entry.GetPath(&path);
505
506 /* Change to parent directory? */
507 if (changedir) {
508 BPath parentpath;
509 path.GetParent(&parentpath);
510 docd(parentpath);
511 }
512
513 /* Is it a directory? If so, cd into it. */
514 BDirectory bdir(&ref);
515 if (bdir.InitCheck() == B_OK) {
516 /* don't cd if we already did it */
517 if (!changedir)
518 docd(path);
519 } else {
520 mch_dirname(IObuff, IOSIZE);
521 char_u *fname = shorten_fname((char_u *)path.Path(), IObuff);
522 if (fname == NULL)
523 fname = (char_u *)path.Path();
524 fnames[fname_index++] = vim_strsave(fname);
525 //fprintf(stderr, "%s\n", fname);
526 }
527
528 /* Only do it for the first file/dir */
529 changedir = false;
530 }
531 }
532 break;
533 case B_ARGV_RECEIVED:
534 //fprintf(stderr, "case B_ARGV_RECEIVED\n");
535 for (int i = 1; i < count; ++i)
536 {
537 char *fname;
538
539 if (m->FindString("argv", i, (const char **) &fname) == B_OK) {
540 fnames[fname_index++] = vim_strsave((char_u *)fname);
541 }
542 }
543 break;
544 default:
545 //fprintf(stderr, "case default\n");
546 break;
547 }
548
549 delete m;
550
551 /* Handle the drop, :edit to get to the file */
552 if (fname_index > 0) {
553 handle_drop(fname_index, fnames, FALSE);
554
555 /* Update the screen display */
556 update_screen(NOT_VALID);
557 setcursor();
558 out_flush();
559 } else {
560 vim_free(fnames);
561 }
562}
563
564VimApp::VimApp(const char *appsig):
565 BApplication(appsig)
566{
567}
568
569VimApp::~VimApp()
570{
571}
572
573 void
574VimApp::ReadyToRun()
575{
576 /*
577 * Apparently signals are inherited by the created thread -
578 * disable the most annoying ones.
579 */
580 signal(SIGINT, SIG_IGN);
581 signal(SIGQUIT, SIG_IGN);
582}
583
584 void
585VimApp::ArgvReceived(int32 arg_argc, char **arg_argv)
586{
587 if (!IsLaunching()) {
588 /*
589 * This can happen if we are set to Single or Exclusive
590 * Launch. Be nice and open the file(s).
591 */
592 if (gui.vimWindow)
593 gui.vimWindow->Minimize(false);
594 BMessage *m = CurrentMessage();
595 DetachCurrentMessage();
596 SendRefs(m, true);
597 }
598}
599
600 void
601VimApp::RefsReceived(BMessage *m)
602{
603 /* Horrible hack!!! XXX XXX XXX
604 * The real problem is that b_start_ffc is set too late for
605 * the initial empty buffer. As a result the window will be
606 * split instead of abandoned.
607 */
608 int limit = 15;
609 while (--limit >= 0 && (curbuf == NULL || curbuf->b_start_ffc == 0))
610 snooze(100000); // 0.1 s
611 if (gui.vimWindow)
612 gui.vimWindow->Minimize(false);
613 DetachCurrentMessage();
614 SendRefs(m, true);
615}
616
617/*
618 * Pass a BMessage on to the main() thread.
619 * Caller must have detached the message.
620 */
621 void
622VimApp::SendRefs(BMessage *m, bool changedir)
623{
624 VimRefsMsg rm;
625 rm.message = m;
626 rm.changedir = changedir;
627
628 write_port(gui.vdcmp, VimMsg::Refs, &rm, sizeof(rm));
629 // calls ::RefsReceived
630}
631
632 bool
633VimApp::QuitRequested()
634{
635 (void)Inherited::QuitRequested();
636 return false;
637}
638
639/* ---------------- VimWindow ---------------- */
640
641VimWindow::VimWindow():
642 BWindow(BRect(40, 40, 150, 150),
643 "Vim",
644 B_TITLED_WINDOW,
645 0,
646 B_CURRENT_WORKSPACE)
647
648{
649 init();
650}
651
652VimWindow::~VimWindow()
653{
654 if (formView) {
655 RemoveChild(formView);
656 delete formView;
657 }
658 gui.vimWindow = NULL;
659}
660
661 void
662VimWindow::init()
663{
664 /* Attach the VimFormView */
665 formView = new VimFormView(Bounds());
666 if (formView != NULL) {
667 AddChild(formView);
668 }
669}
670
671 void
672VimWindow::DispatchMessage(BMessage *m, BHandler *h)
673{
674 /*
675 * Route B_MOUSE_UP messages to MouseUp(), in
676 * a manner that should be compatible with the
677 * intended future system behaviour.
678 */
679 switch (m->what) {
680 case B_MOUSE_UP:
681 // if (!h) h = PreferredHandler();
682// gcc isn't happy without this extra set of braces, complains about
683// jump to case label crosses init of 'class BView * v'
684// richard@whitequeen.com jul 99
685 {
686 BView *v = dynamic_cast<BView *>(h);
687 if (v) {
688 //m->PrintToStream();
689 BPoint where;
690 m->FindPoint("where", &where);
691 v->MouseUp(where);
692 } else {
693 Inherited::DispatchMessage(m, h);
694 }
695 }
696 break;
697 default:
698 Inherited::DispatchMessage(m, h);
699 }
700}
701
702 void
703VimWindow::WindowActivated(bool active)
704{
705 Inherited::WindowActivated(active);
706 /* the textArea gets the keyboard action */
707 if (active && gui.vimTextArea)
708 gui.vimTextArea->MakeFocus(true);
709
710 struct VimFocusMsg fm;
711 fm.active = active;
712
713 write_port(gui.vdcmp, VimMsg::Focus, &fm, sizeof(fm));
714}
715
716 bool
717VimWindow::QuitRequested()
718{
719 struct VimKeyMsg km;
720 km.length = 5;
721 memcpy((char *)km.chars, "\033:qa\r", km.length);
722
723 write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
724
725 return false;
726}
727
728/* ---------------- VimFormView ---------------- */
729
730VimFormView::VimFormView(BRect frame):
731 BView(frame, "VimFormView", B_FOLLOW_ALL_SIDES,
732 B_WILL_DRAW | B_FRAME_EVENTS),
733 menuBar(NULL),
734 textArea(NULL)
735{
736 init(frame);
737}
738
739VimFormView::~VimFormView()
740{
741 if (menuBar) {
742 RemoveChild(menuBar);
743#ifdef never
744 // deleting the menuBar leads to SEGV on exit
745 // richard@whitequeen.com Jul 99
746 delete menuBar;
747#endif
748 }
749 if (textArea) {
750 RemoveChild(textArea);
751 delete textArea;
752 }
753 gui.vimForm = NULL;
754}
755
756 void
757VimFormView::init(BRect frame)
758{
759 menuBar = new BMenuBar(BRect(0,0,-MENUBAR_MARGIN,-MENUBAR_MARGIN),
760 "VimMenuBar");
761
762 AddChild(menuBar);
763
764 BRect remaining = frame;
765 textArea = new VimTextAreaView(remaining);
766 AddChild(textArea);
767 /* The textArea will be resized later when menus are added */
768
769 gui.vimForm = this;
770}
771
772 void
773VimFormView::AllAttached()
774{
775 /*
776 * Apparently signals are inherited by the created thread -
777 * disable the most annoying ones.
778 */
779 signal(SIGINT, SIG_IGN);
780 signal(SIGQUIT, SIG_IGN);
781
782 if (menuBar && textArea) {
783 /*
784 * Resize the textArea to fill the space left over by the menu.
785 * This is somewhat futile since it will be done again once
786 * menus are added to the menu bar.
787 */
788 BRect remaining = Bounds();
789 remaining.top = MenuHeight();
790 textArea->ResizeTo(remaining.Width(), remaining.Height());
791 textArea->MoveTo(remaining.left, remaining.top);
792
793#ifdef FEAT_MENU
794 menuBar->ResizeTo(remaining.right, remaining.top);
795 gui.menu_height = (int) remaining.top;
796#endif
797 }
798 Inherited::AllAttached();
799}
800
801 void
802VimFormView::FrameResized(float new_width, float new_height)
803{
804 BWindow *w = Window();
805#if 1
806 /*
807 * Look if there are more resize messages in the queue.
808 * If so, ignore this one. The later one will be handled
809 * eventually.
810 */
811 BMessageQueue *q = w->MessageQueue();
812 if (q->FindMessage(B_VIEW_RESIZED, 0) != NULL) {
813 return;
814 }
815#endif
816 new_width += 1; // adjust from width to number of pixels occupied
817 new_height += 1;
818
819#if !HAVE_R3_OR_LATER
820 int adjust_h, adjust_w;
821
822 adjust_w = ((int)new_width - gui_get_base_width()) % gui.char_width;
823 adjust_h = ((int)new_height - gui_get_base_height()) % gui.char_height;
824
825 if (adjust_w > 0 || adjust_h > 0) {
826 /*
827 * This will generate a new FrameResized() message.
828 * If we're running R3 or later, SetWindowAlignment() should make
829 * sure that this does not happen.
830 */
831 w->ResizeBy(-adjust_w, -adjust_h);
832
833 return;
834 }
835#endif
836
837 struct VimResizeMsg sm;
838 sm.width = (int) new_width;
839 sm.height = (int) new_height;
840
841 write_port(gui.vdcmp, VimMsg::Resize, &sm, sizeof(sm));
842 // calls gui_resize_shell(new_width, new_height);
843
844 return;
845
846 /*
847 * The area below the vertical scrollbar is erased to the colour
848 * set with SetViewColor() automatically, because we had set
849 * B_WILL_DRAW. Resizing the window tight around the vertical
850 * scroll bar also helps to avoid debris.
851 */
852}
853
854/* ---------------- VimTextAreaView ---------------- */
855
856VimTextAreaView::VimTextAreaView(BRect frame):
857 BView(frame, "VimTextAreaView", B_FOLLOW_ALL_SIDES,
858 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
859 mouseDragEventCount(0)
860{
861 init(frame);
862}
863
864VimTextAreaView::~VimTextAreaView()
865{
866 gui.vimTextArea = NULL;
867}
868
869 void
870VimTextAreaView::init(BRect frame)
871{
872 /* set up global var for fast access */
873 gui.vimTextArea = this;
874
875 /*
876 * Tell the app server not to erase the view: we will
877 * fill it in completely by ourselves.
878 * (Does this really work? Even if not, it won't harm either.)
879 */
880 SetViewColor(B_TRANSPARENT_32_BIT);
881#define PEN_WIDTH 1
882 SetPenSize(PEN_WIDTH);
883}
884
885 void
886VimTextAreaView::Draw(BRect updateRect)
887{
888 /*
889 * XXX Other ports call here:
890 * out_flush(); * make sure all output has been processed *
891 * but we can't do that, since it involves too much information
892 * that is owned by other threads...
893 */
894
895 /*
896 * No need to use gui.vimWindow->Lock(): we are locked already.
897 * However, it would not hurt.
898 */
899 gui_redraw((int) updateRect.left, (int) updateRect.top,
900 (int) (updateRect.Width() + PEN_WIDTH), (int) (updateRect.Height() + PEN_WIDTH));
901
902 /* Clear the border areas if needed */
903 rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
904 SetLowColor(rgb);
905
906 if (updateRect.left < FILL_X(0)) // left border
907 FillRect(BRect(updateRect.left, updateRect.top,
908 FILL_X(0)-PEN_WIDTH, updateRect.bottom), B_SOLID_LOW);
909 if (updateRect.top < FILL_Y(0)) // top border
910 FillRect(BRect(updateRect.left, updateRect.top,
911 updateRect.right, FILL_Y(0)-PEN_WIDTH), B_SOLID_LOW);
912 if (updateRect.right >= FILL_X(Columns)) // right border
913 FillRect(BRect(FILL_X((int)Columns), updateRect.top,
914 updateRect.right, updateRect.bottom), B_SOLID_LOW);
915 if (updateRect.bottom >= FILL_Y(Rows)) // bottom border
916 FillRect(BRect(updateRect.left, FILL_Y((int)Rows),
917 updateRect.right, updateRect.bottom), B_SOLID_LOW);
918}
919
920 void
921VimTextAreaView::KeyDown(const char *bytes, int32 numBytes)
922{
923 struct VimKeyMsg km;
924 char_u *dest = km.chars;
925
926 BMessage *msg = Window()->CurrentMessage();
927 assert(msg);
928 //msg->PrintToStream();
929
930 /*
931 * Convert special keys to Vim codes.
932 * I think it is better to do it in the window thread
933 * so we use at least a little bit of the potential
934 * of our 2 CPUs. Besides, due to the fantastic mapping
935 * of special keys to UTF-8, we have quite some work to
936 * do...
937 * TODO: I'm not quite happy with detection of special
938 * keys. Perhaps I should use scan codes after all...
939 */
940 if (numBytes > 1) {
941 /* This cannot be a special key */
942 if (numBytes > KEY_MSG_BUFSIZ)
943 numBytes = KEY_MSG_BUFSIZ; // should never happen... ???
944 km.length = numBytes;
945 memcpy((char *)dest, bytes, numBytes);
946 } else {
947 int32 scancode = 0;
948 msg->FindInt32("key", &scancode);
949
950 int32 beModifiers = 0;
951 msg->FindInt32("modifiers", &beModifiers);
952
953 char_u string[3];
954 int len = 0;
955 km.length = 0;
956
957 bool canHaveVimModifiers = false;
958
959 /*
960 * For normal, printable ASCII characters, don't look them up
961 * to check if they might be a special key. They aren't.
962 */
963 assert(B_BACKSPACE <= 0x20);
964 assert(B_DELETE == 0x7F);
965 if (((char_u)bytes[0] <= 0x20 || (char_u)bytes[0] == 0x7F) &&
966 numBytes == 1) {
967 /*
968 * Due to the great nature of Be's mapping of special keys,
969 * viz. into the range of the control characters,
970 * we can only be sure it is *really* a special key if
971 * if it is special without using ctrl. So, only if ctrl is
972 * used, we need to check it unmodified.
973 */
974 if (beModifiers & B_CONTROL_KEY) {
975 int index = keyMap->normal_map[scancode];
976 int newNumBytes = keyMapChars[index];
977 char_u *newBytes = (char_u *)&keyMapChars[index + 1];
978
979 /*
980 * Check if still special without the control key.
981 * This is needed for BACKSPACE: that key does produce
982 * different values with modifiers (DEL).
983 * Otherwise we could simply have checked for equality.
984 */
985 if (newNumBytes != 1 || (*newBytes > 0x20 &&
986 *newBytes != 0x7F )) {
987 goto notspecial;
988 }
989 bytes = (char *)newBytes;
990 }
991 canHaveVimModifiers = true;
992
993 uint16 beoskey;
994 int first, last;
995
996 /*
997 * If numBytes == 0 that probably always indicates a special key.
998 * (does not happen yet)
999 */
1000 if (numBytes == 0 || bytes[0] == B_FUNCTION_KEY) {
1001 beoskey = F(scancode);
1002 first = FIRST_FUNCTION_KEY;
1003 last = NUM_SPECIAL_KEYS;
1004 } else if (*bytes == '\n' && scancode == 0x47) {
1005 /* remap the (non-keypad) ENTER key from \n to \r. */
1006 string[0] = '\r';
1007 len = 1;
1008 first = last = 0;
1009 } else {
1010 beoskey = K(bytes[0]);
1011 first = 0;
1012 last = FIRST_FUNCTION_KEY;
1013 }
1014
1015 for (int i = first; i < last; i++) {
1016 if (special_keys[i].BeKeys == beoskey) {
1017 string[0] = CSI;
1018 string[1] = special_keys[i].vim_code0;
1019 string[2] = special_keys[i].vim_code1;
1020 len = 3;
1021 }
1022 }
1023 }
1024 notspecial:
1025 if (len == 0) {
1026 string[0] = bytes[0];
1027 len = 1;
1028 }
1029
1030 /* Special keys (and a few others) may have modifiers */
1031#if 0
1032 if (len == 3 ||
1033 bytes[0] == B_SPACE || bytes[0] == B_TAB ||
1034 bytes[0] == B_RETURN || bytes[0] == '\r' ||
1035 bytes[0] == B_ESCAPE)
1036#else
1037 if (canHaveVimModifiers)
1038#endif
1039 {
1040 int modifiers;
1041 modifiers = 0;
1042 if (beModifiers & B_SHIFT_KEY)
1043 modifiers |= MOD_MASK_SHIFT;
1044 if (beModifiers & B_CONTROL_KEY)
1045 modifiers |= MOD_MASK_CTRL;
1046 if (beModifiers & B_OPTION_KEY)
1047 modifiers |= MOD_MASK_ALT;
1048
1049 /*
1050 * For some keys a shift modifier is translated into another key
1051 * code. Do we need to handle the case where len != 1 and
1052 * string[0] != CSI? (Not for BeOS, since len == 3 implies
1053 * string[0] == CSI...)
1054 */
1055 int key;
1056 if (string[0] == CSI && len == 3)
1057 key = TO_SPECIAL(string[1], string[2]);
1058 else
1059 key = string[0];
1060 key = simplify_key(key, &modifiers);
1061 if (IS_SPECIAL(key))
1062 {
1063 string[0] = CSI;
1064 string[1] = K_SECOND(key);
1065 string[2] = K_THIRD(key);
1066 len = 3;
1067 }
1068 else
1069 {
1070 string[0] = key;
1071 len = 1;
1072 }
1073
1074 if (modifiers)
1075 {
1076 *dest++ = CSI;
1077 *dest++ = KS_MODIFIER;
1078 *dest++ = modifiers;
1079 km.length = 3;
1080 }
1081 }
1082 memcpy((char *)dest, string, len);
1083 km.length += len;
1084 }
1085
1086 write_port(gui.vdcmp, VimMsg::Key, &km, sizeof(km));
1087
1088 /*
1089 * blank out the pointer if necessary
1090 */
1091 if (p_mh && !gui.pointer_hidden)
1092 {
1093 guiBlankMouse(true);
1094 gui.pointer_hidden = TRUE;
1095 }
1096}
1097 void
1098VimTextAreaView::guiSendMouseEvent(
1099 int button,
1100 int x,
1101 int y,
1102 int repeated_click,
1103 int_u modifiers)
1104{
1105 VimMouseMsg mm;
1106
1107 mm.button = button;
1108 mm.x = x;
1109 mm.y = y;
1110 mm.repeated_click = repeated_click;
1111 mm.modifiers = modifiers;
1112
1113 write_port(gui.vdcmp, VimMsg::Mouse, &mm, sizeof(mm));
1114 // calls gui_send_mouse_event()
1115
1116 /*
1117 * if our pointer is currently hidden, then we should show it.
1118 */
1119 if (gui.pointer_hidden)
1120 {
1121 guiBlankMouse(false);
1122 gui.pointer_hidden = FALSE;
1123 }
1124}
1125
1126 void
1127VimTextAreaView::guiBlankMouse(bool should_hide)
1128{
1129 if (should_hide) {
1130 //gui.vimApp->HideCursor();
1131 gui.vimApp->ObscureCursor();
1132 /*
1133 * ObscureCursor() would even be easier, but then
1134 * Vim's idea of mouse visibility does not necessarily
1135 * correspond to reality.
1136 */
1137 } else {
1138 //gui.vimApp->ShowCursor();
1139 }
1140}
1141
1142 int_u
1143VimTextAreaView::mouseModifiersToVim(int32 beModifiers)
1144{
1145 int_u vim_modifiers = 0x0;
1146
1147 if (beModifiers & B_SHIFT_KEY)
1148 vim_modifiers |= MOUSE_SHIFT;
1149 if (beModifiers & B_CONTROL_KEY)
1150 vim_modifiers |= MOUSE_CTRL;
1151 if (beModifiers & B_OPTION_KEY) /* Alt or Meta key */
1152 vim_modifiers |= MOUSE_ALT;
1153
1154 return vim_modifiers;
1155}
1156
1157 void
1158VimTextAreaView::MouseDown(BPoint point)
1159{
1160 BMessage *m = Window()->CurrentMessage();
1161 assert(m);
1162
1163 int32 buttons = 0;
1164 m->FindInt32("buttons", &buttons);
1165
1166 int vimButton;
1167
1168 if (buttons & B_PRIMARY_MOUSE_BUTTON)
1169 vimButton = MOUSE_LEFT;
1170 else if (buttons & B_SECONDARY_MOUSE_BUTTON)
1171 vimButton = MOUSE_RIGHT;
1172 else if (buttons & B_TERTIARY_MOUSE_BUTTON)
1173 vimButton = MOUSE_MIDDLE;
1174 else
1175 return; /* Unknown button */
1176
1177 vimMouseButton = 1; /* don't care which one */
1178
1179 /* Handle multiple clicks */
1180 int32 clicks = 0;
1181 m->FindInt32("clicks", &clicks);
1182
1183 int32 modifiers = 0;
1184 m->FindInt32("modifiers", &modifiers);
1185
1186 vimMouseModifiers = mouseModifiersToVim(modifiers);
1187
1188 guiSendMouseEvent(vimButton, point.x, point.y,
1189 clicks > 1 /* = repeated_click*/, vimMouseModifiers);
1190}
1191
1192 void
1193VimTextAreaView::MouseUp(BPoint point)
1194{
1195 vimMouseButton = 0;
1196
1197 BMessage *m = Window()->CurrentMessage();
1198 assert(m);
1199 //m->PrintToStream();
1200
1201 int32 modifiers = 0;
1202 m->FindInt32("modifiers", &modifiers);
1203
1204 vimMouseModifiers = mouseModifiersToVim(modifiers);
1205
1206 guiSendMouseEvent(MOUSE_RELEASE, point.x, point.y,
1207 0 /* = repeated_click*/, vimMouseModifiers);
1208
1209 Inherited::MouseUp(point);
1210}
1211
1212 void
1213VimTextAreaView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
1214{
1215 /*
1216 * if our pointer is currently hidden, then we should show it.
1217 */
1218 if (gui.pointer_hidden)
1219 {
1220 guiBlankMouse(false);
1221 gui.pointer_hidden = FALSE;
1222 }
1223
1224 if (!vimMouseButton) /* could also check m->"buttons" */
1225 return;
1226
1227 atomic_add(&mouseDragEventCount, 1);
1228
1229 /* Don't care much about "transit" */
1230 guiSendMouseEvent(MOUSE_DRAG, point.x, point.y, 0, vimMouseModifiers);
1231}
1232
1233 void
1234VimTextAreaView::MessageReceived(BMessage *m)
1235{
1236 switch (m->what) {
1237 case 'menu':
1238 {
1239 VimMenuMsg mm;
1240 mm.guiMenu = NULL; /* in case no pointer in msg */
1241 m->FindPointer("VimMenu", (void **)&mm.guiMenu);
1242
1243 write_port(gui.vdcmp, VimMsg::Menu, &mm, sizeof(mm));
1244 }
1245 break;
1246 default:
1247 if (m->WasDropped()) {
1248 BWindow *w = Window();
1249 w->DetachCurrentMessage();
1250 w->Minimize(false);
1251 VimApp::SendRefs(m, (modifiers() & B_SHIFT_KEY) != 0);
1252 } else {
1253 Inherited::MessageReceived(m);
1254 }
1255 break;
1256 }
1257}
1258
1259 int
1260VimTextAreaView::mchInitFont(char_u *name)
1261{
1262 VimFont *newFont = (VimFont *)gui_mch_get_font(name, 0);
1263
1264 gui.norm_font = (GuiFont)newFont;
1265 gui_mch_set_font((GuiFont)newFont);
1266 if (name)
1267 hl_set_font_name(name);
1268
1269 SetDrawingMode(B_OP_COPY);
1270
1271 /*
1272 * Try to load other fonts for bold, italic, and bold-italic.
1273 * We should also try to work out what font to use for these when they are
1274 * not specified by X resources, but we don't yet.
1275 */
1276
1277 return OK;
1278}
1279
1280 void
1281VimTextAreaView::mchDrawString(int row, int col, char_u *s, int len, int flags)
1282{
1283 /*
1284 * First we must erase the area, because DrawString won't do
1285 * that for us. XXX Most of the time this is a waste of effort
1286 * since the bachground has been erased already... DRAW_TRANSP
1287 * should be set when appropriate!!!
1288 * (Rectangles include the bottom and right edge)
1289 */
1290 if (!(flags & DRAW_TRANSP)) {
1291 BRect r(FILL_X(col), FILL_Y(row),
1292 FILL_X(col + len) - PEN_WIDTH, FILL_Y(row + 1) - PEN_WIDTH);
1293 FillRect(r, B_SOLID_LOW);
1294 }
1295 BPoint where(TEXT_X(col), TEXT_Y(row));
1296 DrawString((char *)s, len, where);
1297
1298 if (flags & DRAW_BOLD) {
1299 where.x += 1.0;
1300 SetDrawingMode(B_OP_BLEND);
1301 DrawString((char *)s, len, where);
1302 SetDrawingMode(B_OP_COPY);
1303 }
1304 if (flags & DRAW_UNDERL) {
1305 BPoint start(FILL_X(col), FILL_Y(row + 1) - PEN_WIDTH);
1306 BPoint end(FILL_X(col + len) - PEN_WIDTH, start.y);
1307
1308 StrokeLine(start, end);
1309 }
1310}
1311
1312 void
1313VimTextAreaView::mchClearBlock(
1314 int row1,
1315 int col1,
1316 int row2,
1317 int col2)
1318{
1319 BRect r(FILL_X(col1), FILL_Y(row1),
1320 FILL_X(col2 + 1) - PEN_WIDTH, FILL_Y(row2 + 1) - PEN_WIDTH);
1321 gui_mch_set_bg_color(gui.back_pixel);
1322 FillRect(r, B_SOLID_LOW);
1323}
1324
1325 void
1326VimTextAreaView::mchClearAll()
1327{
1328 gui_mch_set_bg_color(gui.back_pixel);
1329 FillRect(Bounds(), B_SOLID_LOW);
1330}
1331
1332/*
1333 * mchDeleteLines() Lock()s the window by itself.
1334 */
1335 void
1336VimTextAreaView::mchDeleteLines(int row, int num_lines)
1337{
1338 if (row + num_lines > gui.scroll_region_bot)
1339 {
1340 /* Scrolled out of region, just blank the lines out */
1341 gui_clear_block(row, gui.scroll_region_left,
1342 gui.scroll_region_bot, gui.scroll_region_right);
1343 }
1344 else
1345 {
1346 /* copy one extra pixel, for when bold has spilled over */
1347 int width = gui.char_width * (gui.scroll_region_right
1348 - gui.scroll_region_left + 1) + 1 - PEN_WIDTH;
1349 int height = gui.char_height *
1350 (gui.scroll_region_bot - row - num_lines + 1) - PEN_WIDTH;
1351
1352 BRect source, dest;
1353
1354 source.left = FILL_X(gui.scroll_region_left);
1355 source.top = FILL_Y(row + num_lines);
1356 source.right = source.left + width;
1357 source.bottom = source.top + height;
1358
1359 dest.left = FILL_X(gui.scroll_region_left);
1360 dest.top = FILL_Y(row);
1361 dest.right = dest.left + width;
1362 dest.bottom = dest.top + height;
1363
1364 /* XXX Attempt at a hack: */
1365 gui.vimWindow->UpdateIfNeeded();
1366#if 0
1367 /* XXX Attempt at a hack: */
1368 if (gui.vimWindow->NeedsUpdate()) {
1369 fprintf(stderr, "mchDeleteLines: NeedsUpdate!\n");
1370 gui.vimWindow->UpdateIfNeeded();
1371 while (gui.vimWindow->NeedsUpdate()) {
1372 if (false && gui.vimWindow->Lock()) {
1373 Sync();
1374 gui.vimWindow->Unlock();
1375 }
1376 snooze(2);
1377 }
1378 }
1379#endif
1380
1381 if (gui.vimWindow->Lock()) {
1382 Sync();
1383 CopyBits(source, dest);
1384 //Sync();
1385
1386 /* Update gui.cursor_row if the cursor scrolled or copied over */
1387 if (gui.cursor_row >= row
1388 && gui.cursor_col >= gui.scroll_region_left
1389 && gui.cursor_col <= gui.scroll_region_right)
1390 {
1391 if (gui.cursor_row < row + num_lines)
1392 gui.cursor_is_valid = FALSE;
1393 else if (gui.cursor_row <= gui.scroll_region_bot)
1394 gui.cursor_row -= num_lines;
1395 }
1396
1397 /* Clear one column more for when bold has spilled over */
1398 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
1399 gui.scroll_region_left,
1400 gui.scroll_region_bot, gui.scroll_region_right);
1401
1402 gui.vimWindow->Unlock();
1403 /*
1404 * The Draw() callback will be called now if some of the source
1405 * bits were not in the visible region.
1406 */
1407
1408 //gui_x11_check_copy_area();
1409 }
1410 }
1411}
1412
1413/*
1414 * mchInsertLines() Lock()s the window by itself.
1415 */
1416 void
1417VimTextAreaView::mchInsertLines(int row, int num_lines)
1418{
1419 if (row + num_lines > gui.scroll_region_bot)
1420 {
1421 /* Scrolled out of region, just blank the lines out */
1422 gui_clear_block(row, gui.scroll_region_left,
1423 gui.scroll_region_bot, gui.scroll_region_right);
1424 }
1425 else
1426 {
1427 /* copy one extra pixel, for when bold has spilled over */
1428 int width = gui.char_width * (gui.scroll_region_right
1429 - gui.scroll_region_left + 1) + 1 - PEN_WIDTH;
1430 int height = gui.char_height *
1431 (gui.scroll_region_bot - row - num_lines + 1) - PEN_WIDTH;
1432
1433 BRect source, dest;
1434
1435 source.left = FILL_X(gui.scroll_region_left);
1436 source.top = FILL_Y(row);
1437 source.right = source.left + width;
1438 source.bottom = source.top + height;
1439
1440 dest.left = FILL_X(gui.scroll_region_left);
1441 dest.top = FILL_Y(row + num_lines);
1442 dest.right = dest.left + width;
1443 dest.bottom = dest.top + height;
1444
1445 /* XXX Attempt at a hack: */
1446 gui.vimWindow->UpdateIfNeeded();
1447#if 0
1448 /* XXX Attempt at a hack: */
1449 if (gui.vimWindow->NeedsUpdate())
1450 fprintf(stderr, "mchInsertLines: NeedsUpdate!\n");
1451 gui.vimWindow->UpdateIfNeeded();
1452 while (gui.vimWindow->NeedsUpdate())
1453 snooze(2);
1454#endif
1455
1456 if (gui.vimWindow->Lock()) {
1457 Sync();
1458 CopyBits(source, dest);
1459 //Sync();
1460
1461 /* Update gui.cursor_row if the cursor scrolled or copied over */
1462 if (gui.cursor_row >= gui.row
1463 && gui.cursor_col >= gui.scroll_region_left
1464 && gui.cursor_col <= gui.scroll_region_right)
1465 {
1466 if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
1467 gui.cursor_row += num_lines;
1468 else if (gui.cursor_row <= gui.scroll_region_bot)
1469 gui.cursor_is_valid = FALSE;
1470 }
1471 /* Clear one column more for when bold has spilled over */
1472 gui_clear_block(row, gui.scroll_region_left,
1473 row + num_lines - 1, gui.scroll_region_right);
1474
1475 gui.vimWindow->Unlock();
1476 /*
1477 * The Draw() callback will be called now if some of the source
1478 * bits were not in the visible region.
1479 * However, if we scroll too fast it can't keep up and the
1480 * update region gets messed up. This seems to be because copying
1481 * un-Draw()n bits does not generate Draw() calls for the copy...
1482 * I moved the hack to before the CopyBits() to reduce the
1483 * amount of additional waiting needed.
1484 */
1485
1486 //gui_x11_check_copy_area();
1487 }
1488 }
1489
1490}
1491
1492/* ---------------- VimScrollBar ---------------- */
1493
1494/* BUG: XXX
1495 * It seems that BScrollBar determine their direction not from
1496 * "posture" but from if they are "tall" or "wide" in shape...
1497 *
1498 * Also, place them out of sight, because Vim enables them before
1499 * they are positioned.
1500 */
1501VimScrollBar::VimScrollBar(scrollbar_T *g, orientation posture):
1502 BScrollBar(posture == B_HORIZONTAL ? BRect(-100,-100,-10,-90) :
1503 BRect(-100,-100,-90,-10),
1504 "vim scrollbar", (BView *)NULL,
1505 0.0, 10.0, posture),
1506 ignoreValue(-1),
1507 scrollEventCount(0)
1508{
1509 gsb = g;
1510 SetResizingMode(B_FOLLOW_NONE);
1511}
1512
1513VimScrollBar::~VimScrollBar()
1514{
1515}
1516
1517 void
1518VimScrollBar::ValueChanged(float newValue)
1519{
1520 if (ignoreValue >= 0.0 && newValue == ignoreValue) {
1521 ignoreValue = -1;
1522 return;
1523 }
1524 ignoreValue = -1;
1525 /*
1526 * We want to throttle the amount of scroll messages generated.
1527 * Normally I presume you won't get a new message before we've
1528 * handled the previous one, but because we're passing them on this
1529 * happens very quickly. So instead we keep a counter of how many
1530 * scroll events there are (or will be) in the VDCMP, and the
1531 * throttling happens at the receiving end.
1532 */
1533 atomic_add(&scrollEventCount, 1);
1534
1535 struct VimScrollBarMsg sm;
1536
1537 sm.sb = this;
1538 sm.value = (long) newValue;
1539 sm.stillDragging = TRUE;
1540
1541 write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));
1542
1543 // calls gui_drag_scrollbar(sb, newValue, TRUE);
1544}
1545
1546/*
1547 * When the mouse goes up, report that scrolling has stopped.
1548 * MouseUp() is NOT called when the mouse-up occurs outside
1549 * the window, even though the thumb does move while the mouse
1550 * is outside... This has some funny effects... XXX
1551 * So we do special processing when the window de/activates.
1552 */
1553 void
1554VimScrollBar::MouseUp(BPoint where)
1555{
1556 //BMessage *m = Window()->CurrentMessage();
1557 //m->PrintToStream();
1558
1559 atomic_add(&scrollEventCount, 1);
1560
1561 struct VimScrollBarMsg sm;
1562
1563 sm.sb = this;
1564 sm.value = (long) Value();
1565 sm.stillDragging = FALSE;
1566
1567 write_port(gui.vdcmp, VimMsg::ScrollBar, &sm, sizeof(sm));
1568
1569 // calls gui_drag_scrollbar(sb, newValue, FALSE);
1570
1571 Inherited::MouseUp(where);
1572}
1573
1574 void
1575VimScrollBar::SetValue(float newValue)
1576{
1577 if (newValue == Value())
1578 return;
1579
1580 ignoreValue = newValue;
1581 Inherited::SetValue(newValue);
1582}
1583
1584/* ---------------- VimFont ---------------- */
1585
1586VimFont::VimFont(): BFont()
1587{
1588 init();
1589}
1590
1591VimFont::VimFont(const VimFont *rhs): BFont(rhs)
1592{
1593 init();
1594}
1595
1596VimFont::VimFont(const BFont *rhs): BFont(rhs)
1597{
1598 init();
1599}
1600
1601VimFont::VimFont(const VimFont &rhs): BFont(rhs)
1602{
1603 init();
1604}
1605
1606VimFont::~VimFont()
1607{
1608}
1609
1610 void
1611VimFont::init()
1612{
1613 next = NULL;
1614 refcount = 1;
1615 name = NULL;
1616}
1617
1618/* ---------------- ---------------- */
1619
1620// some global variables
1621static char appsig[] = "application/x-vnd.Rhialto-Vim-5";
1622key_map *keyMap;
1623char *keyMapChars;
1624int main_exitcode = 127;
1625
1626 status_t
1627gui_beos_process_event(bigtime_t timeout)
1628{
1629 struct VimMsg vm;
1630 long what;
1631 ssize_t size;
1632
1633 size = read_port_etc(gui.vdcmp, &what, &vm, sizeof(vm),
1634 B_TIMEOUT, timeout);
1635
1636 if (size >= 0) {
1637 switch (what) {
1638 case VimMsg::Key:
1639 {
1640 char_u *string = vm.u.Key.chars;
1641 int len = vm.u.Key.length;
1642 if (len == 1 && string[0] == Ctrl_chr('C')) {
1643 trash_input_buf();
1644 got_int = TRUE;
1645 }
1646 add_to_input_buf(string, len);
1647 }
1648 break;
1649 case VimMsg::Resize:
1650 gui_resize_shell(vm.u.NewSize.width, vm.u.NewSize.height);
1651 break;
1652 case VimMsg::ScrollBar:
1653 {
1654 /*
1655 * If loads of scroll messages queue up, use only the last
1656 * one. Always report when the scrollbar stops dragging.
1657 * This is not perfect yet anyway: these events are queued
1658 * yet again, this time in the keyboard input buffer.
1659 */
1660 int32 oldCount =
1661 atomic_add(&vm.u.Scroll.sb->scrollEventCount, -1);
1662 if (oldCount <= 1 || !vm.u.Scroll.stillDragging)
1663 gui_drag_scrollbar(vm.u.Scroll.sb->getGsb(),
1664 vm.u.Scroll.value, vm.u.Scroll.stillDragging);
1665 }
1666 break;
1667 case VimMsg::Menu:
1668 gui_menu_cb(vm.u.Menu.guiMenu);
1669 break;
1670 case VimMsg::Mouse:
1671 {
1672 int32 oldCount;
1673 if (vm.u.Mouse.button == MOUSE_DRAG)
1674 oldCount =
1675 atomic_add(&gui.vimTextArea->mouseDragEventCount, -1);
1676 else
1677 oldCount = 0;
1678 if (oldCount <= 1)
1679 gui_send_mouse_event(vm.u.Mouse.button, vm.u.Mouse.x,
1680 vm.u.Mouse.y, vm.u.Mouse.repeated_click,
1681 vm.u.Mouse.modifiers);
1682 }
1683 break;
1684 case VimMsg::Focus:
1685 gui.in_focus = vm.u.Focus.active;
1686 /* XXX Signal that scrollbar dragging has stopped?
1687 * This is needed because we don't get a MouseUp if
1688 * that happens while outside the window... :-(
1689 */
1690 if (gui.dragged_sb) {
1691 gui.dragged_sb = SBAR_NONE;
1692 }
1693 gui_update_cursor(TRUE, FALSE);
1694 break;
1695 case VimMsg::Refs:
1696 ::RefsReceived(vm.u.Refs.message, vm.u.Refs.changedir);
1697 break;
1698 default:
1699 // unrecognised message, ignore it
1700 break;
1701 }
1702 }
1703
1704 /*
1705 * If size < B_OK, it is an error code.
1706 */
1707 return size;
1708}
1709
1710/*
1711 * Here are some functions to protect access to ScreenLines[] and
1712 * LineOffset[]. These are used from the window thread to respond
1713 * to a Draw() callback. When that occurs, the window is already
1714 * locked by the system.
1715 *
1716 * Other code that needs to lock is any code that changes these
1717 * variables. Other read-only access, or access merely to the
1718 * contents of the screen buffer, need not be locked.
1719 *
1720 * If there is no window, don't call Lock() but do succeed.
1721 */
1722
1723 int
1724vim_lock_screen()
1725{
1726 return !gui.vimWindow || gui.vimWindow->Lock();
1727}
1728
1729 void
1730vim_unlock_screen()
1731{
1732 if (gui.vimWindow)
1733 gui.vimWindow->Unlock();
1734}
1735
1736#define RUN_BAPPLICATION_IN_NEW_THREAD 0
1737
1738#if RUN_BAPPLICATION_IN_NEW_THREAD
1739
1740 int32
1741run_vimapp(void *args)
1742{
1743 VimApp app(appsig);
1744
1745 gui.vimApp = &app;
1746 app.Run(); /* Run until Quit() called */
1747
1748 return 0;
1749}
1750
1751#else
1752
1753 int32
1754call_main(void *args)
1755{
1756 struct MainArgs *ma = (MainArgs *)args;
1757
1758 return main(ma->argc, ma->argv);
1759}
1760#endif
1761
1762extern "C" {
1763
1764/*
1765 * Parse the GUI related command-line arguments. Any arguments used are
1766 * deleted from argv, and *argc is decremented accordingly. This is called
1767 * when vim is started, whether or not the GUI has been started.
1768 */
1769 void
1770gui_mch_prepare(
1771 int *argc,
1772 char **argv)
1773{
1774 /*
1775 * We don't have any command line arguments for the BeOS GUI yet,
1776 * but this is an excellent place to create our Application object.
1777 */
1778 if (!gui.vimApp) {
1779 thread_info tinfo;
1780 get_thread_info(find_thread(NULL), &tinfo);
1781
1782 /* May need the port very early on to process RefsReceived() */
1783 gui.vdcmp = create_port(B_MAX_PORT_COUNT, "vim VDCMP");
1784
1785#if RUN_BAPPLICATION_IN_NEW_THREAD
1786 thread_id tid = spawn_thread(run_vimapp, "vim VimApp",
1787 tinfo.priority, NULL);
1788 if (tid >= B_OK) {
1789 resume_thread(tid);
1790 } else {
1791 getout(1);
1792 }
1793#else
1794 MainArgs ma = { *argc, argv };
1795 thread_id tid = spawn_thread(call_main, "vim main()",
1796 tinfo.priority, &ma);
1797 if (tid >= B_OK) {
1798 VimApp app(appsig);
1799
1800 gui.vimApp = &app;
1801 resume_thread(tid);
1802 /*
1803 * This is rather horrible.
1804 * call_main will call main() again...
1805 * There will be no infinite recursion since
1806 * gui.vimApp is set now.
1807 */
1808 app.Run(); /* Run until Quit() called */
1809 //fprintf(stderr, "app.Run() returned...\n");
1810 status_t dummy_exitcode;
1811 (void)wait_for_thread(tid, &dummy_exitcode);
1812
1813 /*
1814 * This path should be the normal one taken to exit Vim.
1815 * The main() thread calls mch_exit() which calls
1816 * gui_mch_exit() which terminates its thread.
1817 */
1818 exit(main_exitcode);
1819 }
1820#endif
1821 }
1822 /* Don't fork() when starting the GUI. Spawned threads are not
1823 * duplicated with a fork(). The result is a mess.
1824 */
1825 gui.dofork = FALSE;
1826 /*
1827 * XXX Try to determine whether we were started from
1828 * the Tracker or the terminal.
1829 * It would be nice to have this work, because the Tracker
1830 * follows symlinks, so even if you double-click on gvim,
1831 * when it is a link to vim it will still pass a command name
1832 * of vim...
1833 * We try here to see if stdin comes from /dev/null. If so,
1834 * (or if there is an error, which should never happen) start the GUI.
1835 * This does the wrong thing for vim - </dev/null, and we're
1836 * too early to see the command line parsing. Tough.
1837 * On the other hand, it starts the gui for vim file & which is nice.
1838 */
1839 if (!isatty(0)) {
1840 struct stat stat_stdin, stat_dev_null;
1841
1842 if (fstat(0, &stat_stdin) == -1 ||
1843 stat("/dev/null", &stat_dev_null) == -1 ||
1844 (stat_stdin.st_dev == stat_dev_null.st_dev &&
1845 stat_stdin.st_ino == stat_dev_null.st_ino))
1846 gui.starting = TRUE;
1847 }
1848}
1849
1850/*
1851 * Check if the GUI can be started. Called before gvimrc is sourced.
1852 * Return OK or FAIL.
1853 */
1854 int
1855gui_mch_init_check(void)
1856{
1857 return OK; /* TODO: GUI can always be started? */
1858}
1859
1860/*
1861 * Initialise the GUI. Create all the windows, set up all the call-backs
1862 * etc.
1863 */
1864 int
1865gui_mch_init()
1866{
1867 gui.def_norm_pixel = RGB(0x00, 0x00, 0x00); // black
1868 gui.def_back_pixel = RGB(0xFF, 0xFF, 0xFF); // white
1869 gui.norm_pixel = gui.def_norm_pixel;
1870 gui.back_pixel = gui.def_back_pixel;
1871
1872 gui.scrollbar_width = (int) B_V_SCROLL_BAR_WIDTH;
1873 gui.scrollbar_height = (int) B_H_SCROLL_BAR_HEIGHT;
1874#ifdef FEAT_MENU
1875 gui.menu_height = 19; // initial guess -
1876 // correct for my default settings
1877#endif
1878 gui.border_offset = 3; // coordinates are inside window borders
1879
1880 if (gui.vdcmp < B_OK)
1881 return FAIL;
1882 get_key_map(&keyMap, &keyMapChars);
1883
1884 gui.vimWindow = new VimWindow(); /* hidden and locked */
1885 if (!gui.vimWindow)
1886 return FAIL;
1887
1888 gui.vimWindow->Run(); /* Run() unlocks but does not show */
1889
1890 /* Get the colors from the "Normal" group (set in syntax.c or in a vimrc
1891 * file) */
1892 set_normal_colors();
1893
1894 /*
1895 * Check that none of the colors are the same as the background color
1896 */
1897 gui_check_colors();
1898
1899 /* Get the colors for the highlight groups (gui_check_colors() might have
1900 * changed them) */
1901 highlight_gui_started(); /* re-init colors and fonts */
1902
1903 gui_mch_new_colors(); /* window must exist for this */
1904
1905 return OK;
1906}
1907
1908/*
1909 * Called when the foreground or background color has been changed.
1910 */
1911 void
1912gui_mch_new_colors()
1913{
1914 rgb_color rgb = GUI_TO_RGB(gui.back_pixel);
1915
1916 if (gui.vimWindow->Lock()) {
1917 gui.vimForm->SetViewColor(rgb);
1918 // Does this not have too much effect for those small rectangles?
1919 gui.vimForm->Invalidate();
1920 gui.vimWindow->Unlock();
1921 }
1922}
1923
1924/*
1925 * Open the GUI window which was created by a call to gui_mch_init().
1926 */
1927 int
1928gui_mch_open()
1929{
1930 if (gui_win_x != -1 && gui_win_y != -1)
1931 gui_mch_set_winpos(gui_win_x, gui_win_y);
1932
1933 /* Actually open the window */
1934 if (gui.vimWindow->Lock()) {
1935 gui.vimWindow->Show();
1936 gui.vimWindow->Unlock();
1937
1938#if USE_THREAD_FOR_INPUT_WITH_TIMEOUT
1939 /* Kill the thread that may have been created for the Terminal */
1940 beos_cleanup_read_thread();
1941#endif
1942
1943 return OK;
1944 }
1945
1946 return FAIL;
1947}
1948
1949 void
1950gui_mch_exit(int vim_exitcode)
1951{
1952 if (gui.vimWindow) {
1953 thread_id tid = gui.vimWindow->Thread();
1954 gui.vimWindow->Lock();
1955 gui.vimWindow->Quit();
1956 /* Wait until it is truely gone */
1957 int32 exitcode;
1958 wait_for_thread(tid, &exitcode);
1959 }
1960 delete_port(gui.vdcmp);
1961#if !RUN_BAPPLICATION_IN_NEW_THREAD
1962 /*
1963 * We are in the main() thread - quit the App thread and
1964 * quit ourselves (passing on the exitcode). Use a global since the
1965 * value from exit_thread() is only used if wait_for_thread() is
1966 * called in time (race condition).
1967 */
1968#endif
1969 if (gui.vimApp) {
1970 VimTextAreaView::guiBlankMouse(false);
1971
1972 main_exitcode = vim_exitcode;
1973#if RUN_BAPPLICATION_IN_NEW_THREAD
1974 thread_id tid = gui.vimApp->Thread();
1975 int32 exitcode;
1976 gui.vimApp->Lock();
1977 gui.vimApp->Quit();
1978 gui.vimApp->Unlock();
1979 wait_for_thread(tid, &exitcode);
1980#else
1981 gui.vimApp->Lock();
1982 gui.vimApp->Quit();
1983 gui.vimApp->Unlock();
1984 /* suicide */
1985 exit_thread(vim_exitcode);
1986#endif
1987 }
1988 /* If we are somehow still here, let mch_exit() handle things. */
1989}
1990
1991/*
1992 * Get the position of the top left corner of the window.
1993 */
1994 int
1995gui_mch_get_winpos(int *x, int *y)
1996{
1997 /* TODO */
1998 return FAIL;
1999}
2000
2001/*
2002 * Set the position of the top left corner of the window to the given
2003 * coordinates.
2004 */
2005 void
2006gui_mch_set_winpos(int x, int y)
2007{
2008 /* TODO */
2009}
2010
2011/*
2012 * Set the size of the window to the given width and height in pixels.
2013 */
2014 void
2015gui_mch_set_shellsize(
2016 int width,
2017 int height,
2018 int min_width,
2019 int min_height,
2020 int base_width,
2021 int base_height)
2022{
2023 /*
2024 * We are basically given the size of the VimForm, if I understand
2025 * correctly. Since it fills the window completely, this will also
2026 * be the size of the window.
2027 */
2028 if (gui.vimWindow->Lock()) {
2029 gui.vimWindow->ResizeTo(width - PEN_WIDTH, height - PEN_WIDTH);
2030
2031 /* set size limits */
2032 float minWidth, maxWidth, minHeight, maxHeight;
2033
2034 gui.vimWindow->GetSizeLimits(&minWidth, &maxWidth,
2035 &minHeight, &maxHeight);
2036 gui.vimWindow->SetSizeLimits(min_width, maxWidth,
2037 min_height, maxHeight);
2038
2039#if HAVE_R3_OR_LATER
2040 /*
2041 * Set the resizing alignment depending on font size.
2042 * XXX This is untested, since I don't have R3 yet.
2043 */
2044 SetWindowAlignment(
2045 B_PIXEL_ALIGNMENT, // window_alignment mode,
2046 1, // int32 h,
2047 0, // int32 hOffset = 0,
2048 gui.char_width, // int32 width = 0,
2049 base_width, // int32 widthOffset = 0,
2050 1, // int32 v = 0,
2051 0, // int32 vOffset = 0,
2052 gui.char_height, // int32 height = 0,
2053 base_height // int32 heightOffset = 0
2054 );
2055#else
2056 /* don't know what to do with base_{width,height}. */
2057#endif
2058
2059 gui.vimWindow->Unlock();
2060 }
2061}
2062
2063 void
2064gui_mch_get_screen_dimensions(
2065 int *screen_w,
2066 int *screen_h)
2067{
2068 BRect frame;
2069
2070 {
2071 BScreen screen(gui.vimWindow);
2072
2073 if (screen.IsValid()) {
2074 frame = screen.Frame();
2075 } else {
2076 frame.right = 640;
2077 frame.bottom = 480;
2078 }
2079 }
2080
2081 /* XXX approximations... */
2082 *screen_w = (int) frame.right - 2 * gui.scrollbar_width - 20;
2083 *screen_h = (int) frame.bottom - gui.scrollbar_height
2084#ifdef FEAT_MENU
2085 - gui.menu_height
2086#endif
2087 - 30;
2088}
2089
2090 void
2091gui_mch_set_text_area_pos(
2092 int x,
2093 int y,
2094 int w,
2095 int h)
2096{
2097 if (!gui.vimTextArea)
2098 return;
2099
2100 if (gui.vimWindow->Lock()) {
2101 gui.vimTextArea->MoveTo(x, y);
2102 gui.vimTextArea->ResizeTo(w - PEN_WIDTH, h - PEN_WIDTH);
2103 gui.vimWindow->Unlock();
2104 }
2105}
2106
2107
2108/*
2109 * Scrollbar stuff:
2110 */
2111
2112 void
2113gui_mch_enable_scrollbar(
2114 scrollbar_T *sb,
2115 int flag)
2116{
2117 VimScrollBar *vsb = sb->id;
2118 if (gui.vimWindow->Lock()) {
2119 /*
2120 * This function is supposed to be idempotent, but Show()/Hide()
2121 * is not. Therefore we test if they are needed.
2122 */
2123 if (flag) {
2124 if (vsb->IsHidden()) {
2125 vsb->Show();
2126 }
2127 } else {
2128 if (!vsb->IsHidden()) {
2129 vsb->Hide();
2130 }
2131 }
2132 gui.vimWindow->Unlock();
2133 }
2134}
2135
2136 void
2137gui_mch_set_scrollbar_thumb(
2138 scrollbar_T *sb,
2139 int val,
2140 int size,
2141 int max)
2142{
2143 if (gui.vimWindow->Lock()) {
2144 VimScrollBar *s = sb->id;
2145 if (max == 0) {
2146 s->SetValue(0);
2147 s->SetRange(0.0, 0.0);
2148 } else {
2149 s->SetProportion((float)size / (max + 1.0));
2150 s->SetSteps(1.0, size > 5 ? size - 2 : size);
2151#ifndef SCROLL_PAST_END // really only defined in gui.c...
2152 max = max + 1 - size;
2153#endif
2154 if (max < s->Value()) {
2155 /*
2156 * If the new maximum is lower than the current value,
2157 * setting it would cause the value to be clipped and
2158 * therefore a ValueChanged() call.
2159 * We avoid this by setting the value first, because
2160 * it presumably is <= max.
2161 */
2162 s->SetValue(val);
2163 s->SetRange(0.0, max);
2164 } else {
2165 /*
2166 * In the other case, set the range first, since the
2167 * new value might be higher than the current max.
2168 */
2169 s->SetRange(0.0, max);
2170 s->SetValue(val);
2171 }
2172 }
2173 gui.vimWindow->Unlock();
2174 }
2175}
2176
2177 void
2178gui_mch_set_scrollbar_pos(
2179 scrollbar_T *sb,
2180 int x,
2181 int y,
2182 int w,
2183 int h)
2184{
2185 if (gui.vimWindow->Lock()) {
2186 VimScrollBar *vsb = sb->id;
2187 vsb->MoveTo(x, y);
2188 vsb->ResizeTo(w - PEN_WIDTH, h - PEN_WIDTH);
2189 gui.vimWindow->Unlock();
2190 }
2191}
2192
2193 void
2194gui_mch_create_scrollbar(
2195 scrollbar_T *sb,
2196 int orient) /* SBAR_VERT or SBAR_HORIZ */
2197{
2198 orientation posture =
2199 (orient == SBAR_HORIZ) ? B_HORIZONTAL : B_VERTICAL;
2200
2201 VimScrollBar *vsb = sb->id = new VimScrollBar(sb, posture);
2202 if (gui.vimWindow->Lock()) {
2203 vsb->SetTarget(gui.vimTextArea);
2204 vsb->Hide();
2205 gui.vimForm->AddChild(vsb);
2206 gui.vimWindow->Unlock();
2207 }
2208}
2209
2210#if defined(FEAT_WINDOWS) || defined(PROTO)
2211 void
2212gui_mch_destroy_scrollbar(
2213 scrollbar_T *sb)
2214{
2215 if (gui.vimWindow->Lock()) {
2216 sb->id->RemoveSelf();
2217 delete sb->id;
2218 gui.vimWindow->Unlock();
2219 }
2220}
2221#endif
2222
2223/*
2224 * Cursor blink functions.
2225 *
2226 * This is a simple state machine:
2227 * BLINK_NONE not blinking at all
2228 * BLINK_OFF blinking, cursor is not shown
2229 * BLINK_ON blinking, cursor is shown
2230 */
2231
2232#define BLINK_NONE 0
2233#define BLINK_OFF 1
2234#define BLINK_ON 2
2235
2236static int blink_state = BLINK_NONE;
2237static long_u blink_waittime = 700;
2238static long_u blink_ontime = 400;
2239static long_u blink_offtime = 250;
2240static int blink_timer = 0;
2241
2242 void
2243gui_mch_set_blinking(
2244 long waittime,
2245 long on,
2246 long off)
2247{
2248 /* TODO */
2249 blink_waittime = waittime;
2250 blink_ontime = on;
2251 blink_offtime = off;
2252}
2253
2254/*
2255 * Stop the cursor blinking. Show the cursor if it wasn't shown.
2256 */
2257 void
2258gui_mch_stop_blink()
2259{
2260 /* TODO */
2261 if (blink_timer != 0)
2262 {
2263 //XtRemoveTimeOut(blink_timer);
2264 blink_timer = 0;
2265 }
2266 if (blink_state == BLINK_OFF)
2267 gui_update_cursor(TRUE, FALSE);
2268 blink_state = BLINK_NONE;
2269}
2270
2271/*
2272 * Start the cursor blinking. If it was already blinking, this restarts the
2273 * waiting time and shows the cursor.
2274 */
2275 void
2276gui_mch_start_blink()
2277{
2278 /* TODO */
2279 if (blink_timer != 0)
2280 ;//XtRemoveTimeOut(blink_timer);
2281 /* Only switch blinking on if none of the times is zero */
2282 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
2283 {
2284 blink_timer = 1; //XtAppAddTimeOut(app_context, blink_waittime,
2285 blink_state = BLINK_ON;
2286 gui_update_cursor(TRUE, FALSE);
2287 }
2288}
2289
2290/*
2291 * Initialise vim to use the font with the given name. Return FAIL if the font
2292 * could not be loaded, OK otherwise.
2293 */
2294 int
2295gui_mch_init_font(
2296 char_u *font_name,
2297 int fontset)
2298{
2299 if (gui.vimWindow->Lock())
2300 {
2301 int rc = gui.vimTextArea->mchInitFont(font_name);
2302 gui.vimWindow->Unlock();
2303
2304 return rc;
2305 }
2306
2307 return FAIL;
2308}
2309
2310 int
2311gui_mch_adjust_charsize()
2312{
2313 return FAIL;
2314}
2315
2316 GuiFont
2317gui_mch_get_font(
2318 char_u *name,
2319 int giveErrorIfMissing)
2320{
2321 VimFont *font = 0;
2322 static VimFont *fontList = NULL;
2323
2324 if (!gui.in_use) /* can't do this when GUI not running */
2325 return NOFONT;
2326
2327 if (!name)
2328 name = (char_u *)"be_fixed_font";
2329
2330 VimFont *flp;
2331 for (flp = fontList; flp; flp = flp->next) {
2332 if (STRCMP(name, flp->name) == 0) {
2333 flp->refcount++;
2334 return (GuiFont)flp;
2335 }
2336 }
2337
2338 font = new VimFont(be_fixed_font);
2339
2340 /* Set some universal features: */
2341 font->SetSpacing(B_FIXED_SPACING);
2342 font->SetEncoding(B_ISO_8859_1);
2343
2344 /* Remember font for later use */
2345 font->name = vim_strsave(name);
2346 font->next = fontList;
2347 fontList = font;
2348
2349 font_family family;
2350 font_style style;
2351 int size;
2352 int len;
2353 char_u *end;
2354
2355#ifdef never
2356 // This leads to SEGV/BUS on R4+
2357 // Replace underscores with spaces, and I can't see why ?
2358 // richard@whitequeen.com jul 99
2359 while (end = (char_u *)strchr((char *)name, '_'))
2360 *end = ' ';
2361#endif
2362 /*
2363 * Parse font names as Family/Style/Size.
2364 * On errors, just keep the be_fixed_font.
2365 */
2366 end = (char_u *)strchr((char *)name, '/');
2367 if (!end)
2368 goto error;
2369 strncpy(family, (char *)name, len = end - name);
2370 family[len] = '\0';
2371
2372 name = end + 1;
2373 end = (char_u *)strchr((char *)name, '/');
2374 if (!end)
2375 goto error;
2376 strncpy(style, (char *)name, len = end - name);
2377 style[len] = '\0';
2378
2379 name = end + 1;
2380 size = atoi((char *)name);
2381 if (size <= 0)
2382 goto error;
2383
2384 font->SetFamilyAndStyle(family, style);
2385 font->SetSize(size);
2386 font->SetSpacing(B_FIXED_SPACING);
2387 font->SetEncoding(B_ISO_8859_1);
2388 //font->PrintToStream();
2389
2390 return (GuiFont)font;
2391
2392error:
2393 if (giveErrorIfMissing)
2394 EMSG2("(fe0) Unknown font: %s", name);
2395
2396 return (GuiFont)font;
2397}
2398
2399/*
2400 * Set the current text font.
2401 */
2402 void
2403gui_mch_set_font(
2404 GuiFont font)
2405{
2406 if (gui.vimWindow->Lock()) {
2407 VimFont *vf = (VimFont *)font;
2408
2409 gui.vimTextArea->SetFont(vf);
2410
2411 gui.char_width = (int) vf->StringWidth("n");
2412 font_height fh;
2413 vf->GetHeight(&fh);
2414 gui.char_height = (int)(fh.ascent + 0.9999)
2415 + (int)(fh.descent + 0.9999) + (int)(fh.leading + 0.9999);
2416 gui.char_ascent = (int)(fh.ascent + 0.9999);
2417
2418 gui.vimWindow->Unlock();
2419 }
2420}
2421
2422#if 0 /* not used */
2423/*
2424 * Return TRUE if the two fonts given are equivalent.
2425 */
2426 int
2427gui_mch_same_font(
2428 GuiFont f1,
2429 GuiFont f2)
2430{
2431 VimFont *vf1 = (VimFont *)f1;
2432 VimFont *vf2 = (VimFont *)f2;
2433
2434 return f1 == f2 ||
2435 (vf1->FamilyAndStyle() == vf2->FamilyAndStyle() &&
2436 vf1->Size() == vf2->Size());
2437}
2438#endif
2439
2440/* XXX TODO This is apparently never called... */
2441 void
2442gui_mch_free_font(
2443 GuiFont font)
2444{
2445 VimFont *f = (VimFont *)font;
2446 if (--f->refcount <= 0) {
2447 if (f->refcount < 0)
2448 fprintf(stderr, "VimFont: refcount < 0\n");
2449 delete f;
2450 }
2451}
2452
2453 static int
2454hex_digit(int c)
2455{
2456 if (isdigit(c))
2457 return c - '0';
2458 c = TOLOWER_ASC(c);
2459 if (c >= 'a' && c <= 'f')
2460 return c - 'a' + 10;
2461 return -1000;
2462}
2463
2464/*
2465 * This function has been lifted from gui_w32.c and extended a bit.
2466 *
2467 * Return the Pixel value (color) for the given color name.
2468 * Return INVALCOLOR for error.
2469 */
2470 guicolor_T
2471gui_mch_get_color(
2472 char_u *name)
2473{
2474 typedef struct GuiColourTable
2475 {
2476 char *name;
2477 guicolor_T colour;
2478 } GuiColourTable;
2479
2480#define NSTATIC_COLOURS 32
2481#define NDYNAMIC_COLOURS 33
2482#define NCOLOURS (NSTATIC_COLOURS + NDYNAMIC_COLOURS)
2483
2484 static GuiColourTable table[NCOLOURS] =
2485 {
2486 {"Black", RGB(0x00, 0x00, 0x00)},
2487 {"DarkGray", RGB(0x80, 0x80, 0x80)},
2488 {"DarkGrey", RGB(0x80, 0x80, 0x80)},
2489 {"Gray", RGB(0xC0, 0xC0, 0xC0)},
2490 {"Grey", RGB(0xC0, 0xC0, 0xC0)},
2491 {"LightGray", RGB(0xD3, 0xD3, 0xD3)},
2492 {"LightGrey", RGB(0xD3, 0xD3, 0xD3)},
2493 {"White", RGB(0xFF, 0xFF, 0xFF)},
2494 {"DarkRed", RGB(0x80, 0x00, 0x00)},
2495 {"Red", RGB(0xFF, 0x00, 0x00)},
2496 {"LightRed", RGB(0xFF, 0xA0, 0xA0)},
2497 {"DarkBlue", RGB(0x00, 0x00, 0x80)},
2498 {"Blue", RGB(0x00, 0x00, 0xFF)},
2499 {"LightBlue", RGB(0xA0, 0xA0, 0xFF)},
2500 {"DarkGreen", RGB(0x00, 0x80, 0x00)},
2501 {"Green", RGB(0x00, 0xFF, 0x00)},
2502 {"LightGreen", RGB(0xA0, 0xFF, 0xA0)},
2503 {"DarkCyan", RGB(0x00, 0x80, 0x80)},
2504 {"Cyan", RGB(0x00, 0xFF, 0xFF)},
2505 {"LightCyan", RGB(0xA0, 0xFF, 0xFF)},
2506 {"DarkMagenta", RGB(0x80, 0x00, 0x80)},
2507 {"Magenta", RGB(0xFF, 0x00, 0xFF)},
2508 {"LightMagenta", RGB(0xFF, 0xA0, 0xFF)},
2509 {"Brown", RGB(0x80, 0x40, 0x40)},
2510 {"Yellow", RGB(0xFF, 0xFF, 0x00)},
2511 {"LightYellow", RGB(0xFF, 0xFF, 0xA0)},
2512 {"DarkYellow", RGB(0xBB, 0xBB, 0x00)},
2513 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)},
2514 {"Orange", RGB(0xFF, 0xA5, 0x00)},
2515 {"Purple", RGB(0xA0, 0x20, 0xF0)},
2516 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)},
2517 {"Violet", RGB(0xEE, 0x82, 0xEE)},
2518 };
2519
2520 static int endColour = NSTATIC_COLOURS;
2521 static int newColour = NSTATIC_COLOURS;
2522
2523 int r, g, b;
2524 int i;
2525
2526 if (name[0] == '#' && STRLEN(name) == 7)
2527 {
2528 /* Name is in "#rrggbb" format */
2529 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
2530 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
2531 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
2532 if (r < 0 || g < 0 || b < 0)
2533 return INVALCOLOR;
2534 return RGB(r, g, b);
2535 }
2536 else
2537 {
2538 /* Check if the name is one of the colours we know */
2539 for (i = 0; i < endColour; i++)
2540 if (STRICMP(name, table[i].name) == 0)
2541 return table[i].colour;
2542 }
2543
2544 /*
2545 * Last attempt. Look in the file "$VIM/rgb.txt".
2546 */
2547 {
2548#define LINE_LEN 100
2549 FILE *fd;
2550 char line[LINE_LEN];
2551 char_u *fname;
2552
2553 fname = expand_env_save((char_u *)"$VIM/rgb.txt");
2554 if (fname == NULL)
2555 return INVALCOLOR;
2556
2557 fd = fopen((char *)fname, "rt");
2558 vim_free(fname);
2559 if (fd == NULL)
2560 return INVALCOLOR;
2561
2562 while (!feof(fd))
2563 {
2564 int len;
2565 int pos;
2566 char *colour;
2567
2568 fgets(line, LINE_LEN, fd);
2569 len = strlen(line);
2570
2571 if (len <= 1 || line[len-1] != '\n')
2572 continue;
2573
2574 line[len-1] = '\0';
2575
2576 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
2577 if (i != 3)
2578 continue;
2579
2580 colour = line + pos;
2581
2582 if (STRICMP(colour, name) == 0)
2583 {
2584 fclose(fd);
2585 /*
2586 * Now remember this colour in the table.
2587 * A LRU scheme might be better but this is simpler.
2588 * Or could use a growing array.
2589 */
2590 guicolor_T gcolour = RGB(r,g,b);
2591
2592 vim_free(table[newColour].name);
2593 table[newColour].name = (char *)vim_strsave((char_u *)colour);
2594 table[newColour].colour = gcolour;
2595
2596 newColour++;
2597 if (newColour >= NCOLOURS)
2598 newColour = NSTATIC_COLOURS;
2599 if (endColour < NCOLOURS)
2600 endColour = newColour;
2601
2602 return gcolour;
2603 }
2604 }
2605
2606 fclose(fd);
2607 }
2608
2609 return INVALCOLOR;
2610}
2611
2612/*
2613 * Set the current text foreground color.
2614 */
2615 void
2616gui_mch_set_fg_color(
2617 guicolor_T color)
2618{
2619 rgb_color rgb = GUI_TO_RGB(color);
2620 if (gui.vimWindow->Lock()) {
2621 gui.vimTextArea->SetHighColor(rgb);
2622 gui.vimWindow->Unlock();
2623 }
2624}
2625
2626/*
2627 * Set the current text background color.
2628 */
2629 void
2630gui_mch_set_bg_color(
2631 guicolor_T color)
2632{
2633 rgb_color rgb = GUI_TO_RGB(color);
2634 if (gui.vimWindow->Lock()) {
2635 gui.vimTextArea->SetLowColor(rgb);
2636 gui.vimWindow->Unlock();
2637 }
2638}
2639
2640 void
2641gui_mch_draw_string(
2642 int row,
2643 int col,
2644 char_u *s,
2645 int len,
2646 int flags)
2647{
2648 if (gui.vimWindow->Lock()) {
2649 gui.vimTextArea->mchDrawString(row, col, s, len, flags);
2650 gui.vimWindow->Unlock();
2651 }
2652}
2653
2654/*
2655 * Return OK if the key with the termcap name "name" is supported.
2656 */
2657 int
2658gui_mch_haskey(
2659 char_u *name)
2660{
2661 int i;
2662
2663 for (i = 0; special_keys[i].BeKeys != 0; i++)
2664 if (name[0] == special_keys[i].vim_code0 &&
2665 name[1] == special_keys[i].vim_code1)
2666 return OK;
2667 return FAIL;
2668}
2669
2670 void
2671gui_mch_beep()
2672{
2673 ::beep();
2674}
2675
2676 void
2677gui_mch_flash(int msec)
2678{
2679 /* Do a visual beep by reversing the foreground and background colors */
2680
2681 if (gui.vimWindow->Lock()) {
2682 BRect rect = gui.vimTextArea->Bounds();
2683
2684 gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
2685 gui.vimTextArea->FillRect(rect);
2686 gui.vimTextArea->Sync();
2687 snooze(msec * 1000); /* wait for a few msec */
2688 gui.vimTextArea->FillRect(rect);
2689 gui.vimTextArea->SetDrawingMode(B_OP_COPY);
2690 gui.vimTextArea->Flush();
2691 gui.vimWindow->Unlock();
2692 }
2693}
2694
2695/*
2696 * Invert a rectangle from row r, column c, for nr rows and nc columns.
2697 */
2698 void
2699gui_mch_invert_rectangle(
2700 int r,
2701 int c,
2702 int nr,
2703 int nc)
2704{
2705 BRect rect;
2706 rect.left = FILL_X(c);
2707 rect.top = FILL_Y(r);
2708 rect.right = rect.left + nc * gui.char_width - PEN_WIDTH;
2709 rect.bottom = rect.top + nr * gui.char_height - PEN_WIDTH;
2710
2711 if (gui.vimWindow->Lock()) {
2712 gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
2713 gui.vimTextArea->FillRect(rect);
2714 gui.vimTextArea->SetDrawingMode(B_OP_COPY);
2715 gui.vimWindow->Unlock();
2716 }
2717}
2718
2719/*
2720 * Iconify the GUI window.
2721 */
2722 void
2723gui_mch_iconify()
2724{
2725 if (gui.vimWindow->Lock()) {
2726 gui.vimWindow->Minimize(true);
2727 gui.vimWindow->Unlock();
2728 }
2729}
2730
2731#if defined(FEAT_EVAL) || defined(PROTO)
2732/*
2733 * Bring the Vim window to the foreground.
2734 */
2735 void
2736gui_mch_set_foreground()
2737{
2738 /* TODO */
2739}
2740#endif
2741
2742/*
2743 * Set the window title
2744 */
2745 void
2746gui_mch_settitle(
2747 char_u *title,
2748 char_u *icon)
2749{
2750 if (gui.vimWindow->Lock()) {
2751 gui.vimWindow->SetTitle((char *)title);
2752 gui.vimWindow->Unlock();
2753 }
2754}
2755
2756/*
2757 * Draw a cursor without focus.
2758 */
2759 void
2760gui_mch_draw_hollow_cursor(guicolor_T color)
2761{
2762 gui_mch_set_fg_color(color);
2763
2764 BRect r;
2765 r.left = FILL_X(gui.col);
2766 r.top = FILL_Y(gui.row);
2767 r.right = r.left + gui.char_width - PEN_WIDTH;
2768 r.bottom = r.top + gui.char_height - PEN_WIDTH;
2769
2770 if (gui.vimWindow->Lock()) {
2771 gui.vimTextArea->StrokeRect(r);
2772 gui.vimWindow->Unlock();
2773 //gui_mch_flush();
2774 }
2775}
2776
2777/*
2778 * Draw part of a cursor, only w pixels wide, and h pixels high.
2779 */
2780 void
2781gui_mch_draw_part_cursor(
2782 int w,
2783 int h,
2784 guicolor_T color)
2785{
2786 gui_mch_set_fg_color(color);
2787
2788 BRect r;
2789 r.left =
2790#ifdef FEAT_RIGHTLEFT
2791 /* vertical line should be on the right of current point */
2792 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
2793#endif
2794 FILL_X(gui.col);
2795 r.right = r.left + w - PEN_WIDTH;
2796 r.bottom = FILL_Y(gui.row + 1) - PEN_WIDTH;
2797 r.top = r.bottom - h + PEN_WIDTH;
2798
2799 if (gui.vimWindow->Lock()) {
2800 gui.vimTextArea->FillRect(r);
2801 gui.vimWindow->Unlock();
2802 //gui_mch_flush();
2803 }
2804}
2805
2806/*
2807 * Catch up with any queued events. This may put keyboard input into the
2808 * input buffer, call resize call-backs, trigger timers etc. If there is
2809 * nothing in the event queue (& no timers pending), then we return
2810 * immediately.
2811 */
2812 void
2813gui_mch_update()
2814{
2815 gui_mch_flush();
2816 while (port_count(gui.vdcmp) > 0 &&
2817 !vim_is_input_buf_full() &&
2818 gui_beos_process_event(0) >= B_OK)
2819 /* nothing */ ;
2820}
2821
2822/*
2823 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2824 * from the keyboard.
2825 * wtime == -1 Wait forever.
2826 * wtime == 0 This should never happen.
2827 * wtime > 0 Wait wtime milliseconds for a character.
2828 * Returns OK if a character was found to be available within the given time,
2829 * or FAIL otherwise.
2830 */
2831 int
2832gui_mch_wait_for_chars(
2833 int wtime)
2834{
2835 int focus;
2836 bigtime_t until, timeout;
2837 status_t st;
2838
2839 if (wtime >= 0) {
2840 timeout = wtime * 1000;
2841 until = system_time() + timeout;
2842 } else {
2843 timeout = B_INFINITE_TIMEOUT;
2844 }
2845
2846 focus = gui.in_focus;
2847 for (;;)
2848 {
2849 /* Stop or start blinking when focus changes */
2850 if (gui.in_focus != focus)
2851 {
2852 if (gui.in_focus)
2853 gui_mch_start_blink();
2854 else
2855 gui_mch_stop_blink();
2856 focus = gui.in_focus;
2857 }
2858
2859 gui_mch_flush();
2860 /*
2861 * Don't use gui_mch_update() because then we will spin-lock until a
2862 * char arrives, instead we use gui_beos_process_event() to hang until
2863 * an event arrives. No need to check for input_buf_full because we
2864 * are returning as soon as it contains a single char.
2865 */
2866 st = gui_beos_process_event(timeout);
2867
2868 if (input_available())
2869 return OK;
2870 if (st < B_OK) /* includes B_TIMED_OUT */
2871 return FAIL;
2872
2873 /*
2874 * Calculate how much longer we're willing to wait for the
2875 * next event.
2876 */
2877 if (wtime >= 0) {
2878 timeout = until - system_time();
2879 if (timeout < 0)
2880 break;
2881 }
2882 }
2883 return FAIL;
2884
2885}
2886
2887/*
2888 * Output routines.
2889 */
2890
2891/*
2892 * Flush any output to the screen. This is typically called before
2893 * the app goes to sleep.
2894 */
2895 void
2896gui_mch_flush()
2897{
2898 // does this need to lock the window? Apparently not but be safe.
2899 if (gui.vimWindow->Lock()) {
2900 gui.vimWindow->Flush();
2901 gui.vimWindow->Unlock();
2902 }
2903 return;
2904}
2905
2906/*
2907 * Clear a rectangular region of the screen from text pos (row1, col1) to
2908 * (row2, col2) inclusive.
2909 */
2910 void
2911gui_mch_clear_block(
2912 int row1,
2913 int col1,
2914 int row2,
2915 int col2)
2916{
2917 if (gui.vimWindow->Lock()) {
2918 gui.vimTextArea->mchClearBlock(row1, col1, row2, col2);
2919 gui.vimWindow->Unlock();
2920 }
2921}
2922
2923 void
2924gui_mch_clear_all()
2925{
2926 if (gui.vimWindow->Lock()) {
2927 gui.vimTextArea->mchClearAll();
2928 gui.vimWindow->Unlock();
2929 }
2930}
2931
2932/*
2933 * Delete the given number of lines from the given row, scrolling up any
2934 * text further down within the scroll region.
2935 */
2936 void
2937gui_mch_delete_lines(
2938 int row,
2939 int num_lines)
2940{
2941 gui.vimTextArea->mchDeleteLines(row, num_lines);
2942}
2943
2944/*
2945 * Insert the given number of lines before the given row, scrolling down any
2946 * following text within the scroll region.
2947 */
2948 void
2949gui_mch_insert_lines(
2950 int row,
2951 int num_lines)
2952{
2953 gui.vimTextArea->mchInsertLines(row, num_lines);
2954}
2955
2956#if defined(FEAT_MENU) || defined(PROTO)
2957/*
2958 * Menu stuff.
2959 */
2960
2961 void
2962gui_mch_enable_menu(
2963 int flag)
2964{
2965 if (gui.vimWindow->Lock())
2966 {
2967 BMenuBar *menubar = gui.vimForm->MenuBar();
2968 menubar->SetEnabled(flag);
2969 gui.vimWindow->Unlock();
2970 }
2971}
2972
2973 void
2974gui_mch_set_menu_pos(
2975 int x,
2976 int y,
2977 int w,
2978 int h)
2979{
2980 /* It will be in the right place anyway */
2981}
2982
2983/*
2984 * Add a sub menu to the menu bar.
2985 */
2986 void
2987gui_mch_add_menu(
2988 vimmenu_T *menu,
2989 int idx)
2990{
2991 vimmenu_T *parent = menu->parent;
2992
2993 if (!menu_is_menubar(menu->name)
2994 || (parent != NULL && parent->submenu_id == NULL))
2995 return;
2996
2997 if (gui.vimWindow->Lock())
2998 {
2999/* Major re-write of the menu code, it was failing with memory corruption when
3000 * we started loading multiple files (the Buffer menu)
3001 *
3002 * Note we don't use the preference values yet, all are inserted into the
3003 * menubar on a first come-first served basis...
3004 *
3005 * richard@whitequeen.com jul 99
3006 */
3007
3008 BMenu *tmp;
3009
3010 if ( parent )
3011 tmp = parent->submenu_id;
3012 else
3013 tmp = gui.vimForm->MenuBar();
3014// make sure we don't try and add the same menu twice. The Buffers menu tries to
3015// do this and Be starts to crash...
3016
3017 if ( ! tmp->FindItem((const char *) menu->dname)) {
3018
3019 BMenu *bmenu = new BMenu((char *)menu->dname);
3020
3021 menu->submenu_id = bmenu;
3022
3023// when we add a BMenu to another Menu, it creates the interconnecting BMenuItem
3024 tmp->AddItem(bmenu);
3025
3026// Now its safe to query the menu for the associated MenuItem....
3027 menu->id = tmp->FindItem((const char *) menu->dname);
3028
3029 }
3030 gui.vimWindow->Unlock();
3031 }
3032}
3033
3034 void
3035gui_mch_toggle_tearoffs(int enable)
3036{
3037 /* no tearoff menus */
3038}
3039
3040 static BMessage *
3041MenuMessage(vimmenu_T *menu)
3042{
3043 BMessage *m = new BMessage('menu');
3044 m->AddPointer("VimMenu", (void *)menu);
3045
3046 return m;
3047}
3048
3049/*
3050 * Add a menu item to a menu
3051 */
3052 void
3053gui_mch_add_menu_item(
3054 vimmenu_T *menu,
3055 int idx)
3056{
3057 int mnemonic = 0;
3058 vimmenu_T *parent = menu->parent;
3059
3060 if (parent->submenu_id == NULL)
3061 return;
3062
3063#ifdef never
3064 /* why not add separators ?
3065 * richard
3066 */
3067 /* Don't add menu separator */
3068 if (menu_is_separator(menu->name))
3069 return;
3070#endif
3071
3072 /* TODO: use menu->actext */
3073 /* This is difficult, since on Be, an accelerator must be a single char
3074 * and a lot of Vim ones are the standard VI commands.
3075 *
3076 * Punt for Now...
3077 * richard@whiequeen.com jul 99
3078 */
3079 if (gui.vimWindow->Lock())
3080 {
3081 if ( menu_is_separator(menu->name)) {
3082 BSeparatorItem *item = new BSeparatorItem();
3083 parent->submenu_id->AddItem(item);
3084 menu->id = item;
3085 menu->submenu_id = NULL;
3086 }
3087 else {
3088 BMenuItem *item = new BMenuItem((char *)menu->dname,
3089 MenuMessage(menu));
3090 item->SetTarget(gui.vimTextArea);
3091 item->SetTrigger((char) menu->mnemonic);
3092 parent->submenu_id->AddItem(item);
3093 menu->id = item;
3094 menu->submenu_id = NULL;
3095 }
3096 gui.vimWindow->Unlock();
3097 }
3098}
3099
3100/*
3101 * Destroy the machine specific menu widget.
3102 */
3103 void
3104gui_mch_destroy_menu(
3105 vimmenu_T *menu)
3106{
3107 if (gui.vimWindow->Lock())
3108 {
3109 assert(menu->submenu_id == NULL || menu->submenu_id->CountItems() == 0);
3110 /*
3111 * Detach this menu from its parent, so that it is not deleted
3112 * twice once we get to delete that parent.
3113 * Deleting a BMenuItem also deletes the associated BMenu, if any
3114 * (which does not have any items anymore since they were
3115 * removed and deleted before).
3116 */
3117 BMenu *bmenu = menu->id->Menu();
3118 if (bmenu)
3119 {
3120 bmenu->RemoveItem(menu->id);
3121 /*
3122 * If we removed the last item from the menu bar,
3123 * resize it out of sight.
3124 */
3125 if (bmenu == gui.vimForm->MenuBar() && bmenu->CountItems() == 0)
3126 {
3127 bmenu->ResizeTo(-MENUBAR_MARGIN, -MENUBAR_MARGIN);
3128 }
3129 }
3130 delete menu->id;
3131 menu->id = NULL;
3132 menu->submenu_id = NULL;
3133
3134 gui.menu_height = (int) gui.vimForm->MenuHeight();
3135 gui.vimWindow->Unlock();
3136 }
3137}
3138
3139/*
3140 * Make a menu either grey or not grey.
3141 */
3142 void
3143gui_mch_menu_grey(
3144 vimmenu_T *menu,
3145 int grey)
3146{
3147 if (menu->id != NULL)
3148 menu->id->SetEnabled(!grey);
3149}
3150
3151/*
3152 * Make menu item hidden or not hidden
3153 */
3154 void
3155gui_mch_menu_hidden(
3156 vimmenu_T *menu,
3157 int hidden)
3158{
3159 if (menu->id != NULL)
3160 menu->id->SetEnabled(!hidden);
3161}
3162
3163/*
3164 * This is called after setting all the menus to grey/hidden or not.
3165 */
3166 void
3167gui_mch_draw_menubar()
3168{
3169 /* Nothing to do in BeOS */
3170}
3171
3172#endif /* FEAT_MENU */
3173
3174/* Mouse stuff */
3175
3176#ifdef FEAT_CLIPBOARD
3177/*
3178 * Clipboard stuff, for cutting and pasting text to other windows.
3179 */
3180char textplain[] = "text/plain";
3181char vimselectiontype[] = "application/x-vnd.Rhialto-Vim-selectiontype";
3182
3183/*
3184 * Get the current selection and put it in the clipboard register.
3185 */
3186 void
3187clip_mch_request_selection(VimClipboard *cbd)
3188{
3189 if (be_clipboard->Lock())
3190 {
3191 BMessage *m = be_clipboard->Data();
3192 //m->PrintToStream();
3193
3194 char_u *string = NULL;
3195 ssize_t stringlen = -1;
3196
3197 if (m->FindData(textplain, B_MIME_TYPE,
3198 (const void **)&string, &stringlen) == B_OK
3199 || m->FindString("text", (const char **)&string) == B_OK)
3200 {
3201 if (stringlen == -1)
3202 stringlen = STRLEN(string);
3203
3204 int type;
3205 char *seltype;
3206 ssize_t seltypelen;
3207
3208 /*
3209 * Try to get the special vim selection type first
3210 */
3211 if (m->FindData(vimselectiontype, B_MIME_TYPE,
3212 (const void **)&seltype, &seltypelen) == B_OK)
3213 {
3214 switch (*seltype)
3215 {
3216 default:
3217 case 'L': type = MLINE; break;
3218 case 'C': type = MCHAR; break;
3219#ifdef FEAT_VISUAL
3220 case 'B': type = MBLOCK; break;
3221#endif
3222 }
3223 }
3224 else
3225 {
3226 /* Otherwise use heuristic as documented */
3227 type = memchr(string, stringlen, '\n') ? MLINE : MCHAR;
3228 }
3229 clip_yank_selection(type, string, (long)stringlen, cbd);
3230 }
3231 be_clipboard->Unlock();
3232 }
3233}
3234/*
3235 * Make vim the owner of the current selection.
3236 */
3237 void
3238clip_mch_lose_selection(VimClipboard *cbd)
3239{
3240 /* Nothing needs to be done here */
3241}
3242
3243/*
3244 * Make vim the owner of the current selection. Return OK upon success.
3245 */
3246 int
3247clip_mch_own_selection(VimClipboard *cbd)
3248{
3249 /*
3250 * Never actually own the clipboard. If another application sets the
3251 * clipboard, we don't want to think that we still own it.
3252 */
3253 return FAIL;
3254}
3255
3256/*
3257 * Send the current selection to the clipboard.
3258 */
3259 void
3260clip_mch_set_selection(VimClipboard *cbd)
3261{
3262 if (be_clipboard->Lock())
3263 {
3264 be_clipboard->Clear();
3265 BMessage *m = be_clipboard->Data();
3266 assert(m);
3267
3268 /* If the '*' register isn't already filled in, fill it in now */
3269 cbd->owned = TRUE;
3270 clip_get_selection(cbd);
3271 cbd->owned = FALSE;
3272
3273 char_u *str = NULL;
3274 long_u count;
3275 int type;
3276
3277 type = clip_convert_selection(&str, &count, cbd);
3278
3279 if (type < 0)
3280 return;
3281
3282 m->AddData(textplain, B_MIME_TYPE, (void *)str, count);
3283
3284 /* Add type of selection */
3285 char vtype;
3286 switch (type)
3287 {
3288 default:
3289 case MLINE: vtype = 'L'; break;
3290 case MCHAR: vtype = 'C'; break;
3291#ifdef FEAT_VISUAL
3292 case MBLOCK: vtype = 'B'; break;
3293#endif
3294 }
3295 m->AddData(vimselectiontype, B_MIME_TYPE, (void *)&vtype, 1);
3296
3297 vim_free(str);
3298
3299 be_clipboard->Commit();
3300 be_clipboard->Unlock();
3301 }
3302}
3303
3304#endif /* FEAT_CLIPBOARD */
3305
3306/*
3307 * Return the RGB value of a pixel as long.
3308 */
3309 long_u
3310gui_mch_get_rgb(guicolor_T pixel)
3311{
3312 rgb_color rgb = GUI_TO_RGB(pixel);
3313
3314 return ((rgb.red & 0xff) << 16) + ((rgb.green & 0xff) << 8)
3315 + (rgb.blue & 0xff);
3316}
3317
3318 void
3319gui_mch_setmouse(int x, int y)
3320{
3321 TRACE();
3322 /* TODO */
3323}
3324
3325 void
3326gui_mch_show_popupmenu(vimmenu_T *menu)
3327{
3328 TRACE();
3329 /* TODO */
3330}
3331
3332int
3333gui_mch_get_mouse_x()
3334{
3335 TRACE();
3336 return 0;
3337}
3338
3339
3340int
3341gui_mch_get_mouse_y()
3342{
3343 TRACE();
3344 return 0;
3345}
3346
3347} /* extern "C" */