xref: /haiku/src/kits/tracker/IconMenuItem.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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
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 
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 
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 
112 ModelMenuItem::~ModelMenuItem()
113 {
114 }
115 
116 
117 status_t
118 ModelMenuItem::SetEntry(const BEntry* entry)
119 {
120 	return fModel.SetTo(entry);
121 }
122 
123 
124 void
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
141 ModelMenuItem::Highlight(bool hilited)
142 {
143 	_inherited::Highlight(hilited);
144 	DrawIcon();
145 }
146 
147 
148 void
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
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
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 */
231 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu)
232 	: ModelMenuItem(model, menu)
233 {
234 }
235 
236 
237 void
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 */
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 
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 
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 
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 
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*
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
393 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 
409 IconMenuItem::~IconMenuItem()
410 {
411 	delete fDeviceIcon;
412 }
413 
414 
415 void
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
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
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
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