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 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 82 TTeamMenuItem::TTeamMenuItem(float width, float height) 83 : 84 TTruncatableMenuItem("", NULL) 85 { 86 _Init(NULL, NULL, strdup(""), strdup(""), width, height); 87 } 88 89 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 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 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 187 TTeamMenuItem::SetOverrideSelected(bool selected) 188 { 189 fOverriddenSelected = selected; 190 Highlight(selected); 191 } 192 193 194 void 195 TTeamMenuItem::SetIcon(BBitmap* icon) { 196 delete fIcon; 197 fIcon = icon; 198 } 199 200 201 void 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 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 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 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 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* 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 476 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 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 536 TTeamMenuItem::_IsSelected() const 537 { 538 return IsSelected() || fOverriddenSelected; 539 } 540