xref: /haiku/src/apps/deskbar/TeamMenuItem.cpp (revision b289c1e4066d31da5ac6c5e587d9b98b76493af9)
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 		bool hideLabels = static_cast<TBarApp*>(be_app)->Settings()->hideLabels;
210 		float iconSize = static_cast<TBarApp*>(be_app)->IconSize();
211 		float iconOnlyWidth = (be_control_look->ComposeSpacing(kIconPadding) * 2) + iconSize;
212 
213 		if (fBarView->MiniState()) {
214 			if (hideLabels)
215 				*width = iconOnlyWidth;
216 			else
217 				*width = gMinimumWindowWidth - (gDragRegionWidth + kGutter) * 2;
218 		} else if (!fBarView->Vertical()) {
219 			TExpandoMenuBar* menu = static_cast<TExpandoMenuBar*>(Menu());
220 			*width = menu->MaxHorizontalItemWidth();
221 		} else
222 			*width = static_cast<TBarApp*>(be_app)->Settings()->width;
223 	}
224 
225 	if (fOverrideHeight != -1.0f)
226 		*height = fOverrideHeight;
227 	else
228 		*height = fBarView->TeamMenuItemHeight();
229 }
230 
231 
232 void
233 TTeamMenuItem::Draw()
234 {
235 	BRect frame = Frame();
236 	BMenu* menu = Menu();
237 
238 	menu->PushState();
239 
240 	rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
241 	bool canHandle = !fBarView->Dragging()
242 		|| fBarView->AppCanHandleTypes(Signature());
243 	uint32 flags = 0;
244 	if (_IsSelected() && canHandle)
245 		flags |= BControlLook::B_ACTIVATED;
246 
247 	uint32 borders = BControlLook::B_TOP_BORDER;
248 	if (fBarView->Vertical()) {
249 		menu->SetHighColor(tint_color(menuColor, B_DARKEN_1_TINT));
250 		borders |= BControlLook::B_LEFT_BORDER
251 			| BControlLook::B_RIGHT_BORDER;
252 		menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
253 		frame.bottom--;
254 
255 		be_control_look->DrawMenuBarBackground(menu, frame, frame,
256 			menuColor, flags, borders);
257 	} else {
258 		if (flags & BControlLook::B_ACTIVATED)
259 			menu->SetHighColor(tint_color(menuColor, B_DARKEN_3_TINT));
260 		else
261 			menu->SetHighColor(tint_color(menuColor, 1.22));
262 		borders |= BControlLook::B_BOTTOM_BORDER;
263 		menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
264 		frame.left++;
265 
266 		be_control_look->DrawButtonBackground(menu, frame, frame,
267 			menuColor, flags, borders);
268 	}
269 
270 	menu->MovePenTo(ContentLocation());
271 	DrawContent();
272 
273 	menu->PopState();
274 }
275 
276 
277 void
278 TTeamMenuItem::DrawContent()
279 {
280 	BMenu* menu = Menu();
281 	BRect frame = Frame();
282 
283 	if (fIcon != NULL) {
284 		if (fIcon->ColorSpace() == B_RGBA32) {
285 			menu->SetDrawingMode(B_OP_ALPHA);
286 			menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
287 		} else
288 			menu->SetDrawingMode(B_OP_OVER);
289 
290 		BRect iconBounds = fIcon != NULL ? fIcon->Bounds()
291 			: BRect(0, 0, kMinimumIconSize - 1, kMinimumIconSize - 1);
292 		BRect updateRect = iconBounds;
293 		BPoint contentLocation = ContentLocation();
294 		BPoint drawLocation = contentLocation + BPoint(sHPad, sVPad);
295 
296 		if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels
297 			|| (fBarView->Vertical() && iconBounds.Width() > 32)) {
298 			// determine icon location (centered horizontally)
299 			float offsetx = contentLocation.x
300 				+ floorf((frame.Width() - iconBounds.Width()) / 2);
301 			float offsety = contentLocation.y + sVPad + kGutter;
302 
303 			// draw icon
304 			updateRect.OffsetTo(BPoint(offsetx, offsety));
305 			menu->DrawBitmapAsync(fIcon, updateRect);
306 
307 			// determine label position (below icon)
308 			drawLocation.x = floorf((frame.Width() - fLabelWidth) / 2);
309 			drawLocation.y = frame.top + sVPad + iconBounds.Height() + sVPad;
310 		} else {
311 			// determine icon location (centered vertically)
312 			float offsetx = contentLocation.x + sHPad;
313 			float offsety = contentLocation.y +
314 				floorf((frame.Height() - iconBounds.Height()) / 2);
315 
316 			// draw icon
317 			updateRect.OffsetTo(BPoint(offsetx, offsety));
318 			menu->DrawBitmapAsync(fIcon, updateRect);
319 
320 			// determine label position (centered vertically)
321 			drawLocation.x += iconBounds.Width() + sLabelOffset;
322 			drawLocation.y = frame.top
323 				+ ceilf((frame.Height() - fLabelHeight) / 2);
324 		}
325 
326 		menu->MovePenTo(drawLocation);
327 	}
328 
329 	// override the drawing of the content when the item is disabled
330 	// the wrong lowcolor is used when the item is disabled since the
331 	// text color does not change
332 	menu->SetDrawingMode(B_OP_OVER);
333 	menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
334 
335 	bool canHandle = !fBarView->Dragging()
336 		|| fBarView->AppCanHandleTypes(Signature());
337 	if (_IsSelected() && IsEnabled() && canHandle)
338 		menu->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
339 			B_HIGHLIGHT_BACKGROUND_TINT));
340 	else
341 		menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
342 
343 	if (IsSelected())
344 		menu->SetHighColor(ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR));
345 	else
346 		menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
347 
348 	menu->MovePenBy(0, fLabelAscent);
349 
350 	// draw label
351 	if (!static_cast<TBarApp*>(be_app)->Settings()->hideLabels) {
352 		float labelWidth = menu->StringWidth(Label());
353 		BPoint penLocation = menu->PenLocation();
354 		// truncate to max width
355 		float offset = penLocation.x - frame.left;
356 		menu->DrawString(Label(labelWidth + offset));
357 	}
358 
359 	// draw expander arrow
360 	if (fBarView->Vertical()
361 		&& static_cast<TBarApp*>(be_app)->Settings()->superExpando
362 		&& fBarView->ExpandoState()) {
363 		DrawExpanderArrow();
364 	}
365 }
366 
367 
368 void
369 TTeamMenuItem::DrawExpanderArrow()
370 {
371 	BRect frame = Frame();
372 	BRect rect(0.0f, 0.0f, kSwitchWidth, sHPad + 2.0f);
373 	rect.OffsetTo(BPoint(frame.right - rect.Width(),
374 		ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
375 
376 	float colorTint = B_DARKEN_3_TINT;
377 	rgb_color bgColor = ui_color(B_MENU_BACKGROUND_COLOR);
378 	if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3)
379 		colorTint = B_LIGHTEN_2_TINT;
380 
381 	be_control_look->DrawArrowShape(Menu(), rect, Menu()->Frame(),
382 		bgColor, fArrowDirection, 0, colorTint);
383 }
384 
385 
386 void
387 TTeamMenuItem::ToggleExpandState(bool resizeWindow)
388 {
389 	fExpanded = !fExpanded;
390 	fArrowDirection = fExpanded ? BControlLook::B_DOWN_ARROW
391 		: BControlLook::B_RIGHT_ARROW;
392 
393 	if (fExpanded) {
394 		// Populate Menu() with the stuff from SubMenu().
395 		TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu()));
396 		if (sub != NULL) {
397 			// force the menu to update it's contents.
398 			bool locked = sub->LockLooper();
399 				// if locking the looper failed, the menu is just not visible
400 			sub->AttachedToWindow();
401 			if (locked)
402 				sub->UnlockLooper();
403 
404 			if (sub->CountItems() > 1) {
405 				TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
406 				int myindex = parent->IndexOf(this) + 1;
407 
408 				TWindowMenuItem* windowItem = NULL;
409 				int32 childIndex = 0;
410 				int32 totalChildren = sub->CountItems() - 4;
411 					// hide, show, close, separator.
412 				for (; childIndex < totalChildren; childIndex++) {
413 					windowItem = static_cast<TWindowMenuItem*>
414 						(sub->RemoveItem((int32)0));
415 					parent->AddItem(windowItem, myindex + childIndex);
416 					windowItem->SetExpanded(true);
417 				}
418 				sub->SetExpanded(true, myindex + childIndex);
419 
420 				if (resizeWindow)
421 					parent->SizeWindow(-1);
422 			}
423 		}
424 	} else {
425 		// Remove the goodies from the Menu() that should be in the SubMenu();
426 		TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu());
427 		if (sub != NULL) {
428 			TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
429 
430 			TWindowMenuItem* windowItem = NULL;
431 			int32 childIndex = parent->IndexOf(this) + 1;
432 			while (parent->SubmenuAt(childIndex) == NULL
433 				&& childIndex < parent->CountItems()) {
434 				windowItem = static_cast<TWindowMenuItem*>(
435 					parent->RemoveItem(childIndex));
436 				sub->AddItem(windowItem, 0);
437 				windowItem->SetExpanded(false);
438 			}
439 			sub->SetExpanded(false, 0);
440 
441 			if (resizeWindow)
442 				parent->SizeWindow(1);
443 		}
444 	}
445 }
446 
447 
448 TWindowMenuItem*
449 TTeamMenuItem::ExpandedWindowItem(int32 id)
450 {
451 	if (!fExpanded) {
452 		// Paranoia
453 		return NULL;
454 	}
455 
456 	TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
457 	int childIndex = parent->IndexOf(this) + 1;
458 
459 	while (!parent->SubmenuAt(childIndex)
460 		&& childIndex < parent->CountItems()) {
461 		TWindowMenuItem* item
462 			= static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex));
463 		if (item->ID() == id)
464 			return item;
465 
466 		childIndex++;
467 	}
468 	return NULL;
469 }
470 
471 
472 BRect
473 TTeamMenuItem::ExpanderBounds() const
474 {
475 	BRect bounds(Frame());
476 	bounds.left = bounds.right - kSwitchWidth;
477 	return bounds;
478 }
479 
480 
481 //	#pragma mark - Private methods
482 
483 
484 void
485 TTeamMenuItem::_Init(BList* team, BBitmap* icon, char* name, char* signature,
486 	float width, float height)
487 {
488 	if (sHPad == 0.0f) {
489 		// Initialize the padding values.
490 		sHPad = be_control_look->ComposeSpacing(B_USE_SMALL_SPACING);
491 		sVPad = ceilf(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 4.0f);
492 		sLabelOffset = ceilf((be_control_look->DefaultLabelSpacing() / 3.0f) * 4.0f);
493 	}
494 
495 	fTeam = team;
496 	fIcon = icon;
497 	fSignature = signature;
498 
499 	if (name == NULL) {
500 		char temp[32];
501 		snprintf(temp, sizeof(temp), "team %ld", (addr_t)team->ItemAt(0));
502 		name = strdup(temp);
503 	}
504 
505 	SetLabel(name);
506 
507 	fOverrideWidth = width;
508 	fOverrideHeight = height;
509 
510 	fBarView = static_cast<TBarApp*>(be_app)->BarView();
511 
512 	BFont font(be_plain_font);
513 	fLabelWidth = ceilf(font.StringWidth(name));
514 	font_height fontHeight;
515 	font.GetHeight(&fontHeight);
516 	fLabelAscent = ceilf(fontHeight.ascent);
517 	fLabelDescent = ceilf(fontHeight.descent + fontHeight.leading);
518 	fLabelHeight = fLabelAscent + fLabelDescent;
519 
520 	fOverriddenSelected = false;
521 
522 	fExpanded = false;
523 	fArrowDirection = BControlLook::B_RIGHT_ARROW;
524 }
525 
526 
527 bool
528 TTeamMenuItem::_IsSelected() const
529 {
530 	return IsSelected() || fOverriddenSelected;
531 }
532