1 | /*
|
---|
2 | * Copyright 2001-2006, Haiku, Inc.
|
---|
3 | * Distributed under the terms of the MIT License.
|
---|
4 | *
|
---|
5 | * Authors:
|
---|
6 | * Marc Flerackers (mflerackers@androme.be)
|
---|
7 | * Stefano Ceccherini (burton666@libero.it)
|
---|
8 | */
|
---|
9 |
|
---|
10 | #include <new>
|
---|
11 | #include <string.h>
|
---|
12 |
|
---|
13 | #include <Debug.h>
|
---|
14 | #include <File.h>
|
---|
15 | #include <FindDirectory.h>
|
---|
16 | #include <Menu.h>
|
---|
17 | #include <MenuBar.h>
|
---|
18 | #include <MenuItem.h>
|
---|
19 | #include <Path.h>
|
---|
20 | #include <PropertyInfo.h>
|
---|
21 | #include <Screen.h>
|
---|
22 | #include <Window.h>
|
---|
23 |
|
---|
24 | #include <AppServerLink.h>
|
---|
25 | #include <BMCPrivate.h>
|
---|
26 | #include <MenuPrivate.h>
|
---|
27 | #include <MenuWindow.h>
|
---|
28 | #include <ServerProtocol.h>
|
---|
29 |
|
---|
30 | using std::nothrow;
|
---|
31 |
|
---|
32 | class _ExtraMenuData_ {
|
---|
33 | public:
|
---|
34 | menu_tracking_hook trackingHook;
|
---|
35 | void *trackingState;
|
---|
36 |
|
---|
37 | _ExtraMenuData_(menu_tracking_hook func, void *state)
|
---|
38 | {
|
---|
39 | trackingHook = func;
|
---|
40 | trackingState = state;
|
---|
41 | };
|
---|
42 | };
|
---|
43 |
|
---|
44 |
|
---|
45 | menu_info BMenu::sMenuInfo;
|
---|
46 | bool BMenu::sAltAsCommandKey;
|
---|
47 |
|
---|
48 |
|
---|
49 | static property_info
|
---|
50 | sPropList[] = {
|
---|
51 | { "Enabled", { B_GET_PROPERTY, 0 },
|
---|
52 | { B_DIRECT_SPECIFIER, 0 }, "Returns true if menu or menu item is enabled; false "
|
---|
53 | "otherwise.",
|
---|
54 | 0, { B_BOOL_TYPE }
|
---|
55 | },
|
---|
56 |
|
---|
57 | { "Enabled", { B_SET_PROPERTY, 0 },
|
---|
58 | { B_DIRECT_SPECIFIER, 0 }, "Enables or disables menu or menu item.",
|
---|
59 | 0, { B_BOOL_TYPE }
|
---|
60 | },
|
---|
61 |
|
---|
62 | { "Label", { B_GET_PROPERTY, 0 },
|
---|
63 | { B_DIRECT_SPECIFIER, 0 }, "Returns the string label of the menu or menu item.",
|
---|
64 | 0, { B_STRING_TYPE }
|
---|
65 | },
|
---|
66 |
|
---|
67 | { "Label", { B_SET_PROPERTY, 0 },
|
---|
68 | { B_DIRECT_SPECIFIER, 0 }, "Sets the string label of the menu or menu item.",
|
---|
69 | 0, { B_STRING_TYPE }
|
---|
70 | },
|
---|
71 |
|
---|
72 | { "Mark", { B_GET_PROPERTY, 0 },
|
---|
73 | { B_DIRECT_SPECIFIER, 0 }, "Returns true if the menu item or the menu's superitem "
|
---|
74 | "is marked; false otherwise.",
|
---|
75 | 0, { B_BOOL_TYPE }
|
---|
76 | },
|
---|
77 |
|
---|
78 | { "Mark", { B_SET_PROPERTY, 0 },
|
---|
79 | { B_DIRECT_SPECIFIER, 0 }, "Marks or unmarks the menu item or the menu's superitem.",
|
---|
80 | 0, { B_BOOL_TYPE }
|
---|
81 | },
|
---|
82 |
|
---|
83 | { "Menu", { B_CREATE_PROPERTY, 0 },
|
---|
84 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
85 | "Adds a new menu item at the specified index with the text label found in \"data\" "
|
---|
86 | "and the int32 command found in \"what\" (used as the what field in the CMessage "
|
---|
87 | "sent by the item)." , 0, {},
|
---|
88 | { {{{"data", B_STRING_TYPE}}}
|
---|
89 | }
|
---|
90 | },
|
---|
91 |
|
---|
92 | { "Menu", { B_DELETE_PROPERTY, 0 },
|
---|
93 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
94 | "Removes the selected menu or menus.", 0, {}
|
---|
95 | },
|
---|
96 |
|
---|
97 | { "Menu", { },
|
---|
98 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
99 | "Directs scripting message to the specified menu, first popping the current "
|
---|
100 | "specifier off the stack.", 0, {}
|
---|
101 | },
|
---|
102 |
|
---|
103 | { "MenuItem", { B_COUNT_PROPERTIES, 0 },
|
---|
104 | { B_DIRECT_SPECIFIER, 0 }, "Counts the number of menu items in the specified menu.",
|
---|
105 | 0, { B_INT32_TYPE }
|
---|
106 | },
|
---|
107 |
|
---|
108 | { "MenuItem", { B_CREATE_PROPERTY, 0 },
|
---|
109 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
110 | "Adds a new menu item at the specified index with the text label found in \"data\" "
|
---|
111 | "and the int32 command found in \"what\" (used as the what field in the CMessage "
|
---|
112 | "sent by the item).", 0, {},
|
---|
113 | { { {{"data", B_STRING_TYPE },
|
---|
114 | {"be:invoke_message", B_MESSAGE_TYPE},
|
---|
115 | {"what", B_INT32_TYPE},
|
---|
116 | {"be:target", B_MESSENGER_TYPE}} }
|
---|
117 | }
|
---|
118 | },
|
---|
119 |
|
---|
120 | { "MenuItem", { B_DELETE_PROPERTY, 0 },
|
---|
121 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
122 | "Removes the specified menu item from its parent menu."
|
---|
123 | },
|
---|
124 |
|
---|
125 | { "MenuItem", { B_EXECUTE_PROPERTY, 0 },
|
---|
126 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
127 | "Invokes the specified menu item."
|
---|
128 | },
|
---|
129 |
|
---|
130 | { "MenuItem", { },
|
---|
131 | { B_NAME_SPECIFIER, B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, 0 },
|
---|
132 | "Directs scripting message to the specified menu, first popping the current "
|
---|
133 | "specifier off the stack."
|
---|
134 | },
|
---|
135 |
|
---|
136 | {}
|
---|
137 | };
|
---|
138 |
|
---|
139 |
|
---|
140 | const char *kEmptyMenuLabel = "<empty>";
|
---|
141 |
|
---|
142 |
|
---|
143 | BMenu::BMenu(const char *name, menu_layout layout)
|
---|
144 | : BView(BRect(0, 0, 0, 0), name, 0, B_WILL_DRAW),
|
---|
145 | fChosenItem(NULL),
|
---|
146 | fPad(14.0f, 2.0f, 20.0f, 0.0f),
|
---|
147 | fSelected(NULL),
|
---|
148 | fCachedMenuWindow(NULL),
|
---|
149 | fSuper(NULL),
|
---|
150 | fSuperitem(NULL),
|
---|
151 | fAscent(-1.0f),
|
---|
152 | fDescent(-1.0f),
|
---|
153 | fFontHeight(-1.0f),
|
---|
154 | fState(0),
|
---|
155 | fLayout(layout),
|
---|
156 | fExtraRect(NULL),
|
---|
157 | fMaxContentWidth(0.0f),
|
---|
158 | fInitMatrixSize(NULL),
|
---|
159 | fExtraMenuData(NULL),
|
---|
160 | fTrigger(0),
|
---|
161 | fResizeToFit(true),
|
---|
162 | fUseCachedMenuLayout(false),
|
---|
163 | fEnabled(true),
|
---|
164 | fDynamicName(false),
|
---|
165 | fRadioMode(false),
|
---|
166 | fTrackNewBounds(false),
|
---|
167 | fStickyMode(false),
|
---|
168 | fIgnoreHidden(true),
|
---|
169 | fTriggerEnabled(true),
|
---|
170 | fRedrawAfterSticky(false),
|
---|
171 | fAttachAborted(false)
|
---|
172 | {
|
---|
173 | InitData(NULL);
|
---|
174 | }
|
---|
175 |
|
---|
176 |
|
---|
177 | BMenu::BMenu(const char *name, float width, float height)
|
---|
178 | : BView(BRect(0.0f, width, 0.0f, height), name, 0, B_WILL_DRAW),
|
---|
179 | fChosenItem(NULL),
|
---|
180 | fSelected(NULL),
|
---|
181 | fCachedMenuWindow(NULL),
|
---|
182 | fSuper(NULL),
|
---|
183 | fSuperitem(NULL),
|
---|
184 | fAscent(-1.0f),
|
---|
185 | fDescent(-1.0f),
|
---|
186 | fFontHeight(-1.0f),
|
---|
187 | fState(0),
|
---|
188 | fLayout(B_ITEMS_IN_MATRIX),
|
---|
189 | fExtraRect(NULL),
|
---|
190 | fMaxContentWidth(0.0f),
|
---|
191 | fInitMatrixSize(NULL),
|
---|
192 | fExtraMenuData(NULL),
|
---|
193 | fTrigger(0),
|
---|
194 | fResizeToFit(true),
|
---|
195 | fUseCachedMenuLayout(false),
|
---|
196 | fEnabled(true),
|
---|
197 | fDynamicName(false),
|
---|
198 | fRadioMode(false),
|
---|
199 | fTrackNewBounds(false),
|
---|
200 | fStickyMode(false),
|
---|
201 | fIgnoreHidden(true),
|
---|
202 | fTriggerEnabled(true),
|
---|
203 | fRedrawAfterSticky(false),
|
---|
204 | fAttachAborted(false)
|
---|
205 | {
|
---|
206 | InitData(NULL);
|
---|
207 | }
|
---|
208 |
|
---|
209 |
|
---|
210 | BMenu::~BMenu()
|
---|
211 | {
|
---|
212 | DeleteMenuWindow();
|
---|
213 |
|
---|
214 | RemoveItems(0, CountItems(), true);
|
---|
215 |
|
---|
216 | delete fInitMatrixSize;
|
---|
217 | delete fExtraMenuData;
|
---|
218 | }
|
---|
219 |
|
---|
220 |
|
---|
221 | BMenu::BMenu(BMessage *archive)
|
---|
222 | : BView(archive),
|
---|
223 | fChosenItem(NULL),
|
---|
224 | fPad(14.0f, 2.0f, 20.0f, 0.0f),
|
---|
225 | fSelected(NULL),
|
---|
226 | fCachedMenuWindow(NULL),
|
---|
227 | fSuper(NULL),
|
---|
228 | fSuperitem(NULL),
|
---|
229 | fAscent(-1.0f),
|
---|
230 | fDescent(-1.0f),
|
---|
231 | fFontHeight(-1.0f),
|
---|
232 | fState(0),
|
---|
233 | fLayout(B_ITEMS_IN_ROW),
|
---|
234 | fExtraRect(NULL),
|
---|
235 | fMaxContentWidth(0.0f),
|
---|
236 | fInitMatrixSize(NULL),
|
---|
237 | fExtraMenuData(NULL),
|
---|
238 | fTrigger(0),
|
---|
239 | fResizeToFit(true),
|
---|
240 | fUseCachedMenuLayout(false),
|
---|
241 | fEnabled(true),
|
---|
242 | fDynamicName(false),
|
---|
243 | fRadioMode(false),
|
---|
244 | fTrackNewBounds(false),
|
---|
245 | fStickyMode(false),
|
---|
246 | fIgnoreHidden(true),
|
---|
247 | fTriggerEnabled(true),
|
---|
248 | fRedrawAfterSticky(false),
|
---|
249 | fAttachAborted(false)
|
---|
250 | {
|
---|
251 | InitData(archive);
|
---|
252 | }
|
---|
253 |
|
---|
254 |
|
---|
255 | BArchivable *
|
---|
256 | BMenu::Instantiate(BMessage *data)
|
---|
257 | {
|
---|
258 | if (validate_instantiation(data, "BMenu"))
|
---|
259 | return new (nothrow) BMenu(data);
|
---|
260 |
|
---|
261 | return NULL;
|
---|
262 | }
|
---|
263 |
|
---|
264 |
|
---|
265 | status_t
|
---|
266 | BMenu::Archive(BMessage *data, bool deep) const
|
---|
267 | {
|
---|
268 | status_t err = BView::Archive(data, deep);
|
---|
269 |
|
---|
270 | if (err == B_OK && Layout() != B_ITEMS_IN_ROW)
|
---|
271 | err = data->AddInt32("_layout", Layout());
|
---|
272 | if (err == B_OK)
|
---|
273 | err = data->AddBool("_rsize_to_fit", fResizeToFit);
|
---|
274 | if (err == B_OK)
|
---|
275 | err = data->AddBool("_disable", !IsEnabled());
|
---|
276 | if (err == B_OK)
|
---|
277 | err = data->AddBool("_radio", IsRadioMode());
|
---|
278 | if (err == B_OK)
|
---|
279 | err = data->AddBool("_trig_disabled", AreTriggersEnabled());
|
---|
280 | if (err == B_OK)
|
---|
281 | err = data->AddBool("_dyn_label", fDynamicName);
|
---|
282 | if (err == B_OK)
|
---|
283 | err = data->AddFloat("_maxwidth", fMaxContentWidth);
|
---|
284 | if (err == B_OK && deep) {
|
---|
285 | BMenuItem *item = NULL;
|
---|
286 | int32 index = 0;
|
---|
287 | while ((item = ItemAt(index++)) != NULL) {
|
---|
288 | BMessage itemData;
|
---|
289 | item->Archive(&itemData, deep);
|
---|
290 | err = data->AddMessage("_items", &itemData);
|
---|
291 | if (err != B_OK)
|
---|
292 | break;
|
---|
293 | if (fLayout == B_ITEMS_IN_MATRIX) {
|
---|
294 | err = data->AddRect("_i_frames", item->fBounds);
|
---|
295 | }
|
---|
296 | }
|
---|
297 | }
|
---|
298 |
|
---|
299 | return err;
|
---|
300 | }
|
---|
301 |
|
---|
302 |
|
---|
303 | void
|
---|
304 | BMenu::AttachedToWindow()
|
---|
305 | {
|
---|
306 | BView::AttachedToWindow();
|
---|
307 |
|
---|
308 | sAltAsCommandKey = true;
|
---|
309 | key_map *keys = NULL;
|
---|
310 | char *chars = NULL;
|
---|
311 | get_key_map(&keys, &chars);
|
---|
312 | if (keys == NULL || keys->left_command_key != 0x5d || keys->right_command_key != 0x5f)
|
---|
313 | sAltAsCommandKey = false;
|
---|
314 | free(chars);
|
---|
315 | free(keys);
|
---|
316 |
|
---|
317 | BMenuItem *superItem = Superitem();
|
---|
318 | BMenu *superMenu = Supermenu();
|
---|
319 | if (AddDynamicItem(B_INITIAL_ADD)) {
|
---|
320 | do {
|
---|
321 | if (superMenu != NULL && !superMenu->OkToProceed(superItem)) {
|
---|
322 | AddDynamicItem(B_ABORT);
|
---|
323 | fAttachAborted = true;
|
---|
324 | break;
|
---|
325 | }
|
---|
326 | } while (AddDynamicItem(B_PROCESSING));
|
---|
327 | }
|
---|
328 |
|
---|
329 | if (!fAttachAborted) {
|
---|
330 | CacheFontInfo();
|
---|
331 | LayoutItems(0);
|
---|
332 | //UpdateWindowViewSize();
|
---|
333 | }
|
---|
334 | }
|
---|
335 |
|
---|
336 |
|
---|
337 | void
|
---|
338 | BMenu::DetachedFromWindow()
|
---|
339 | {
|
---|
340 | BView::DetachedFromWindow();
|
---|
341 | }
|
---|
342 |
|
---|
343 |
|
---|
344 | bool
|
---|
345 | BMenu::AddItem(BMenuItem *item)
|
---|
346 | {
|
---|
347 | return AddItem(item, CountItems());
|
---|
348 | }
|
---|
349 |
|
---|
350 |
|
---|
351 | bool
|
---|
352 | BMenu::AddItem(BMenuItem *item, int32 index)
|
---|
353 | {
|
---|
354 | if (fLayout == B_ITEMS_IN_MATRIX)
|
---|
355 | debugger("BMenu::AddItem(BMenuItem *, int32) this method can only "
|
---|
356 | "be called if the menu layout is not B_ITEMS_IN_MATRIX");
|
---|
357 |
|
---|
358 | if (!item || !_AddItem(item, index))
|
---|
359 | return false;
|
---|
360 |
|
---|
361 | InvalidateLayout();
|
---|
362 | if (LockLooper()) {
|
---|
363 | if (!Window()->IsHidden()) {
|
---|
364 | LayoutItems(index);
|
---|
365 | Invalidate();
|
---|
366 | }
|
---|
367 | UnlockLooper();
|
---|
368 | }
|
---|
369 | return true;
|
---|
370 | }
|
---|
371 |
|
---|
372 |
|
---|
373 | bool
|
---|
374 | BMenu::AddItem(BMenuItem *item, BRect frame)
|
---|
375 | {
|
---|
376 | if (fLayout != B_ITEMS_IN_MATRIX)
|
---|
377 | debugger("BMenu::AddItem(BMenuItem *, BRect) this method can only "
|
---|
378 | "be called if the menu layout is B_ITEMS_IN_MATRIX");
|
---|
379 |
|
---|
380 | if (!item)
|
---|
381 | return false;
|
---|
382 |
|
---|
383 | item->fBounds = frame;
|
---|
384 |
|
---|
385 | int32 index = CountItems();
|
---|
386 | if (!_AddItem(item, index)) {
|
---|
387 | return false;
|
---|
388 | }
|
---|
389 |
|
---|
390 | if (LockLooper()) {
|
---|
391 | if (!Window()->IsHidden()) {
|
---|
392 | LayoutItems(index);
|
---|
393 | Invalidate();
|
---|
394 | }
|
---|
395 | UnlockLooper();
|
---|
396 | }
|
---|
397 |
|
---|
398 | return true;
|
---|
399 | }
|
---|
400 |
|
---|
401 |
|
---|
402 | bool
|
---|
403 | BMenu::AddItem(BMenu *submenu)
|
---|
404 | {
|
---|
405 | BMenuItem *item = new (nothrow) BMenuItem(submenu);
|
---|
406 | if (!item)
|
---|
407 | return false;
|
---|
408 |
|
---|
409 | if (!AddItem(item, CountItems())) {
|
---|
410 | item->fSubmenu = NULL;
|
---|
411 | delete item;
|
---|
412 | return false;
|
---|
413 | }
|
---|
414 |
|
---|
415 | return true;
|
---|
416 | }
|
---|
417 |
|
---|
418 |
|
---|
419 | bool
|
---|
420 | BMenu::AddItem(BMenu *submenu, int32 index)
|
---|
421 | {
|
---|
422 | if (fLayout == B_ITEMS_IN_MATRIX)
|
---|
423 | debugger("BMenu::AddItem(BMenuItem *, int32) this method can only "
|
---|
424 | "be called if the menu layout is not B_ITEMS_IN_MATRIX");
|
---|
425 |
|
---|
426 | BMenuItem *item = new (nothrow) BMenuItem(submenu);
|
---|
427 | if (!item)
|
---|
428 | return false;
|
---|
429 |
|
---|
430 | if (!AddItem(item, index)) {
|
---|
431 | item->fSubmenu = NULL;
|
---|
432 | delete item;
|
---|
433 | return false;
|
---|
434 | }
|
---|
435 |
|
---|
436 | return true;
|
---|
437 | }
|
---|
438 |
|
---|
439 |
|
---|
440 | bool
|
---|
441 | BMenu::AddItem(BMenu *submenu, BRect frame)
|
---|
442 | {
|
---|
443 | if (fLayout != B_ITEMS_IN_MATRIX)
|
---|
444 | debugger("BMenu::AddItem(BMenu *, BRect) this method can only "
|
---|
445 | "be called if the menu layout is B_ITEMS_IN_MATRIX");
|
---|
446 |
|
---|
447 | BMenuItem *item = new (nothrow) BMenuItem(submenu);
|
---|
448 | if (!item)
|
---|
449 | return false;
|
---|
450 |
|
---|
451 | if (!AddItem(item, frame)) {
|
---|
452 | item->fSubmenu = NULL;
|
---|
453 | delete item;
|
---|
454 | return false;
|
---|
455 | }
|
---|
456 |
|
---|
457 | return true;
|
---|
458 | }
|
---|
459 |
|
---|
460 |
|
---|
461 | bool
|
---|
462 | BMenu::AddList(BList *list, int32 index)
|
---|
463 | {
|
---|
464 | // TODO: test this function, it's not documented in the bebook.
|
---|
465 | if (list == NULL)
|
---|
466 | return false;
|
---|
467 |
|
---|
468 | bool locked = LockLooper();
|
---|
469 |
|
---|
470 | int32 numItems = list->CountItems();
|
---|
471 | for (int32 i = 0; i < numItems; i++) {
|
---|
472 | BMenuItem *item = static_cast<BMenuItem *>(list->ItemAt(i));
|
---|
473 | if (item != NULL) {
|
---|
474 | if (!_AddItem(item, index + i))
|
---|
475 | break;
|
---|
476 | }
|
---|
477 | }
|
---|
478 |
|
---|
479 | InvalidateLayout();
|
---|
480 | if (locked && Window() != NULL && !Window()->IsHidden()) {
|
---|
481 | // Make sure we update the layout if needed.
|
---|
482 | LayoutItems(index);
|
---|
483 | //UpdateWindowViewSize();
|
---|
484 | Invalidate();
|
---|
485 | }
|
---|
486 |
|
---|
487 | if (locked)
|
---|
488 | UnlockLooper();
|
---|
489 |
|
---|
490 | return true;
|
---|
491 | }
|
---|
492 |
|
---|
493 |
|
---|
494 | bool
|
---|
495 | BMenu::AddSeparatorItem()
|
---|
496 | {
|
---|
497 | BMenuItem *item = new (nothrow) BSeparatorItem();
|
---|
498 | if (!item || !AddItem(item, CountItems())) {
|
---|
499 | delete item;
|
---|
500 | return false;
|
---|
501 | }
|
---|
502 |
|
---|
503 | return true;
|
---|
504 | }
|
---|
505 |
|
---|
506 |
|
---|
507 | bool
|
---|
508 | BMenu::RemoveItem(BMenuItem *item)
|
---|
509 | {
|
---|
510 | // TODO: Check if item is also deleted
|
---|
511 | return RemoveItems(0, 0, item, false);
|
---|
512 | }
|
---|
513 |
|
---|
514 |
|
---|
515 | BMenuItem *
|
---|
516 | BMenu::RemoveItem(int32 index)
|
---|
517 | {
|
---|
518 | BMenuItem *item = ItemAt(index);
|
---|
519 | if (item != NULL)
|
---|
520 | RemoveItems(0, 0, item, false);
|
---|
521 | return item;
|
---|
522 | }
|
---|
523 |
|
---|
524 |
|
---|
525 | bool
|
---|
526 | BMenu::RemoveItems(int32 index, int32 count, bool del)
|
---|
527 | {
|
---|
528 | return RemoveItems(index, count, NULL, del);
|
---|
529 | }
|
---|
530 |
|
---|
531 |
|
---|
532 | bool
|
---|
533 | BMenu::RemoveItem(BMenu *submenu)
|
---|
534 | {
|
---|
535 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
536 | if (static_cast<BMenuItem *>(fItems.ItemAtFast(i))->Submenu() == submenu)
|
---|
537 | return RemoveItems(i, 1, NULL, false);
|
---|
538 | }
|
---|
539 |
|
---|
540 | return false;
|
---|
541 | }
|
---|
542 |
|
---|
543 |
|
---|
544 | int32
|
---|
545 | BMenu::CountItems() const
|
---|
546 | {
|
---|
547 | return fItems.CountItems();
|
---|
548 | }
|
---|
549 |
|
---|
550 |
|
---|
551 | BMenuItem *
|
---|
552 | BMenu::ItemAt(int32 index) const
|
---|
553 | {
|
---|
554 | return static_cast<BMenuItem *>(fItems.ItemAt(index));
|
---|
555 | }
|
---|
556 |
|
---|
557 |
|
---|
558 | BMenu *
|
---|
559 | BMenu::SubmenuAt(int32 index) const
|
---|
560 | {
|
---|
561 | BMenuItem *item = static_cast<BMenuItem *>(fItems.ItemAt(index));
|
---|
562 | return (item != NULL) ? item->Submenu() : NULL;
|
---|
563 | }
|
---|
564 |
|
---|
565 |
|
---|
566 | int32
|
---|
567 | BMenu::IndexOf(BMenuItem *item) const
|
---|
568 | {
|
---|
569 | return fItems.IndexOf(item);
|
---|
570 | }
|
---|
571 |
|
---|
572 |
|
---|
573 | int32
|
---|
574 | BMenu::IndexOf(BMenu *submenu) const
|
---|
575 | {
|
---|
576 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
577 | if (ItemAt(i)->Submenu() == submenu)
|
---|
578 | return i;
|
---|
579 | }
|
---|
580 |
|
---|
581 | return -1;
|
---|
582 | }
|
---|
583 |
|
---|
584 |
|
---|
585 | BMenuItem *
|
---|
586 | BMenu::FindItem(const char *label) const
|
---|
587 | {
|
---|
588 | BMenuItem *item = NULL;
|
---|
589 |
|
---|
590 | for (int32 i = 0; i < CountItems(); i++) {
|
---|
591 | item = ItemAt(i);
|
---|
592 |
|
---|
593 | if (item->Label() && strcmp(item->Label(), label) == 0)
|
---|
594 | return item;
|
---|
595 |
|
---|
596 | if (item->Submenu() != NULL) {
|
---|
597 | item = item->Submenu()->FindItem(label);
|
---|
598 | if (item != NULL)
|
---|
599 | return item;
|
---|
600 | }
|
---|
601 | }
|
---|
602 |
|
---|
603 | return NULL;
|
---|
604 | }
|
---|
605 |
|
---|
606 |
|
---|
607 | BMenuItem *
|
---|
608 | BMenu::FindItem(uint32 command) const
|
---|
609 | {
|
---|
610 | BMenuItem *item = NULL;
|
---|
611 |
|
---|
612 | for (int32 i = 0; i < CountItems(); i++) {
|
---|
613 | item = ItemAt(i);
|
---|
614 |
|
---|
615 | if (item->Command() == command)
|
---|
616 | return item;
|
---|
617 |
|
---|
618 | if (item->Submenu() != NULL) {
|
---|
619 | item = item->Submenu()->FindItem(command);
|
---|
620 | if (item != NULL)
|
---|
621 | return item;
|
---|
622 | }
|
---|
623 | }
|
---|
624 |
|
---|
625 | return NULL;
|
---|
626 | }
|
---|
627 |
|
---|
628 |
|
---|
629 | status_t
|
---|
630 | BMenu::SetTargetForItems(BHandler *handler)
|
---|
631 | {
|
---|
632 | status_t status = B_OK;
|
---|
633 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
634 | status = ItemAt(i)->SetTarget(handler);
|
---|
635 | if (status < B_OK)
|
---|
636 | break;
|
---|
637 | }
|
---|
638 |
|
---|
639 | return status;
|
---|
640 | }
|
---|
641 |
|
---|
642 |
|
---|
643 | status_t
|
---|
644 | BMenu::SetTargetForItems(BMessenger messenger)
|
---|
645 | {
|
---|
646 | status_t status = B_OK;
|
---|
647 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
648 | status = ItemAt(i)->SetTarget(messenger);
|
---|
649 | if (status < B_OK)
|
---|
650 | break;
|
---|
651 | }
|
---|
652 |
|
---|
653 | return status;
|
---|
654 | }
|
---|
655 |
|
---|
656 |
|
---|
657 | void
|
---|
658 | BMenu::SetEnabled(bool enabled)
|
---|
659 | {
|
---|
660 | if (fEnabled == enabled)
|
---|
661 | return;
|
---|
662 |
|
---|
663 | fEnabled = enabled;
|
---|
664 |
|
---|
665 | if (fSuperitem)
|
---|
666 | fSuperitem->SetEnabled(enabled);
|
---|
667 | }
|
---|
668 |
|
---|
669 |
|
---|
670 | void
|
---|
671 | BMenu::SetRadioMode(bool flag)
|
---|
672 | {
|
---|
673 | fRadioMode = flag;
|
---|
674 | if (!flag)
|
---|
675 | SetLabelFromMarked(false);
|
---|
676 | }
|
---|
677 |
|
---|
678 |
|
---|
679 | void
|
---|
680 | BMenu::SetTriggersEnabled(bool flag)
|
---|
681 | {
|
---|
682 | fTriggerEnabled = flag;
|
---|
683 | }
|
---|
684 |
|
---|
685 |
|
---|
686 | void
|
---|
687 | BMenu::SetMaxContentWidth(float width)
|
---|
688 | {
|
---|
689 | fMaxContentWidth = width;
|
---|
690 | }
|
---|
691 |
|
---|
692 |
|
---|
693 | void
|
---|
694 | BMenu::SetLabelFromMarked(bool flag)
|
---|
695 | {
|
---|
696 | fDynamicName = flag;
|
---|
697 | if (flag)
|
---|
698 | SetRadioMode(true);
|
---|
699 | }
|
---|
700 |
|
---|
701 |
|
---|
702 | bool
|
---|
703 | BMenu::IsLabelFromMarked()
|
---|
704 | {
|
---|
705 | return fDynamicName;
|
---|
706 | }
|
---|
707 |
|
---|
708 |
|
---|
709 | bool
|
---|
710 | BMenu::IsEnabled() const
|
---|
711 | {
|
---|
712 | if (!fEnabled)
|
---|
713 | return false;
|
---|
714 |
|
---|
715 | return fSuper ? fSuper->IsEnabled() : true ;
|
---|
716 | }
|
---|
717 |
|
---|
718 |
|
---|
719 | bool
|
---|
720 | BMenu::IsRadioMode() const
|
---|
721 | {
|
---|
722 | return fRadioMode;
|
---|
723 | }
|
---|
724 |
|
---|
725 |
|
---|
726 | bool
|
---|
727 | BMenu::AreTriggersEnabled() const
|
---|
728 | {
|
---|
729 | return fTriggerEnabled;
|
---|
730 | }
|
---|
731 |
|
---|
732 |
|
---|
733 | bool
|
---|
734 | BMenu::IsRedrawAfterSticky() const
|
---|
735 | {
|
---|
736 | return fRedrawAfterSticky;
|
---|
737 | }
|
---|
738 |
|
---|
739 |
|
---|
740 | float
|
---|
741 | BMenu::MaxContentWidth() const
|
---|
742 | {
|
---|
743 | return fMaxContentWidth;
|
---|
744 | }
|
---|
745 |
|
---|
746 |
|
---|
747 | BMenuItem *
|
---|
748 | BMenu::FindMarked()
|
---|
749 | {
|
---|
750 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
751 | BMenuItem *item = ItemAt(i);
|
---|
752 | if (item->IsMarked())
|
---|
753 | return item;
|
---|
754 | }
|
---|
755 |
|
---|
756 | return NULL;
|
---|
757 | }
|
---|
758 |
|
---|
759 |
|
---|
760 | BMenu *
|
---|
761 | BMenu::Supermenu() const
|
---|
762 | {
|
---|
763 | return fSuper;
|
---|
764 | }
|
---|
765 |
|
---|
766 |
|
---|
767 | BMenuItem *
|
---|
768 | BMenu::Superitem() const
|
---|
769 | {
|
---|
770 | return fSuperitem;
|
---|
771 | }
|
---|
772 |
|
---|
773 |
|
---|
774 | void
|
---|
775 | BMenu::MessageReceived(BMessage *msg)
|
---|
776 | {
|
---|
777 | BView::MessageReceived(msg);
|
---|
778 | }
|
---|
779 |
|
---|
780 |
|
---|
781 | void
|
---|
782 | BMenu::KeyDown(const char *bytes, int32 numBytes)
|
---|
783 | {
|
---|
784 | // TODO: Test how it works on beos and implement it correctly
|
---|
785 | switch (bytes[0]) {
|
---|
786 | case B_UP_ARROW:
|
---|
787 | if (fLayout == B_ITEMS_IN_COLUMN)
|
---|
788 | SelectNextItem(fSelected, false);
|
---|
789 | break;
|
---|
790 |
|
---|
791 | case B_DOWN_ARROW:
|
---|
792 | if (fLayout == B_ITEMS_IN_COLUMN)
|
---|
793 | SelectNextItem(fSelected, true);
|
---|
794 | break;
|
---|
795 |
|
---|
796 | case B_LEFT_ARROW:
|
---|
797 | if (fLayout == B_ITEMS_IN_ROW)
|
---|
798 | SelectNextItem(fSelected, false);
|
---|
799 | break;
|
---|
800 |
|
---|
801 | case B_RIGHT_ARROW:
|
---|
802 | if (fLayout == B_ITEMS_IN_ROW)
|
---|
803 | SelectNextItem(fSelected, true);
|
---|
804 | break;
|
---|
805 |
|
---|
806 | case B_ENTER:
|
---|
807 | case B_SPACE:
|
---|
808 | if (fSelected)
|
---|
809 | InvokeItem(fSelected);
|
---|
810 |
|
---|
811 | break;
|
---|
812 |
|
---|
813 | case B_ESCAPE:
|
---|
814 | QuitTracking();
|
---|
815 | break;
|
---|
816 |
|
---|
817 | default:
|
---|
818 | BView::KeyDown(bytes, numBytes);
|
---|
819 | }
|
---|
820 | }
|
---|
821 |
|
---|
822 |
|
---|
823 | void
|
---|
824 | BMenu::Draw(BRect updateRect)
|
---|
825 | {
|
---|
826 | if (RelayoutIfNeeded()) {
|
---|
827 | Invalidate();
|
---|
828 | return;
|
---|
829 | }
|
---|
830 |
|
---|
831 | DrawBackground(updateRect);
|
---|
832 | DrawItems(updateRect);
|
---|
833 | }
|
---|
834 |
|
---|
835 |
|
---|
836 | void
|
---|
837 | BMenu::GetPreferredSize(float *_width, float *_height)
|
---|
838 | {
|
---|
839 | ComputeLayout(0, true, false, _width, _height);
|
---|
840 | }
|
---|
841 |
|
---|
842 |
|
---|
843 | void
|
---|
844 | BMenu::ResizeToPreferred()
|
---|
845 | {
|
---|
846 | BView::ResizeToPreferred();
|
---|
847 | }
|
---|
848 |
|
---|
849 |
|
---|
850 | void
|
---|
851 | BMenu::FrameMoved(BPoint new_position)
|
---|
852 | {
|
---|
853 | BView::FrameMoved(new_position);
|
---|
854 | }
|
---|
855 |
|
---|
856 |
|
---|
857 | void
|
---|
858 | BMenu::FrameResized(float new_width, float new_height)
|
---|
859 | {
|
---|
860 | BView::FrameResized(new_width, new_height);
|
---|
861 | }
|
---|
862 |
|
---|
863 |
|
---|
864 | void
|
---|
865 | BMenu::InvalidateLayout()
|
---|
866 | {
|
---|
867 | fUseCachedMenuLayout = false;
|
---|
868 | }
|
---|
869 |
|
---|
870 |
|
---|
871 | BHandler *
|
---|
872 | BMenu::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
|
---|
873 | int32 form, const char *property)
|
---|
874 | {
|
---|
875 | BPropertyInfo propInfo(sPropList);
|
---|
876 | BHandler *target = NULL;
|
---|
877 |
|
---|
878 | switch (propInfo.FindMatch(msg, 0, specifier, form, property)) {
|
---|
879 | case B_ERROR:
|
---|
880 | break;
|
---|
881 |
|
---|
882 | case 0:
|
---|
883 | case 1:
|
---|
884 | case 2:
|
---|
885 | case 3:
|
---|
886 | case 4:
|
---|
887 | case 5:
|
---|
888 | case 6:
|
---|
889 | case 7:
|
---|
890 | target = this;
|
---|
891 | break;
|
---|
892 | case 8:
|
---|
893 | // TODO: redirect to menu
|
---|
894 | target = this;
|
---|
895 | break;
|
---|
896 | case 9:
|
---|
897 | case 10:
|
---|
898 | case 11:
|
---|
899 | case 12:
|
---|
900 | target = this;
|
---|
901 | break;
|
---|
902 | case 13:
|
---|
903 | // TODO: redirect to menuitem
|
---|
904 | target = this;
|
---|
905 | break;
|
---|
906 | }
|
---|
907 |
|
---|
908 | if (!target)
|
---|
909 | target = BView::ResolveSpecifier(msg, index, specifier, form,
|
---|
910 | property);
|
---|
911 |
|
---|
912 | return target;
|
---|
913 | }
|
---|
914 |
|
---|
915 |
|
---|
916 | status_t
|
---|
917 | BMenu::GetSupportedSuites(BMessage *data)
|
---|
918 | {
|
---|
919 | if (data == NULL)
|
---|
920 | return B_BAD_VALUE;
|
---|
921 |
|
---|
922 | status_t err = data->AddString("suites", "suite/vnd.Be-menu");
|
---|
923 |
|
---|
924 | if (err < B_OK)
|
---|
925 | return err;
|
---|
926 |
|
---|
927 | BPropertyInfo propertyInfo(sPropList);
|
---|
928 | err = data->AddFlat("messages", &propertyInfo);
|
---|
929 |
|
---|
930 | if (err < B_OK)
|
---|
931 | return err;
|
---|
932 |
|
---|
933 | return BView::GetSupportedSuites(data);
|
---|
934 | }
|
---|
935 |
|
---|
936 |
|
---|
937 | status_t
|
---|
938 | BMenu::Perform(perform_code d, void *arg)
|
---|
939 | {
|
---|
940 | return BView::Perform(d, arg);
|
---|
941 | }
|
---|
942 |
|
---|
943 |
|
---|
944 | void
|
---|
945 | BMenu::MakeFocus(bool focused)
|
---|
946 | {
|
---|
947 | BView::MakeFocus(focused);
|
---|
948 | }
|
---|
949 |
|
---|
950 |
|
---|
951 | void
|
---|
952 | BMenu::AllAttached()
|
---|
953 | {
|
---|
954 | BView::AllAttached();
|
---|
955 | }
|
---|
956 |
|
---|
957 |
|
---|
958 | void
|
---|
959 | BMenu::AllDetached()
|
---|
960 | {
|
---|
961 | BView::AllDetached();
|
---|
962 | }
|
---|
963 |
|
---|
964 |
|
---|
965 | BMenu::BMenu(BRect frame, const char *name, uint32 resizingMode, uint32 flags,
|
---|
966 | menu_layout layout, bool resizeToFit)
|
---|
967 | : BView(frame, name, resizingMode, flags),
|
---|
968 | fChosenItem(NULL),
|
---|
969 | fSelected(NULL),
|
---|
970 | fCachedMenuWindow(NULL),
|
---|
971 | fSuper(NULL),
|
---|
972 | fSuperitem(NULL),
|
---|
973 | fAscent(-1.0f),
|
---|
974 | fDescent(-1.0f),
|
---|
975 | fFontHeight(-1.0f),
|
---|
976 | fState(0),
|
---|
977 | fLayout(layout),
|
---|
978 | fExtraRect(NULL),
|
---|
979 | fMaxContentWidth(0.0f),
|
---|
980 | fInitMatrixSize(NULL),
|
---|
981 | fExtraMenuData(NULL),
|
---|
982 | fTrigger(0),
|
---|
983 | fResizeToFit(resizeToFit),
|
---|
984 | fUseCachedMenuLayout(false),
|
---|
985 | fEnabled(true),
|
---|
986 | fDynamicName(false),
|
---|
987 | fRadioMode(false),
|
---|
988 | fTrackNewBounds(false),
|
---|
989 | fStickyMode(false),
|
---|
990 | fIgnoreHidden(true),
|
---|
991 | fTriggerEnabled(true),
|
---|
992 | fRedrawAfterSticky(false),
|
---|
993 | fAttachAborted(false)
|
---|
994 | {
|
---|
995 | InitData(NULL);
|
---|
996 | }
|
---|
997 |
|
---|
998 |
|
---|
999 | void
|
---|
1000 | BMenu::SetItemMargins(float left, float top, float right, float bottom)
|
---|
1001 | {
|
---|
1002 | fPad.Set(left, top, right, bottom);
|
---|
1003 | }
|
---|
1004 |
|
---|
1005 |
|
---|
1006 | void
|
---|
1007 | BMenu::GetItemMargins(float *left, float *top, float *right,
|
---|
1008 | float *bottom) const
|
---|
1009 | {
|
---|
1010 | if (left != NULL)
|
---|
1011 | *left = fPad.left;
|
---|
1012 | if (top != NULL)
|
---|
1013 | *top = fPad.top;
|
---|
1014 | if (right != NULL)
|
---|
1015 | *right = fPad.right;
|
---|
1016 | if (bottom != NULL)
|
---|
1017 | *bottom = fPad.bottom;
|
---|
1018 | }
|
---|
1019 |
|
---|
1020 |
|
---|
1021 | menu_layout
|
---|
1022 | BMenu::Layout() const
|
---|
1023 | {
|
---|
1024 | return fLayout;
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 |
|
---|
1028 | void
|
---|
1029 | BMenu::Show()
|
---|
1030 | {
|
---|
1031 | Show(false);
|
---|
1032 | }
|
---|
1033 |
|
---|
1034 |
|
---|
1035 | void
|
---|
1036 | BMenu::Show(bool selectFirst)
|
---|
1037 | {
|
---|
1038 | Install(NULL);
|
---|
1039 | _show(selectFirst);
|
---|
1040 | }
|
---|
1041 |
|
---|
1042 |
|
---|
1043 | void
|
---|
1044 | BMenu::Hide()
|
---|
1045 | {
|
---|
1046 | _hide();
|
---|
1047 | Uninstall();
|
---|
1048 | }
|
---|
1049 |
|
---|
1050 |
|
---|
1051 | BMenuItem *
|
---|
1052 | BMenu::Track(bool sticky, BRect *clickToOpenRect)
|
---|
1053 | {
|
---|
1054 | if (sticky && LockLooper()) {
|
---|
1055 | RedrawAfterSticky(Bounds());
|
---|
1056 | UnlockLooper();
|
---|
1057 | }
|
---|
1058 |
|
---|
1059 | if (clickToOpenRect != NULL && LockLooper()) {
|
---|
1060 | fExtraRect = clickToOpenRect;
|
---|
1061 | ConvertFromScreen(fExtraRect);
|
---|
1062 | UnlockLooper();
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | // If sticky is false, pass 0 to the tracking function
|
---|
1066 | // so the menu will stay in nonsticky mode
|
---|
1067 | const bigtime_t trackTime = sticky ? system_time() : 0;
|
---|
1068 | int action;
|
---|
1069 | BMenuItem *menuItem = _track(&action, trackTime);
|
---|
1070 |
|
---|
1071 | SetStickyMode(false);
|
---|
1072 | fExtraRect = NULL;
|
---|
1073 |
|
---|
1074 | return menuItem;
|
---|
1075 | }
|
---|
1076 |
|
---|
1077 |
|
---|
1078 | bool
|
---|
1079 | BMenu::AddDynamicItem(add_state s)
|
---|
1080 | {
|
---|
1081 | // Implemented in subclasses
|
---|
1082 | return false;
|
---|
1083 | }
|
---|
1084 |
|
---|
1085 |
|
---|
1086 | void
|
---|
1087 | BMenu::DrawBackground(BRect update)
|
---|
1088 | {
|
---|
1089 | rgb_color oldColor = HighColor();
|
---|
1090 | SetHighColor(sMenuInfo.background_color);
|
---|
1091 | FillRect(Bounds() & update, B_SOLID_HIGH);
|
---|
1092 | SetHighColor(oldColor);
|
---|
1093 | }
|
---|
1094 |
|
---|
1095 |
|
---|
1096 | void
|
---|
1097 | BMenu::SetTrackingHook(menu_tracking_hook func, void *state)
|
---|
1098 | {
|
---|
1099 | delete fExtraMenuData;
|
---|
1100 | fExtraMenuData = new (nothrow) _ExtraMenuData_(func, state);
|
---|
1101 | }
|
---|
1102 |
|
---|
1103 |
|
---|
1104 | void BMenu::_ReservedMenu3() {}
|
---|
1105 | void BMenu::_ReservedMenu4() {}
|
---|
1106 | void BMenu::_ReservedMenu5() {}
|
---|
1107 | void BMenu::_ReservedMenu6() {}
|
---|
1108 |
|
---|
1109 |
|
---|
1110 | BMenu &
|
---|
1111 | BMenu::operator=(const BMenu &)
|
---|
1112 | {
|
---|
1113 | return *this;
|
---|
1114 | }
|
---|
1115 |
|
---|
1116 |
|
---|
1117 | void
|
---|
1118 | BMenu::InitData(BMessage *data)
|
---|
1119 | {
|
---|
1120 | // TODO: Get _color, _fname, _fflt from the message, if present
|
---|
1121 | BFont font;
|
---|
1122 | font.SetFamilyAndStyle(sMenuInfo.f_family, sMenuInfo.f_style);
|
---|
1123 | font.SetSize(sMenuInfo.font_size);
|
---|
1124 | SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE);
|
---|
1125 |
|
---|
1126 | SetLowColor(sMenuInfo.background_color);
|
---|
1127 | SetViewColor(sMenuInfo.background_color);
|
---|
1128 |
|
---|
1129 | if (data != NULL) {
|
---|
1130 | data->FindInt32("_layout", (int32 *)&fLayout);
|
---|
1131 | data->FindBool("_rsize_to_fit", &fResizeToFit);
|
---|
1132 | bool disabled;
|
---|
1133 | if (data->FindBool("_disable", &disabled) == B_OK)
|
---|
1134 | fEnabled = !disabled;
|
---|
1135 | data->FindBool("_radio", &fRadioMode);
|
---|
1136 |
|
---|
1137 | bool disableTrigger = false;
|
---|
1138 | data->FindBool("_trig_disabled", &disableTrigger);
|
---|
1139 | fTriggerEnabled = !disableTrigger;
|
---|
1140 |
|
---|
1141 | data->FindBool("_dyn_label", &fDynamicName);
|
---|
1142 | data->FindFloat("_maxwidth", &fMaxContentWidth);
|
---|
1143 |
|
---|
1144 | BMessage msg;
|
---|
1145 | for (int32 i = 0; data->FindMessage("_items", i, &msg) == B_OK; i++) {
|
---|
1146 | BArchivable *object = instantiate_object(&msg);
|
---|
1147 | if (BMenuItem *item = dynamic_cast<BMenuItem *>(object)) {
|
---|
1148 | BRect bounds;
|
---|
1149 | if ((fLayout == B_ITEMS_IN_MATRIX)
|
---|
1150 | && (data->FindRect("_i_frames", i, &bounds) == B_OK))
|
---|
1151 | AddItem(item, bounds);
|
---|
1152 | else
|
---|
1153 | AddItem(item);
|
---|
1154 | }
|
---|
1155 | }
|
---|
1156 | }
|
---|
1157 | }
|
---|
1158 |
|
---|
1159 |
|
---|
1160 | bool
|
---|
1161 | BMenu::_show(bool selectFirstItem)
|
---|
1162 | {
|
---|
1163 | // See if the supermenu has a cached menuwindow,
|
---|
1164 | // and use that one if possible.
|
---|
1165 | BMenuWindow *window = NULL;
|
---|
1166 | bool ourWindow = false;
|
---|
1167 | if (fSuper != NULL) {
|
---|
1168 | fSuperbounds = fSuper->ConvertToScreen(fSuper->Bounds());
|
---|
1169 | window = fSuper->MenuWindow();
|
---|
1170 | }
|
---|
1171 |
|
---|
1172 | // Otherwise, create a new one
|
---|
1173 | // This happens for "stand alone" BPopUpMenus
|
---|
1174 | // (i.e. not within a BMenuField)
|
---|
1175 | if (window == NULL) {
|
---|
1176 | // Menu windows get the BMenu's handler name
|
---|
1177 | window = new (nothrow) BMenuWindow(Name());
|
---|
1178 | ourWindow = true;
|
---|
1179 | }
|
---|
1180 |
|
---|
1181 | if (window == NULL)
|
---|
1182 | return false;
|
---|
1183 |
|
---|
1184 | if (window->Lock()) {
|
---|
1185 | fAttachAborted = false;
|
---|
1186 | window->AttachMenu(this);
|
---|
1187 |
|
---|
1188 | // Menu didn't have the time to add its items: aborting...
|
---|
1189 | if (fAttachAborted) {
|
---|
1190 | window->DetachMenu();
|
---|
1191 | // TODO: Probably not needed, we can just let _hide() quit the window
|
---|
1192 | if (ourWindow)
|
---|
1193 | window->Quit();
|
---|
1194 | else
|
---|
1195 | window->Unlock();
|
---|
1196 | return false;
|
---|
1197 | }
|
---|
1198 |
|
---|
1199 | // Move the BMenu to 1, 1, if it's attached to a BMenuWindow,
|
---|
1200 | // (that means it's a BMenu, BMenuBars are attached to regular BWindows).
|
---|
1201 | // This is needed to be able to draw the frame around the BMenu.
|
---|
1202 | if (dynamic_cast<BMenuWindow *>(window) != NULL)
|
---|
1203 | MoveTo(1, 1);
|
---|
1204 |
|
---|
1205 | UpdateWindowViewSize();
|
---|
1206 | window->Show();
|
---|
1207 |
|
---|
1208 | if (selectFirstItem)
|
---|
1209 | SelectItem(ItemAt(0));
|
---|
1210 |
|
---|
1211 | window->Unlock();
|
---|
1212 | }
|
---|
1213 |
|
---|
1214 | return true;
|
---|
1215 | }
|
---|
1216 |
|
---|
1217 |
|
---|
1218 | void
|
---|
1219 | BMenu::_hide()
|
---|
1220 | {
|
---|
1221 | BMenuWindow *window = static_cast<BMenuWindow *>(Window());
|
---|
1222 | if (window == NULL || !window->Lock())
|
---|
1223 | return;
|
---|
1224 |
|
---|
1225 | if (fSelected != NULL)
|
---|
1226 | SelectItem(NULL);
|
---|
1227 |
|
---|
1228 | window->Hide();
|
---|
1229 | window->DetachMenu();
|
---|
1230 | // we don't want to be deleted when the window is removed
|
---|
1231 |
|
---|
1232 | // Delete the menu window used by our submenus
|
---|
1233 | DeleteMenuWindow();
|
---|
1234 |
|
---|
1235 | window->Unlock();
|
---|
1236 |
|
---|
1237 | if (fSuper == NULL && window->Lock())
|
---|
1238 | window->Quit();
|
---|
1239 | }
|
---|
1240 |
|
---|
1241 |
|
---|
1242 | const bigtime_t kHysteresis = 200000; // TODO: Test and reduce if needed.
|
---|
1243 |
|
---|
1244 |
|
---|
1245 | BMenuItem *
|
---|
1246 | BMenu::_track(int *action, bigtime_t trackTime, long start)
|
---|
1247 | {
|
---|
1248 | // TODO: cleanup
|
---|
1249 | BMenuItem *item = NULL;
|
---|
1250 | bigtime_t openTime = system_time();
|
---|
1251 | bigtime_t closeTime = 0;
|
---|
1252 |
|
---|
1253 | fState = MENU_STATE_TRACKING;
|
---|
1254 | if (fSuper != NULL)
|
---|
1255 | fSuper->fState = MENU_STATE_TRACKING_SUBMENU;
|
---|
1256 |
|
---|
1257 | while (true) {
|
---|
1258 | if (fExtraMenuData != NULL && fExtraMenuData->trackingHook != NULL
|
---|
1259 | && fExtraMenuData->trackingState != NULL) {
|
---|
1260 | /*bool result =*/ fExtraMenuData->trackingHook(this, fExtraMenuData->trackingState);
|
---|
1261 | //printf("tracking hook returned %s\n", result ? "true" : "false");
|
---|
1262 | }
|
---|
1263 |
|
---|
1264 | bool locked = LockLooper();
|
---|
1265 | if (!locked)
|
---|
1266 | break;
|
---|
1267 |
|
---|
1268 | bigtime_t snoozeAmount = 50000;
|
---|
1269 | BPoint location;
|
---|
1270 | ulong buttons;
|
---|
1271 | GetMouse(&location, &buttons, true);
|
---|
1272 |
|
---|
1273 | Window()->UpdateIfNeeded();
|
---|
1274 | BPoint screenLocation = ConvertToScreen(location);
|
---|
1275 | item = HitTestItems(location, B_ORIGIN);
|
---|
1276 | if (item != NULL) {
|
---|
1277 | if (item != fSelected && system_time() > closeTime + kHysteresis) {
|
---|
1278 | SelectItem(item, -1);
|
---|
1279 | openTime = system_time();
|
---|
1280 | } else if (system_time() > kHysteresis + openTime && item->Submenu() != NULL
|
---|
1281 | && item->Submenu()->Window() == NULL) {
|
---|
1282 | // Open the submenu if it's not opened yet, but only if
|
---|
1283 | // the mouse pointer stayed over there for some time
|
---|
1284 | // (hysteresis)
|
---|
1285 | SelectItem(item);
|
---|
1286 | closeTime = system_time();
|
---|
1287 | }
|
---|
1288 | fState = MENU_STATE_TRACKING;
|
---|
1289 | }
|
---|
1290 |
|
---|
1291 | // Track the submenu
|
---|
1292 | if (fSelected != NULL && OverSubmenu(fSelected, screenLocation)) {
|
---|
1293 | UnlockLooper();
|
---|
1294 | locked = false;
|
---|
1295 | int submenuAction = MENU_STATE_TRACKING;
|
---|
1296 | BMenu *submenu = fSelected->Submenu();
|
---|
1297 | if (IsStickyMode())
|
---|
1298 | submenu->SetStickyMode(true);
|
---|
1299 | BMenuItem *submenuItem = submenu->_track(&submenuAction, trackTime);
|
---|
1300 | //submenu->Window()->Activate();
|
---|
1301 | if (submenuAction == MENU_STATE_CLOSED) {
|
---|
1302 | item = submenuItem;
|
---|
1303 | fState = submenuAction;
|
---|
1304 | break;
|
---|
1305 | }
|
---|
1306 |
|
---|
1307 | locked = LockLooper();
|
---|
1308 | if (!locked)
|
---|
1309 | break;
|
---|
1310 | }
|
---|
1311 |
|
---|
1312 | if (item == NULL) {
|
---|
1313 | if (OverSuper(screenLocation)) {
|
---|
1314 | fState = MENU_STATE_TRACKING;
|
---|
1315 | UnlockLooper();
|
---|
1316 | break;
|
---|
1317 | }
|
---|
1318 |
|
---|
1319 | if (fSelected != NULL && !OverSubmenu(fSelected, screenLocation)
|
---|
1320 | && system_time() > closeTime + kHysteresis
|
---|
1321 | && fState != MENU_STATE_TRACKING_SUBMENU) {
|
---|
1322 | SelectItem(NULL);
|
---|
1323 | fState = MENU_STATE_TRACKING;
|
---|
1324 | }
|
---|
1325 |
|
---|
1326 | if (fSuper != NULL) {
|
---|
1327 | if (locked)
|
---|
1328 | UnlockLooper();
|
---|
1329 | *action = fState;
|
---|
1330 | return NULL;
|
---|
1331 | }
|
---|
1332 | }
|
---|
1333 |
|
---|
1334 | if (locked)
|
---|
1335 | UnlockLooper();
|
---|
1336 |
|
---|
1337 | if (buttons != 0 && IsStickyMode())
|
---|
1338 | fState = MENU_STATE_CLOSED;
|
---|
1339 | else if (buttons == 0 && !IsStickyMode()) {
|
---|
1340 | if (system_time() < trackTime + 1000000
|
---|
1341 | || (fExtraRect != NULL && fExtraRect->Contains(location)))
|
---|
1342 | SetStickyMode(true);
|
---|
1343 | else
|
---|
1344 | fState = MENU_STATE_CLOSED;
|
---|
1345 | }
|
---|
1346 |
|
---|
1347 | if (fState == MENU_STATE_CLOSED)
|
---|
1348 | break;
|
---|
1349 |
|
---|
1350 | snooze(snoozeAmount);
|
---|
1351 | }
|
---|
1352 |
|
---|
1353 | if (action != NULL)
|
---|
1354 | *action = fState;
|
---|
1355 |
|
---|
1356 | if (fSelected != NULL && LockLooper()) {
|
---|
1357 | SelectItem(NULL);
|
---|
1358 | UnlockLooper();
|
---|
1359 | }
|
---|
1360 |
|
---|
1361 | if (IsStickyMode())
|
---|
1362 | SetStickyMode(false);
|
---|
1363 |
|
---|
1364 | // delete the menu window recycled for all the child menus
|
---|
1365 | DeleteMenuWindow();
|
---|
1366 |
|
---|
1367 | return item;
|
---|
1368 | }
|
---|
1369 |
|
---|
1370 |
|
---|
1371 | bool
|
---|
1372 | BMenu::_AddItem(BMenuItem *item, int32 index)
|
---|
1373 | {
|
---|
1374 | ASSERT(item != NULL);
|
---|
1375 | if (index < 0 || index > fItems.CountItems())
|
---|
1376 | return false;
|
---|
1377 |
|
---|
1378 | if (!fItems.AddItem(item, index))
|
---|
1379 | return false;
|
---|
1380 |
|
---|
1381 | // install the item on the supermenu's window
|
---|
1382 | // or onto our window, if we are a root menu
|
---|
1383 | BWindow* window = NULL;
|
---|
1384 | if (Superitem() != NULL)
|
---|
1385 | window = Superitem()->fWindow;
|
---|
1386 | else
|
---|
1387 | window = Window();
|
---|
1388 | if (window != NULL)
|
---|
1389 | item->Install(window);
|
---|
1390 |
|
---|
1391 | item->SetSuper(this);
|
---|
1392 |
|
---|
1393 | return true;
|
---|
1394 | }
|
---|
1395 |
|
---|
1396 |
|
---|
1397 | bool
|
---|
1398 | BMenu::RemoveItems(int32 index, int32 count, BMenuItem *item, bool deleteItems)
|
---|
1399 | {
|
---|
1400 | bool success = false;
|
---|
1401 | bool invalidateLayout = false;
|
---|
1402 |
|
---|
1403 | bool locked = LockLooper();
|
---|
1404 | BWindow *window = Window();
|
---|
1405 |
|
---|
1406 | // The plan is simple: If we're given a BMenuItem directly, we use it
|
---|
1407 | // and ignore index and count. Otherwise, we use them instead.
|
---|
1408 | if (item != NULL) {
|
---|
1409 | if (fItems.RemoveItem(item)) {
|
---|
1410 | if (item == fSelected && window != NULL)
|
---|
1411 | SelectItem(NULL);
|
---|
1412 | item->Uninstall();
|
---|
1413 | item->SetSuper(NULL);
|
---|
1414 | if (deleteItems)
|
---|
1415 | delete item;
|
---|
1416 | success = invalidateLayout = true;
|
---|
1417 | }
|
---|
1418 | } else {
|
---|
1419 | // We iterate backwards because it's simpler
|
---|
1420 | int32 i = min_c(index + count - 1, fItems.CountItems() - 1);
|
---|
1421 | // NOTE: the range check for "index" is done after
|
---|
1422 | // calculating the last index to be removed, so
|
---|
1423 | // that the range is not "shifted" unintentionally
|
---|
1424 | index = max_c(0, index);
|
---|
1425 | for (; i >= index; i--) {
|
---|
1426 | item = static_cast<BMenuItem*>(fItems.ItemAt(i));
|
---|
1427 | if (item != NULL) {
|
---|
1428 | if (fItems.RemoveItem(item)) {
|
---|
1429 | if (item == fSelected && window != NULL)
|
---|
1430 | SelectItem(NULL);
|
---|
1431 | item->Uninstall();
|
---|
1432 | item->SetSuper(NULL);
|
---|
1433 | if (deleteItems)
|
---|
1434 | delete item;
|
---|
1435 | success = true;
|
---|
1436 | invalidateLayout = true;
|
---|
1437 | } else {
|
---|
1438 | // operation not entirely successful
|
---|
1439 | success = false;
|
---|
1440 | break;
|
---|
1441 | }
|
---|
1442 | }
|
---|
1443 | }
|
---|
1444 | }
|
---|
1445 |
|
---|
1446 | if (invalidateLayout && locked && window != NULL) {
|
---|
1447 | LayoutItems(0);
|
---|
1448 | //UpdateWindowViewSize();
|
---|
1449 | Invalidate();
|
---|
1450 | }
|
---|
1451 |
|
---|
1452 | if (locked)
|
---|
1453 | UnlockLooper();
|
---|
1454 |
|
---|
1455 | return success;
|
---|
1456 | }
|
---|
1457 |
|
---|
1458 |
|
---|
1459 | bool
|
---|
1460 | BMenu::RelayoutIfNeeded()
|
---|
1461 | {
|
---|
1462 | if (!fUseCachedMenuLayout) {
|
---|
1463 | fUseCachedMenuLayout = true;
|
---|
1464 | CacheFontInfo();
|
---|
1465 | LayoutItems(0);
|
---|
1466 | return true;
|
---|
1467 | }
|
---|
1468 | return false;
|
---|
1469 | }
|
---|
1470 |
|
---|
1471 |
|
---|
1472 | void
|
---|
1473 | BMenu::LayoutItems(int32 index)
|
---|
1474 | {
|
---|
1475 | CalcTriggers();
|
---|
1476 |
|
---|
1477 | float width, height;
|
---|
1478 | ComputeLayout(index, fResizeToFit, true, &width, &height);
|
---|
1479 |
|
---|
1480 | ResizeTo(width, height);
|
---|
1481 | }
|
---|
1482 |
|
---|
1483 |
|
---|
1484 | void
|
---|
1485 | BMenu::ComputeLayout(int32 index, bool bestFit, bool moveItems,
|
---|
1486 | float* _width, float* _height)
|
---|
1487 | {
|
---|
1488 | // TODO: Take "bestFit", "moveItems", "index" into account,
|
---|
1489 | // Recalculate only the needed items,
|
---|
1490 | // not the whole layout every time
|
---|
1491 |
|
---|
1492 | BRect frame(0, 0, 0, 0);
|
---|
1493 | float iWidth, iHeight;
|
---|
1494 | BMenuItem *item = NULL;
|
---|
1495 |
|
---|
1496 | BFont font;
|
---|
1497 | GetFont(&font);
|
---|
1498 | switch (fLayout) {
|
---|
1499 | case B_ITEMS_IN_COLUMN:
|
---|
1500 | {
|
---|
1501 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
1502 | item = ItemAt(i);
|
---|
1503 | if (item != NULL) {
|
---|
1504 | item->GetContentSize(&iWidth, &iHeight);
|
---|
1505 |
|
---|
1506 | if (item->fModifiers && item->fShortcutChar)
|
---|
1507 | iWidth += 2 * font.Size();
|
---|
1508 | if (item->fSubmenu != NULL)
|
---|
1509 | iWidth += 2 * font.Size();
|
---|
1510 |
|
---|
1511 | item->fBounds.left = 0.0f;
|
---|
1512 | item->fBounds.top = frame.bottom;
|
---|
1513 | item->fBounds.bottom = item->fBounds.top + iHeight + fPad.top + fPad.bottom;
|
---|
1514 |
|
---|
1515 | frame.right = max_c(frame.right, iWidth + fPad.left + fPad.right);
|
---|
1516 | frame.bottom = item->fBounds.bottom + 1.0f;
|
---|
1517 | }
|
---|
1518 | }
|
---|
1519 | if (fMaxContentWidth > 0)
|
---|
1520 | frame.right = min_c(frame.right, fMaxContentWidth);
|
---|
1521 |
|
---|
1522 | if (moveItems) {
|
---|
1523 | for (int32 i = 0; i < fItems.CountItems(); i++)
|
---|
1524 | ItemAt(i)->fBounds.right = frame.right;
|
---|
1525 | }
|
---|
1526 | frame.right = ceilf(frame.right);
|
---|
1527 | frame.bottom--;
|
---|
1528 | break;
|
---|
1529 | }
|
---|
1530 |
|
---|
1531 | case B_ITEMS_IN_ROW:
|
---|
1532 | {
|
---|
1533 | font_height fh;
|
---|
1534 | GetFontHeight(&fh);
|
---|
1535 | frame = BRect(0.0f, 0.0f, 0.0f, ceilf(fh.ascent + fh.descent + fPad.top + fPad.bottom));
|
---|
1536 |
|
---|
1537 | for (int32 i = 0; i < fItems.CountItems(); i++) {
|
---|
1538 | item = ItemAt(i);
|
---|
1539 | if (item != NULL) {
|
---|
1540 | item->GetContentSize(&iWidth, &iHeight);
|
---|
1541 |
|
---|
1542 | item->fBounds.left = frame.right;
|
---|
1543 | item->fBounds.top = 0.0f;
|
---|
1544 | item->fBounds.right = item->fBounds.left + iWidth + fPad.left + fPad.right;
|
---|
1545 |
|
---|
1546 | frame.right = item->Frame().right + 1.0f;
|
---|
1547 | frame.bottom = max_c(frame.bottom, iHeight + fPad.top + fPad.bottom);
|
---|
1548 | }
|
---|
1549 | }
|
---|
1550 |
|
---|
1551 | if (moveItems) {
|
---|
1552 | for (int32 i = 0; i < fItems.CountItems(); i++)
|
---|
1553 | ItemAt(i)->fBounds.bottom = frame.bottom;
|
---|
1554 | }
|
---|
1555 |
|
---|
1556 | if (bestFit)
|
---|
1557 | frame.right = ceilf(frame.right);
|
---|
1558 | else
|
---|
1559 | frame.right = Bounds().right;
|
---|
1560 | break;
|
---|
1561 | }
|
---|
1562 |
|
---|
1563 | case B_ITEMS_IN_MATRIX:
|
---|
1564 | {
|
---|
1565 | for (int32 i = 0; i < CountItems(); i++) {
|
---|
1566 | item = ItemAt(i);
|
---|
1567 | if (item != NULL) {
|
---|
1568 | frame.left = min_c(frame.left, item->Frame().left);
|
---|
1569 | frame.right = max_c(frame.right, item->Frame().right);
|
---|
1570 | frame.top = min_c(frame.top, item->Frame().top);
|
---|
1571 | frame.bottom = max_c(frame.bottom, item->Frame().bottom);
|
---|
1572 | }
|
---|
1573 | }
|
---|
1574 | break;
|
---|
1575 | }
|
---|
1576 |
|
---|
1577 | default:
|
---|
1578 | break;
|
---|
1579 | }
|
---|
1580 |
|
---|
1581 | if (_width) {
|
---|
1582 | // change width depending on resize mode
|
---|
1583 | if ((ResizingMode() & B_FOLLOW_LEFT_RIGHT) == B_FOLLOW_LEFT_RIGHT) {
|
---|
1584 | if (Parent())
|
---|
1585 | *_width = Parent()->Frame().Width() + 1;
|
---|
1586 | else if (Window())
|
---|
1587 | *_width = Window()->Frame().Width() + 1;
|
---|
1588 | else
|
---|
1589 | *_width = Bounds().Width();
|
---|
1590 | } else
|
---|
1591 | *_width = frame.Width();
|
---|
1592 | }
|
---|
1593 |
|
---|
1594 | if (_height)
|
---|
1595 | *_height = frame.Height();
|
---|
1596 |
|
---|
1597 | if (moveItems)
|
---|
1598 | fUseCachedMenuLayout = true;
|
---|
1599 | }
|
---|
1600 |
|
---|
1601 |
|
---|
1602 | BRect
|
---|
1603 | BMenu::Bump(BRect current, BPoint extent, int32 index) const
|
---|
1604 | {
|
---|
1605 | // ToDo: what's this?
|
---|
1606 | return BRect();
|
---|
1607 | }
|
---|
1608 |
|
---|
1609 |
|
---|
1610 | BPoint
|
---|
1611 | BMenu::ItemLocInRect(BRect frame) const
|
---|
1612 | {
|
---|
1613 | // ToDo: what's this?
|
---|
1614 | return BPoint();
|
---|
1615 | }
|
---|
1616 |
|
---|
1617 |
|
---|
1618 | BPoint
|
---|
1619 | BMenu::ScreenLocation()
|
---|
1620 | {
|
---|
1621 | BMenu *superMenu = Supermenu();
|
---|
1622 | BMenuItem *superItem = Superitem();
|
---|
1623 |
|
---|
1624 | if (superMenu == NULL || superItem == NULL) {
|
---|
1625 | debugger("BMenu can't determine where to draw."
|
---|
1626 | "Override BMenu::ScreenLocation() to determine location.");
|
---|
1627 | }
|
---|
1628 |
|
---|
1629 | BPoint point;
|
---|
1630 | if (superMenu->Layout() == B_ITEMS_IN_COLUMN)
|
---|
1631 | point = superItem->Frame().RightTop() + BPoint(1.0f, 1.0f);
|
---|
1632 | else
|
---|
1633 | point = superItem->Frame().LeftBottom() + BPoint(1.0f, 1.0f);
|
---|
1634 |
|
---|
1635 | superMenu->ConvertToScreen(&point);
|
---|
1636 |
|
---|
1637 | return point;
|
---|
1638 | }
|
---|
1639 |
|
---|
1640 |
|
---|
1641 | BRect
|
---|
1642 | BMenu::CalcFrame(BPoint where, bool *scrollOn)
|
---|
1643 | {
|
---|
1644 | // TODO: Improve me
|
---|
1645 | BRect bounds = Bounds();
|
---|
1646 | BRect frame = bounds.OffsetToCopy(where);
|
---|
1647 |
|
---|
1648 | BScreen screen(Window());
|
---|
1649 | BRect screenFrame = screen.Frame();
|
---|
1650 |
|
---|
1651 | BMenu *superMenu = Supermenu();
|
---|
1652 | BMenuItem *superItem = Superitem();
|
---|
1653 |
|
---|
1654 | if (scrollOn != NULL) {
|
---|
1655 | // basically, if this returns false, it means
|
---|
1656 | // that the menu frame won't fit completely inside the screen
|
---|
1657 | *scrollOn = !screenFrame.Contains(bounds);
|
---|
1658 | }
|
---|
1659 |
|
---|
1660 | // TODO: Horrible hack:
|
---|
1661 | // When added to a BMenuField, a BPopUpMenu is the child of
|
---|
1662 | // a _BMCMenuBar_ to "fake" the menu hierarchy
|
---|
1663 | if (superMenu == NULL || superItem == NULL
|
---|
1664 | || dynamic_cast<_BMCMenuBar_ *>(superMenu) != NULL) {
|
---|
1665 | // just move the window on screen
|
---|
1666 |
|
---|
1667 | if (frame.bottom > screenFrame.bottom)
|
---|
1668 | frame.OffsetBy(0, screenFrame.bottom - frame.bottom);
|
---|
1669 | else if (frame.top < screenFrame.top)
|
---|
1670 | frame.OffsetBy(0, -frame.top);
|
---|
1671 |
|
---|
1672 | if (frame.right > screenFrame.right)
|
---|
1673 | frame.OffsetBy(screenFrame.right - frame.right, 0);
|
---|
1674 | else if (frame.left < screenFrame.left)
|
---|
1675 | frame.OffsetBy(-frame.left, 0);
|
---|
1676 |
|
---|
1677 | return frame;
|
---|
1678 | }
|
---|
1679 |
|
---|
1680 | if (superMenu->Layout() == B_ITEMS_IN_COLUMN) {
|
---|
1681 | if (frame.right > screenFrame.right)
|
---|
1682 | frame.OffsetBy(-superItem->Frame().Width() - frame.Width() - 2, 0);
|
---|
1683 |
|
---|
1684 | if (frame.left < 0)
|
---|
1685 | frame.OffsetBy(-frame.left, 0);
|
---|
1686 |
|
---|
1687 | if (frame.bottom > screenFrame.bottom)
|
---|
1688 | frame.OffsetBy(0, screenFrame.bottom - frame.bottom);
|
---|
1689 | } else {
|
---|
1690 | if (frame.bottom > screenFrame.bottom)
|
---|
1691 | frame.OffsetBy(0, -superItem->Frame().Height() - frame.Height() - 3);
|
---|
1692 |
|
---|
1693 | if (frame.right > screenFrame.right)
|
---|
1694 | frame.OffsetBy(screenFrame.right - frame.right, 0);
|
---|
1695 | }
|
---|
1696 |
|
---|
1697 | return frame;
|
---|
1698 | }
|
---|
1699 |
|
---|
1700 |
|
---|
1701 | bool
|
---|
1702 | BMenu::ScrollMenu(BRect bounds, BPoint loc, bool *fast)
|
---|
1703 | {
|
---|
1704 | return false;
|
---|
1705 | }
|
---|
1706 |
|
---|
1707 |
|
---|
1708 | void
|
---|
1709 | BMenu::ScrollIntoView(BMenuItem *item)
|
---|
1710 | {
|
---|
1711 | }
|
---|
1712 |
|
---|
1713 |
|
---|
1714 | void
|
---|
1715 | BMenu::DrawItems(BRect updateRect)
|
---|
1716 | {
|
---|
1717 | int32 itemCount = fItems.CountItems();
|
---|
1718 | for (int32 i = 0; i < itemCount; i++) {
|
---|
1719 | BMenuItem *item = ItemAt(i);
|
---|
1720 | if (item->Frame().Intersects(updateRect))
|
---|
1721 | item->Draw();
|
---|
1722 | }
|
---|
1723 | }
|
---|
1724 |
|
---|
1725 |
|
---|
1726 | int
|
---|
1727 | BMenu::State(BMenuItem **item) const
|
---|
1728 | {
|
---|
1729 | return 0;
|
---|
1730 | }
|
---|
1731 |
|
---|
1732 |
|
---|
1733 | void
|
---|
1734 | BMenu::InvokeItem(BMenuItem *item, bool now)
|
---|
1735 | {
|
---|
1736 | if (!item->IsEnabled())
|
---|
1737 | return;
|
---|
1738 |
|
---|
1739 | // Do the "selected" animation
|
---|
1740 | if (!item->Submenu() && LockLooper()) {
|
---|
1741 | snooze(50000);
|
---|
1742 | item->Select(true);
|
---|
1743 | Sync();
|
---|
1744 | snooze(50000);
|
---|
1745 | item->Select(false);
|
---|
1746 | Sync();
|
---|
1747 | snooze(50000);
|
---|
1748 | item->Select(true);
|
---|
1749 | Sync();
|
---|
1750 | snooze(50000);
|
---|
1751 | item->Select(false);
|
---|
1752 | Sync();
|
---|
1753 | UnlockLooper();
|
---|
1754 | }
|
---|
1755 |
|
---|
1756 | item->Invoke();
|
---|
1757 | }
|
---|
1758 |
|
---|
1759 |
|
---|
1760 | bool
|
---|
1761 | BMenu::OverSuper(BPoint location)
|
---|
1762 | {
|
---|
1763 | if (!Supermenu())
|
---|
1764 | return false;
|
---|
1765 |
|
---|
1766 | return fSuperbounds.Contains(location);
|
---|
1767 | }
|
---|
1768 |
|
---|
1769 |
|
---|
1770 | bool
|
---|
1771 | BMenu::OverSubmenu(BMenuItem *item, BPoint loc)
|
---|
1772 | {
|
---|
1773 | // we assume that loc is in screen coords
|
---|
1774 | BMenu *subMenu = item->Submenu();
|
---|
1775 | if (subMenu == NULL || subMenu->Window() == NULL)
|
---|
1776 | return false;
|
---|
1777 |
|
---|
1778 | if (subMenu->Window()->Frame().Contains(loc))
|
---|
1779 | return true;
|
---|
1780 |
|
---|
1781 | if (subMenu->fSelected == NULL)
|
---|
1782 | return false;
|
---|
1783 |
|
---|
1784 | return subMenu->OverSubmenu(subMenu->fSelected, loc);
|
---|
1785 | }
|
---|
1786 |
|
---|
1787 |
|
---|
1788 | BMenuWindow *
|
---|
1789 | BMenu::MenuWindow()
|
---|
1790 | {
|
---|
1791 | if (fCachedMenuWindow == NULL) {
|
---|
1792 | char windowName[64];
|
---|
1793 | snprintf(windowName, 64, "%s cached menu", Name());
|
---|
1794 | fCachedMenuWindow = new (nothrow) BMenuWindow(windowName);
|
---|
1795 | }
|
---|
1796 |
|
---|
1797 | return fCachedMenuWindow;
|
---|
1798 | }
|
---|
1799 |
|
---|
1800 |
|
---|
1801 | void
|
---|
1802 | BMenu::DeleteMenuWindow()
|
---|
1803 | {
|
---|
1804 | if (fCachedMenuWindow != NULL) {
|
---|
1805 | fCachedMenuWindow->Lock();
|
---|
1806 | fCachedMenuWindow->Quit();
|
---|
1807 | fCachedMenuWindow = NULL;
|
---|
1808 | }
|
---|
1809 | }
|
---|
1810 |
|
---|
1811 |
|
---|
1812 | BMenuItem *
|
---|
1813 | BMenu::HitTestItems(BPoint where, BPoint slop) const
|
---|
1814 | {
|
---|
1815 | // TODO: Take "slop" into account ?
|
---|
1816 |
|
---|
1817 | // if the point doesn't lie within the menu's
|
---|
1818 | // bounds, bail out immediately
|
---|
1819 | if (!Bounds().Contains(where))
|
---|
1820 | return NULL;
|
---|
1821 |
|
---|
1822 | int32 itemCount = CountItems();
|
---|
1823 | for (int32 i = 0; i < itemCount; i++) {
|
---|
1824 | BMenuItem *item = ItemAt(i);
|
---|
1825 | if (item->Frame().Contains(where))
|
---|
1826 | return item;
|
---|
1827 | }
|
---|
1828 |
|
---|
1829 | return NULL;
|
---|
1830 | }
|
---|
1831 |
|
---|
1832 |
|
---|
1833 | BRect
|
---|
1834 | BMenu::Superbounds() const
|
---|
1835 | {
|
---|
1836 | return fSuperbounds;
|
---|
1837 | }
|
---|
1838 |
|
---|
1839 |
|
---|
1840 | void
|
---|
1841 | BMenu::CacheFontInfo()
|
---|
1842 | {
|
---|
1843 | font_height fh;
|
---|
1844 | GetFontHeight(&fh);
|
---|
1845 | fAscent = fh.ascent;
|
---|
1846 | fDescent = fh.descent;
|
---|
1847 | fFontHeight = ceilf(fh.ascent + fh.descent + fh.leading);
|
---|
1848 | }
|
---|
1849 |
|
---|
1850 |
|
---|
1851 | void
|
---|
1852 | BMenu::ItemMarked(BMenuItem *item)
|
---|
1853 | {
|
---|
1854 | if (IsRadioMode()) {
|
---|
1855 | for (int32 i = 0; i < CountItems(); i++)
|
---|
1856 | if (ItemAt(i) != item)
|
---|
1857 | ItemAt(i)->SetMarked(false);
|
---|
1858 | InvalidateLayout();
|
---|
1859 | }
|
---|
1860 |
|
---|
1861 | if (IsLabelFromMarked() && Superitem())
|
---|
1862 | Superitem()->SetLabel(item->Label());
|
---|
1863 | }
|
---|
1864 |
|
---|
1865 |
|
---|
1866 | void
|
---|
1867 | BMenu::Install(BWindow *target)
|
---|
1868 | {
|
---|
1869 | for (int32 i = 0; i < CountItems(); i++)
|
---|
1870 | ItemAt(i)->Install(target);
|
---|
1871 | }
|
---|
1872 |
|
---|
1873 |
|
---|
1874 | void
|
---|
1875 | BMenu::Uninstall()
|
---|
1876 | {
|
---|
1877 | for (int32 i = 0; i < CountItems(); i++)
|
---|
1878 | ItemAt(i)->Uninstall();
|
---|
1879 | }
|
---|
1880 |
|
---|
1881 |
|
---|
1882 | void
|
---|
1883 | BMenu::SelectItem(BMenuItem *menuItem, uint32 showSubmenu, bool selectFirstItem)
|
---|
1884 | {
|
---|
1885 | // Avoid deselecting and then reselecting the same item
|
---|
1886 | // which would cause flickering
|
---|
1887 | if (menuItem != fSelected) {
|
---|
1888 | if (fSelected != NULL) {
|
---|
1889 | fSelected->Select(false);
|
---|
1890 | BMenu *subMenu = fSelected->Submenu();
|
---|
1891 | if (subMenu != NULL && subMenu->Window() != NULL)
|
---|
1892 | subMenu->_hide();
|
---|
1893 | }
|
---|
1894 |
|
---|
1895 | fSelected = menuItem;
|
---|
1896 | if (fSelected != NULL)
|
---|
1897 | fSelected->Select(true);
|
---|
1898 | }
|
---|
1899 |
|
---|
1900 | if (fSelected != NULL && showSubmenu == 0) {
|
---|
1901 | BMenu *subMenu = fSelected->Submenu();
|
---|
1902 | if (subMenu != NULL && subMenu->Window() == NULL) {
|
---|
1903 | if (!subMenu->_show(selectFirstItem)) {
|
---|
1904 | // something went wrong, deselect the item
|
---|
1905 | fSelected->Select(false);
|
---|
1906 | fSelected = NULL;
|
---|
1907 | }
|
---|
1908 | //subMenu->Window()->Activate();
|
---|
1909 | }
|
---|
1910 | }
|
---|
1911 | }
|
---|
1912 |
|
---|
1913 |
|
---|
1914 | BMenuItem *
|
---|
1915 | BMenu::CurrentSelection() const
|
---|
1916 | {
|
---|
1917 | return fSelected;
|
---|
1918 | }
|
---|
1919 |
|
---|
1920 |
|
---|
1921 | bool
|
---|
1922 | BMenu::SelectNextItem(BMenuItem *item, bool forward)
|
---|
1923 | {
|
---|
1924 | BMenuItem *nextItem = NextItem(item, forward);
|
---|
1925 | if (nextItem == NULL)
|
---|
1926 | return false;
|
---|
1927 |
|
---|
1928 | SelectItem(nextItem);
|
---|
1929 | return true;
|
---|
1930 | }
|
---|
1931 |
|
---|
1932 |
|
---|
1933 | BMenuItem *
|
---|
1934 | BMenu::NextItem(BMenuItem *item, bool forward) const
|
---|
1935 | {
|
---|
1936 | if (item == NULL) {
|
---|
1937 | if (forward)
|
---|
1938 | return ItemAt(CountItems() - 1);
|
---|
1939 | else
|
---|
1940 | return ItemAt(0);
|
---|
1941 | }
|
---|
1942 |
|
---|
1943 | int32 index = fItems.IndexOf(item);
|
---|
1944 | if (forward)
|
---|
1945 | index++;
|
---|
1946 | else
|
---|
1947 | index--;
|
---|
1948 |
|
---|
1949 | if (index < 0 || index >= fItems.CountItems())
|
---|
1950 | return NULL;
|
---|
1951 |
|
---|
1952 | return ItemAt(index);
|
---|
1953 | }
|
---|
1954 |
|
---|
1955 |
|
---|
1956 | bool
|
---|
1957 | BMenu::IsItemVisible(BMenuItem *item) const
|
---|
1958 | {
|
---|
1959 | BRect itemFrame = item->Frame();
|
---|
1960 | ConvertToScreen(&itemFrame);
|
---|
1961 |
|
---|
1962 | BRect visibilityFrame = Window()->Frame() & BScreen(Window()).Frame();
|
---|
1963 |
|
---|
1964 | return visibilityFrame.Intersects(itemFrame);
|
---|
1965 | }
|
---|
1966 |
|
---|
1967 |
|
---|
1968 | void
|
---|
1969 | BMenu::SetIgnoreHidden(bool on)
|
---|
1970 | {
|
---|
1971 | fIgnoreHidden = on;
|
---|
1972 | }
|
---|
1973 |
|
---|
1974 |
|
---|
1975 | void
|
---|
1976 | BMenu::SetStickyMode(bool on)
|
---|
1977 | {
|
---|
1978 | if (fStickyMode != on) {
|
---|
1979 | // TODO: Ugly hack, but it needs to be done right here in this method
|
---|
1980 | BMenuBar *menuBar = dynamic_cast<BMenuBar *>(this);
|
---|
1981 | if (on && menuBar != NULL && menuBar->LockLooper()) {
|
---|
1982 | // Steal the focus from the current focus view
|
---|
1983 | // (needed to handle keyboard navigation)
|
---|
1984 | menuBar->StealFocus();
|
---|
1985 | menuBar->UnlockLooper();
|
---|
1986 | }
|
---|
1987 |
|
---|
1988 | fStickyMode = on;
|
---|
1989 | }
|
---|
1990 |
|
---|
1991 | // If we are switching to sticky mode, propagate the status
|
---|
1992 | // back to the super menu
|
---|
1993 | if (on && fSuper != NULL)
|
---|
1994 | fSuper->SetStickyMode(on);
|
---|
1995 | }
|
---|
1996 |
|
---|
1997 |
|
---|
1998 | bool
|
---|
1999 | BMenu::IsStickyMode() const
|
---|
2000 | {
|
---|
2001 | return fStickyMode;
|
---|
2002 | }
|
---|
2003 |
|
---|
2004 |
|
---|
2005 | void
|
---|
2006 | BMenu::CalcTriggers()
|
---|
2007 | {
|
---|
2008 | BList triggersList;
|
---|
2009 |
|
---|
2010 | // Gathers the existing triggers
|
---|
2011 | // TODO: Oh great, reinterpret_cast.
|
---|
2012 | for (int32 i = 0; i < CountItems(); i++) {
|
---|
2013 | char trigger = ItemAt(i)->Trigger();
|
---|
2014 | if (trigger != 0)
|
---|
2015 | triggersList.AddItem(reinterpret_cast<void *>((uint32)trigger));
|
---|
2016 | }
|
---|
2017 |
|
---|
2018 | // Set triggers for items which don't have one yet
|
---|
2019 | for (int32 i = 0; i < CountItems(); i++) {
|
---|
2020 | BMenuItem *item = ItemAt(i);
|
---|
2021 | if (item->Trigger() == 0) {
|
---|
2022 | const char *newTrigger = ChooseTrigger(item->Label(), &triggersList);
|
---|
2023 | if (newTrigger != NULL)
|
---|
2024 | item->SetAutomaticTrigger(*newTrigger);
|
---|
2025 | }
|
---|
2026 | }
|
---|
2027 | }
|
---|
2028 |
|
---|
2029 |
|
---|
2030 | const char *
|
---|
2031 | BMenu::ChooseTrigger(const char *title, BList *chars)
|
---|
2032 | {
|
---|
2033 | ASSERT(chars != NULL);
|
---|
2034 |
|
---|
2035 | if (title == NULL)
|
---|
2036 | return NULL;
|
---|
2037 |
|
---|
2038 | char trigger;
|
---|
2039 | // TODO: Oh great, reinterpret_cast all around
|
---|
2040 | while ((trigger = title[0]) != '\0') {
|
---|
2041 | if (!chars->HasItem(reinterpret_cast<void *>((uint32)trigger))) {
|
---|
2042 | chars->AddItem(reinterpret_cast<void *>((uint32)trigger));
|
---|
2043 | return title;
|
---|
2044 | }
|
---|
2045 |
|
---|
2046 | title++;
|
---|
2047 | }
|
---|
2048 |
|
---|
2049 | return NULL;
|
---|
2050 | }
|
---|
2051 |
|
---|
2052 |
|
---|
2053 | void
|
---|
2054 | BMenu::UpdateWindowViewSize(bool upWind)
|
---|
2055 | {
|
---|
2056 | BWindow *window = Window();
|
---|
2057 | if (window == NULL)
|
---|
2058 | return;
|
---|
2059 |
|
---|
2060 | bool scroll;
|
---|
2061 | BRect frame = CalcFrame(ScreenLocation(), &scroll);
|
---|
2062 | ResizeTo(frame.Width(), frame.Height());
|
---|
2063 |
|
---|
2064 | if (fItems.CountItems() > 0)
|
---|
2065 | window->ResizeTo(Bounds().Width() + 2, Bounds().Height() + 2);
|
---|
2066 | else {
|
---|
2067 | CacheFontInfo();
|
---|
2068 | window->ResizeTo(StringWidth(kEmptyMenuLabel) + fPad.left + fPad.right,
|
---|
2069 | fFontHeight + fPad.top + fPad.bottom);
|
---|
2070 | }
|
---|
2071 | window->MoveTo(frame.LeftTop());
|
---|
2072 | }
|
---|
2073 |
|
---|
2074 |
|
---|
2075 | bool
|
---|
2076 | BMenu::IsStickyPrefOn()
|
---|
2077 | {
|
---|
2078 | return true;
|
---|
2079 | }
|
---|
2080 |
|
---|
2081 |
|
---|
2082 | void
|
---|
2083 | BMenu::RedrawAfterSticky(BRect bounds)
|
---|
2084 | {
|
---|
2085 | }
|
---|
2086 |
|
---|
2087 |
|
---|
2088 | bool
|
---|
2089 | BMenu::OkToProceed(BMenuItem* item)
|
---|
2090 | {
|
---|
2091 | bool proceed = true;
|
---|
2092 | BPoint where;
|
---|
2093 | ulong buttons;
|
---|
2094 | GetMouse(&where, &buttons, false);
|
---|
2095 | bool stickyMode = IsStickyMode();
|
---|
2096 | // Quit if user clicks the mouse button in sticky mode
|
---|
2097 | // or releases the mouse button in nonsticky mode
|
---|
2098 | // or moves the pointer over another item
|
---|
2099 | // TODO: I added the check for BMenuBar to solve a problem with Deskbar.
|
---|
2100 | // Beos seems to do something similar. This could also be a bug in Deskbar, though.
|
---|
2101 | if ((buttons != 0 && stickyMode)
|
---|
2102 | || (dynamic_cast<BMenuBar *>(this) == NULL && (buttons == 0 && !stickyMode)
|
---|
2103 | || HitTestItems(where) != item))
|
---|
2104 | proceed = false;
|
---|
2105 |
|
---|
2106 |
|
---|
2107 | return proceed;
|
---|
2108 | }
|
---|
2109 |
|
---|
2110 |
|
---|
2111 | void
|
---|
2112 | BMenu::QuitTracking()
|
---|
2113 | {
|
---|
2114 | SelectItem(NULL);
|
---|
2115 | if (BMenuBar *menuBar = dynamic_cast<BMenuBar *>(this))
|
---|
2116 | menuBar->RestoreFocus();
|
---|
2117 |
|
---|
2118 | fChosenItem = NULL;
|
---|
2119 | fState = MENU_STATE_CLOSED;
|
---|
2120 | }
|
---|
2121 |
|
---|
2122 |
|
---|
2123 | status_t
|
---|
2124 | BMenu::ParseMsg(BMessage *msg, int32 *sindex, BMessage *spec,
|
---|
2125 | int32 *form, const char **prop, BMenu **tmenu,
|
---|
2126 | BMenuItem **titem, int32 *user_data,
|
---|
2127 | BMessage *reply) const
|
---|
2128 | {
|
---|
2129 | return B_ERROR;
|
---|
2130 | }
|
---|
2131 |
|
---|
2132 |
|
---|
2133 | status_t
|
---|
2134 | BMenu::DoMenuMsg(BMenuItem **next, BMenu *menu, BMessage *message,
|
---|
2135 | BMessage *r, BMessage *spec, int32 f) const
|
---|
2136 | {
|
---|
2137 | return B_ERROR;
|
---|
2138 | }
|
---|
2139 |
|
---|
2140 |
|
---|
2141 | status_t
|
---|
2142 | BMenu::DoMenuItemMsg(BMenuItem **next, BMenu *menu, BMessage *message,
|
---|
2143 | BMessage *r, BMessage *spec, int32 f) const
|
---|
2144 | {
|
---|
2145 | return B_ERROR;
|
---|
2146 | }
|
---|
2147 |
|
---|
2148 |
|
---|
2149 | status_t
|
---|
2150 | BMenu::DoEnabledMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
|
---|
2151 | BMessage *r) const
|
---|
2152 | {
|
---|
2153 | return B_ERROR;
|
---|
2154 | }
|
---|
2155 |
|
---|
2156 |
|
---|
2157 | status_t
|
---|
2158 | BMenu::DoLabelMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
|
---|
2159 | BMessage *r) const
|
---|
2160 | {
|
---|
2161 | return B_ERROR;
|
---|
2162 | }
|
---|
2163 |
|
---|
2164 |
|
---|
2165 | status_t
|
---|
2166 | BMenu::DoMarkMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
|
---|
2167 | BMessage *r) const
|
---|
2168 | {
|
---|
2169 | return B_ERROR;
|
---|
2170 | }
|
---|
2171 |
|
---|
2172 |
|
---|
2173 | status_t
|
---|
2174 | BMenu::DoDeleteMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
|
---|
2175 | BMessage *r) const
|
---|
2176 | {
|
---|
2177 | return B_ERROR;
|
---|
2178 | }
|
---|
2179 |
|
---|
2180 |
|
---|
2181 | status_t
|
---|
2182 | BMenu::DoCreateMsg(BMenuItem *ti, BMenu *tm, BMessage *m,
|
---|
2183 | BMessage *r, bool menu) const
|
---|
2184 | {
|
---|
2185 | return B_ERROR;
|
---|
2186 | }
|
---|
2187 |
|
---|
2188 |
|
---|
2189 | // TODO: Maybe the following two methods would fit better into InterfaceDefs.cpp
|
---|
2190 | // In R5, they do all the work client side, we let the app_server handle the details.
|
---|
2191 | status_t
|
---|
2192 | set_menu_info(menu_info *info)
|
---|
2193 | {
|
---|
2194 | if (!info)
|
---|
2195 | return B_BAD_VALUE;
|
---|
2196 |
|
---|
2197 | BPrivate::AppServerLink link;
|
---|
2198 | link.StartMessage(AS_SET_MENU_INFO);
|
---|
2199 | link.Attach<menu_info>(*info);
|
---|
2200 |
|
---|
2201 | status_t status = B_ERROR;
|
---|
2202 | if (link.FlushWithReply(status) == B_OK && status == B_OK)
|
---|
2203 | BMenu::sMenuInfo = *info;
|
---|
2204 | // Update also the local copy, in case anyone relies on it
|
---|
2205 |
|
---|
2206 | return status;
|
---|
2207 | }
|
---|
2208 |
|
---|
2209 |
|
---|
2210 | status_t
|
---|
2211 | get_menu_info(menu_info *info)
|
---|
2212 | {
|
---|
2213 | if (!info)
|
---|
2214 | return B_BAD_VALUE;
|
---|
2215 |
|
---|
2216 | BPrivate::AppServerLink link;
|
---|
2217 | link.StartMessage(AS_GET_MENU_INFO);
|
---|
2218 |
|
---|
2219 | status_t status = B_ERROR;
|
---|
2220 | if (link.FlushWithReply(status) == B_OK && status == B_OK)
|
---|
2221 | link.Read<menu_info>(info);
|
---|
2222 |
|
---|
2223 | return status;
|
---|
2224 | }
|
---|