xref: /haiku/src/apps/deskbar/TeamMenuItem.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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 <algorithm>
44 
45 #include <Bitmap.h>
46 #include <ControlLook.h>
47 #include <Debug.h>
48 #include <Font.h>
49 #include <Mime.h>
50 #include <Region.h>
51 #include <Roster.h>
52 #include <Resources.h>
53 #include <Window.h>
54 
55 #include "BarApp.h"
56 #include "BarMenuBar.h"
57 #include "BarView.h"
58 #include "ExpandoMenuBar.h"
59 #include "ResourceSet.h"
60 #include "ShowHideMenuItem.h"
61 #include "StatusView.h"
62 #include "TeamMenu.h"
63 #include "WindowMenu.h"
64 #include "WindowMenuItem.h"
65 
66 
67 static float sHPad, sVPad, sLabelOffset = 0.0f;
68 
69 
70 //	#pragma mark - TTeamMenuItem
71 
72 
73 TTeamMenuItem::TTeamMenuItem(BList* team, BBitmap* icon, char* name,
74 	char* signature, float width, float height)
75 	:
76 	TTruncatableMenuItem(new TWindowMenu(team, signature))
77 {
78 	_Init(team, icon, name, signature, width, height);
79 }
80 
81 
82 TTeamMenuItem::TTeamMenuItem(float width, float height)
83 	:
84 	TTruncatableMenuItem("", NULL)
85 {
86 	_Init(NULL, NULL, strdup(""), strdup(""), width, height);
87 }
88 
89 
90 TTeamMenuItem::~TTeamMenuItem()
91 {
92 	delete fTeam;
93 	delete fIcon;
94 	free(fSignature);
95 }
96 
97 
98 /*!	Vulcan Death Grip and other team mouse button handling
99 
100 	\returns true if handled, false otherwise
101 */
102 bool
103 TTeamMenuItem::HandleMouseDown(BPoint where)
104 {
105 	BMenu* menu = Menu();
106 	if (menu == NULL)
107 		return false;
108 
109 	BWindow* window = menu->Window();
110 	if (window == NULL)
111 		return false;
112 
113 	BMessage* message = window->CurrentMessage();
114 	if (message == NULL)
115 		return false;
116 
117 	int32 modifiers = 0;
118 	int32 buttons = 0;
119 	message->FindInt32("modifiers", &modifiers);
120 	message->FindInt32("buttons", &buttons);
121 
122 	// check for three finger salute, a.k.a. Vulcan Death Grip
123 	if ((modifiers & B_COMMAND_KEY) != 0
124 		&& (modifiers & B_CONTROL_KEY) != 0
125 		&& (modifiers & B_SHIFT_KEY) != 0) {
126 		BMessage appMessage(B_SOME_APP_QUIT);
127 		int32 teamCount = fTeam->CountItems();
128 		BMessage quitMessage(teamCount == 1 ? B_SOME_APP_QUIT : kRemoveTeam);
129 		quitMessage.AddInt32("itemIndex", menu->IndexOf(this));
130 		for (int32 index = 0; index < teamCount; index++) {
131 			team_id team = (addr_t)fTeam->ItemAt(index);
132 			appMessage.AddInt32("be:team", team);
133 			quitMessage.AddInt32("team", team);
134 
135 			kill_team(team);
136 		}
137 		be_app->PostMessage(&appMessage);
138 		TExpandoMenuBar* expando = dynamic_cast<TExpandoMenuBar*>(menu);
139 		window->PostMessage(&quitMessage, expando != NULL ? expando : menu);
140 		return true;
141 	} else if ((modifiers & B_SHIFT_KEY) == 0
142 		&& (buttons & B_TERTIARY_MOUSE_BUTTON) != 0) {
143 		// launch new team
144 		be_roster->Launch(Signature());
145 		return true;
146 	} else if ((modifiers & B_CONTROL_KEY) != 0) {
147 		// control click - show all/hide all shortcut
148 		BMessage showMessage((modifiers & B_SHIFT_KEY) != 0
149 			? kMinimizeTeam : kBringTeamToFront);
150 		showMessage.AddInt32("itemIndex", menu->IndexOf(this));
151 		window->PostMessage(&showMessage, menu);
152 		return true;
153 	}
154 
155 	return false;
156 }
157 
158 
159 status_t
160 TTeamMenuItem::Invoke(BMessage* message)
161 {
162 	if (fBarView != NULL) {
163 		if (fBarView->InvokeItem(Signature())) {
164 			// handles drop on application
165 			return B_OK;
166 		}
167 
168 		// if the app could not handle the drag message
169 		// and we were dragging, then kill the drag
170 		// should never get here, disabled item will not invoke
171 		if (fBarView->Dragging())
172 			fBarView->DragStop();
173 	}
174 
175 	// bring to front or minimize shortcuts
176 	uint32 mods = modifiers();
177 	if (mods & B_CONTROL_KEY) {
178 		TShowHideMenuItem::TeamShowHideCommon((mods & B_SHIFT_KEY)
179 			? B_MINIMIZE_WINDOW : B_BRING_TO_FRONT, Teams());
180 	}
181 
182 	return BMenuItem::Invoke(message);
183 }
184 
185 
186 void
187 TTeamMenuItem::SetOverrideSelected(bool selected)
188 {
189 	fOverriddenSelected = selected;
190 	Highlight(selected);
191 }
192 
193 
194 void
195 TTeamMenuItem::SetIcon(BBitmap* icon) {
196 	delete fIcon;
197 	fIcon = icon;
198 }
199 
200 
201 void
202 TTeamMenuItem::GetContentSize(float* width, float* height)
203 {
204 	BMenuItem::GetContentSize(width, height);
205 
206 	if (fOverrideWidth != -1.0f)
207 		*width = fOverrideWidth;
208 	else {
209 		const float iconSize = static_cast<TBarApp*>(be_app)->TeamIconSize();
210 		const float iconPadding = be_control_look->ComposeSpacing(kIconPadding);
211 		float iconOnlyWidth = iconSize + iconPadding;
212 		if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels)
213 			iconOnlyWidth += iconPadding; // add an extra icon padding
214 
215 		if (fBarView->MiniState()) {
216 			if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels)
217 				*width = iconOnlyWidth;
218 			else
219 				*width = gMinimumWindowWidth - (gDragRegionWidth + kGutter) * 2;
220 		} else if (!fBarView->Vertical()) {
221 			TExpandoMenuBar* menu = static_cast<TExpandoMenuBar*>(Menu());
222 			*width = menu->MaxHorizontalItemWidth();
223 		} else
224 			*width = static_cast<TBarApp*>(be_app)->Settings()->width;
225 	}
226 
227 	if (fOverrideHeight != -1.0f)
228 		*height = fOverrideHeight;
229 	else
230 		*height = fBarView->TeamMenuItemHeight();
231 }
232 
233 
234 void
235 TTeamMenuItem::Draw()
236 {
237 	BRect frame = Frame();
238 	BMenu* menu = Menu();
239 
240 	menu->PushState();
241 
242 	rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
243 	bool canHandle = !fBarView->Dragging()
244 		|| fBarView->AppCanHandleTypes(Signature());
245 	uint32 flags = 0;
246 	if (_IsSelected() && canHandle)
247 		flags |= BControlLook::B_ACTIVATED;
248 
249 	uint32 borders = BControlLook::B_TOP_BORDER;
250 	if (fBarView->Vertical()) {
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 
275 	menu->PopState();
276 }
277 
278 
279 void
280 TTeamMenuItem::DrawContent()
281 {
282 	BMenu* menu = Menu();
283 	BRect frame = Frame();
284 
285 	if (fIcon != NULL) {
286 		if (fIcon->ColorSpace() == B_RGBA32) {
287 			menu->SetDrawingMode(B_OP_ALPHA);
288 			menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
289 		} else
290 			menu->SetDrawingMode(B_OP_OVER);
291 
292 		BRect iconBounds = fIcon->Bounds();
293 		BRect updateRect = iconBounds;
294 		BPoint contentLocation = ContentLocation();
295 		BPoint drawLocation = contentLocation + BPoint(sHPad, sVPad);
296 		const int32 large = be_control_look->ComposeIconSize(B_LARGE_ICON)
297 			.IntegerWidth() + 1;
298 
299 		if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels
300 			|| (fBarView->Vertical() && iconBounds.Width() > large)) {
301 			// determine icon location (centered horizontally)
302 			float offsetx = contentLocation.x
303 				+ floorf((frame.Width() - iconBounds.Width()) / 2);
304 			float offsety = contentLocation.y + sVPad + kGutter;
305 
306 			// draw icon
307 			updateRect.OffsetTo(BPoint(offsetx, offsety));
308 			menu->DrawBitmapAsync(fIcon, updateRect);
309 
310 			// determine label position (below icon)
311 			drawLocation.x = floorf((frame.Width() - fLabelWidth) / 2);
312 			drawLocation.y = frame.top + sVPad + iconBounds.Height() + sVPad;
313 		} else {
314 			// determine icon location (centered vertically)
315 			float offsetx = contentLocation.x + sHPad;
316 			float offsety = contentLocation.y +
317 				floorf((frame.Height() - iconBounds.Height()) / 2);
318 
319 			// draw icon
320 			updateRect.OffsetTo(BPoint(offsetx, offsety));
321 			menu->DrawBitmapAsync(fIcon, updateRect);
322 
323 			// determine label position (centered vertically)
324 			drawLocation.x += iconBounds.Width() + sLabelOffset;
325 			drawLocation.y = frame.top
326 				+ ceilf((frame.Height() - fLabelHeight) / 2);
327 		}
328 
329 		menu->MovePenTo(drawLocation);
330 	}
331 
332 	// override the drawing of the content when the item is disabled
333 	// the wrong lowcolor is used when the item is disabled since the
334 	// text color does not change
335 	menu->SetDrawingMode(B_OP_OVER);
336 	menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
337 
338 	bool canHandle = !fBarView->Dragging()
339 		|| fBarView->AppCanHandleTypes(Signature());
340 	if (_IsSelected() && IsEnabled() && canHandle)
341 		menu->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
342 			B_HIGHLIGHT_BACKGROUND_TINT));
343 	else
344 		menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
345 
346 	if (IsSelected())
347 		menu->SetHighColor(ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR));
348 	else
349 		menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
350 
351 	menu->MovePenBy(0, fLabelAscent);
352 
353 	// draw label
354 	if (!static_cast<TBarApp*>(be_app)->Settings()->hideLabels) {
355 		float labelWidth = menu->StringWidth(Label());
356 		BPoint penLocation = menu->PenLocation();
357 		// truncate to max width
358 		float offset = penLocation.x - frame.left;
359 		menu->DrawString(Label(labelWidth + offset));
360 	}
361 
362 	// draw expander arrow
363 	if (fBarView->Vertical()
364 		&& static_cast<TBarApp*>(be_app)->Settings()->superExpando
365 		&& fBarView->ExpandoState()) {
366 		DrawExpanderArrow();
367 	}
368 }
369 
370 
371 void
372 TTeamMenuItem::DrawExpanderArrow()
373 {
374 	BRect frame = Frame();
375 	BRect rect(0.0f, 0.0f, kSwitchWidth, sHPad + 2.0f);
376 	rect.OffsetTo(BPoint(frame.right - rect.Width(),
377 		ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
378 
379 	float colorTint = B_DARKEN_3_TINT;
380 	rgb_color bgColor = ui_color(B_MENU_BACKGROUND_COLOR);
381 	if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3)
382 		colorTint = B_LIGHTEN_2_TINT;
383 
384 	be_control_look->DrawArrowShape(Menu(), rect, Menu()->Frame(),
385 		bgColor, fArrowDirection, 0, colorTint);
386 }
387 
388 
389 void
390 TTeamMenuItem::ToggleExpandState(bool resizeWindow)
391 {
392 	fExpanded = !fExpanded;
393 	fArrowDirection = fExpanded ? BControlLook::B_DOWN_ARROW
394 		: BControlLook::B_RIGHT_ARROW;
395 
396 	if (fExpanded) {
397 		// Populate Menu() with the stuff from SubMenu().
398 		TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu()));
399 		if (sub != NULL) {
400 			// force the menu to update it's contents.
401 			bool locked = sub->LockLooper();
402 				// if locking the looper failed, the menu is just not visible
403 			sub->AttachedToWindow();
404 			if (locked)
405 				sub->UnlockLooper();
406 
407 			if (sub->CountItems() > 1) {
408 				TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
409 				int myindex = parent->IndexOf(this) + 1;
410 
411 				TWindowMenuItem* windowItem = NULL;
412 				int32 childIndex = 0;
413 				int32 totalChildren = sub->CountItems() - 4;
414 					// hide, show, close, separator.
415 				for (; childIndex < totalChildren; childIndex++) {
416 					windowItem = static_cast<TWindowMenuItem*>
417 						(sub->RemoveItem((int32)0));
418 					parent->AddItem(windowItem, myindex + childIndex);
419 					windowItem->SetExpanded(true);
420 				}
421 				sub->SetExpanded(true, myindex + childIndex);
422 
423 				if (resizeWindow)
424 					parent->SizeWindow(-1);
425 			}
426 		}
427 	} else {
428 		// Remove the goodies from the Menu() that should be in the SubMenu();
429 		TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu());
430 		if (sub != NULL) {
431 			TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
432 
433 			TWindowMenuItem* windowItem = NULL;
434 			int32 childIndex = parent->IndexOf(this) + 1;
435 			while (parent->SubmenuAt(childIndex) == NULL
436 				&& childIndex < parent->CountItems()) {
437 				windowItem = static_cast<TWindowMenuItem*>(
438 					parent->RemoveItem(childIndex));
439 				sub->AddItem(windowItem, 0);
440 				windowItem->SetExpanded(false);
441 			}
442 			sub->SetExpanded(false, 0);
443 
444 			if (resizeWindow)
445 				parent->SizeWindow(1);
446 		}
447 	}
448 }
449 
450 
451 TWindowMenuItem*
452 TTeamMenuItem::ExpandedWindowItem(int32 id)
453 {
454 	if (!fExpanded) {
455 		// Paranoia
456 		return NULL;
457 	}
458 
459 	TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
460 	int childIndex = parent->IndexOf(this) + 1;
461 
462 	while (!parent->SubmenuAt(childIndex)
463 		&& childIndex < parent->CountItems()) {
464 		TWindowMenuItem* item
465 			= static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex));
466 		if (item->ID() == id)
467 			return item;
468 
469 		childIndex++;
470 	}
471 	return NULL;
472 }
473 
474 
475 BRect
476 TTeamMenuItem::ExpanderBounds() const
477 {
478 	BRect bounds(Frame());
479 	bounds.left = bounds.right - kSwitchWidth;
480 	return bounds;
481 }
482 
483 
484 //	#pragma mark - Private methods
485 
486 
487 void
488 TTeamMenuItem::_Init(BList* team, BBitmap* icon, char* name, char* signature,
489 	float width, float height)
490 {
491 	if (sHPad == 0.0f) {
492 		// Initialize the padding values.
493 		sHPad = be_control_look->ComposeSpacing(B_USE_SMALL_SPACING);
494 		sVPad = ceilf(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 4.0f);
495 		sLabelOffset = ceilf((be_control_look->DefaultLabelSpacing() / 3.0f) * 4.0f);
496 	}
497 
498 	fTeam = team;
499 	fIcon = icon;
500 	fSignature = signature;
501 
502 	if (name == NULL) {
503 		char temp[32];
504 		snprintf(temp, sizeof(temp), "team %ld", (addr_t)team->ItemAt(0));
505 		name = strdup(temp);
506 	}
507 
508 	SetLabel(name);
509 
510 	fOverrideWidth = width;
511 	fOverrideHeight = height;
512 
513 	fBarView = static_cast<TBarApp*>(be_app)->BarView();
514 
515 	BFont font(be_plain_font);
516 	fLabelWidth = ceilf(font.StringWidth(name));
517 	font_height fontHeight;
518 	font.GetHeight(&fontHeight);
519 	fLabelAscent = ceilf(fontHeight.ascent);
520 	fLabelDescent = ceilf(fontHeight.descent + fontHeight.leading);
521 	fLabelHeight = fLabelAscent + fLabelDescent;
522 
523 	fOverriddenSelected = false;
524 
525 	fExpanded = false;
526 	fArrowDirection = BControlLook::B_RIGHT_ARROW;
527 }
528 
529 
530 bool
531 TTeamMenuItem::_IsSelected() const
532 {
533 	return IsSelected() || fOverriddenSelected;
534 }
535