xref: /haiku/src/apps/deskbar/TeamMenuItem.cpp (revision 0ce4c23d22fae64d10e5575687490fbdf8ee52b8)
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
30 trademarks of Be Incorporated in the United States and other countries. Other
31 brand product names are registered trademarks or trademarks of their respective
32 holders.
33 All rights reserved.
34 */
35 
36 
37 #include "TeamMenuItem.h"
38 
39 #include <string.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 
43 #include <Bitmap.h>
44 #include <ControlLook.h>
45 #include <Debug.h>
46 #include <Font.h>
47 #include <Region.h>
48 #include <Roster.h>
49 #include <Resources.h>
50 
51 #include "BarApp.h"
52 #include "BarMenuBar.h"
53 #include "ExpandoMenuBar.h"
54 #include "ResourceSet.h"
55 #include "ShowHideMenuItem.h"
56 #include "TeamMenu.h"
57 #include "WindowMenu.h"
58 #include "WindowMenuItem.h"
59 
60 
61 const float kHPad = 8.0f;
62 const float kVPad = 1.0f;
63 const float kLabelOffset = 8.0f;
64 const float kSwitchWidth = 12;
65 
66 
67 TTeamMenuItem::TTeamMenuItem(BList* team, BBitmap* icon, char* name, char* sig,
68 	float width, float height, bool drawLabel, bool vertical)
69 	:	BMenuItem(new TWindowMenu(team, sig))
70 {
71 	InitData(team, icon, name, sig, width, height, drawLabel, vertical);
72 }
73 
74 
75 TTeamMenuItem::TTeamMenuItem(float width, float height, bool vertical)
76 	:	BMenuItem("", NULL)
77 {
78 	InitData(NULL, NULL, strdup(""), strdup(""), width, height, false,
79 		vertical);
80 }
81 
82 
83 void
84 TTeamMenuItem::InitData(BList* team, BBitmap* icon, char* name, char* sig,
85 	float width, float height, bool drawLabel, bool vertical)
86 {
87 	fTeam = team;
88 	fIcon = icon;
89 	fName = name;
90 	fSig = sig;
91 	if (fName == NULL) {
92 		char temp[32];
93 		snprintf(temp, sizeof(temp), "team %ld", (addr_t)team->ItemAt(0));
94 		fName = strdup(temp);
95 	}
96 
97 	SetLabel(fName);
98 
99 	BFont font(be_plain_font);
100 	fLabelWidth = ceilf(font.StringWidth(fName));
101 	font_height fontHeight;
102 	font.GetHeight(&fontHeight);
103 	fLabelAscent = ceilf(fontHeight.ascent);
104 	fLabelDescent = ceilf(fontHeight.descent + fontHeight.leading);
105 
106 	fOverrideWidth = width;
107 	fOverrideHeight = height;
108 	fOverriddenSelected = false;
109 
110 	fVertical = vertical;
111 	fDrawLabel = drawLabel;
112 
113 	fExpanded = false;
114 }
115 
116 
117 TTeamMenuItem::~TTeamMenuItem()
118 {
119 	delete fTeam;
120 	delete fIcon;
121 	free(fName);
122 	free(fSig);
123 }
124 
125 
126 status_t
127 TTeamMenuItem::Invoke(BMessage* message)
128 {
129 	if ((static_cast<TBarApp*>(be_app))->BarView()->InvokeItem(Signature()))
130 		// handles drop on application
131 		return B_OK;
132 
133 	// if the app could not handle the drag message
134 	// and we were dragging, then kill the drag
135 	// should never get here, disabled item will not invoke
136 	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
137 	if (barView && barView->Dragging())
138 		barView->DragStop();
139 
140 	// bring to front or minimize shortcuts
141 	uint32 mods = modifiers();
142 	if (mods & B_CONTROL_KEY) {
143 		TShowHideMenuItem::TeamShowHideCommon((mods & B_SHIFT_KEY)
144 			? B_MINIMIZE_WINDOW : B_BRING_TO_FRONT, Teams());
145 	}
146 
147 	return BMenuItem::Invoke(message);
148 }
149 
150 
151 void
152 TTeamMenuItem::SetOverrideWidth(float width)
153 {
154 	fOverrideWidth = width;
155 }
156 
157 
158 void
159 TTeamMenuItem::SetOverrideHeight(float height)
160 {
161 	fOverrideHeight = height;
162 }
163 
164 
165 void
166 TTeamMenuItem::SetOverrideSelected(bool selected)
167 {
168 	fOverriddenSelected = selected;
169 	Highlight(selected);
170 }
171 
172 
173 bool
174 TTeamMenuItem::HasLabel() const
175 {
176 	return fDrawLabel;
177 }
178 
179 
180 void
181 TTeamMenuItem::SetHasLabel(bool drawLabel)
182 {
183 	fDrawLabel = drawLabel;
184 }
185 
186 
187 float
188 TTeamMenuItem::LabelWidth() const
189 {
190 	return fLabelWidth;
191 }
192 
193 
194 BList*
195 TTeamMenuItem::Teams() const
196 {
197 	return fTeam;
198 }
199 
200 
201 const char*
202 TTeamMenuItem::Signature() const
203 {
204 	return fSig;
205 }
206 
207 
208 const char*
209 TTeamMenuItem::Name() const
210 {
211 	return fName;
212 }
213 
214 
215 void
216 TTeamMenuItem::GetContentSize(float* width, float* height)
217 {
218 	BRect iconBounds;
219 
220 	if (fIcon)
221 		iconBounds = fIcon->Bounds();
222 	else
223 		iconBounds = BRect(0, 0, kMinimumIconSize - 1, kMinimumIconSize - 1);
224 
225 	BMenuItem::GetContentSize(width, height);
226 
227 	if (fOverrideWidth != -1.0f)
228 		*width = fOverrideWidth;
229 	else {
230 		*width = kHPad + iconBounds.Width() + kHPad;
231 		if (iconBounds.Width() <= 32 && fDrawLabel)
232 			*width += LabelWidth() + kHPad;
233 	}
234 
235 	if (fOverrideHeight != -1.0f)
236 		*height = fOverrideHeight;
237 	else {
238 		if (fVertical) {
239 			*height = iconBounds.Height() + kVPad * 4;
240 			if (fDrawLabel && iconBounds.Width() > 32)
241 				*height += fLabelAscent + fLabelDescent;
242 		} else {
243 			*height = iconBounds.Height() + kVPad * 4;
244 		}
245 	}
246 	*height += 2;
247 }
248 
249 
250 void
251 TTeamMenuItem::Draw()
252 {
253 	BRect frame(Frame());
254 	BMenu* menu = Menu();
255 	menu->PushState();
256 	rgb_color menuColor = menu->LowColor();
257 	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
258 
259 	bool canHandle = !barView->Dragging()
260 		|| barView->AppCanHandleTypes(Signature());
261 
262 	if (be_control_look != NULL) {
263 		uint32 flags = 0;
264 		if (_IsSelected() && canHandle)
265 			flags |= BControlLook::B_ACTIVATED;
266 
267 		uint32 borders = BControlLook::B_TOP_BORDER;
268 		if (fVertical) {
269 			menu->SetHighColor(tint_color(menuColor, B_DARKEN_1_TINT));
270 			borders |= BControlLook::B_LEFT_BORDER
271 				| BControlLook::B_RIGHT_BORDER;
272 			menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
273 			frame.bottom--;
274 
275 			be_control_look->DrawMenuBarBackground(menu, frame, frame,
276 				menuColor, flags, borders);
277 		} else {
278 			if (flags & BControlLook::B_ACTIVATED)
279 				menu->SetHighColor(tint_color(menuColor, B_DARKEN_3_TINT));
280 			else
281 				menu->SetHighColor(tint_color(menuColor, 1.22));
282 			borders |= BControlLook::B_BOTTOM_BORDER;
283 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
284 			frame.left++;
285 
286 			be_control_look->DrawButtonBackground(menu, frame, frame,
287 				menuColor, flags, borders);
288 		}
289 
290 		menu->MovePenTo(ContentLocation());
291 		DrawContent();
292 		menu->PopState();
293 		return;
294 	}
295 
296 	// if not selected or being tracked on, fill with gray
297 	if ((!_IsSelected() && !menu->IsRedrawAfterSticky()) || !canHandle
298 		|| !IsEnabled()) {
299 		frame.InsetBy(1, 1);
300 		menu->SetHighColor(menuColor);
301 		menu->FillRect(frame);
302 	}
303 
304 	// draw the gray, unselected item, border
305 	if (!_IsSelected() || !IsEnabled()) {
306 		rgb_color shadow = tint_color(menuColor, B_DARKEN_1_TINT);
307 		rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
308 
309 		frame = Frame();
310 
311 		menu->SetHighColor(shadow);
312 		if (fVertical)
313 			menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
314 		else
315 			menu->StrokeLine(frame.LeftBottom() + BPoint(1, 0),
316 				frame.RightBottom());
317 
318 		menu->StrokeLine(frame.RightBottom(), frame.RightTop());
319 
320 		menu->SetHighColor(light);
321 		menu->StrokeLine(frame.RightTop() + BPoint(-1, 0), frame.LeftTop());
322 		if (fVertical)
323 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom()
324 				+ BPoint(0, -1));
325 		else
326 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
327 	}
328 
329 	// if selected or being tracked on, fill with the hilite gray color
330 	if (IsEnabled() && _IsSelected() && !menu->IsRedrawAfterSticky()
331 		&& canHandle) {
332 		// fill
333 		menu->SetHighColor(tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT));
334 		menu->FillRect(frame);
335 
336 		// these continue the dark grey border on the left or top edge
337 		menu->SetHighColor(tint_color(menuColor, B_DARKEN_4_TINT));
338 		if (fVertical) {
339 			// dark line at top
340 			menu->StrokeLine(frame.LeftTop(), frame.RightTop());
341 		} else {
342 			// dark line on the left
343 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
344 		}
345 	} else
346 		menu->SetLowColor(menuColor);
347 
348 	menu->MovePenTo(ContentLocation());
349 	DrawContent();
350 	menu->PopState();
351 }
352 
353 
354 void
355 TTeamMenuItem::DrawContent()
356 {
357 	BMenu* menu = Menu();
358 	if (fIcon != NULL) {
359 		if (fIcon->ColorSpace() == B_RGBA32) {
360 			menu->SetDrawingMode(B_OP_ALPHA);
361 			menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
362 		} else
363 			menu->SetDrawingMode(B_OP_OVER);
364 
365 		BRect frame(Frame());
366 		BRect iconBounds(fIcon->Bounds());
367 		BRect dstRect(iconBounds);
368 		float extra = fVertical ? 0.0f : -1.0f;
369 		BPoint contLoc = ContentLocation();
370 		BPoint drawLoc = contLoc + BPoint(kHPad, kVPad);
371 
372 		if (!fDrawLabel || (fVertical && iconBounds.Width() > 32)) {
373 			float offsetx = contLoc.x
374 				+ ((frame.Width() - iconBounds.Width()) / 2) + extra;
375 			float offsety = contLoc.y + 3.0f + extra;
376 
377 			dstRect.OffsetTo(BPoint(offsetx, offsety));
378 			menu->DrawBitmapAsync(fIcon, dstRect);
379 
380 			drawLoc.x = ((frame.Width() - LabelWidth()) / 2);
381 			drawLoc.y = frame.top + iconBounds.Height() + 4.0f;
382 		} else {
383 			float offsetx = contLoc.x + kHPad;
384 			float offsety = contLoc.y +
385 				((frame.Height() - iconBounds.Height()) / 2) + extra;
386 
387 			dstRect.OffsetTo(BPoint(offsetx, offsety));
388 			menu->DrawBitmapAsync(fIcon, dstRect);
389 
390 			float labelHeight = fLabelAscent + fLabelDescent;
391 			drawLoc.x += iconBounds.Width() + kLabelOffset;
392 			drawLoc.y = frame.top + ((frame.Height() - labelHeight) / 2) + extra;
393 		}
394 
395 		menu->MovePenTo(drawLoc);
396 	}
397 
398 	// set the pen to black so that either method will draw in the same color
399 	// low color is set in inherited::DrawContent, override makes sure its
400 	// what we want
401 	if (fDrawLabel) {
402 		menu->SetDrawingMode(B_OP_OVER);
403 		menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
404 
405 		// override the drawing of the content when the item is disabled
406 		// the wrong lowcolor is used when the item is disabled since the
407 		// text color does not change
408 		DrawContentLabel();
409 	}
410 
411 	// Draw the expandable icon.
412 	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
413 	if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando
414 		&& barView->ExpandoState()) {
415 		BRect frame(Frame());
416 		BRect rect(0, 0, kSwitchWidth, 10);
417 		rect.OffsetTo(BPoint(frame.right - rect.Width(),
418 			ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
419 
420 		if (be_control_look != NULL) {
421 			uint32 arrowDirection = fExpanded
422 				? BControlLook::B_UP_ARROW : BControlLook::B_DOWN_ARROW;
423 			be_control_look->DrawArrowShape(menu, rect, rect, menu->LowColor(),
424 				arrowDirection, 0, B_DARKEN_3_TINT);
425 		} else {
426 			rgb_color outlineColor = {80, 80, 80, 255};
427 			rgb_color middleColor = {200, 200, 200, 255};
428 
429 			menu->SetDrawingMode(B_OP_OVER);
430 
431 			if (!fExpanded) {
432 				menu->BeginLineArray(6);
433 
434 				menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
435 					BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
436 				menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
437 					BPoint(rect.left + 7, rect.top + 5), outlineColor);
438 				menu->AddLine(BPoint(rect.left + 7, rect.top + 5),
439 					BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
440 
441 				menu->AddLine(BPoint(rect.left + 4, rect.top + 3),
442 					BPoint(rect.left + 4, rect.bottom - 3), middleColor);
443 				menu->AddLine(BPoint(rect.left + 5, rect.top + 4),
444 					BPoint(rect.left + 5, rect.bottom - 4), middleColor);
445 				menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
446 					BPoint(rect.left + 6, rect.top + 5), middleColor);
447 				menu->EndLineArray();
448 			} else {
449 				// expanded state
450 
451 				menu->BeginLineArray(6);
452 				menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
453 					BPoint(rect.right - 3, rect.top + 3), outlineColor);
454 				menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
455 					BPoint(rect.left + 5, rect.top + 7), outlineColor);
456 				menu->AddLine(BPoint(rect.left + 5, rect.top + 7),
457 					BPoint(rect.right - 3, rect.top + 3), outlineColor);
458 
459 				menu->AddLine(BPoint(rect.left + 3, rect.top + 4),
460 					BPoint(rect.right - 5, rect.top + 4), middleColor);
461 				menu->AddLine(BPoint(rect.left + 4, rect.top + 5),
462 					BPoint(rect.right - 6, rect.top + 5), middleColor);
463 				menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
464 					BPoint(rect.left + 5, rect.top + 6), middleColor);
465 				menu->EndLineArray();
466 			}
467 		}
468 	}
469 }
470 
471 
472 void
473 TTeamMenuItem::DrawContentLabel()
474 {
475 	BMenu* menu = Menu();
476 	menu->MovePenBy(0, fLabelAscent);
477 
478 	float cachedWidth = menu->StringWidth(Label());
479 	if (Submenu() && fVertical)
480 		cachedWidth += 18;
481 
482 	const char* label = Label();
483 	char* truncLabel = NULL;
484 	float max = 0;
485 
486 	if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando)
487 		max = menu->MaxContentWidth() - kSwitchWidth;
488 	else
489 		max = menu->MaxContentWidth() - 4.0f;
490 
491 	if (max > 0) {
492 		BPoint penloc = menu->PenLocation();
493 		BRect frame = Frame();
494 		float offset = penloc.x - frame.left;
495 		if (cachedWidth + offset > max) {
496 			truncLabel = (char*)malloc(strlen(label) + 4);
497 			if (!truncLabel)
498 				return;
499 			TruncateLabel(max-offset, truncLabel);
500 			label = truncLabel;
501 		}
502 	}
503 
504 	if (!label)
505 		label = Label();
506 
507 	TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView();
508 	bool canHandle = !barview->Dragging()
509 		|| barview->AppCanHandleTypes(Signature());
510 	if (_IsSelected() && IsEnabled() && canHandle)
511 		menu->SetLowColor(tint_color(menu->LowColor(),
512 			B_HIGHLIGHT_BACKGROUND_TINT));
513 	else
514 		menu->SetLowColor(menu->LowColor());
515 
516 	menu->DrawString(label);
517 
518 	free(truncLabel);
519 }
520 
521 
522 bool
523 TTeamMenuItem::IsExpanded()
524 {
525 	return fExpanded;
526 }
527 
528 
529 void
530 TTeamMenuItem::ToggleExpandState(bool resizeWindow)
531 {
532 	fExpanded = !fExpanded;
533 
534 	if (fExpanded) {
535 		// Populate Menu() with the stuff from SubMenu().
536 		TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu()));
537 		if (sub) {
538 			// force the menu to update it's contents.
539 			bool locked = sub->LockLooper();
540 				// if locking the looper failed, the menu is just not visible
541 			sub->AttachedToWindow();
542 			if (locked)
543 				sub->UnlockLooper();
544 
545 			if (sub->CountItems() > 1) {
546 				TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
547 				int myindex = parent->IndexOf(this) + 1;
548 
549 				TWindowMenuItem* windowItem = NULL;
550 				int childIndex = 0;
551 				int totalChildren = sub->CountItems() - 4;
552 					// hide, show, close, separator.
553 				for (; childIndex < totalChildren; childIndex++) {
554 					windowItem = static_cast<TWindowMenuItem*>
555 						(sub->RemoveItem((int32)0));
556 					parent->AddItem(windowItem, myindex + childIndex);
557 					windowItem->ExpandedItem(true);
558 				}
559 				sub->SetExpanded(true, myindex + childIndex);
560 
561 				if (resizeWindow)
562 					parent->SizeWindow(-1);
563 			}
564 		}
565 	} else {
566 		// Remove the goodies from the Menu() that should be in the SubMenu();
567 		TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu());
568 
569 		if (sub) {
570 			TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
571 
572 			TWindowMenuItem* windowItem = NULL;
573 			int childIndex = parent->IndexOf(this) + 1;
574 			while (!parent->SubmenuAt(childIndex) && childIndex
575 				< parent->CountItems()) {
576 				windowItem = static_cast<TWindowMenuItem*>
577 					(parent->RemoveItem(childIndex));
578 				sub->AddItem(windowItem, 0);
579 				windowItem->ExpandedItem(false);
580 			}
581 			sub->SetExpanded(false, 0);
582 
583 			if (resizeWindow)
584 				parent->SizeWindow(1);
585 		}
586 	}
587 }
588 
589 
590 TWindowMenuItem*
591 TTeamMenuItem::ExpandedWindowItem(int32 id)
592 {
593 	if (!fExpanded) {
594 		// Paranoia
595 		return NULL;
596 	}
597 
598 	TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
599 	int childIndex = parent->IndexOf(this) + 1;
600 
601 	while (!parent->SubmenuAt(childIndex)
602 		&& childIndex < parent->CountItems()) {
603 		TWindowMenuItem* item
604 			= static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex));
605 		if (item->ID() == id)
606 			return item;
607 
608 		childIndex++;
609 	}
610 	return NULL;
611 }
612 
613 
614 BRect
615 TTeamMenuItem::ExpanderBounds() const
616 {
617 	BRect bounds(Frame());
618 	bounds.left = bounds.right - kSwitchWidth;
619 	return bounds;
620 }
621 
622 
623 bool
624 TTeamMenuItem::_IsSelected() const
625 {
626 	return IsSelected() || fOverriddenSelected;
627 }
628