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