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