blob: 32e4db0cb426942bc2a9c354f1dd3d6aeb4a7484 [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/*
Bram Moolenaard8b0cf12004-12-12 11:33:30 +00002400 * Return the name of font "font" in allocated memory.
2401 */
2402 char_u *
2403gui_mch_get_fontname(GuiFont font, char_u *name)
2404{
2405 return vim_strsave(((VimFont *)font)->name);
2406}
2407
2408/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 * Set the current text font.
2410 */
2411 void
2412gui_mch_set_font(
2413 GuiFont font)
2414{
2415 if (gui.vimWindow->Lock()) {
2416 VimFont *vf = (VimFont *)font;
2417
2418 gui.vimTextArea->SetFont(vf);
2419
2420 gui.char_width = (int) vf->StringWidth("n");
2421 font_height fh;
2422 vf->GetHeight(&fh);
2423 gui.char_height = (int)(fh.ascent + 0.9999)
2424 + (int)(fh.descent + 0.9999) + (int)(fh.leading + 0.9999);
2425 gui.char_ascent = (int)(fh.ascent + 0.9999);
2426
2427 gui.vimWindow->Unlock();
2428 }
2429}
2430
2431#if 0 /* not used */
2432/*
2433 * Return TRUE if the two fonts given are equivalent.
2434 */
2435 int
2436gui_mch_same_font(
2437 GuiFont f1,
2438 GuiFont f2)
2439{
2440 VimFont *vf1 = (VimFont *)f1;
2441 VimFont *vf2 = (VimFont *)f2;
2442
2443 return f1 == f2 ||
2444 (vf1->FamilyAndStyle() == vf2->FamilyAndStyle() &&
2445 vf1->Size() == vf2->Size());
2446}
2447#endif
2448
2449/* XXX TODO This is apparently never called... */
2450 void
2451gui_mch_free_font(
2452 GuiFont font)
2453{
2454 VimFont *f = (VimFont *)font;
2455 if (--f->refcount <= 0) {
2456 if (f->refcount < 0)
2457 fprintf(stderr, "VimFont: refcount < 0\n");
2458 delete f;
2459 }
2460}
2461
2462 static int
2463hex_digit(int c)
2464{
2465 if (isdigit(c))
2466 return c - '0';
2467 c = TOLOWER_ASC(c);
2468 if (c >= 'a' && c <= 'f')
2469 return c - 'a' + 10;
2470 return -1000;
2471}
2472
2473/*
2474 * This function has been lifted from gui_w32.c and extended a bit.
2475 *
2476 * Return the Pixel value (color) for the given color name.
2477 * Return INVALCOLOR for error.
2478 */
2479 guicolor_T
2480gui_mch_get_color(
2481 char_u *name)
2482{
2483 typedef struct GuiColourTable
2484 {
2485 char *name;
2486 guicolor_T colour;
2487 } GuiColourTable;
2488
2489#define NSTATIC_COLOURS 32
2490#define NDYNAMIC_COLOURS 33
2491#define NCOLOURS (NSTATIC_COLOURS + NDYNAMIC_COLOURS)
2492
2493 static GuiColourTable table[NCOLOURS] =
2494 {
2495 {"Black", RGB(0x00, 0x00, 0x00)},
2496 {"DarkGray", RGB(0x80, 0x80, 0x80)},
2497 {"DarkGrey", RGB(0x80, 0x80, 0x80)},
2498 {"Gray", RGB(0xC0, 0xC0, 0xC0)},
2499 {"Grey", RGB(0xC0, 0xC0, 0xC0)},
2500 {"LightGray", RGB(0xD3, 0xD3, 0xD3)},
2501 {"LightGrey", RGB(0xD3, 0xD3, 0xD3)},
2502 {"White", RGB(0xFF, 0xFF, 0xFF)},
2503 {"DarkRed", RGB(0x80, 0x00, 0x00)},
2504 {"Red", RGB(0xFF, 0x00, 0x00)},
2505 {"LightRed", RGB(0xFF, 0xA0, 0xA0)},
2506 {"DarkBlue", RGB(0x00, 0x00, 0x80)},
2507 {"Blue", RGB(0x00, 0x00, 0xFF)},
2508 {"LightBlue", RGB(0xA0, 0xA0, 0xFF)},
2509 {"DarkGreen", RGB(0x00, 0x80, 0x00)},
2510 {"Green", RGB(0x00, 0xFF, 0x00)},
2511 {"LightGreen", RGB(0xA0, 0xFF, 0xA0)},
2512 {"DarkCyan", RGB(0x00, 0x80, 0x80)},
2513 {"Cyan", RGB(0x00, 0xFF, 0xFF)},
2514 {"LightCyan", RGB(0xA0, 0xFF, 0xFF)},
2515 {"DarkMagenta", RGB(0x80, 0x00, 0x80)},
2516 {"Magenta", RGB(0xFF, 0x00, 0xFF)},
2517 {"LightMagenta", RGB(0xFF, 0xA0, 0xFF)},
2518 {"Brown", RGB(0x80, 0x40, 0x40)},
2519 {"Yellow", RGB(0xFF, 0xFF, 0x00)},
2520 {"LightYellow", RGB(0xFF, 0xFF, 0xA0)},
2521 {"DarkYellow", RGB(0xBB, 0xBB, 0x00)},
2522 {"SeaGreen", RGB(0x2E, 0x8B, 0x57)},
2523 {"Orange", RGB(0xFF, 0xA5, 0x00)},
2524 {"Purple", RGB(0xA0, 0x20, 0xF0)},
2525 {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)},
2526 {"Violet", RGB(0xEE, 0x82, 0xEE)},
2527 };
2528
2529 static int endColour = NSTATIC_COLOURS;
2530 static int newColour = NSTATIC_COLOURS;
2531
2532 int r, g, b;
2533 int i;
2534
2535 if (name[0] == '#' && STRLEN(name) == 7)
2536 {
2537 /* Name is in "#rrggbb" format */
2538 r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
2539 g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
2540 b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
2541 if (r < 0 || g < 0 || b < 0)
2542 return INVALCOLOR;
2543 return RGB(r, g, b);
2544 }
2545 else
2546 {
2547 /* Check if the name is one of the colours we know */
2548 for (i = 0; i < endColour; i++)
2549 if (STRICMP(name, table[i].name) == 0)
2550 return table[i].colour;
2551 }
2552
2553 /*
2554 * Last attempt. Look in the file "$VIM/rgb.txt".
2555 */
2556 {
2557#define LINE_LEN 100
2558 FILE *fd;
2559 char line[LINE_LEN];
2560 char_u *fname;
2561
2562 fname = expand_env_save((char_u *)"$VIM/rgb.txt");
2563 if (fname == NULL)
2564 return INVALCOLOR;
2565
2566 fd = fopen((char *)fname, "rt");
2567 vim_free(fname);
2568 if (fd == NULL)
2569 return INVALCOLOR;
2570
2571 while (!feof(fd))
2572 {
2573 int len;
2574 int pos;
2575 char *colour;
2576
2577 fgets(line, LINE_LEN, fd);
2578 len = strlen(line);
2579
2580 if (len <= 1 || line[len-1] != '\n')
2581 continue;
2582
2583 line[len-1] = '\0';
2584
2585 i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos);
2586 if (i != 3)
2587 continue;
2588
2589 colour = line + pos;
2590
2591 if (STRICMP(colour, name) == 0)
2592 {
2593 fclose(fd);
2594 /*
2595 * Now remember this colour in the table.
2596 * A LRU scheme might be better but this is simpler.
2597 * Or could use a growing array.
2598 */
2599 guicolor_T gcolour = RGB(r,g,b);
2600
2601 vim_free(table[newColour].name);
2602 table[newColour].name = (char *)vim_strsave((char_u *)colour);
2603 table[newColour].colour = gcolour;
2604
2605 newColour++;
2606 if (newColour >= NCOLOURS)
2607 newColour = NSTATIC_COLOURS;
2608 if (endColour < NCOLOURS)
2609 endColour = newColour;
2610
2611 return gcolour;
2612 }
2613 }
2614
2615 fclose(fd);
2616 }
2617
2618 return INVALCOLOR;
2619}
2620
2621/*
2622 * Set the current text foreground color.
2623 */
2624 void
2625gui_mch_set_fg_color(
2626 guicolor_T color)
2627{
2628 rgb_color rgb = GUI_TO_RGB(color);
2629 if (gui.vimWindow->Lock()) {
2630 gui.vimTextArea->SetHighColor(rgb);
2631 gui.vimWindow->Unlock();
2632 }
2633}
2634
2635/*
2636 * Set the current text background color.
2637 */
2638 void
2639gui_mch_set_bg_color(
2640 guicolor_T color)
2641{
2642 rgb_color rgb = GUI_TO_RGB(color);
2643 if (gui.vimWindow->Lock()) {
2644 gui.vimTextArea->SetLowColor(rgb);
2645 gui.vimWindow->Unlock();
2646 }
2647}
2648
2649 void
2650gui_mch_draw_string(
2651 int row,
2652 int col,
2653 char_u *s,
2654 int len,
2655 int flags)
2656{
2657 if (gui.vimWindow->Lock()) {
2658 gui.vimTextArea->mchDrawString(row, col, s, len, flags);
2659 gui.vimWindow->Unlock();
2660 }
2661}
2662
2663/*
2664 * Return OK if the key with the termcap name "name" is supported.
2665 */
2666 int
2667gui_mch_haskey(
2668 char_u *name)
2669{
2670 int i;
2671
2672 for (i = 0; special_keys[i].BeKeys != 0; i++)
2673 if (name[0] == special_keys[i].vim_code0 &&
2674 name[1] == special_keys[i].vim_code1)
2675 return OK;
2676 return FAIL;
2677}
2678
2679 void
2680gui_mch_beep()
2681{
2682 ::beep();
2683}
2684
2685 void
2686gui_mch_flash(int msec)
2687{
2688 /* Do a visual beep by reversing the foreground and background colors */
2689
2690 if (gui.vimWindow->Lock()) {
2691 BRect rect = gui.vimTextArea->Bounds();
2692
2693 gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
2694 gui.vimTextArea->FillRect(rect);
2695 gui.vimTextArea->Sync();
2696 snooze(msec * 1000); /* wait for a few msec */
2697 gui.vimTextArea->FillRect(rect);
2698 gui.vimTextArea->SetDrawingMode(B_OP_COPY);
2699 gui.vimTextArea->Flush();
2700 gui.vimWindow->Unlock();
2701 }
2702}
2703
2704/*
2705 * Invert a rectangle from row r, column c, for nr rows and nc columns.
2706 */
2707 void
2708gui_mch_invert_rectangle(
2709 int r,
2710 int c,
2711 int nr,
2712 int nc)
2713{
2714 BRect rect;
2715 rect.left = FILL_X(c);
2716 rect.top = FILL_Y(r);
2717 rect.right = rect.left + nc * gui.char_width - PEN_WIDTH;
2718 rect.bottom = rect.top + nr * gui.char_height - PEN_WIDTH;
2719
2720 if (gui.vimWindow->Lock()) {
2721 gui.vimTextArea->SetDrawingMode(B_OP_INVERT);
2722 gui.vimTextArea->FillRect(rect);
2723 gui.vimTextArea->SetDrawingMode(B_OP_COPY);
2724 gui.vimWindow->Unlock();
2725 }
2726}
2727
2728/*
2729 * Iconify the GUI window.
2730 */
2731 void
2732gui_mch_iconify()
2733{
2734 if (gui.vimWindow->Lock()) {
2735 gui.vimWindow->Minimize(true);
2736 gui.vimWindow->Unlock();
2737 }
2738}
2739
2740#if defined(FEAT_EVAL) || defined(PROTO)
2741/*
2742 * Bring the Vim window to the foreground.
2743 */
2744 void
2745gui_mch_set_foreground()
2746{
2747 /* TODO */
2748}
2749#endif
2750
2751/*
2752 * Set the window title
2753 */
2754 void
2755gui_mch_settitle(
2756 char_u *title,
2757 char_u *icon)
2758{
2759 if (gui.vimWindow->Lock()) {
2760 gui.vimWindow->SetTitle((char *)title);
2761 gui.vimWindow->Unlock();
2762 }
2763}
2764
2765/*
2766 * Draw a cursor without focus.
2767 */
2768 void
2769gui_mch_draw_hollow_cursor(guicolor_T color)
2770{
2771 gui_mch_set_fg_color(color);
2772
2773 BRect r;
2774 r.left = FILL_X(gui.col);
2775 r.top = FILL_Y(gui.row);
2776 r.right = r.left + gui.char_width - PEN_WIDTH;
2777 r.bottom = r.top + gui.char_height - PEN_WIDTH;
2778
2779 if (gui.vimWindow->Lock()) {
2780 gui.vimTextArea->StrokeRect(r);
2781 gui.vimWindow->Unlock();
2782 //gui_mch_flush();
2783 }
2784}
2785
2786/*
2787 * Draw part of a cursor, only w pixels wide, and h pixels high.
2788 */
2789 void
2790gui_mch_draw_part_cursor(
2791 int w,
2792 int h,
2793 guicolor_T color)
2794{
2795 gui_mch_set_fg_color(color);
2796
2797 BRect r;
2798 r.left =
2799#ifdef FEAT_RIGHTLEFT
2800 /* vertical line should be on the right of current point */
2801 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
2802#endif
2803 FILL_X(gui.col);
2804 r.right = r.left + w - PEN_WIDTH;
2805 r.bottom = FILL_Y(gui.row + 1) - PEN_WIDTH;
2806 r.top = r.bottom - h + PEN_WIDTH;
2807
2808 if (gui.vimWindow->Lock()) {
2809 gui.vimTextArea->FillRect(r);
2810 gui.vimWindow->Unlock();
2811 //gui_mch_flush();
2812 }
2813}
2814
2815/*
2816 * Catch up with any queued events. This may put keyboard input into the
2817 * input buffer, call resize call-backs, trigger timers etc. If there is
2818 * nothing in the event queue (& no timers pending), then we return
2819 * immediately.
2820 */
2821 void
2822gui_mch_update()
2823{
2824 gui_mch_flush();
2825 while (port_count(gui.vdcmp) > 0 &&
2826 !vim_is_input_buf_full() &&
2827 gui_beos_process_event(0) >= B_OK)
2828 /* nothing */ ;
2829}
2830
2831/*
2832 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2833 * from the keyboard.
2834 * wtime == -1 Wait forever.
2835 * wtime == 0 This should never happen.
2836 * wtime > 0 Wait wtime milliseconds for a character.
2837 * Returns OK if a character was found to be available within the given time,
2838 * or FAIL otherwise.
2839 */
2840 int
2841gui_mch_wait_for_chars(
2842 int wtime)
2843{
2844 int focus;
2845 bigtime_t until, timeout;
2846 status_t st;
2847
2848 if (wtime >= 0) {
2849 timeout = wtime * 1000;
2850 until = system_time() + timeout;
2851 } else {
2852 timeout = B_INFINITE_TIMEOUT;
2853 }
2854
2855 focus = gui.in_focus;
2856 for (;;)
2857 {
2858 /* Stop or start blinking when focus changes */
2859 if (gui.in_focus != focus)
2860 {
2861 if (gui.in_focus)
2862 gui_mch_start_blink();
2863 else
2864 gui_mch_stop_blink();
2865 focus = gui.in_focus;
2866 }
2867
2868 gui_mch_flush();
2869 /*
2870 * Don't use gui_mch_update() because then we will spin-lock until a
2871 * char arrives, instead we use gui_beos_process_event() to hang until
2872 * an event arrives. No need to check for input_buf_full because we
2873 * are returning as soon as it contains a single char.
2874 */
2875 st = gui_beos_process_event(timeout);
2876
2877 if (input_available())
2878 return OK;
2879 if (st < B_OK) /* includes B_TIMED_OUT */
2880 return FAIL;
2881
2882 /*
2883 * Calculate how much longer we're willing to wait for the
2884 * next event.
2885 */
2886 if (wtime >= 0) {
2887 timeout = until - system_time();
2888 if (timeout < 0)
2889 break;
2890 }
2891 }
2892 return FAIL;
2893
2894}
2895
2896/*
2897 * Output routines.
2898 */
2899
2900/*
2901 * Flush any output to the screen. This is typically called before
2902 * the app goes to sleep.
2903 */
2904 void
2905gui_mch_flush()
2906{
2907 // does this need to lock the window? Apparently not but be safe.
2908 if (gui.vimWindow->Lock()) {
2909 gui.vimWindow->Flush();
2910 gui.vimWindow->Unlock();
2911 }
2912 return;
2913}
2914
2915/*
2916 * Clear a rectangular region of the screen from text pos (row1, col1) to
2917 * (row2, col2) inclusive.
2918 */
2919 void
2920gui_mch_clear_block(
2921 int row1,
2922 int col1,
2923 int row2,
2924 int col2)
2925{
2926 if (gui.vimWindow->Lock()) {
2927 gui.vimTextArea->mchClearBlock(row1, col1, row2, col2);
2928 gui.vimWindow->Unlock();
2929 }
2930}
2931
2932 void
2933gui_mch_clear_all()
2934{
2935 if (gui.vimWindow->Lock()) {
2936 gui.vimTextArea->mchClearAll();
2937 gui.vimWindow->Unlock();
2938 }
2939}
2940
2941/*
2942 * Delete the given number of lines from the given row, scrolling up any
2943 * text further down within the scroll region.
2944 */
2945 void
2946gui_mch_delete_lines(
2947 int row,
2948 int num_lines)
2949{
2950 gui.vimTextArea->mchDeleteLines(row, num_lines);
2951}
2952
2953/*
2954 * Insert the given number of lines before the given row, scrolling down any
2955 * following text within the scroll region.
2956 */
2957 void
2958gui_mch_insert_lines(
2959 int row,
2960 int num_lines)
2961{
2962 gui.vimTextArea->mchInsertLines(row, num_lines);
2963}
2964
2965#if defined(FEAT_MENU) || defined(PROTO)
2966/*
2967 * Menu stuff.
2968 */
2969
2970 void
2971gui_mch_enable_menu(
2972 int flag)
2973{
2974 if (gui.vimWindow->Lock())
2975 {
2976 BMenuBar *menubar = gui.vimForm->MenuBar();
2977 menubar->SetEnabled(flag);
2978 gui.vimWindow->Unlock();
2979 }
2980}
2981
2982 void
2983gui_mch_set_menu_pos(
2984 int x,
2985 int y,
2986 int w,
2987 int h)
2988{
2989 /* It will be in the right place anyway */
2990}
2991
2992/*
2993 * Add a sub menu to the menu bar.
2994 */
2995 void
2996gui_mch_add_menu(
2997 vimmenu_T *menu,
2998 int idx)
2999{
3000 vimmenu_T *parent = menu->parent;
3001
3002 if (!menu_is_menubar(menu->name)
3003 || (parent != NULL && parent->submenu_id == NULL))
3004 return;
3005
3006 if (gui.vimWindow->Lock())
3007 {
3008/* Major re-write of the menu code, it was failing with memory corruption when
3009 * we started loading multiple files (the Buffer menu)
3010 *
3011 * Note we don't use the preference values yet, all are inserted into the
3012 * menubar on a first come-first served basis...
3013 *
3014 * richard@whitequeen.com jul 99
3015 */
3016
3017 BMenu *tmp;
3018
3019 if ( parent )
3020 tmp = parent->submenu_id;
3021 else
3022 tmp = gui.vimForm->MenuBar();
3023// make sure we don't try and add the same menu twice. The Buffers menu tries to
3024// do this and Be starts to crash...
3025
3026 if ( ! tmp->FindItem((const char *) menu->dname)) {
3027
3028 BMenu *bmenu = new BMenu((char *)menu->dname);
3029
3030 menu->submenu_id = bmenu;
3031
3032// when we add a BMenu to another Menu, it creates the interconnecting BMenuItem
3033 tmp->AddItem(bmenu);
3034
3035// Now its safe to query the menu for the associated MenuItem....
3036 menu->id = tmp->FindItem((const char *) menu->dname);
3037
3038 }
3039 gui.vimWindow->Unlock();
3040 }
3041}
3042
3043 void
3044gui_mch_toggle_tearoffs(int enable)
3045{
3046 /* no tearoff menus */
3047}
3048
3049 static BMessage *
3050MenuMessage(vimmenu_T *menu)
3051{
3052 BMessage *m = new BMessage('menu');
3053 m->AddPointer("VimMenu", (void *)menu);
3054
3055 return m;
3056}
3057
3058/*
3059 * Add a menu item to a menu
3060 */
3061 void
3062gui_mch_add_menu_item(
3063 vimmenu_T *menu,
3064 int idx)
3065{
3066 int mnemonic = 0;
3067 vimmenu_T *parent = menu->parent;
3068
3069 if (parent->submenu_id == NULL)
3070 return;
3071
3072#ifdef never
3073 /* why not add separators ?
3074 * richard
3075 */
3076 /* Don't add menu separator */
3077 if (menu_is_separator(menu->name))
3078 return;
3079#endif
3080
3081 /* TODO: use menu->actext */
3082 /* This is difficult, since on Be, an accelerator must be a single char
3083 * and a lot of Vim ones are the standard VI commands.
3084 *
3085 * Punt for Now...
3086 * richard@whiequeen.com jul 99
3087 */
3088 if (gui.vimWindow->Lock())
3089 {
3090 if ( menu_is_separator(menu->name)) {
3091 BSeparatorItem *item = new BSeparatorItem();
3092 parent->submenu_id->AddItem(item);
3093 menu->id = item;
3094 menu->submenu_id = NULL;
3095 }
3096 else {
3097 BMenuItem *item = new BMenuItem((char *)menu->dname,
3098 MenuMessage(menu));
3099 item->SetTarget(gui.vimTextArea);
3100 item->SetTrigger((char) menu->mnemonic);
3101 parent->submenu_id->AddItem(item);
3102 menu->id = item;
3103 menu->submenu_id = NULL;
3104 }
3105 gui.vimWindow->Unlock();
3106 }
3107}
3108
3109/*
3110 * Destroy the machine specific menu widget.
3111 */
3112 void
3113gui_mch_destroy_menu(
3114 vimmenu_T *menu)
3115{
3116 if (gui.vimWindow->Lock())
3117 {
3118 assert(menu->submenu_id == NULL || menu->submenu_id->CountItems() == 0);
3119 /*
3120 * Detach this menu from its parent, so that it is not deleted
3121 * twice once we get to delete that parent.
3122 * Deleting a BMenuItem also deletes the associated BMenu, if any
3123 * (which does not have any items anymore since they were
3124 * removed and deleted before).
3125 */
3126 BMenu *bmenu = menu->id->Menu();
3127 if (bmenu)
3128 {
3129 bmenu->RemoveItem(menu->id);
3130 /*
3131 * If we removed the last item from the menu bar,
3132 * resize it out of sight.
3133 */
3134 if (bmenu == gui.vimForm->MenuBar() && bmenu->CountItems() == 0)
3135 {
3136 bmenu->ResizeTo(-MENUBAR_MARGIN, -MENUBAR_MARGIN);
3137 }
3138 }
3139 delete menu->id;
3140 menu->id = NULL;
3141 menu->submenu_id = NULL;
3142
3143 gui.menu_height = (int) gui.vimForm->MenuHeight();
3144 gui.vimWindow->Unlock();
3145 }
3146}
3147
3148/*
3149 * Make a menu either grey or not grey.
3150 */
3151 void
3152gui_mch_menu_grey(
3153 vimmenu_T *menu,
3154 int grey)
3155{
3156 if (menu->id != NULL)
3157 menu->id->SetEnabled(!grey);
3158}
3159
3160/*
3161 * Make menu item hidden or not hidden
3162 */
3163 void
3164gui_mch_menu_hidden(
3165 vimmenu_T *menu,
3166 int hidden)
3167{
3168 if (menu->id != NULL)
3169 menu->id->SetEnabled(!hidden);
3170}
3171
3172/*
3173 * This is called after setting all the menus to grey/hidden or not.
3174 */
3175 void
3176gui_mch_draw_menubar()
3177{
3178 /* Nothing to do in BeOS */
3179}
3180
3181#endif /* FEAT_MENU */
3182
3183/* Mouse stuff */
3184
3185#ifdef FEAT_CLIPBOARD
3186/*
3187 * Clipboard stuff, for cutting and pasting text to other windows.
3188 */
3189char textplain[] = "text/plain";
3190char vimselectiontype[] = "application/x-vnd.Rhialto-Vim-selectiontype";
3191
3192/*
3193 * Get the current selection and put it in the clipboard register.
3194 */
3195 void
3196clip_mch_request_selection(VimClipboard *cbd)
3197{
3198 if (be_clipboard->Lock())
3199 {
3200 BMessage *m = be_clipboard->Data();
3201 //m->PrintToStream();
3202
3203 char_u *string = NULL;
3204 ssize_t stringlen = -1;
3205
3206 if (m->FindData(textplain, B_MIME_TYPE,
3207 (const void **)&string, &stringlen) == B_OK
3208 || m->FindString("text", (const char **)&string) == B_OK)
3209 {
3210 if (stringlen == -1)
3211 stringlen = STRLEN(string);
3212
3213 int type;
3214 char *seltype;
3215 ssize_t seltypelen;
3216
3217 /*
3218 * Try to get the special vim selection type first
3219 */
3220 if (m->FindData(vimselectiontype, B_MIME_TYPE,
3221 (const void **)&seltype, &seltypelen) == B_OK)
3222 {
3223 switch (*seltype)
3224 {
3225 default:
3226 case 'L': type = MLINE; break;
3227 case 'C': type = MCHAR; break;
3228#ifdef FEAT_VISUAL
3229 case 'B': type = MBLOCK; break;
3230#endif
3231 }
3232 }
3233 else
3234 {
3235 /* Otherwise use heuristic as documented */
3236 type = memchr(string, stringlen, '\n') ? MLINE : MCHAR;
3237 }
3238 clip_yank_selection(type, string, (long)stringlen, cbd);
3239 }
3240 be_clipboard->Unlock();
3241 }
3242}
3243/*
3244 * Make vim the owner of the current selection.
3245 */
3246 void
3247clip_mch_lose_selection(VimClipboard *cbd)
3248{
3249 /* Nothing needs to be done here */
3250}
3251
3252/*
3253 * Make vim the owner of the current selection. Return OK upon success.
3254 */
3255 int
3256clip_mch_own_selection(VimClipboard *cbd)
3257{
3258 /*
3259 * Never actually own the clipboard. If another application sets the
3260 * clipboard, we don't want to think that we still own it.
3261 */
3262 return FAIL;
3263}
3264
3265/*
3266 * Send the current selection to the clipboard.
3267 */
3268 void
3269clip_mch_set_selection(VimClipboard *cbd)
3270{
3271 if (be_clipboard->Lock())
3272 {
3273 be_clipboard->Clear();
3274 BMessage *m = be_clipboard->Data();
3275 assert(m);
3276
3277 /* If the '*' register isn't already filled in, fill it in now */
3278 cbd->owned = TRUE;
3279 clip_get_selection(cbd);
3280 cbd->owned = FALSE;
3281
3282 char_u *str = NULL;
3283 long_u count;
3284 int type;
3285
3286 type = clip_convert_selection(&str, &count, cbd);
3287
3288 if (type < 0)
3289 return;
3290
3291 m->AddData(textplain, B_MIME_TYPE, (void *)str, count);
3292
3293 /* Add type of selection */
3294 char vtype;
3295 switch (type)
3296 {
3297 default:
3298 case MLINE: vtype = 'L'; break;
3299 case MCHAR: vtype = 'C'; break;
3300#ifdef FEAT_VISUAL
3301 case MBLOCK: vtype = 'B'; break;
3302#endif
3303 }
3304 m->AddData(vimselectiontype, B_MIME_TYPE, (void *)&vtype, 1);
3305
3306 vim_free(str);
3307
3308 be_clipboard->Commit();
3309 be_clipboard->Unlock();
3310 }
3311}
3312
3313#endif /* FEAT_CLIPBOARD */
3314
3315/*
3316 * Return the RGB value of a pixel as long.
3317 */
3318 long_u
3319gui_mch_get_rgb(guicolor_T pixel)
3320{
3321 rgb_color rgb = GUI_TO_RGB(pixel);
3322
3323 return ((rgb.red & 0xff) << 16) + ((rgb.green & 0xff) << 8)
3324 + (rgb.blue & 0xff);
3325}
3326
3327 void
3328gui_mch_setmouse(int x, int y)
3329{
3330 TRACE();
3331 /* TODO */
3332}
3333
3334 void
3335gui_mch_show_popupmenu(vimmenu_T *menu)
3336{
3337 TRACE();
3338 /* TODO */
3339}
3340
3341int
3342gui_mch_get_mouse_x()
3343{
3344 TRACE();
3345 return 0;
3346}
3347
3348
3349int
3350gui_mch_get_mouse_y()
3351{
3352 TRACE();
3353 return 0;
3354}
3355
3356} /* extern "C" */