xref: /haiku/src/kits/tracker/IconMenuItem.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
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, (icon_size)ListIconSize());
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, (icon_size)ListIconSize(), 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 	float iconSize = ListIconSize();
185 	fHeightDelta = iconSize - *height;
186 	if (*height < iconSize)
187 		*height = iconSize;
188 	*width = *width + iconSize / 4 + iconSize + (fExtraPad ? 18 : 0);
189 }
190 
191 
192 status_t
193 ModelMenuItem::Invoke(BMessage* message)
194 {
195 	if (Menu() == NULL)
196 		return B_ERROR;
197 
198 	if (!IsEnabled())
199 		return B_ERROR;
200 
201 	if (message == NULL)
202 		message = Message();
203 
204 	if (message == NULL)
205 		return B_BAD_VALUE;
206 
207 	BMessage clone(*message);
208 	clone.AddInt32("index", Menu()->IndexOf(this));
209 	clone.AddInt64("when", system_time());
210 	clone.AddPointer("source", this);
211 
212 	if ((modifiers() & B_OPTION_KEY) == 0) {
213 		// if option not held, remove refs to close to prevent closing
214 		// parent window
215 		clone.RemoveData("nodeRefsToClose");
216 	}
217 
218 	return BInvoker::Invoke(&clone);
219 }
220 
221 
222 //	#pragma mark - SpecialModelMenuItem
223 
224 
225 /*!	A ModelMenuItem subclass that draws its label in italics.
226 
227 	It's used for example in the "Copy To" menu to indicate some special
228 	folders like the parent folder.
229 */
230 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu)
231 	: ModelMenuItem(model, menu)
232 {
233 }
234 
235 
236 void
237 SpecialModelMenuItem::DrawContent()
238 {
239 	Menu()->PushState();
240 
241 	BFont font;
242 	Menu()->GetFont(&font);
243 	font.SetFace(B_ITALIC_FACE);
244 	Menu()->SetFont(&font);
245 
246 	_inherited::DrawContent();
247 	Menu()->PopState();
248 }
249 
250 
251 //	#pragma mark - IconMenuItem
252 
253 
254 /*!	A menu item that draws an icon alongside the label.
255 
256 	It's currently used in the mount and new file template menus.
257 */
258 IconMenuItem::IconMenuItem(const char* label, BMessage* message, BBitmap* icon,
259 	icon_size which)
260 	:
261 	PositionPassingMenuItem(label, message),
262 	fDeviceIcon(NULL),
263 	fHeightDelta(0),
264 	fWhich(which)
265 {
266 	SetIcon(icon);
267 
268 	// IconMenuItem is used in synchronously invoked menus, make sure
269 	// we invoke with a timeout
270 	SetTimeout(kSynchMenuInvokeTimeout);
271 }
272 
273 
274 IconMenuItem::IconMenuItem(const char* label, BMessage* message,
275 	const BNodeInfo* nodeInfo, icon_size which)
276 	:
277 	PositionPassingMenuItem(label, message),
278 	fDeviceIcon(NULL),
279 	fHeightDelta(0),
280 	fWhich(which)
281 {
282 	if (nodeInfo != NULL) {
283 		fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1),
284 			kDefaultIconDepth);
285 
286 		if (nodeInfo->GetTrackerIcon(fDeviceIcon, B_MINI_ICON) != 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(0, 0, which - 1, which - 1),
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(0, 0, which - 1, which - 1),
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(0, 0, fWhich - 1, fWhich - 1),
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 	fHeightDelta = 16 - *height;
421 	if (*height < 16)
422 		*height = 16;
423 
424 	*width += 20;
425 }
426 
427 
428 void
429 IconMenuItem::DrawContent()
430 {
431 	BPoint drawPoint(ContentLocation());
432 	if (fDeviceIcon != NULL)
433 		drawPoint.x += (float)fWhich + 4.0f;
434 
435 	if (fHeightDelta > 0)
436 		drawPoint.y += ceilf(fHeightDelta / 2);
437 
438 	Menu()->MovePenTo(drawPoint);
439 	_inherited::DrawContent();
440 
441 	Menu()->PushState();
442 
443 	BPoint where(ContentLocation());
444 	float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0;
445 	where.y += ceilf(deltaHeight / 2);
446 
447 	if (fDeviceIcon != NULL) {
448 		if (IsEnabled())
449 			Menu()->SetDrawingMode(B_OP_ALPHA);
450 		else {
451 			Menu()->SetDrawingMode(B_OP_ALPHA);
452 			Menu()->SetHighColor(0, 0, 0, 64);
453 			Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
454 		}
455 		Menu()->DrawBitmapAsync(fDeviceIcon, where);
456 	}
457 
458 	Menu()->PopState();
459 }
460 
461 
462 void
463 IconMenuItem::SetMarked(bool mark)
464 {
465 	_inherited::SetMarked(mark);
466 
467 	if (!mark)
468 		return;
469 
470 	// we are marking the item
471 
472 	BMenu* menu = Menu();
473 	if (menu == NULL)
474 		return;
475 
476 	// we have a parent menu
477 
478 	BMenu* _menu = menu;
479 	while ((_menu = _menu->Supermenu()) != NULL)
480 		menu = _menu;
481 
482 	// went up the hierarchy to found the topmost menu
483 
484 	if (menu == NULL || menu->Parent() == NULL)
485 		return;
486 
487 	// our topmost menu has a parent
488 
489 	if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL)
490 		return;
491 
492 	// our topmost menu's parent is a BMenuField
493 
494 	BMenuItem* topLevelItem = menu->ItemAt((int32)0);
495 
496 	if (topLevelItem == NULL)
497 		return;
498 
499 	// our topmost menu has a menu item
500 
501 	IconMenuItem* topLevelIconMenuItem
502 		= dynamic_cast<IconMenuItem*>(topLevelItem);
503 	if (topLevelIconMenuItem == NULL)
504 		return;
505 
506 	// our topmost menu's item is an IconMenuItem
507 
508 	// update the icon
509 	topLevelIconMenuItem->SetIcon(fDeviceIcon);
510 	menu->Invalidate();
511 }
512 
513 
514 void
515 IconMenuItem::SetIcon(BBitmap* icon)
516 {
517 	if (icon != NULL) {
518 		if (fDeviceIcon != NULL)
519 			delete fDeviceIcon;
520 
521 		fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1),
522 			icon->ColorSpace());
523 		fDeviceIcon->SetBits(icon->Bits(), icon->BitsLength(), 0,
524 			icon->ColorSpace());
525 	} else {
526 		delete fDeviceIcon;
527 		fDeviceIcon = NULL;
528 	}
529 }
530