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 bool hideLabels = static_cast<TBarApp*>(be_app)->Settings()->hideLabels; 210 float iconSize = static_cast<TBarApp*>(be_app)->IconSize(); 211 float iconOnlyWidth = (be_control_look->ComposeSpacing(kIconPadding) * 2) + iconSize; 212 213 if (fBarView->MiniState()) { 214 if (hideLabels) 215 *width = iconOnlyWidth; 216 else 217 *width = gMinimumWindowWidth - (gDragRegionWidth + kGutter) * 2; 218 } else if (!fBarView->Vertical()) { 219 TExpandoMenuBar* menu = static_cast<TExpandoMenuBar*>(Menu()); 220 *width = menu->MaxHorizontalItemWidth(); 221 } else 222 *width = static_cast<TBarApp*>(be_app)->Settings()->width; 223 } 224 225 if (fOverrideHeight != -1.0f) 226 *height = fOverrideHeight; 227 else 228 *height = fBarView->TeamMenuItemHeight(); 229 } 230 231 232 void 233 TTeamMenuItem::Draw() 234 { 235 BRect frame = Frame(); 236 BMenu* menu = Menu(); 237 238 menu->PushState(); 239 240 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR); 241 bool canHandle = !fBarView->Dragging() 242 || fBarView->AppCanHandleTypes(Signature()); 243 uint32 flags = 0; 244 if (_IsSelected() && canHandle) 245 flags |= BControlLook::B_ACTIVATED; 246 247 uint32 borders = BControlLook::B_TOP_BORDER; 248 if (fBarView->Vertical()) { 249 menu->SetHighColor(tint_color(menuColor, B_DARKEN_1_TINT)); 250 borders |= BControlLook::B_LEFT_BORDER 251 | BControlLook::B_RIGHT_BORDER; 252 menu->StrokeLine(frame.LeftBottom(), frame.RightBottom()); 253 frame.bottom--; 254 255 be_control_look->DrawMenuBarBackground(menu, frame, frame, 256 menuColor, flags, borders); 257 } else { 258 if (flags & BControlLook::B_ACTIVATED) 259 menu->SetHighColor(tint_color(menuColor, B_DARKEN_3_TINT)); 260 else 261 menu->SetHighColor(tint_color(menuColor, 1.22)); 262 borders |= BControlLook::B_BOTTOM_BORDER; 263 menu->StrokeLine(frame.LeftTop(), frame.LeftBottom()); 264 frame.left++; 265 266 be_control_look->DrawButtonBackground(menu, frame, frame, 267 menuColor, flags, borders); 268 } 269 270 menu->MovePenTo(ContentLocation()); 271 DrawContent(); 272 273 menu->PopState(); 274 } 275 276 277 void 278 TTeamMenuItem::DrawContent() 279 { 280 BMenu* menu = Menu(); 281 BRect frame = Frame(); 282 283 if (fIcon != NULL) { 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 iconBounds = fIcon != NULL ? fIcon->Bounds() 291 : BRect(0, 0, kMinimumIconSize - 1, kMinimumIconSize - 1); 292 BRect updateRect = iconBounds; 293 BPoint contentLocation = ContentLocation(); 294 BPoint drawLocation = contentLocation + BPoint(sHPad, sVPad); 295 296 if (static_cast<TBarApp*>(be_app)->Settings()->hideLabels 297 || (fBarView->Vertical() && iconBounds.Width() > 32)) { 298 // determine icon location (centered horizontally) 299 float offsetx = contentLocation.x 300 + floorf((frame.Width() - iconBounds.Width()) / 2); 301 float offsety = contentLocation.y + sVPad + kGutter; 302 303 // draw icon 304 updateRect.OffsetTo(BPoint(offsetx, offsety)); 305 menu->DrawBitmapAsync(fIcon, updateRect); 306 307 // determine label position (below icon) 308 drawLocation.x = floorf((frame.Width() - fLabelWidth) / 2); 309 drawLocation.y = frame.top + sVPad + iconBounds.Height() + sVPad; 310 } else { 311 // determine icon location (centered vertically) 312 float offsetx = contentLocation.x + sHPad; 313 float offsety = contentLocation.y + 314 floorf((frame.Height() - iconBounds.Height()) / 2); 315 316 // draw icon 317 updateRect.OffsetTo(BPoint(offsetx, offsety)); 318 menu->DrawBitmapAsync(fIcon, updateRect); 319 320 // determine label position (centered vertically) 321 drawLocation.x += iconBounds.Width() + sLabelOffset; 322 drawLocation.y = frame.top 323 + ceilf((frame.Height() - fLabelHeight) / 2); 324 } 325 326 menu->MovePenTo(drawLocation); 327 } 328 329 // override the drawing of the content when the item is disabled 330 // the wrong lowcolor is used when the item is disabled since the 331 // text color does not change 332 menu->SetDrawingMode(B_OP_OVER); 333 menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); 334 335 bool canHandle = !fBarView->Dragging() 336 || fBarView->AppCanHandleTypes(Signature()); 337 if (_IsSelected() && IsEnabled() && canHandle) 338 menu->SetLowColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 339 B_HIGHLIGHT_BACKGROUND_TINT)); 340 else 341 menu->SetLowColor(ui_color(B_MENU_BACKGROUND_COLOR)); 342 343 if (IsSelected()) 344 menu->SetHighColor(ui_color(B_MENU_SELECTED_ITEM_TEXT_COLOR)); 345 else 346 menu->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); 347 348 menu->MovePenBy(0, fLabelAscent); 349 350 // draw label 351 if (!static_cast<TBarApp*>(be_app)->Settings()->hideLabels) { 352 float labelWidth = menu->StringWidth(Label()); 353 BPoint penLocation = menu->PenLocation(); 354 // truncate to max width 355 float offset = penLocation.x - frame.left; 356 menu->DrawString(Label(labelWidth + offset)); 357 } 358 359 // draw expander arrow 360 if (fBarView->Vertical() 361 && static_cast<TBarApp*>(be_app)->Settings()->superExpando 362 && fBarView->ExpandoState()) { 363 DrawExpanderArrow(); 364 } 365 } 366 367 368 void 369 TTeamMenuItem::DrawExpanderArrow() 370 { 371 BRect frame = Frame(); 372 BRect rect(0.0f, 0.0f, kSwitchWidth, sHPad + 2.0f); 373 rect.OffsetTo(BPoint(frame.right - rect.Width(), 374 ContentLocation().y + ((frame.Height() - rect.Height()) / 2))); 375 376 float colorTint = B_DARKEN_3_TINT; 377 rgb_color bgColor = ui_color(B_MENU_BACKGROUND_COLOR); 378 if (bgColor.red + bgColor.green + bgColor.blue <= 128 * 3) 379 colorTint = B_LIGHTEN_2_TINT; 380 381 be_control_look->DrawArrowShape(Menu(), rect, Menu()->Frame(), 382 bgColor, fArrowDirection, 0, colorTint); 383 } 384 385 386 void 387 TTeamMenuItem::ToggleExpandState(bool resizeWindow) 388 { 389 fExpanded = !fExpanded; 390 fArrowDirection = fExpanded ? BControlLook::B_DOWN_ARROW 391 : BControlLook::B_RIGHT_ARROW; 392 393 if (fExpanded) { 394 // Populate Menu() with the stuff from SubMenu(). 395 TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu())); 396 if (sub != NULL) { 397 // force the menu to update it's contents. 398 bool locked = sub->LockLooper(); 399 // if locking the looper failed, the menu is just not visible 400 sub->AttachedToWindow(); 401 if (locked) 402 sub->UnlockLooper(); 403 404 if (sub->CountItems() > 1) { 405 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu()); 406 int myindex = parent->IndexOf(this) + 1; 407 408 TWindowMenuItem* windowItem = NULL; 409 int32 childIndex = 0; 410 int32 totalChildren = sub->CountItems() - 4; 411 // hide, show, close, separator. 412 for (; childIndex < totalChildren; childIndex++) { 413 windowItem = static_cast<TWindowMenuItem*> 414 (sub->RemoveItem((int32)0)); 415 parent->AddItem(windowItem, myindex + childIndex); 416 windowItem->SetExpanded(true); 417 } 418 sub->SetExpanded(true, myindex + childIndex); 419 420 if (resizeWindow) 421 parent->SizeWindow(-1); 422 } 423 } 424 } else { 425 // Remove the goodies from the Menu() that should be in the SubMenu(); 426 TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu()); 427 if (sub != NULL) { 428 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu()); 429 430 TWindowMenuItem* windowItem = NULL; 431 int32 childIndex = parent->IndexOf(this) + 1; 432 while (parent->SubmenuAt(childIndex) == NULL 433 && childIndex < parent->CountItems()) { 434 windowItem = static_cast<TWindowMenuItem*>( 435 parent->RemoveItem(childIndex)); 436 sub->AddItem(windowItem, 0); 437 windowItem->SetExpanded(false); 438 } 439 sub->SetExpanded(false, 0); 440 441 if (resizeWindow) 442 parent->SizeWindow(1); 443 } 444 } 445 } 446 447 448 TWindowMenuItem* 449 TTeamMenuItem::ExpandedWindowItem(int32 id) 450 { 451 if (!fExpanded) { 452 // Paranoia 453 return NULL; 454 } 455 456 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu()); 457 int childIndex = parent->IndexOf(this) + 1; 458 459 while (!parent->SubmenuAt(childIndex) 460 && childIndex < parent->CountItems()) { 461 TWindowMenuItem* item 462 = static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex)); 463 if (item->ID() == id) 464 return item; 465 466 childIndex++; 467 } 468 return NULL; 469 } 470 471 472 BRect 473 TTeamMenuItem::ExpanderBounds() const 474 { 475 BRect bounds(Frame()); 476 bounds.left = bounds.right - kSwitchWidth; 477 return bounds; 478 } 479 480 481 // #pragma mark - Private methods 482 483 484 void 485 TTeamMenuItem::_Init(BList* team, BBitmap* icon, char* name, char* signature, 486 float width, float height) 487 { 488 if (sHPad == 0.0f) { 489 // Initialize the padding values. 490 sHPad = be_control_look->ComposeSpacing(B_USE_SMALL_SPACING); 491 sVPad = ceilf(be_control_look->ComposeSpacing(B_USE_SMALL_SPACING) / 4.0f); 492 sLabelOffset = ceilf((be_control_look->DefaultLabelSpacing() / 3.0f) * 4.0f); 493 } 494 495 fTeam = team; 496 fIcon = icon; 497 fSignature = signature; 498 499 if (name == NULL) { 500 char temp[32]; 501 snprintf(temp, sizeof(temp), "team %ld", (addr_t)team->ItemAt(0)); 502 name = strdup(temp); 503 } 504 505 SetLabel(name); 506 507 fOverrideWidth = width; 508 fOverrideHeight = height; 509 510 fBarView = static_cast<TBarApp*>(be_app)->BarView(); 511 512 BFont font(be_plain_font); 513 fLabelWidth = ceilf(font.StringWidth(name)); 514 font_height fontHeight; 515 font.GetHeight(&fontHeight); 516 fLabelAscent = ceilf(fontHeight.ascent); 517 fLabelDescent = ceilf(fontHeight.descent + fontHeight.leading); 518 fLabelHeight = fLabelAscent + fLabelDescent; 519 520 fOverriddenSelected = false; 521 522 fExpanded = false; 523 fArrowDirection = BControlLook::B_RIGHT_ARROW; 524 } 525 526 527 bool 528 TTeamMenuItem::_IsSelected() const 529 { 530 return IsSelected() || fOverriddenSelected; 531 } 532