xref: /haiku/src/apps/deskbar/TeamMenuItem.cpp (revision ed6250c95736c0b55da79d6e9dd01369532260c0)
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 		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 frame(Frame());
291 
292 		if (!fVertical)
293 			frame.top += 1;
294 
295 		BRect iconBounds(fIcon->Bounds());
296 		BRect dstRect(iconBounds);
297 		float extra = fVertical ? 0.0f : 1.0f;
298 		BPoint contLoc = ContentLocation();
299 		dstRect.OffsetTo(BPoint(contLoc.x + kHPad, contLoc.y +
300 			((frame.Height() - iconBounds.Height()) / 2) + extra));
301 		menu->DrawBitmapAsync(fIcon, dstRect);
302 
303 		menu->SetDrawingMode(B_OP_COPY);
304 
305 		float labelHeight = fLabelAscent + fLabelDescent;
306 		BPoint drawLoc = contLoc + BPoint(kHPad, kVPad);
307 		drawLoc.x += iconBounds.Width() + kLabelOffset;
308 		drawLoc.y = frame.top + ((frame.Height() - labelHeight) / 2) + 1.0f;
309 		menu->MovePenTo(drawLoc);
310 	}
311 
312 	//	set the pen to black so that either method will draw in the same color
313 	//	low color is set in inherited::DrawContent, override makes sure its what we want
314 	if (fDrawLabel) {
315 		menu->SetHighColor(0, 0, 0);
316 
317 		//	override the drawing of the content when the item is disabled
318 		//	the wrong lowcolor is used when the item is disabled since the
319 		//	text color does not change
320 		DrawContentLabel();
321 	}
322 
323 	// Draw the expandable icon.
324 	TBarView *barView = (static_cast<TBarApp *>(be_app))->BarView();
325 	if (fVertical && static_cast<TBarApp *>(be_app)->Settings()->superExpando
326 		&& barView->Expando()) {
327 		BRect frame(Frame());
328 		BRect rect(0, 0, kSwitchWidth, 10);
329 		rect.OffsetTo(BPoint(frame.right - rect.Width(),
330 			ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
331 
332 		rgb_color outlineColor = {80, 80, 80, 255};
333 		rgb_color middleColor = {200, 200, 200, 255};
334 
335 		menu->SetDrawingMode(B_OP_OVER);
336 
337 		if (!fExpanded) {
338 			menu->BeginLineArray(6);
339 
340 			menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
341 				BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
342 			menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
343 				BPoint(rect.left + 7, rect.top + 5), outlineColor);
344 			menu->AddLine(BPoint(rect.left + 7, rect.top + 5),
345 				BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
346 
347 			menu->AddLine(BPoint(rect.left + 4, rect.top + 3),
348 				BPoint(rect.left + 4, rect.bottom - 3), middleColor);
349 			menu->AddLine(BPoint(rect.left + 5, rect.top + 4),
350 				BPoint(rect.left + 5, rect.bottom - 4), middleColor);
351 			menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
352 				BPoint(rect.left + 6, rect.top + 5), middleColor);
353 			menu->EndLineArray();
354 		} else {
355 			// expanded state
356 
357 			menu->BeginLineArray(6);
358 			menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
359 				BPoint(rect.right - 3, rect.top + 3), outlineColor);
360 			menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
361 				BPoint(rect.left + 5, rect.top + 7), outlineColor);
362 			menu->AddLine(BPoint(rect.left + 5, rect.top + 7),
363 				BPoint(rect.right - 3, rect.top + 3), outlineColor);
364 
365 			menu->AddLine(BPoint(rect.left + 3, rect.top + 4),
366 				BPoint(rect.right - 5, rect.top + 4), middleColor);
367 			menu->AddLine(BPoint(rect.left + 4, rect.top + 5),
368 				BPoint(rect.right - 6, rect.top + 5), middleColor);
369 			menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
370 				BPoint(rect.left + 5, rect.top + 6), middleColor);
371 			menu->EndLineArray();
372 		}
373 	}
374 }
375 
376 
377 void
378 TTeamMenuItem::DrawContentLabel()
379 {
380 	BMenu *menu = Menu();
381 	menu->MovePenBy(0, fLabelAscent);
382 	menu->SetDrawingMode(B_OP_COPY);
383 
384 	float cachedWidth = menu->StringWidth(Label());
385 	if (Submenu() && fVertical)
386 		cachedWidth += 18;
387 
388 	const char *label = Label();
389 	char *truncLabel = NULL;
390 	float max = 0;
391 	if (static_cast<TBarApp *>(be_app)->Settings()->superExpando && fVertical)
392 		max = menu->MaxContentWidth() - kSwitchWidth;
393 	else
394 		max = menu->MaxContentWidth();
395 
396 	if (max > 0) {
397  		BPoint penloc = menu->PenLocation();
398 		BRect frame = Frame();
399 		float offset = penloc.x - frame.left;
400 	 	if (cachedWidth + offset > max) {
401 			truncLabel = (char *)malloc(strlen(label) + 4);
402 			if (!truncLabel)
403 				return;
404 			TruncateLabel(max-offset, truncLabel);
405 			label = truncLabel;
406 		}
407 	}
408 
409 	if (!label)
410 		label = Label();
411 
412 	TBarView *barview = (static_cast<TBarApp *>(be_app))->BarView();
413 	bool canHandle = !barview->Dragging() || barview->AppCanHandleTypes(Signature());
414 	if (IsSelected() && IsEnabled() && canHandle)
415 		menu->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
416 			B_HIGHLIGHT_BACKGROUND_TINT));
417 	else
418 		menu->SetLowColor(menu->ViewColor());
419 
420 	menu->DrawString(label);
421 
422 	if (truncLabel)
423 		free(truncLabel);
424 }
425 
426 
427 bool
428 TTeamMenuItem::IsExpanded()
429 {
430 	return fExpanded;
431 }
432 
433 
434 void
435 TTeamMenuItem::ToggleExpandState(bool resizeWindow)
436 {
437 	fExpanded = !fExpanded;
438 
439 	if (fExpanded) {
440 		// Populate Menu() with the stuff from SubMenu().
441 		TWindowMenu *sub = (static_cast<TWindowMenu *>(Submenu()));
442 		if (sub) {
443 			// force the menu to update it's contents.
444 			bool locked = sub->LockLooper();
445 				// if locking the looper failed, the menu is just not visible
446 			sub->AttachedToWindow();
447 			if (locked)
448 				sub->UnlockLooper();
449 
450 			if (sub->CountItems() > 1){
451 				TExpandoMenuBar *parent = static_cast<TExpandoMenuBar *>(Menu());
452 				int myindex = parent->IndexOf(this) + 1;
453 
454 				TWindowMenuItem *windowItem = NULL;
455 				int childIndex = 0;
456 				int totalChildren = sub->CountItems() - 4; // hide, show, close, separator.
457 				for (; childIndex < totalChildren; childIndex++) {
458 					windowItem = static_cast<TWindowMenuItem *>(sub->RemoveItem((int32)0));
459 					parent->AddItem(windowItem, myindex + childIndex);
460 					windowItem->ExpandedItem(true);
461 				}
462 				sub->SetExpanded(true, myindex + childIndex);
463 
464 				if (resizeWindow)
465 					parent->SizeWindow();
466 			} else
467 				fExpanded = fExpanded;
468 		}
469 	} else {
470 		// Remove the goodies from the Menu() that should be in the SubMenu();
471 		TWindowMenu *sub = static_cast<TWindowMenu *>(Submenu());
472 
473 		if (sub) {
474 			TExpandoMenuBar *parent = static_cast<TExpandoMenuBar *>(Menu());
475 
476 			TWindowMenuItem *windowItem = NULL;
477 			int childIndex = parent->IndexOf(this) + 1;
478 			while (!parent->SubmenuAt(childIndex) && childIndex < parent->CountItems()) {
479 				windowItem = static_cast<TWindowMenuItem *>(parent->RemoveItem(childIndex));
480 				sub->AddItem(windowItem, 0);
481 				windowItem->ExpandedItem(false);
482 			}
483 			sub->SetExpanded(false, 0);
484 
485 			if (resizeWindow)
486 				parent->SizeWindow();
487 		} else
488 			fExpanded = fExpanded;
489 	}
490 }
491 
492 
493 TWindowMenuItem*
494 TTeamMenuItem::ExpandedWindowItem(int32 id)
495 {
496 	if (!fExpanded)	// Paranoia
497 		return NULL;
498 
499 	TExpandoMenuBar *parent = static_cast<TExpandoMenuBar *>(Menu());
500 	int childIndex = parent->IndexOf(this) + 1;
501 
502 	while (!parent->SubmenuAt(childIndex) && childIndex < parent->CountItems()) {
503 		TWindowMenuItem *item = static_cast<TWindowMenuItem *>(parent->ItemAt(childIndex));
504 		if (item->ID() == id)
505 			return item;
506 
507 		childIndex++;
508 	}
509 	return NULL;
510 }
511 
512 
513 BRect
514 TTeamMenuItem::ExpanderBounds() const
515 {
516 	BRect bounds(Frame());
517 	bounds.left = bounds.right - kSwitchWidth;
518 	return bounds;
519 }
520 
521