1 /*
2 Open Tracker License
3
4 Terms and Conditions
5
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34
35 //! Menu items with small icons.
36
37
38 #include "IconMenuItem.h"
39
40 #include <ControlLook.h>
41 #include <Debug.h>
42 #include <Menu.h>
43 #include <MenuField.h>
44 #include <NodeInfo.h>
45
46 #include "IconCache.h"
47
48
49 static void
DimmedIconBlitter(BView * view,BPoint where,BBitmap * bitmap,void *)50 DimmedIconBlitter(BView* view, BPoint where, BBitmap* bitmap, void*)
51 {
52 if (bitmap->ColorSpace() == B_RGBA32) {
53 rgb_color oldHighColor = view->HighColor();
54 view->SetHighColor(0, 0, 0, 128);
55 view->SetDrawingMode(B_OP_ALPHA);
56 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
57 view->DrawBitmap(bitmap, where);
58 view->SetHighColor(oldHighColor);
59 } else {
60 view->SetDrawingMode(B_OP_BLEND);
61 view->DrawBitmap(bitmap, where);
62 }
63 view->SetDrawingMode(B_OP_OVER);
64 }
65
66
67 // #pragma mark - ModelMenuItem
68
69
ModelMenuItem(const Model * model,const char * title,BMessage * message,char shortcut,uint32 modifiers,bool drawText,bool extraPad)70 ModelMenuItem::ModelMenuItem(const Model* model, const char* title,
71 BMessage* message, char shortcut, uint32 modifiers,
72 bool drawText, bool extraPad)
73 :
74 BMenuItem(title, message, shortcut, modifiers),
75 fModel(*model),
76 fHeightDelta(0),
77 fDrawText(drawText),
78 fExtraPad(extraPad)
79 {
80 ThrowOnInitCheckError(&fModel);
81 // The 'fExtraPad' field is used to when this menu item is added to
82 // a menubar instead of a menu. Menus and MenuBars space out items
83 // differently (more space around items in a menu). This class wants
84 // to be able to space item the same, no matter where they are. The
85 // fExtraPad field allows for that.
86
87 if (model->IsRoot())
88 SetLabel(model->Name());
89
90 // ModelMenuItem is used in synchronously invoked menus, make sure
91 // we invoke with a timeout
92 SetTimeout(kSynchMenuInvokeTimeout);
93 }
94
95
ModelMenuItem(const Model * model,BMenu * menu,bool drawText,bool extraPad)96 ModelMenuItem::ModelMenuItem(const Model* model, BMenu* menu, bool drawText,
97 bool extraPad)
98 :
99 BMenuItem(menu),
100 fModel(*model),
101 fHeightDelta(0),
102 fDrawText(drawText),
103 fExtraPad(extraPad)
104 {
105 ThrowOnInitCheckError(&fModel);
106 // ModelMenuItem is used in synchronously invoked menus, make sure
107 // we invoke with a timeout
108 SetTimeout(kSynchMenuInvokeTimeout);
109 }
110
111
~ModelMenuItem()112 ModelMenuItem::~ModelMenuItem()
113 {
114 }
115
116
117 status_t
SetEntry(const BEntry * entry)118 ModelMenuItem::SetEntry(const BEntry* entry)
119 {
120 return fModel.SetTo(entry);
121 }
122
123
124 void
DrawContent()125 ModelMenuItem::DrawContent()
126 {
127 if (fDrawText) {
128 BPoint drawPoint(ContentLocation());
129 drawPoint.x += ListIconSize() + (ListIconSize() / 4)
130 + (fExtraPad ? 6 : 0);
131 if (fHeightDelta > 0)
132 drawPoint.y += ceil(fHeightDelta / 2);
133 Menu()->MovePenTo(drawPoint);
134 _inherited::DrawContent();
135 }
136 DrawIcon();
137 }
138
139
140 void
Highlight(bool hilited)141 ModelMenuItem::Highlight(bool hilited)
142 {
143 _inherited::Highlight(hilited);
144 DrawIcon();
145 }
146
147
148 void
DrawIcon()149 ModelMenuItem::DrawIcon()
150 {
151 Menu()->PushState();
152
153 BPoint where(ContentLocation());
154 // center icon with text.
155
156 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0;
157 where.y += ceil(deltaHeight / 2);
158
159 if (fExtraPad)
160 where.x += 6;
161
162 Menu()->SetDrawingMode(B_OP_OVER);
163 Menu()->SetLowColor(B_TRANSPARENT_32_BIT);
164
165 // draw small icon, synchronously
166 if (IsEnabled()) {
167 IconCache::sIconCache->Draw(fModel.ResolveIfLink(), Menu(), where,
168 kNormalIcon, BSize(ListIconSize() - 1, ListIconSize() - 1));
169 } else {
170 // dimmed, for now use a special blitter; icon cache should
171 // know how to blit one eventually
172 IconCache::sIconCache->SyncDraw(fModel.ResolveIfLink(), Menu(), where,
173 kNormalIcon, BSize(ListIconSize() - 1, ListIconSize() - 1), DimmedIconBlitter);
174 }
175
176 Menu()->PopState();
177 }
178
179
180 void
GetContentSize(float * width,float * height)181 ModelMenuItem::GetContentSize(float* width, float* height)
182 {
183 _inherited::GetContentSize(width, height);
184
185 const float iconSize = ListIconSize();
186 fHeightDelta = iconSize - *height;
187 if (*height < iconSize)
188 *height = iconSize;
189 *width += iconSize / 4 + iconSize + (fExtraPad ? 18 : 0);
190 }
191
192
193 status_t
Invoke(BMessage * message)194 ModelMenuItem::Invoke(BMessage* message)
195 {
196 if (Menu() == NULL)
197 return B_ERROR;
198
199 if (!IsEnabled())
200 return B_ERROR;
201
202 if (message == NULL)
203 message = Message();
204
205 if (message == NULL)
206 return B_BAD_VALUE;
207
208 BMessage clone(*message);
209 clone.AddInt32("index", Menu()->IndexOf(this));
210 clone.AddInt64("when", system_time());
211 clone.AddPointer("source", this);
212
213 if ((modifiers() & B_OPTION_KEY) == 0) {
214 // if option not held, remove refs to close to prevent closing
215 // parent window
216 clone.RemoveData("nodeRefsToClose");
217 }
218
219 return BInvoker::Invoke(&clone);
220 }
221
222
223 // #pragma mark - SpecialModelMenuItem
224
225
226 /*! A ModelMenuItem subclass that draws its label in italics.
227
228 It's used for example in the "Copy To" menu to indicate some special
229 folders like the parent folder.
230 */
SpecialModelMenuItem(const Model * model,BMenu * menu)231 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu)
232 : ModelMenuItem(model, menu)
233 {
234 }
235
236
237 void
DrawContent()238 SpecialModelMenuItem::DrawContent()
239 {
240 Menu()->PushState();
241
242 BFont font;
243 Menu()->GetFont(&font);
244 font.SetFace(B_ITALIC_FACE);
245 Menu()->SetFont(&font);
246
247 _inherited::DrawContent();
248 Menu()->PopState();
249 }
250
251
252 // #pragma mark - IconMenuItem
253
254
255 /*! A menu item that draws an icon alongside the label.
256
257 It's currently used in the mount and new file template menus.
258 */
IconMenuItem(const char * label,BMessage * message,BBitmap * icon,icon_size which)259 IconMenuItem::IconMenuItem(const char* label, BMessage* message, BBitmap* icon,
260 icon_size which)
261 :
262 PositionPassingMenuItem(label, message),
263 fDeviceIcon(NULL),
264 fHeightDelta(0),
265 fWhich(which)
266 {
267 SetIcon(icon);
268
269 // IconMenuItem is used in synchronously invoked menus, make sure
270 // we invoke with a timeout
271 SetTimeout(kSynchMenuInvokeTimeout);
272 }
273
274
IconMenuItem(const char * label,BMessage * message,const BNodeInfo * nodeInfo,icon_size which)275 IconMenuItem::IconMenuItem(const char* label, BMessage* message,
276 const BNodeInfo* nodeInfo, icon_size which)
277 :
278 PositionPassingMenuItem(label, message),
279 fDeviceIcon(NULL),
280 fHeightDelta(0),
281 fWhich(which)
282 {
283 if (nodeInfo != NULL) {
284 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0),
285 be_control_look->ComposeIconSize(which)), kDefaultIconDepth);
286 if (nodeInfo->GetTrackerIcon(fDeviceIcon, (icon_size)-1) != B_OK) {
287 delete fDeviceIcon;
288 fDeviceIcon = NULL;
289 }
290 }
291
292 // IconMenuItem is used in synchronously invoked menus, make sure
293 // we invoke with a timeout
294 SetTimeout(kSynchMenuInvokeTimeout);
295 }
296
297
IconMenuItem(const char * label,BMessage * message,const char * iconType,icon_size which)298 IconMenuItem::IconMenuItem(const char* label, BMessage* message,
299 const char* iconType, icon_size which)
300 :
301 PositionPassingMenuItem(label, message),
302 fDeviceIcon(NULL),
303 fHeightDelta(0),
304 fWhich(which)
305 {
306 BMimeType mime(iconType);
307 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(which)),
308 kDefaultIconDepth);
309
310 if (mime.GetIcon(fDeviceIcon, which) != B_OK) {
311 BMimeType super;
312 mime.GetSupertype(&super);
313 if (super.GetIcon(fDeviceIcon, which) != B_OK) {
314 delete fDeviceIcon;
315 fDeviceIcon = NULL;
316 }
317 }
318
319 // IconMenuItem is used in synchronously invoked menus, make sure
320 // we invoke with a timeout
321 SetTimeout(kSynchMenuInvokeTimeout);
322 }
323
324
IconMenuItem(BMenu * submenu,BMessage * message,const char * iconType,icon_size which)325 IconMenuItem::IconMenuItem(BMenu* submenu, BMessage* message,
326 const char* iconType, icon_size which)
327 :
328 PositionPassingMenuItem(submenu, message),
329 fDeviceIcon(NULL),
330 fHeightDelta(0),
331 fWhich(which)
332 {
333 BMimeType mime(iconType);
334 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(which)),
335 kDefaultIconDepth);
336
337 if (mime.GetIcon(fDeviceIcon, which) != B_OK) {
338 BMimeType super;
339 mime.GetSupertype(&super);
340 if (super.GetIcon(fDeviceIcon, which) != B_OK) {
341 delete fDeviceIcon;
342 fDeviceIcon = NULL;
343 }
344 }
345
346 // IconMenuItem is used in synchronously invoked menus, make sure
347 // we invoke with a timeout
348 SetTimeout(kSynchMenuInvokeTimeout);
349 }
350
351
IconMenuItem(BMessage * data)352 IconMenuItem::IconMenuItem(BMessage* data)
353 :
354 PositionPassingMenuItem(data),
355 fDeviceIcon(NULL),
356 fHeightDelta(0),
357 fWhich(B_MINI_ICON)
358 {
359 if (data != NULL) {
360 fWhich = (icon_size)data->GetInt32("_which", B_MINI_ICON);
361
362 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(fWhich)),
363 kDefaultIconDepth);
364
365 if (data->HasData("_deviceIconBits", B_RAW_TYPE)) {
366 ssize_t numBytes;
367 const void* bits;
368 if (data->FindData("_deviceIconBits", B_RAW_TYPE, &bits, &numBytes)
369 == B_OK) {
370 fDeviceIcon->SetBits(bits, numBytes, (int32)0,
371 kDefaultIconDepth);
372 }
373 }
374 }
375
376 // IconMenuItem is used in synchronously invoked menus, make sure
377 // we invoke with a timeout
378 SetTimeout(kSynchMenuInvokeTimeout);
379 }
380
381
382 BArchivable*
Instantiate(BMessage * data)383 IconMenuItem::Instantiate(BMessage* data)
384 {
385 //if (validate_instantiation(data, "IconMenuItem"))
386 return new IconMenuItem(data);
387
388 return NULL;
389 }
390
391
392 status_t
Archive(BMessage * data,bool deep) const393 IconMenuItem::Archive(BMessage* data, bool deep) const
394 {
395 status_t result = PositionPassingMenuItem::Archive(data, deep);
396
397 if (result == B_OK)
398 result = data->AddInt32("_which", (int32)fWhich);
399
400 if (result == B_OK && fDeviceIcon != NULL) {
401 result = data->AddData("_deviceIconBits", B_RAW_TYPE,
402 fDeviceIcon->Bits(), fDeviceIcon->BitsLength());
403 }
404
405 return result;
406 }
407
408
~IconMenuItem()409 IconMenuItem::~IconMenuItem()
410 {
411 delete fDeviceIcon;
412 }
413
414
415 void
GetContentSize(float * width,float * height)416 IconMenuItem::GetContentSize(float* width, float* height)
417 {
418 _inherited::GetContentSize(width, height);
419
420 int32 iconHeight = fWhich;
421 if (fDeviceIcon != NULL)
422 iconHeight = fDeviceIcon->Bounds().IntegerHeight() + 1;
423
424 fHeightDelta = iconHeight - *height;
425 if (*height < iconHeight)
426 *height = iconHeight;
427
428 if (fDeviceIcon != NULL)
429 *width += fDeviceIcon->Bounds().Width() + be_control_look->DefaultLabelSpacing();
430 }
431
432
433 void
DrawContent()434 IconMenuItem::DrawContent()
435 {
436 BPoint drawPoint(ContentLocation());
437 if (fDeviceIcon != NULL)
438 drawPoint.x += fDeviceIcon->Bounds().Width() + be_control_look->DefaultLabelSpacing();
439
440 if (fHeightDelta > 0)
441 drawPoint.y += ceilf(fHeightDelta / 2);
442
443 Menu()->MovePenTo(drawPoint);
444 _inherited::DrawContent();
445
446 Menu()->PushState();
447
448 BPoint where(ContentLocation());
449 float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0;
450 where.y += ceilf(deltaHeight / 2);
451
452 if (fDeviceIcon != NULL) {
453 if (IsEnabled())
454 Menu()->SetDrawingMode(B_OP_ALPHA);
455 else {
456 Menu()->SetDrawingMode(B_OP_ALPHA);
457 Menu()->SetHighColor(0, 0, 0, 64);
458 Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
459 }
460 Menu()->DrawBitmapAsync(fDeviceIcon, where);
461 }
462
463 Menu()->PopState();
464 }
465
466
467 void
SetMarked(bool mark)468 IconMenuItem::SetMarked(bool mark)
469 {
470 _inherited::SetMarked(mark);
471
472 if (!mark)
473 return;
474
475 // we are marking the item
476
477 BMenu* menu = Menu();
478 if (menu == NULL)
479 return;
480
481 // we have a parent menu
482
483 BMenu* _menu = menu;
484 while ((_menu = _menu->Supermenu()) != NULL)
485 menu = _menu;
486
487 // went up the hierarchy to found the topmost menu
488
489 if (menu == NULL || menu->Parent() == NULL)
490 return;
491
492 // our topmost menu has a parent
493
494 if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL)
495 return;
496
497 // our topmost menu's parent is a BMenuField
498
499 BMenuItem* topLevelItem = menu->ItemAt((int32)0);
500
501 if (topLevelItem == NULL)
502 return;
503
504 // our topmost menu has a menu item
505
506 IconMenuItem* topLevelMenuItem = dynamic_cast<IconMenuItem*>(topLevelItem);
507 if (topLevelMenuItem == NULL)
508 return;
509
510 // our topmost menu's item is an IconMenuItem
511
512 // update the icon
513 topLevelMenuItem->SetIcon(fDeviceIcon);
514 menu->Invalidate();
515 }
516
517
518 void
SetIcon(BBitmap * icon)519 IconMenuItem::SetIcon(BBitmap* icon)
520 {
521 if (icon != NULL) {
522 if (fDeviceIcon != NULL)
523 delete fDeviceIcon;
524
525 fDeviceIcon = new BBitmap(BRect(BPoint(0, 0),
526 be_control_look->ComposeIconSize(fWhich)), icon->ColorSpace());
527 fDeviceIcon->ImportBits(icon);
528 } else {
529 delete fDeviceIcon;
530 fDeviceIcon = NULL;
531 }
532 }
533