xref: /haiku/src/apps/deskbar/TeamMenuItem.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
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 #include <Debug.h>
37 
38 #include "TeamMenuItem.h"
39 
40 #include <string.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 
44 #include <Bitmap.h>
45 #include <ControlLook.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", (int32)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 	fDrawLabel = drawLabel;
111 	fVertical = vertical;
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 float
174 TTeamMenuItem::LabelWidth() const
175 {
176 	return fLabelWidth;
177 }
178 
179 
180 BList*
181 TTeamMenuItem::Teams() const
182 {
183 	return fTeam;
184 }
185 
186 
187 const char*
188 TTeamMenuItem::Signature() const
189 {
190 	return fSig;
191 }
192 
193 
194 const char*
195 TTeamMenuItem::Name() const
196 {
197 	return fName;
198 }
199 
200 
201 void
202 TTeamMenuItem::GetContentSize(float* width, float* height)
203 {
204 	BRect iconBounds;
205 
206 	if (fIcon)
207 		iconBounds = fIcon->Bounds();
208 	else
209 		iconBounds = BRect(0, 0, 15, 15);
210 
211 	BMenuItem::GetContentSize(width, height);
212 
213 	if (fOverrideWidth != -1.0f)
214 		*width = fOverrideWidth;
215 	else
216 		*width = kHPad + iconBounds.Width() + kLabelOffset + fLabelWidth + kHPad
217 			+ 20;
218 
219 	if (fOverrideHeight != -1.0f)
220 		*height = fOverrideHeight;
221 	else {
222 		*height = iconBounds.Height();
223 		float labelHeight = fLabelAscent + fLabelDescent;
224 		if (labelHeight > *height)
225 			*height = labelHeight;
226 		*height += (kVPad * 2) + 2;
227 	}
228 	*height += 2;
229 }
230 
231 
232 void
233 TTeamMenuItem::Draw()
234 {
235 	BRect frame(Frame());
236 	BMenu* menu = Menu();
237 	menu->PushState();
238 	rgb_color menuColor = menu->LowColor();
239 	TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView();
240 
241 	bool canHandle = !barview->Dragging()
242 		|| barview->AppCanHandleTypes(Signature());
243 
244 	if (be_control_look != NULL) {
245 		uint32 flags = 0;
246 		if (_IsSelected() && canHandle)
247 			flags |= BControlLook::B_ACTIVATED;
248 
249 		uint32 borders = BControlLook::B_TOP_BORDER;
250 		if (fVertical) {
251 			menu->SetHighColor(tint_color(menuColor, B_DARKEN_1_TINT));
252 			borders |= BControlLook::B_LEFT_BORDER
253 				| BControlLook::B_RIGHT_BORDER;
254 			menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
255 			frame.bottom--;
256 
257 			be_control_look->DrawMenuBarBackground(menu, frame, frame,
258 				menuColor, flags, borders);
259 		} else {
260 			if (flags & BControlLook::B_ACTIVATED)
261 				menu->SetHighColor(tint_color(menuColor, B_DARKEN_3_TINT));
262 			else
263 				menu->SetHighColor(tint_color(menuColor, 1.22));
264 			borders |= BControlLook::B_BOTTOM_BORDER;
265 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
266 			frame.left++;
267 
268 			be_control_look->DrawButtonBackground(menu, frame, frame,
269 				menuColor, flags, borders);
270 		}
271 
272 		menu->MovePenTo(ContentLocation());
273 		DrawContent();
274 		menu->PopState();
275 		return;
276 	}
277 
278 	//	if not selected or being tracked on, fill with gray
279 	if ((!_IsSelected() && !menu->IsRedrawAfterSticky()) || !canHandle
280 		|| !IsEnabled()) {
281 		frame.InsetBy(1, 1);
282 		menu->SetHighColor(menuColor);
283 		menu->FillRect(frame);
284 	}
285 
286 	//	draw the gray, unselected item, border
287 	if (!_IsSelected() || !IsEnabled()) {
288 		rgb_color shadow = tint_color(menuColor, B_DARKEN_1_TINT);
289 		rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
290 
291 		frame = Frame();
292 
293 		menu->SetHighColor(shadow);
294 		if (fVertical)
295 			menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
296 		else
297 			menu->StrokeLine(frame.LeftBottom() + BPoint(1, 0),
298 				frame.RightBottom());
299 
300 		menu->StrokeLine(frame.RightBottom(), frame.RightTop());
301 
302 		menu->SetHighColor(light);
303 		menu->StrokeLine(frame.RightTop() + BPoint(-1, 0), frame.LeftTop());
304 		if (fVertical)
305 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom()
306 				+ BPoint(0, -1));
307 		else
308 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
309 	}
310 
311 	//	if selected or being tracked on, fill with the hilite gray color
312 	if (IsEnabled() && _IsSelected() && !menu->IsRedrawAfterSticky()
313 		&& canHandle) {
314 		// fill
315 		menu->SetHighColor(tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT));
316 		menu->FillRect(frame);
317 
318 		// these continue the dark grey border on the left or top edge
319 		menu->SetHighColor(tint_color(menuColor, B_DARKEN_4_TINT));
320 		if (fVertical) {
321 			// dark line at top
322 			menu->StrokeLine(frame.LeftTop(), frame.RightTop());
323 		} else {
324 			// dark line on the left
325 			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
326 		}
327 	} else
328 		menu->SetLowColor(menuColor);
329 
330 	menu->MovePenTo(ContentLocation());
331 	DrawContent();
332 	menu->PopState();
333 }
334 
335 
336 void
337 TTeamMenuItem::DrawContent()
338 {
339 	BMenu* menu = Menu();
340 	if (fIcon) {
341 		if (fIcon->ColorSpace() == B_RGBA32) {
342 			menu->SetDrawingMode(B_OP_ALPHA);
343 			menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
344 		} else
345 			menu->SetDrawingMode(B_OP_OVER);
346 
347 		BRect frame(Frame());
348 		BRect iconBounds(fIcon->Bounds());
349 		BRect dstRect(iconBounds);
350 		float extra = fVertical ? 0.0f : 1.0f;
351 		BPoint contLoc = ContentLocation();
352 
353 		dstRect.OffsetTo(BPoint(contLoc.x + kHPad, contLoc.y +
354 			((frame.Height() - iconBounds.Height()) / 2) + extra));
355 		menu->DrawBitmapAsync(fIcon, dstRect);
356 
357 		float labelHeight = fLabelAscent + fLabelDescent;
358 		BPoint drawLoc = contLoc + BPoint(kHPad, kVPad);
359 		drawLoc.x += iconBounds.Width() + kLabelOffset;
360 		drawLoc.y = frame.top + ((frame.Height() - labelHeight) / 2) + 1.0f;
361 		menu->MovePenTo(drawLoc);
362 	}
363 
364 	// set the pen to black so that either method will draw in the same color
365 	// low color is set in inherited::DrawContent, override makes sure its
366 	// what we want
367 	if (fDrawLabel) {
368 		menu->SetDrawingMode(B_OP_OVER);
369 		menu->SetHighColor(0, 0, 0);
370 
371 		// override the drawing of the content when the item is disabled
372 		// the wrong lowcolor is used when the item is disabled since the
373 		// text color does not change
374 		DrawContentLabel();
375 	}
376 
377 	// Draw the expandable icon.
378 	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
379 	if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando
380 		&& barView->Expando()) {
381 		BRect frame(Frame());
382 		BRect rect(0, 0, kSwitchWidth, 10);
383 		rect.OffsetTo(BPoint(frame.right - rect.Width(),
384 			ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
385 
386 		if (be_control_look != NULL) {
387 			uint32 arrowDirection = fExpanded
388 				? BControlLook::B_UP_ARROW : BControlLook::B_DOWN_ARROW;
389 			be_control_look->DrawArrowShape(menu, rect, rect, menu->LowColor(),
390 				arrowDirection, 0, B_DARKEN_3_TINT);
391 		} else {
392 			rgb_color outlineColor = {80, 80, 80, 255};
393 			rgb_color middleColor = {200, 200, 200, 255};
394 
395 			menu->SetDrawingMode(B_OP_OVER);
396 
397 			if (!fExpanded) {
398 				menu->BeginLineArray(6);
399 
400 				menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
401 					BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
402 				menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
403 					BPoint(rect.left + 7, rect.top + 5), outlineColor);
404 				menu->AddLine(BPoint(rect.left + 7, rect.top + 5),
405 					BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
406 
407 				menu->AddLine(BPoint(rect.left + 4, rect.top + 3),
408 					BPoint(rect.left + 4, rect.bottom - 3), middleColor);
409 				menu->AddLine(BPoint(rect.left + 5, rect.top + 4),
410 					BPoint(rect.left + 5, rect.bottom - 4), middleColor);
411 				menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
412 					BPoint(rect.left + 6, rect.top + 5), middleColor);
413 				menu->EndLineArray();
414 			} else {
415 				// expanded state
416 
417 				menu->BeginLineArray(6);
418 				menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
419 					BPoint(rect.right - 3, rect.top + 3), outlineColor);
420 				menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
421 					BPoint(rect.left + 5, rect.top + 7), outlineColor);
422 				menu->AddLine(BPoint(rect.left + 5, rect.top + 7),
423 					BPoint(rect.right - 3, rect.top + 3), outlineColor);
424 
425 				menu->AddLine(BPoint(rect.left + 3, rect.top + 4),
426 					BPoint(rect.right - 5, rect.top + 4), middleColor);
427 				menu->AddLine(BPoint(rect.left + 4, rect.top + 5),
428 					BPoint(rect.right - 6, rect.top + 5), middleColor);
429 				menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
430 					BPoint(rect.left + 5, rect.top + 6), middleColor);
431 				menu->EndLineArray();
432 			}
433 		}
434 	}
435 }
436 
437 
438 void
439 TTeamMenuItem::DrawContentLabel()
440 {
441 	BMenu* menu = Menu();
442 	menu->MovePenBy(0, fLabelAscent);
443 
444 	float cachedWidth = menu->StringWidth(Label());
445 	if (Submenu() && fVertical)
446 		cachedWidth += 18;
447 
448 	const char* label = Label();
449 	char* truncLabel = NULL;
450 	float max = 0;
451 
452 	if (static_cast<TBarApp*>(be_app)->Settings()->superExpando && fVertical)
453 		max = menu->MaxContentWidth() - kSwitchWidth;
454 	else
455 		max = menu->MaxContentWidth();
456 
457 	if (max > 0) {
458 		BPoint penloc = menu->PenLocation();
459 		BRect frame = Frame();
460 		float offset = penloc.x - frame.left;
461 		if (cachedWidth + offset > max) {
462 			truncLabel = (char*)malloc(strlen(label) + 4);
463 			if (!truncLabel)
464 				return;
465 			TruncateLabel(max-offset, truncLabel);
466 			label = truncLabel;
467 		}
468 	}
469 
470 	if (!label)
471 		label = Label();
472 
473 	TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView();
474 	bool canHandle = !barview->Dragging()
475 		|| barview->AppCanHandleTypes(Signature());
476 	if (_IsSelected() && IsEnabled() && canHandle)
477 		menu->SetLowColor(tint_color(menu->ViewColor(),
478 			B_HIGHLIGHT_BACKGROUND_TINT));
479 	else
480 		menu->SetLowColor(menu->ViewColor());
481 
482 	menu->DrawString(label);
483 
484 	free(truncLabel);
485 }
486 
487 
488 bool
489 TTeamMenuItem::IsExpanded()
490 {
491 	return fExpanded;
492 }
493 
494 
495 void
496 TTeamMenuItem::ToggleExpandState(bool resizeWindow)
497 {
498 	fExpanded = !fExpanded;
499 
500 	if (fExpanded) {
501 		// Populate Menu() with the stuff from SubMenu().
502 		TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu()));
503 		if (sub) {
504 			// force the menu to update it's contents.
505 			bool locked = sub->LockLooper();
506 				// if locking the looper failed, the menu is just not visible
507 			sub->AttachedToWindow();
508 			if (locked)
509 				sub->UnlockLooper();
510 
511 			if (sub->CountItems() > 1) {
512 				TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
513 				int myindex = parent->IndexOf(this) + 1;
514 
515 				TWindowMenuItem* windowItem = NULL;
516 				int childIndex = 0;
517 				int totalChildren = sub->CountItems() - 4;
518 					// hide, show, close, separator.
519 				for (; childIndex < totalChildren; childIndex++) {
520 					windowItem = static_cast<TWindowMenuItem*>
521 						(sub->RemoveItem((int32)0));
522 					parent->AddItem(windowItem, myindex + childIndex);
523 					windowItem->ExpandedItem(true);
524 				}
525 				sub->SetExpanded(true, myindex + childIndex);
526 
527 				if (resizeWindow)
528 					parent->SizeWindow();
529 			}
530 		}
531 	} else {
532 		// Remove the goodies from the Menu() that should be in the SubMenu();
533 		TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu());
534 
535 		if (sub) {
536 			TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
537 
538 			TWindowMenuItem* windowItem = NULL;
539 			int childIndex = parent->IndexOf(this) + 1;
540 			while (!parent->SubmenuAt(childIndex) && childIndex
541 				< parent->CountItems()) {
542 				windowItem = static_cast<TWindowMenuItem*>
543 					(parent->RemoveItem(childIndex));
544 				sub->AddItem(windowItem, 0);
545 				windowItem->ExpandedItem(false);
546 			}
547 			sub->SetExpanded(false, 0);
548 
549 			if (resizeWindow)
550 				parent->SizeWindow();
551 		}
552 	}
553 }
554 
555 
556 TWindowMenuItem*
557 TTeamMenuItem::ExpandedWindowItem(int32 id)
558 {
559 	if (!fExpanded) {
560 		// Paranoia
561 		return NULL;
562 	}
563 
564 	TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
565 	int childIndex = parent->IndexOf(this) + 1;
566 
567 	while (!parent->SubmenuAt(childIndex)
568 		&& childIndex < parent->CountItems()) {
569 		TWindowMenuItem* item
570 			= static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex));
571 		if (item->ID() == id)
572 			return item;
573 
574 		childIndex++;
575 	}
576 	return NULL;
577 }
578 
579 
580 BRect
581 TTeamMenuItem::ExpanderBounds() const
582 {
583 	BRect bounds(Frame());
584 	bounds.left = bounds.right - kSwitchWidth;
585 	return bounds;
586 }
587 
588 
589 bool
590 TTeamMenuItem::_IsSelected() const
591 {
592 	return IsSelected() || fOverriddenSelected;
593 }
594 
595