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