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