1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
30trademarks of Be Incorporated in the United States and other countries. Other
31brand product names are registered trademarks or trademarks of their respective
32holders.
33All 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 "ExpandoMenuBar.h"
54#include "ResourceSet.h"
55#include "ShowHideMenuItem.h"
56#include "TeamMenu.h"
57#include "WindowMenu.h"
58#include "WindowMenuItem.h"
59
60
61const float kHPad = 8.0f;
62const float kVPad = 1.0f;
63const float kLabelOffset = 8.0f;
64const float kSwitchWidth = 12;
65
66
67TTeamMenuItem::TTeamMenuItem(BList* team, BBitmap* icon, char* name, char* sig,
68	float width, float height, bool drawLabel, bool vertical)
69	:	BMenuItem(new TWindowMenu(team, sig))
70{
71	InitData(team, icon, name, sig, width, height, drawLabel, vertical);
72}
73
74
75TTeamMenuItem::TTeamMenuItem(float width, float height, bool vertical)
76	:	BMenuItem("", NULL)
77{
78	InitData(NULL, NULL, strdup(""), strdup(""), width, height, false,
79		vertical);
80}
81
82
83void
84TTeamMenuItem::InitData(BList* team, BBitmap* icon, char* name, char* sig,
85	float width, float height, bool drawLabel, bool vertical)
86{
87	fTeam = team;
88	fIcon = icon;
89	fName = name;
90	fSig = sig;
91	if (fName == NULL) {
92		char temp[32];
93		snprintf(temp, sizeof(temp), "team %ld", (addr_t)team->ItemAt(0));
94		fName = strdup(temp);
95	}
96
97	SetLabel(fName);
98
99	BFont font(be_plain_font);
100	fLabelWidth = ceilf(font.StringWidth(fName));
101	font_height fontHeight;
102	font.GetHeight(&fontHeight);
103	fLabelAscent = ceilf(fontHeight.ascent);
104	fLabelDescent = ceilf(fontHeight.descent + fontHeight.leading);
105
106	fOverrideWidth = width;
107	fOverrideHeight = height;
108	fOverriddenSelected = false;
109
110	fVertical = vertical;
111	fDrawLabel = drawLabel;
112
113	fExpanded = false;
114}
115
116
117TTeamMenuItem::~TTeamMenuItem()
118{
119	delete fTeam;
120	delete fIcon;
121	free(fName);
122	free(fSig);
123}
124
125
126status_t
127TTeamMenuItem::Invoke(BMessage* message)
128{
129	if ((static_cast<TBarApp*>(be_app))->BarView()->InvokeItem(Signature()))
130		// handles drop on application
131		return B_OK;
132
133	// if the app could not handle the drag message
134	// and we were dragging, then kill the drag
135	// should never get here, disabled item will not invoke
136	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
137	if (barView && barView->Dragging())
138		barView->DragStop();
139
140	// bring to front or minimize shortcuts
141	uint32 mods = modifiers();
142	if (mods & B_CONTROL_KEY) {
143		TShowHideMenuItem::TeamShowHideCommon((mods & B_SHIFT_KEY)
144			? B_MINIMIZE_WINDOW : B_BRING_TO_FRONT, Teams());
145	}
146
147	return BMenuItem::Invoke(message);
148}
149
150
151void
152TTeamMenuItem::SetOverrideWidth(float width)
153{
154	fOverrideWidth = width;
155}
156
157
158void
159TTeamMenuItem::SetOverrideHeight(float height)
160{
161	fOverrideHeight = height;
162}
163
164
165void
166TTeamMenuItem::SetOverrideSelected(bool selected)
167{
168	fOverriddenSelected = selected;
169	Highlight(selected);
170}
171
172
173bool
174TTeamMenuItem::HasLabel() const
175{
176	return fDrawLabel;
177}
178
179
180void
181TTeamMenuItem::SetHasLabel(bool drawLabel)
182{
183	fDrawLabel = drawLabel;
184}
185
186
187float
188TTeamMenuItem::LabelWidth() const
189{
190	return fLabelWidth;
191}
192
193
194BList*
195TTeamMenuItem::Teams() const
196{
197	return fTeam;
198}
199
200
201const char*
202TTeamMenuItem::Signature() const
203{
204	return fSig;
205}
206
207
208const char*
209TTeamMenuItem::Name() const
210{
211	return fName;
212}
213
214
215void
216TTeamMenuItem::GetContentSize(float* width, float* height)
217{
218	BRect iconBounds;
219
220	if (fIcon)
221		iconBounds = fIcon->Bounds();
222	else
223		iconBounds = BRect(0, 0, kMinimumIconSize - 1, kMinimumIconSize - 1);
224
225	BMenuItem::GetContentSize(width, height);
226
227	if (fOverrideWidth != -1.0f)
228		*width = fOverrideWidth;
229	else {
230		*width = kHPad + iconBounds.Width() + kHPad;
231		if (iconBounds.Width() <= 32 && fDrawLabel)
232			*width += LabelWidth() + kHPad;
233	}
234
235	if (fOverrideHeight != -1.0f)
236		*height = fOverrideHeight;
237	else {
238		if (fVertical) {
239			*height = iconBounds.Height() + kVPad * 4;
240			if (fDrawLabel && iconBounds.Width() > 32)
241				*height += fLabelAscent + fLabelDescent;
242		} else {
243			*height = iconBounds.Height() + kVPad * 4;
244		}
245	}
246	*height += 2;
247}
248
249
250void
251TTeamMenuItem::Draw()
252{
253	BRect frame(Frame());
254	BMenu* menu = Menu();
255	menu->PushState();
256	rgb_color menuColor = menu->LowColor();
257	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
258
259	bool canHandle = !barView->Dragging()
260		|| barView->AppCanHandleTypes(Signature());
261
262	if (be_control_look != NULL) {
263		uint32 flags = 0;
264		if (_IsSelected() && canHandle)
265			flags |= BControlLook::B_ACTIVATED;
266
267		uint32 borders = BControlLook::B_TOP_BORDER;
268		if (fVertical) {
269			menu->SetHighColor(tint_color(menuColor, B_DARKEN_1_TINT));
270			borders |= BControlLook::B_LEFT_BORDER
271				| BControlLook::B_RIGHT_BORDER;
272			menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
273			frame.bottom--;
274
275			be_control_look->DrawMenuBarBackground(menu, frame, frame,
276				menuColor, flags, borders);
277		} else {
278			if (flags & BControlLook::B_ACTIVATED)
279				menu->SetHighColor(tint_color(menuColor, B_DARKEN_3_TINT));
280			else
281				menu->SetHighColor(tint_color(menuColor, 1.22));
282			borders |= BControlLook::B_BOTTOM_BORDER;
283			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
284			frame.left++;
285
286			be_control_look->DrawButtonBackground(menu, frame, frame,
287				menuColor, flags, borders);
288		}
289
290		menu->MovePenTo(ContentLocation());
291		DrawContent();
292		menu->PopState();
293		return;
294	}
295
296	// if not selected or being tracked on, fill with gray
297	if ((!_IsSelected() && !menu->IsRedrawAfterSticky()) || !canHandle
298		|| !IsEnabled()) {
299		frame.InsetBy(1, 1);
300		menu->SetHighColor(menuColor);
301		menu->FillRect(frame);
302	}
303
304	// draw the gray, unselected item, border
305	if (!_IsSelected() || !IsEnabled()) {
306		rgb_color shadow = tint_color(menuColor, B_DARKEN_1_TINT);
307		rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT);
308
309		frame = Frame();
310
311		menu->SetHighColor(shadow);
312		if (fVertical)
313			menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
314		else
315			menu->StrokeLine(frame.LeftBottom() + BPoint(1, 0),
316				frame.RightBottom());
317
318		menu->StrokeLine(frame.RightBottom(), frame.RightTop());
319
320		menu->SetHighColor(light);
321		menu->StrokeLine(frame.RightTop() + BPoint(-1, 0), frame.LeftTop());
322		if (fVertical)
323			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom()
324				+ BPoint(0, -1));
325		else
326			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
327	}
328
329	// if selected or being tracked on, fill with the hilite gray color
330	if (IsEnabled() && _IsSelected() && !menu->IsRedrawAfterSticky()
331		&& canHandle) {
332		// fill
333		menu->SetHighColor(tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT));
334		menu->FillRect(frame);
335
336		// these continue the dark grey border on the left or top edge
337		menu->SetHighColor(tint_color(menuColor, B_DARKEN_4_TINT));
338		if (fVertical) {
339			// dark line at top
340			menu->StrokeLine(frame.LeftTop(), frame.RightTop());
341		} else {
342			// dark line on the left
343			menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
344		}
345	} else
346		menu->SetLowColor(menuColor);
347
348	menu->MovePenTo(ContentLocation());
349	DrawContent();
350	menu->PopState();
351}
352
353
354void
355TTeamMenuItem::DrawContent()
356{
357	BMenu* menu = Menu();
358	if (fIcon != NULL) {
359		if (fIcon->ColorSpace() == B_RGBA32) {
360			menu->SetDrawingMode(B_OP_ALPHA);
361			menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
362		} else
363			menu->SetDrawingMode(B_OP_OVER);
364
365		BRect frame(Frame());
366		BRect iconBounds(fIcon->Bounds());
367		BRect dstRect(iconBounds);
368		float extra = fVertical ? 0.0f : -1.0f;
369		BPoint contLoc = ContentLocation();
370		BPoint drawLoc = contLoc + BPoint(kHPad, kVPad);
371
372		if (!fDrawLabel || (fVertical && iconBounds.Width() > 32)) {
373			float offsetx = contLoc.x
374				+ ((frame.Width() - iconBounds.Width()) / 2) + extra;
375			float offsety = contLoc.y + 3.0f + extra;
376
377			dstRect.OffsetTo(BPoint(offsetx, offsety));
378			menu->DrawBitmapAsync(fIcon, dstRect);
379
380			drawLoc.x = ((frame.Width() - LabelWidth()) / 2);
381			drawLoc.y = frame.top + iconBounds.Height() + 4.0f;
382		} else {
383			float offsetx = contLoc.x + kHPad;
384			float offsety = contLoc.y +
385				((frame.Height() - iconBounds.Height()) / 2) + extra;
386
387			dstRect.OffsetTo(BPoint(offsetx, offsety));
388			menu->DrawBitmapAsync(fIcon, dstRect);
389
390			float labelHeight = fLabelAscent + fLabelDescent;
391			drawLoc.x += iconBounds.Width() + kLabelOffset;
392			drawLoc.y = frame.top + ((frame.Height() - labelHeight) / 2) + extra;
393		}
394
395		menu->MovePenTo(drawLoc);
396	}
397
398	// set the pen to black so that either method will draw in the same color
399	// low color is set in inherited::DrawContent, override makes sure its
400	// what we want
401	if (fDrawLabel) {
402		menu->SetDrawingMode(B_OP_OVER);
403		menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
404
405		// override the drawing of the content when the item is disabled
406		// the wrong lowcolor is used when the item is disabled since the
407		// text color does not change
408		DrawContentLabel();
409	}
410
411	// Draw the expandable icon.
412	TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView();
413	if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando
414		&& barView->ExpandoState()) {
415		BRect frame(Frame());
416		BRect rect(0, 0, kSwitchWidth, 10);
417		rect.OffsetTo(BPoint(frame.right - rect.Width(),
418			ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
419
420		if (be_control_look != NULL) {
421			uint32 arrowDirection = fExpanded
422				? BControlLook::B_UP_ARROW : BControlLook::B_DOWN_ARROW;
423			be_control_look->DrawArrowShape(menu, rect, rect, menu->LowColor(),
424				arrowDirection, 0, B_DARKEN_3_TINT);
425		} else {
426			rgb_color outlineColor = {80, 80, 80, 255};
427			rgb_color middleColor = {200, 200, 200, 255};
428
429			menu->SetDrawingMode(B_OP_OVER);
430
431			if (!fExpanded) {
432				menu->BeginLineArray(6);
433
434				menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
435					BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
436				menu->AddLine(BPoint(rect.left + 3, rect.top + 1),
437					BPoint(rect.left + 7, rect.top + 5), outlineColor);
438				menu->AddLine(BPoint(rect.left + 7, rect.top + 5),
439					BPoint(rect.left + 3, rect.bottom - 1), outlineColor);
440
441				menu->AddLine(BPoint(rect.left + 4, rect.top + 3),
442					BPoint(rect.left + 4, rect.bottom - 3), middleColor);
443				menu->AddLine(BPoint(rect.left + 5, rect.top + 4),
444					BPoint(rect.left + 5, rect.bottom - 4), middleColor);
445				menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
446					BPoint(rect.left + 6, rect.top + 5), middleColor);
447				menu->EndLineArray();
448			} else {
449				// expanded state
450
451				menu->BeginLineArray(6);
452				menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
453					BPoint(rect.right - 3, rect.top + 3), outlineColor);
454				menu->AddLine(BPoint(rect.left + 1, rect.top + 3),
455					BPoint(rect.left + 5, rect.top + 7), outlineColor);
456				menu->AddLine(BPoint(rect.left + 5, rect.top + 7),
457					BPoint(rect.right - 3, rect.top + 3), outlineColor);
458
459				menu->AddLine(BPoint(rect.left + 3, rect.top + 4),
460					BPoint(rect.right - 5, rect.top + 4), middleColor);
461				menu->AddLine(BPoint(rect.left + 4, rect.top + 5),
462					BPoint(rect.right - 6, rect.top + 5), middleColor);
463				menu->AddLine(BPoint(rect.left + 5, rect.top + 5),
464					BPoint(rect.left + 5, rect.top + 6), middleColor);
465				menu->EndLineArray();
466			}
467		}
468	}
469}
470
471
472void
473TTeamMenuItem::DrawContentLabel()
474{
475	BMenu* menu = Menu();
476	menu->MovePenBy(0, fLabelAscent);
477
478	float cachedWidth = menu->StringWidth(Label());
479	if (Submenu() && fVertical)
480		cachedWidth += 18;
481
482	const char* label = Label();
483	char* truncLabel = NULL;
484	float max = 0;
485
486	if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando)
487		max = menu->MaxContentWidth() - kSwitchWidth;
488	else
489		max = menu->MaxContentWidth() - 4.0f;
490
491	if (max > 0) {
492		BPoint penloc = menu->PenLocation();
493		BRect frame = Frame();
494		float offset = penloc.x - frame.left;
495		if (cachedWidth + offset > max) {
496			truncLabel = (char*)malloc(strlen(label) + 4);
497			if (!truncLabel)
498				return;
499			TruncateLabel(max-offset, truncLabel);
500			label = truncLabel;
501		}
502	}
503
504	if (!label)
505		label = Label();
506
507	TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView();
508	bool canHandle = !barview->Dragging()
509		|| barview->AppCanHandleTypes(Signature());
510	if (_IsSelected() && IsEnabled() && canHandle)
511		menu->SetLowColor(tint_color(menu->LowColor(),
512			B_HIGHLIGHT_BACKGROUND_TINT));
513	else
514		menu->SetLowColor(menu->LowColor());
515
516	menu->DrawString(label);
517
518	free(truncLabel);
519}
520
521
522bool
523TTeamMenuItem::IsExpanded()
524{
525	return fExpanded;
526}
527
528
529void
530TTeamMenuItem::ToggleExpandState(bool resizeWindow)
531{
532	fExpanded = !fExpanded;
533
534	if (fExpanded) {
535		// Populate Menu() with the stuff from SubMenu().
536		TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu()));
537		if (sub) {
538			// force the menu to update it's contents.
539			bool locked = sub->LockLooper();
540				// if locking the looper failed, the menu is just not visible
541			sub->AttachedToWindow();
542			if (locked)
543				sub->UnlockLooper();
544
545			if (sub->CountItems() > 1) {
546				TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
547				int myindex = parent->IndexOf(this) + 1;
548
549				TWindowMenuItem* windowItem = NULL;
550				int childIndex = 0;
551				int totalChildren = sub->CountItems() - 4;
552					// hide, show, close, separator.
553				for (; childIndex < totalChildren; childIndex++) {
554					windowItem = static_cast<TWindowMenuItem*>
555						(sub->RemoveItem((int32)0));
556					parent->AddItem(windowItem, myindex + childIndex);
557					windowItem->ExpandedItem(true);
558				}
559				sub->SetExpanded(true, myindex + childIndex);
560
561				if (resizeWindow)
562					parent->SizeWindow(-1);
563			}
564		}
565	} else {
566		// Remove the goodies from the Menu() that should be in the SubMenu();
567		TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu());
568
569		if (sub) {
570			TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
571
572			TWindowMenuItem* windowItem = NULL;
573			int childIndex = parent->IndexOf(this) + 1;
574			while (!parent->SubmenuAt(childIndex) && childIndex
575				< parent->CountItems()) {
576				windowItem = static_cast<TWindowMenuItem*>
577					(parent->RemoveItem(childIndex));
578				sub->AddItem(windowItem, 0);
579				windowItem->ExpandedItem(false);
580			}
581			sub->SetExpanded(false, 0);
582
583			if (resizeWindow)
584				parent->SizeWindow(1);
585		}
586	}
587}
588
589
590TWindowMenuItem*
591TTeamMenuItem::ExpandedWindowItem(int32 id)
592{
593	if (!fExpanded) {
594		// Paranoia
595		return NULL;
596	}
597
598	TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
599	int childIndex = parent->IndexOf(this) + 1;
600
601	while (!parent->SubmenuAt(childIndex)
602		&& childIndex < parent->CountItems()) {
603		TWindowMenuItem* item
604			= static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex));
605		if (item->ID() == id)
606			return item;
607
608		childIndex++;
609	}
610	return NULL;
611}
612
613
614BRect
615TTeamMenuItem::ExpanderBounds() const
616{
617	BRect bounds(Frame());
618	bounds.left = bounds.right - kSwitchWidth;
619	return bounds;
620}
621
622
623bool
624TTeamMenuItem::_IsSelected() const
625{
626	return IsSelected() || fOverriddenSelected;
627}
628