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