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 <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 61 const float kHPad = 8.0f; 62 const float kVPad = 1.0f; 63 const float kLabelOffset = 8.0f; 64 const float kSwitchWidth = 12; 65 66 67 TTeamMenuItem::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 75 TTeamMenuItem::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 83 void 84 TTeamMenuItem::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 117 TTeamMenuItem::~TTeamMenuItem() 118 { 119 delete fTeam; 120 delete fIcon; 121 free(fName); 122 free(fSig); 123 } 124 125 126 status_t 127 TTeamMenuItem::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 151 void 152 TTeamMenuItem::SetOverrideWidth(float width) 153 { 154 fOverrideWidth = width; 155 } 156 157 158 void 159 TTeamMenuItem::SetOverrideHeight(float height) 160 { 161 fOverrideHeight = height; 162 } 163 164 165 void 166 TTeamMenuItem::SetOverrideSelected(bool selected) 167 { 168 fOverriddenSelected = selected; 169 Highlight(selected); 170 } 171 172 173 bool 174 TTeamMenuItem::HasLabel() const 175 { 176 return fDrawLabel; 177 } 178 179 180 void 181 TTeamMenuItem::SetHasLabel(bool drawLabel) 182 { 183 fDrawLabel = drawLabel; 184 } 185 186 187 float 188 TTeamMenuItem::LabelWidth() const 189 { 190 return fLabelWidth; 191 } 192 193 194 BList* 195 TTeamMenuItem::Teams() const 196 { 197 return fTeam; 198 } 199 200 201 const char* 202 TTeamMenuItem::Signature() const 203 { 204 return fSig; 205 } 206 207 208 const char* 209 TTeamMenuItem::Name() const 210 { 211 return fName; 212 } 213 214 215 void 216 TTeamMenuItem::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 250 void 251 TTeamMenuItem::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 354 void 355 TTeamMenuItem::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 472 void 473 TTeamMenuItem::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 522 bool 523 TTeamMenuItem::IsExpanded() 524 { 525 return fExpanded; 526 } 527 528 529 void 530 TTeamMenuItem::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 590 TWindowMenuItem* 591 TTeamMenuItem::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 614 BRect 615 TTeamMenuItem::ExpanderBounds() const 616 { 617 BRect bounds(Frame()); 618 bounds.left = bounds.right - kSwitchWidth; 619 return bounds; 620 } 621 622 623 bool 624 TTeamMenuItem::_IsSelected() const 625 { 626 return IsSelected() || fOverriddenSelected; 627 } 628