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 <algorithm>
44
45 #include <Bitmap.h>
46 #include <ControlLook.h>
47 #include <Debug.h>
48 #include <Font.h>
49 #include <Mime.h>
50 #include <Region.h>
51 #include <Roster.h>
52 #include <Resources.h>
53 #include <Window.h>
54
55 #include "BarApp.h"
56 #include "BarMenuBar.h"
57 #include "BarView.h"
58 #include "ExpandoMenuBar.h"
59 #include "ResourceSet.h"
60 #include "ShowHideMenuItem.h"
61 #include "StatusView.h"
62 #include "TeamMenu.h"
63 #include "WindowMenu.h"
64 #include "WindowMenuItem.h"
65
66
67 static float sHPad, sVPad, sLabelOffset = 0.0f;
68
69
70 // #pragma mark - TTeamMenuItem
71
72
TTeamMenuItem(BList * team,BBitmap * icon,char * name,char * signature,float width,float height)73 TTeamMenuItem::TTeamMenuItem(BList* team, BBitmap* icon, char* name,
74 char* signature, float width, float height)
75 :
76 TTruncatableMenuItem(new TWindowMenu(team, signature))
77 {
78 _Init(team, icon, name, signature, width, height);
79 }
80
81
TTeamMenuItem(float width,float height)82 TTeamMenuItem::TTeamMenuItem(float width, float height)
83 :
84 TTruncatableMenuItem("", NULL)
85 {
86 _Init(NULL, NULL, strdup(""), strdup(""), width, height);
87 }
88
89
~TTeamMenuItem()90 TTeamMenuItem::~TTeamMenuItem()
91 {
92 delete fTeam;
93 delete fIcon;
94 free(fSignature);
95 }
96
97
98 /*! Vulcan Death Grip and other team mouse button handling
99
100 \returns true if handled, false otherwise
101 */
102 bool
HandleMouseDown(BPoint where)103 TTeamMenuItem::HandleMouseDown(BPoint where)
104 {
105 BMenu* menu = Menu();
106 if (menu == NULL)
107 return false;
108
109 BWindow* window = menu->Window();
110 if (window == NULL)
111 return false;
112
113 BMessage* message = window->CurrentMessage();
114 if (message == NULL)
115 return false;
116
117 int32 modifiers = 0;
118 int32 buttons = 0;
119 message->FindInt32("modifiers", &modifiers);
120 message->FindInt32("buttons", &buttons);
121
122 // check for three finger salute, a.k.a. Vulcan Death Grip
123 if ((modifiers & B_COMMAND_KEY) != 0
124 && (modifiers & B_CONTROL_KEY) != 0
125 && (modifiers & B_SHIFT_KEY) != 0) {
126 BMessage appMessage(B_SOME_APP_QUIT);
127 int32 teamCount = fTeam->CountItems();
128 BMessage quitMessage(teamCount == 1 ? B_SOME_APP_QUIT : kRemoveTeam);
129 quitMessage.AddInt32("itemIndex", menu->IndexOf(this));
130 for (int32 index = 0; index < teamCount; index++) {
131 team_id team = (addr_t)fTeam->ItemAt(index);
132 appMessage.AddInt32("be:team", team);
133 quitMessage.AddInt32("team", team);
134
135 kill_team(team);
136 }
137 be_app->PostMessage(&appMessage);
138 TExpandoMenuBar* expando = dynamic_cast<TExpandoMenuBar*>(menu);
139 window->PostMessage(&quitMessage, expando != NULL ? expando : menu);
140 return true;
141 } else if ((modifiers & B_SHIFT_KEY) == 0
142 && (buttons & B_TERTIARY_MOUSE_BUTTON) != 0) {
143 // launch new team
144 be_roster->Launch(Signature());
145 return true;
146 } else if ((modifiers & B_CONTROL_KEY) != 0) {
147 // control click - show all/hide all shortcut
148 BMessage showMessage((modifiers & B_SHIFT_KEY) != 0
149 ? kMinimizeTeam : kBringTeamToFront);
150 showMessage.AddInt32("itemIndex", menu->IndexOf(this));
151 window->PostMessage(&showMessage, menu);
152 return true;
153 }
154
155 return false;
156 }
157
158
159 status_t
Invoke(BMessage * message)160 TTeamMenuItem::Invoke(BMessage* message)
161 {
162 if (fBarView != NULL) {
163 if (fBarView->InvokeItem(Signature())) {
164 // handles drop on application
165 return B_OK;
166 }
167
168 // if the app could not handle the drag message
169 // and we were dragging, then kill the drag
170 // should never get here, disabled item will not invoke
171 if (fBarView->Dragging())
172 fBarView->DragStop();
173 }
174
175 // bring to front or minimize shortcuts
176 uint32 mods = modifiers();
177 if (mods & B_CONTROL_KEY) {
178 TShowHideMenuItem::TeamShowHideCommon((mods & B_SHIFT_KEY)
179 ? B_MINIMIZE_WINDOW : B_BRING_TO_FRONT, Teams());
180 }
181
182 return BMenuItem::Invoke(message);
183 }
184
185
186 void
SetOverrideSelected(bool selected)187 TTeamMenuItem::SetOverrideSelected(bool selected)
188 {
189 fOverriddenSelected = selected;
190 Highlight(selected);
191 }
192
193
194 void
SetIcon(BBitmap * icon)195 TTeamMenuItem::SetIcon(BBitmap* icon) {
196 delete fIcon;
197 fIcon = icon;
198 }
199
200
201 void
GetContentSize(float * width,float * height)202 TTeamMenuItem::GetContentSize(float* width, float* height)
203 {
204 BMenuItem::GetContentSize(width, height);
205
206 if (fOverrideWidth != -1.0f)
207 *width = fOverrideWidth;
208 else {
209 const float iconSize = static_cast<TBarApp*>(be_app)->TeamIconSize();
210 const float iconPadding = be_control_look->ComposeSpacing(kIconPadding);
211 float iconOnlyWidth = iconSize + iconPadding;
212 if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels)
213 iconOnlyWidth += iconPadding; // add an extra icon padding
214
215 if (fBarView->MiniState()) {
216 if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels)
217 *width = iconOnlyWidth;
218 else
219 *width = gMinimumWindowWidth - (gDragRegionWidth + kGutter) * 2;
220 } else if (!fBarView->Vertical()) {
221 TExpandoMenuBar* menu = static_cast<TExpandoMenuBar*>(Menu());
222 *width = menu->MaxHorizontalItemWidth();
223 } else
224 *width = static_cast<TBarApp*>(be_app)->Settings()->width;
225 }
226
227 if (fOverrideHeight != -1.0f)
228 *height = fOverrideHeight;
229 else
230 *height = fBarView->TeamMenuItemHeight();
231 }
232
233
234 void
Draw()235 TTeamMenuItem::Draw()
236 {
237 BRect frame = Frame();
238 BMenu* menu = Menu();
239
240 menu->PushState();
241
242 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR);
243 bool canHandle = !fBarView->Dragging()
244 || fBarView->AppCanHandleTypes(Signature());
245 uint32 flags = 0;
246 if (_IsSelected() && canHandle)
247 flags |= BControlLook::B_ACTIVATED;
248
249 uint32 borders = BControlLook::B_TOP_BORDER;
250 if (fBarView->Vertical()) {
251 menu->SetHighColor(tint_color(menuColor, B_DARKEN_1_TINT));
252 borders |= BControlLook::B_LEFT_BORDER
253 | BControlLook::B_RIGHT_BORDER;
254 menu->StrokeLine(frame.LeftBottom(), frame.RightBottom());
255 frame.bottom--;
256
257 be_control_look->DrawMenuBarBackground(menu, frame, frame,
258 menuColor, flags, borders);
259 } else {
260 if (flags & BControlLook::B_ACTIVATED)
261 menu->SetHighColor(tint_color(menuColor, B_DARKEN_3_TINT));
262 else
263 menu->SetHighColor(tint_color(menuColor, 1.22));
264 borders |= BControlLook::B_BOTTOM_BORDER;
265 menu->StrokeLine(frame.LeftTop(), frame.LeftBottom());
266 frame.left++;
267
268 be_control_look->DrawButtonBackground(menu, frame, frame,
269 menuColor, flags, borders);
270 }
271
272 menu->MovePenTo(ContentLocation());
273 DrawContent();
274
275 menu->PopState();
276 }
277
278
279 void
DrawContent()280 TTeamMenuItem::DrawContent()
281 {
282 BMenu* menu = Menu();
283 BRect frame = Frame();
284
285 if (fIcon != NULL) {
286 if (fIcon->ColorSpace() == B_RGBA32) {
287 menu->SetDrawingMode(B_OP_ALPHA);
288 menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
289 } else
290 menu->SetDrawingMode(B_OP_OVER);
291
292 BRect iconBounds = fIcon->Bounds();
293 BRect updateRect = iconBounds;
294 BPoint contentLocation = ContentLocation();
295 BPoint drawLocation = contentLocation + BPoint(sHPad, sVPad);
296 const int32 large = be_control_look->ComposeIconSize(B_LARGE_ICON)
297 .IntegerWidth() + 1;
298
299 if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels
300 || (fBarView->Vertical() && iconBounds.Width() > large)) {
301 // determine icon location (centered horizontally)
302 float offsetx = contentLocation.x
303 + floorf((frame.Width() - iconBounds.Width()) / 2);
304 float offsety = contentLocation.y + sVPad + kGutter;
305
306 // draw icon
307 updateRect.OffsetTo(BPoint(offsetx, offsety));
308 menu->DrawBitmapAsync(fIcon, updateRect);
309
310 // determine label position (below icon)
311 drawLocation.x = floorf((frame.Width() - fLabelWidth) / 2);
312 drawLocation.y = frame.top + sVPad + iconBounds.Height() + sVPad;
313 } else {
314 // determine icon location (centered vertically)
315 float offsetx = contentLocation.x + sHPad;
316 float offsety = contentLocation.y +
317 floorf((frame.Height() - iconBounds.Height()) / 2);
318
319 // draw icon
320 updateRect.OffsetTo(BPoint(offsetx, offsety));
321 menu->DrawBitmapAsync(fIcon, updateRect);
322
323 // determine label position (centered vertically)
324 drawLocation.x += iconBounds.Width() + sLabelOffset;
325 drawLocation.y = frame.top
326 + ceilf((frame.Height() - fLabelHeight) / 2);
327 }
328
329 menu->MovePenTo(drawLocation);
330 }
331
332 // override the drawing of the content when the item is disabled
333 // the wrong lowcolor is used when the item is disabled since the
334 // text color does not change
335 menu->SetDrawingMode(B_OP_OVER);
336 menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
337
338 bool canHandle = !fBarView->Dragging()
339 || fBarView->AppCanHandleTypes(Signature());
340 if (_IsSelected() && IsEnabled() && canHandle)
341 menu->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR),
342 B_HIGHLIGHT_BACKGROUND_TINT));
343 else
344 menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR));
345
346 if (IsSelected())
347 menu->SetHighColor(ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR));
348 else
349 menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR));
350
351 menu->MovePenBy(0, fLabelAscent);
352
353 // draw label
354 if (!static_cast<TBarApp*>(be_app)->Settings()->hideLabels) {
355 float labelWidth = menu->StringWidth(Label());
356 BPoint penLocation = menu->PenLocation();
357 // truncate to max width
358 float offset = penLocation.x - frame.left;
359 menu->DrawString(Label(labelWidth + offset));
360 }
361
362 // draw expander arrow
363 if (fBarView->Vertical()
364 && static_cast<TBarApp*>(be_app)->Settings()->superExpando
365 && fBarView->ExpandoState()) {
366 DrawExpanderArrow();
367 }
368 }
369
370
371 void
DrawExpanderArrow()372 TTeamMenuItem::DrawExpanderArrow()
373 {
374 BRect frame = Frame();
375 BRect rect(0.0f, 0.0f, kSwitchWidth, sHPad + 2.0f);
376 rect.OffsetTo(BPoint(frame.right - rect.Width(),
377 ContentLocation().y + ((frame.Height() - rect.Height()) / 2)));
378
379 float colorTint = B_DARKEN_3_TINT;
380 rgb_color bgColor = ui_color(B_MENU_BACKGROUND_COLOR);
381 if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3)
382 colorTint = B_LIGHTEN_2_TINT;
383
384 be_control_look->DrawArrowShape(Menu(), rect, Menu()->Frame(),
385 bgColor, fArrowDirection, 0, colorTint);
386 }
387
388
389 void
ToggleExpandState(bool resizeWindow)390 TTeamMenuItem::ToggleExpandState(bool resizeWindow)
391 {
392 fExpanded = !fExpanded;
393 fArrowDirection = fExpanded ? BControlLook::B_DOWN_ARROW
394 : BControlLook::B_RIGHT_ARROW;
395
396 if (fExpanded) {
397 // Populate Menu() with the stuff from SubMenu().
398 TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu()));
399 if (sub != NULL) {
400 // force the menu to update it's contents.
401 bool locked = sub->LockLooper();
402 // if locking the looper failed, the menu is just not visible
403 sub->AttachedToWindow();
404 if (locked)
405 sub->UnlockLooper();
406
407 if (sub->CountItems() > 1) {
408 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
409 int myindex = parent->IndexOf(this) + 1;
410
411 TWindowMenuItem* windowItem = NULL;
412 int32 childIndex = 0;
413 int32 totalChildren = sub->CountItems() - 4;
414 // hide, show, close, separator.
415 for (; childIndex < totalChildren; childIndex++) {
416 windowItem = static_cast<TWindowMenuItem*>
417 (sub->RemoveItem((int32)0));
418 parent->AddItem(windowItem, myindex + childIndex);
419 windowItem->SetExpanded(true);
420 }
421 sub->SetExpanded(true, myindex + childIndex);
422
423 if (resizeWindow)
424 parent->SizeWindow(-1);
425 }
426 }
427 } else {
428 // Remove the goodies from the Menu() that should be in the SubMenu();
429 TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu());
430 if (sub != NULL) {
431 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
432
433 TWindowMenuItem* windowItem = NULL;
434 int32 childIndex = parent->IndexOf(this) + 1;
435 while (parent->SubmenuAt(childIndex) == NULL
436 && childIndex < parent->CountItems()) {
437 windowItem = static_cast<TWindowMenuItem*>(
438 parent->RemoveItem(childIndex));
439 sub->AddItem(windowItem, 0);
440 windowItem->SetExpanded(false);
441 }
442 sub->SetExpanded(false, 0);
443
444 if (resizeWindow)
445 parent->SizeWindow(1);
446 }
447 }
448 }
449
450
451 TWindowMenuItem*
ExpandedWindowItem(int32 id)452 TTeamMenuItem::ExpandedWindowItem(int32 id)
453 {
454 if (!fExpanded) {
455 // Paranoia
456 return NULL;
457 }
458
459 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu());
460 int childIndex = parent->IndexOf(this) + 1;
461
462 while (!parent->SubmenuAt(childIndex)
463 && childIndex < parent->CountItems()) {
464 TWindowMenuItem* item
465 = static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex));
466 if (item->ID() == id)
467 return item;
468
469 childIndex++;
470 }
471 return NULL;
472 }
473
474
475 BRect
ExpanderBounds() const476 TTeamMenuItem::ExpanderBounds() const
477 {
478 BRect bounds(Frame());
479 bounds.left = bounds.right - kSwitchWidth;
480 return bounds;
481 }
482
483
484 // #pragma mark - Private methods
485
486
487 void
_Init(BList * team,BBitmap * icon,char * name,char * signature,float width,float height)488 TTeamMenuItem::_Init(BList* team, BBitmap* icon, char* name, char* signature,
489 float width, float height)
490 {
491 if (sHPad == 0.0f) {
492 // Initialize the padding values.
493 sHPad = be_control_look->ComposeSpacing(B_USE_SMALL_SPACING);
494 sVPad = ceilf(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 4.0f);
495 sLabelOffset = ceilf((be_control_look->DefaultLabelSpacing() / 3.0f) * 4.0f);
496 }
497
498 fTeam = team;
499 fIcon = icon;
500 fSignature = signature;
501
502 if (name == NULL) {
503 char temp[32];
504 snprintf(temp, sizeof(temp), "team %ld", (addr_t)team->ItemAt(0));
505 name = strdup(temp);
506 }
507
508 SetLabel(name);
509
510 fOverrideWidth = width;
511 fOverrideHeight = height;
512
513 fBarView = static_cast<TBarApp*>(be_app)->BarView();
514
515 // use menu font (parent font not available yet)
516 menu_info info;
517 get_menu_info(&info);
518 BFont font;
519 font.SetFamilyAndStyle(info.f_family, info.f_style);
520 font.SetSize(info.font_size);
521 fLabelWidth = ceilf(font.StringWidth(name));
522 font_height fontHeight;
523 font.GetHeight(&fontHeight);
524 fLabelAscent = ceilf(fontHeight.ascent);
525 fLabelDescent = ceilf(fontHeight.descent + fontHeight.leading);
526 fLabelHeight = fLabelAscent + fLabelDescent;
527
528 fOverriddenSelected = false;
529
530 fExpanded = false;
531 fArrowDirection = BControlLook::B_RIGHT_ARROW;
532 }
533
534
535 bool
_IsSelected() const536 TTeamMenuItem::_IsSelected() const
537 {
538 return IsSelected() || fOverriddenSelected;
539 }
540