xref: /haiku/src/kits/tracker/IconMenuItem.cpp (revision 8a6724a0ee3803f1e9f487d8111bb3f6cb8d16db)
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 += 20 + (fExtraPad ? 6 : 0);
130 		if (fHeightDelta > 0)
131 			drawPoint.y += ceil(fHeightDelta / 2);
132 		Menu()->MovePenTo(drawPoint);
133 		_inherited::DrawContent();
134 	}
135 	DrawIcon();
136 }
137 
138 
139 void
140 ModelMenuItem::Highlight(bool hilited)
141 {
142 	_inherited::Highlight(hilited);
143 	DrawIcon();
144 }
145 
146 
147 void
148 ModelMenuItem::DrawIcon()
149 {
150 	Menu()->PushState();
151 
152 	BPoint where(ContentLocation());
153 	// center icon with text.
154 
155 	float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0;
156 	where.y += ceil(deltaHeight / 2);
157 
158 	if (fExtraPad)
159 		where.x += 6;
160 
161 	Menu()->SetDrawingMode(B_OP_OVER);
162 	Menu()->SetLowColor(B_TRANSPARENT_32_BIT);
163 
164 	// draw small icon, synchronously
165 	if (IsEnabled()) {
166 		IconCache::sIconCache->Draw(fModel.ResolveIfLink(), Menu(), where,
167 			kNormalIcon, B_MINI_ICON);
168 	} else {
169 		// dimmed, for now use a special blitter; icon cache should
170 		// know how to blit one eventually
171 		IconCache::sIconCache->SyncDraw(fModel.ResolveIfLink(), Menu(), where,
172 			kNormalIcon, B_MINI_ICON, DimmedIconBlitter);
173 	}
174 
175 	Menu()->PopState();
176 }
177 
178 
179 void
180 ModelMenuItem::GetContentSize(float* width, float* height)
181 {
182 	_inherited::GetContentSize(width, height);
183 	fHeightDelta = 16 - *height;
184 	if (*height < 16)
185 		*height = 16;
186 	*width = *width + 20 + (fExtraPad ? 18 : 0);
187 }
188 
189 
190 status_t
191 ModelMenuItem::Invoke(BMessage* message)
192 {
193 	if (Menu() == NULL)
194 		return B_ERROR;
195 
196 	if (!IsEnabled())
197 		return B_ERROR;
198 
199 	if (message == NULL)
200 		message = Message();
201 
202 	if (message == NULL)
203 		return B_BAD_VALUE;
204 
205 	BMessage clone(*message);
206 	clone.AddInt32("index", Menu()->IndexOf(this));
207 	clone.AddInt64("when", system_time());
208 	clone.AddPointer("source", this);
209 
210 	if ((modifiers() & B_OPTION_KEY) == 0) {
211 		// if option not held, remove refs to close to prevent closing
212 		// parent window
213 		clone.RemoveData("nodeRefsToClose");
214 	}
215 
216 	return BInvoker::Invoke(&clone);
217 }
218 
219 
220 //	#pragma mark - SpecialModelMenuItem
221 
222 
223 /*!	A ModelMenuItem subclass that draws its label in italics.
224 
225 	It's used for example in the "Copy To" menu to indicate some special
226 	folders like the parent folder.
227 */
228 SpecialModelMenuItem::SpecialModelMenuItem(const Model* model, BMenu* menu)
229 	: ModelMenuItem(model, menu)
230 {
231 }
232 
233 
234 void
235 SpecialModelMenuItem::DrawContent()
236 {
237 	Menu()->PushState();
238 
239 	BFont font;
240 	Menu()->GetFont(&font);
241 	font.SetFace(B_ITALIC_FACE);
242 	Menu()->SetFont(&font);
243 
244 	_inherited::DrawContent();
245 	Menu()->PopState();
246 }
247 
248 
249 //	#pragma mark - IconMenuItem
250 
251 
252 /*!	A menu item that draws an icon alongside the label.
253 
254 	It's currently used in the mount and new file template menus.
255 */
256 IconMenuItem::IconMenuItem(const char* label, BMessage* message, BBitmap* icon,
257 	icon_size which)
258 	:
259 	PositionPassingMenuItem(label, message),
260 	fDeviceIcon(NULL),
261 	fHeightDelta(0),
262 	fWhich(which)
263 {
264 	SetIcon(icon);
265 
266 	// IconMenuItem is used in synchronously invoked menus, make sure
267 	// we invoke with a timeout
268 	SetTimeout(kSynchMenuInvokeTimeout);
269 }
270 
271 
272 IconMenuItem::IconMenuItem(const char* label, BMessage* message,
273 	const BNodeInfo* nodeInfo, icon_size which)
274 	:
275 	PositionPassingMenuItem(label, message),
276 	fDeviceIcon(NULL),
277 	fHeightDelta(0),
278 	fWhich(which)
279 {
280 	if (nodeInfo != NULL) {
281 		fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1),
282 			kDefaultIconDepth);
283 
284 		if (nodeInfo->GetTrackerIcon(fDeviceIcon, B_MINI_ICON) != B_OK) {
285 			delete fDeviceIcon;
286 			fDeviceIcon = NULL;
287 		}
288 	}
289 
290 	// IconMenuItem is used in synchronously invoked menus, make sure
291 	// we invoke with a timeout
292 	SetTimeout(kSynchMenuInvokeTimeout);
293 }
294 
295 
296 IconMenuItem::IconMenuItem(const char* label, BMessage* message,
297 	const char* iconType, icon_size which)
298 	:
299 	PositionPassingMenuItem(label, message),
300 	fDeviceIcon(NULL),
301 	fHeightDelta(0),
302 	fWhich(which)
303 {
304 	BMimeType mime(iconType);
305 	fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1),
306 		kDefaultIconDepth);
307 
308 	if (mime.GetIcon(fDeviceIcon, which) != B_OK) {
309 		BMimeType super;
310 		mime.GetSupertype(&super);
311 		if (super.GetIcon(fDeviceIcon, which) != B_OK) {
312 			delete fDeviceIcon;
313 			fDeviceIcon = NULL;
314 		}
315 	}
316 
317 	// IconMenuItem is used in synchronously invoked menus, make sure
318 	// we invoke with a timeout
319 	SetTimeout(kSynchMenuInvokeTimeout);
320 }
321 
322 
323 IconMenuItem::IconMenuItem(BMenu* submenu, BMessage* message,
324 	const char* iconType, icon_size which)
325 	:
326 	PositionPassingMenuItem(submenu, message),
327 	fDeviceIcon(NULL),
328 	fHeightDelta(0),
329 	fWhich(which)
330 {
331 	BMimeType mime(iconType);
332 	fDeviceIcon = new BBitmap(BRect(0, 0, which - 1, which - 1),
333 		kDefaultIconDepth);
334 
335 	if (mime.GetIcon(fDeviceIcon, which) != B_OK) {
336 		BMimeType super;
337 		mime.GetSupertype(&super);
338 		if (super.GetIcon(fDeviceIcon, which) != B_OK) {
339 			delete fDeviceIcon;
340 			fDeviceIcon = NULL;
341 		}
342 	}
343 
344 	// IconMenuItem is used in synchronously invoked menus, make sure
345 	// we invoke with a timeout
346 	SetTimeout(kSynchMenuInvokeTimeout);
347 }
348 
349 
350 IconMenuItem::IconMenuItem(BMessage* data)
351 	:
352 	PositionPassingMenuItem(data),
353 	fDeviceIcon(NULL),
354 	fHeightDelta(0),
355 	fWhich(B_MINI_ICON)
356 {
357 	if (data != NULL) {
358 		fWhich = (icon_size)data->GetInt32("_which", B_MINI_ICON);
359 
360 		fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1),
361 			kDefaultIconDepth);
362 
363 		if (data->HasData("_deviceIconBits", B_RAW_TYPE)) {
364 			ssize_t numBytes;
365 			const void* bits;
366 			if (data->FindData("_deviceIconBits", B_RAW_TYPE, &bits, &numBytes)
367 					== B_OK) {
368 				fDeviceIcon->SetBits(bits, numBytes, (int32)0,
369 					kDefaultIconDepth);
370 			}
371 		}
372 	}
373 
374 	// IconMenuItem is used in synchronously invoked menus, make sure
375 	// we invoke with a timeout
376 	SetTimeout(kSynchMenuInvokeTimeout);
377 }
378 
379 
380 BArchivable*
381 IconMenuItem::Instantiate(BMessage* data)
382 {
383 	//if (validate_instantiation(data, "IconMenuItem"))
384 		return new IconMenuItem(data);
385 
386 	return NULL;
387 }
388 
389 
390 status_t
391 IconMenuItem::Archive(BMessage* data, bool deep) const
392 {
393 	status_t result = PositionPassingMenuItem::Archive(data, deep);
394 
395 	if (result == B_OK)
396 		result = data->AddInt32("_which", (int32)fWhich);
397 
398 	if (result == B_OK && fDeviceIcon != NULL) {
399 		result = data->AddData("_deviceIconBits", B_RAW_TYPE,
400 			fDeviceIcon->Bits(), fDeviceIcon->BitsLength());
401 	}
402 
403 	return result;
404 }
405 
406 
407 IconMenuItem::~IconMenuItem()
408 {
409 	delete fDeviceIcon;
410 }
411 
412 
413 void
414 IconMenuItem::GetContentSize(float* width, float* height)
415 {
416 	_inherited::GetContentSize(width, height);
417 
418 	fHeightDelta = 16 - *height;
419 	if (*height < 16)
420 		*height = 16;
421 
422 	*width += 20;
423 }
424 
425 
426 void
427 IconMenuItem::DrawContent()
428 {
429 	BPoint drawPoint(ContentLocation());
430 	if (fDeviceIcon != NULL)
431 		drawPoint.x += (float)fWhich + 4.0f;
432 
433 	if (fHeightDelta > 0)
434 		drawPoint.y += ceilf(fHeightDelta / 2);
435 
436 	Menu()->MovePenTo(drawPoint);
437 	_inherited::DrawContent();
438 
439 	Menu()->PushState();
440 
441 	BPoint where(ContentLocation());
442 	float deltaHeight = fHeightDelta < 0 ? -fHeightDelta : 0;
443 	where.y += ceilf(deltaHeight / 2);
444 
445 	if (fDeviceIcon != NULL) {
446 		if (IsEnabled())
447 			Menu()->SetDrawingMode(B_OP_ALPHA);
448 		else {
449 			Menu()->SetDrawingMode(B_OP_ALPHA);
450 			Menu()->SetHighColor(0, 0, 0, 64);
451 			Menu()->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
452 		}
453 		Menu()->DrawBitmapAsync(fDeviceIcon, where);
454 	}
455 
456 	Menu()->PopState();
457 }
458 
459 
460 void
461 IconMenuItem::SetMarked(bool mark)
462 {
463 	_inherited::SetMarked(mark);
464 
465 	if (!mark)
466 		return;
467 
468 	// we are marking the item
469 
470 	BMenu* menu = Menu();
471 	if (menu == NULL)
472 		return;
473 
474 	// we have a parent menu
475 
476 	BMenu* _menu = menu;
477 	while ((_menu = _menu->Supermenu()) != NULL)
478 		menu = _menu;
479 
480 	// went up the hierarchy to found the topmost menu
481 
482 	if (menu == NULL || menu->Parent() == NULL)
483 		return;
484 
485 	// our topmost menu has a parent
486 
487 	if (dynamic_cast<BMenuField*>(menu->Parent()) == NULL)
488 		return;
489 
490 	// our topmost menu's parent is a BMenuField
491 
492 	BMenuItem* topLevelItem = menu->ItemAt((int32)0);
493 
494 	if (topLevelItem == NULL)
495 		return;
496 
497 	// our topmost menu has a menu item
498 
499 	IconMenuItem* topLevelIconMenuItem
500 		= dynamic_cast<IconMenuItem*>(topLevelItem);
501 	if (topLevelIconMenuItem == NULL)
502 		return;
503 
504 	// our topmost menu's item is an IconMenuItem
505 
506 	// update the icon
507 	topLevelIconMenuItem->SetIcon(fDeviceIcon);
508 	menu->Invalidate();
509 }
510 
511 
512 void
513 IconMenuItem::SetIcon(BBitmap* icon)
514 {
515 	if (icon != NULL) {
516 		if (fDeviceIcon != NULL)
517 			delete fDeviceIcon;
518 
519 		fDeviceIcon = new BBitmap(BRect(0, 0, fWhich - 1, fWhich - 1),
520 			icon->ColorSpace());
521 		fDeviceIcon->SetBits(icon->Bits(), icon->BitsLength(), 0,
522 			icon->ColorSpace());
523 	} else {
524 		delete fDeviceIcon;
525 		fDeviceIcon = NULL;
526 	}
527 }
528