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 #include <Debug.h> 37 38 #include "TeamMenuItem.h" 39 40 #include <string.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 44 #include <Bitmap.h> 45 #include <ControlLook.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", (int32)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 fDrawLabel = drawLabel; 111 fVertical = vertical; 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 float 174 TTeamMenuItem::LabelWidth() const 175 { 176 return fLabelWidth; 177 } 178 179 180 BList* 181 TTeamMenuItem::Teams() const 182 { 183 return fTeam; 184 } 185 186 187 const char* 188 TTeamMenuItem::Signature() const 189 { 190 return fSig; 191 } 192 193 194 const char* 195 TTeamMenuItem::Name() const 196 { 197 return fName; 198 } 199 200 201 void 202 TTeamMenuItem::GetContentSize(float* width, float* height) 203 { 204 BRect iconBounds; 205 206 if (fIcon) 207 iconBounds = fIcon->Bounds(); 208 else 209 iconBounds = BRect(0, 0, 15, 15); 210 211 BMenuItem::GetContentSize(width, height); 212 213 if (fOverrideWidth != -1.0f) 214 *width = fOverrideWidth; 215 else 216 *width = kHPad + iconBounds.Width() + kLabelOffset + fLabelWidth + kHPad 217 + 20; 218 219 if (fOverrideHeight != -1.0f) 220 *height = fOverrideHeight; 221 else { 222 *height = iconBounds.Height(); 223 float labelHeight = fLabelAscent + fLabelDescent; 224 if (labelHeight > *height) 225 *height = labelHeight; 226 *height += (kVPad * 2) + 2; 227 } 228 *height += 2; 229 } 230 231 232 void 233 TTeamMenuItem::Draw() 234 { 235 BRect frame(Frame()); 236 BMenu* menu = Menu(); 237 menu->PushState(); 238 rgb_color menuColor = menu->LowColor(); 239 TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView(); 240 241 bool canHandle = !barview->Dragging() 242 || barview->AppCanHandleTypes(Signature()); 243 244 if (be_control_look != NULL) { 245 uint32 flags = 0; 246 if (_IsSelected() && canHandle) 247 flags |= BControlLook::B_ACTIVATED; 248 249 uint32 borders = BControlLook::B_TOP_BORDER; 250 if (fVertical) { 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 menu->PopState(); 275 return; 276 } 277 278 // if not selected or being tracked on, fill with gray 279 if ((!_IsSelected() && !menu->IsRedrawAfterSticky()) || !canHandle 280 || !IsEnabled()) { 281 frame.InsetBy(1, 1); 282 menu->SetHighColor(menuColor); 283 menu->FillRect(frame); 284 } 285 286 // draw the gray, unselected item, border 287 if (!_IsSelected() || !IsEnabled()) { 288 rgb_color shadow = tint_color(menuColor, B_DARKEN_1_TINT); 289 rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT); 290 291 frame = Frame(); 292 293 menu->SetHighColor(shadow); 294 if (fVertical) 295 menu->StrokeLine(frame.LeftBottom(), frame.RightBottom()); 296 else 297 menu->StrokeLine(frame.LeftBottom() + BPoint(1, 0), 298 frame.RightBottom()); 299 300 menu->StrokeLine(frame.RightBottom(), frame.RightTop()); 301 302 menu->SetHighColor(light); 303 menu->StrokeLine(frame.RightTop() + BPoint(-1, 0), frame.LeftTop()); 304 if (fVertical) 305 menu->StrokeLine(frame.LeftTop(), frame.LeftBottom() 306 + BPoint(0, -1)); 307 else 308 menu->StrokeLine(frame.LeftTop(), frame.LeftBottom()); 309 } 310 311 // if selected or being tracked on, fill with the hilite gray color 312 if (IsEnabled() && _IsSelected() && !menu->IsRedrawAfterSticky() 313 && canHandle) { 314 // fill 315 menu->SetHighColor(tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT)); 316 menu->FillRect(frame); 317 318 // these continue the dark grey border on the left or top edge 319 menu->SetHighColor(tint_color(menuColor, B_DARKEN_4_TINT)); 320 if (fVertical) { 321 // dark line at top 322 menu->StrokeLine(frame.LeftTop(), frame.RightTop()); 323 } else { 324 // dark line on the left 325 menu->StrokeLine(frame.LeftTop(), frame.LeftBottom()); 326 } 327 } else 328 menu->SetLowColor(menuColor); 329 330 menu->MovePenTo(ContentLocation()); 331 DrawContent(); 332 menu->PopState(); 333 } 334 335 336 void 337 TTeamMenuItem::DrawContent() 338 { 339 BMenu* menu = Menu(); 340 if (fIcon) { 341 if (fIcon->ColorSpace() == B_RGBA32) { 342 menu->SetDrawingMode(B_OP_ALPHA); 343 menu->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 344 } else 345 menu->SetDrawingMode(B_OP_OVER); 346 347 BRect frame(Frame()); 348 BRect iconBounds(fIcon->Bounds()); 349 BRect dstRect(iconBounds); 350 float extra = fVertical ? 0.0f : 1.0f; 351 BPoint contLoc = ContentLocation(); 352 353 dstRect.OffsetTo(BPoint(contLoc.x + kHPad, contLoc.y + 354 ((frame.Height() - iconBounds.Height()) / 2) + extra)); 355 menu->DrawBitmapAsync(fIcon, dstRect); 356 357 float labelHeight = fLabelAscent + fLabelDescent; 358 BPoint drawLoc = contLoc + BPoint(kHPad, kVPad); 359 drawLoc.x += iconBounds.Width() + kLabelOffset; 360 drawLoc.y = frame.top + ((frame.Height() - labelHeight) / 2) + 1.0f; 361 menu->MovePenTo(drawLoc); 362 } 363 364 // set the pen to black so that either method will draw in the same color 365 // low color is set in inherited::DrawContent, override makes sure its 366 // what we want 367 if (fDrawLabel) { 368 menu->SetDrawingMode(B_OP_OVER); 369 menu->SetHighColor(0, 0, 0); 370 371 // override the drawing of the content when the item is disabled 372 // the wrong lowcolor is used when the item is disabled since the 373 // text color does not change 374 DrawContentLabel(); 375 } 376 377 // Draw the expandable icon. 378 TBarView* barView = (static_cast<TBarApp*>(be_app))->BarView(); 379 if (fVertical && static_cast<TBarApp*>(be_app)->Settings()->superExpando 380 && barView->Expando()) { 381 BRect frame(Frame()); 382 BRect rect(0, 0, kSwitchWidth, 10); 383 rect.OffsetTo(BPoint(frame.right - rect.Width(), 384 ContentLocation().y + ((frame.Height() - rect.Height()) / 2))); 385 386 if (be_control_look != NULL) { 387 uint32 arrowDirection = fExpanded 388 ? BControlLook::B_UP_ARROW : BControlLook::B_DOWN_ARROW; 389 be_control_look->DrawArrowShape(menu, rect, rect, menu->LowColor(), 390 arrowDirection, 0, B_DARKEN_3_TINT); 391 } else { 392 rgb_color outlineColor = {80, 80, 80, 255}; 393 rgb_color middleColor = {200, 200, 200, 255}; 394 395 menu->SetDrawingMode(B_OP_OVER); 396 397 if (!fExpanded) { 398 menu->BeginLineArray(6); 399 400 menu->AddLine(BPoint(rect.left + 3, rect.top + 1), 401 BPoint(rect.left + 3, rect.bottom - 1), outlineColor); 402 menu->AddLine(BPoint(rect.left + 3, rect.top + 1), 403 BPoint(rect.left + 7, rect.top + 5), outlineColor); 404 menu->AddLine(BPoint(rect.left + 7, rect.top + 5), 405 BPoint(rect.left + 3, rect.bottom - 1), outlineColor); 406 407 menu->AddLine(BPoint(rect.left + 4, rect.top + 3), 408 BPoint(rect.left + 4, rect.bottom - 3), middleColor); 409 menu->AddLine(BPoint(rect.left + 5, rect.top + 4), 410 BPoint(rect.left + 5, rect.bottom - 4), middleColor); 411 menu->AddLine(BPoint(rect.left + 5, rect.top + 5), 412 BPoint(rect.left + 6, rect.top + 5), middleColor); 413 menu->EndLineArray(); 414 } else { 415 // expanded state 416 417 menu->BeginLineArray(6); 418 menu->AddLine(BPoint(rect.left + 1, rect.top + 3), 419 BPoint(rect.right - 3, rect.top + 3), outlineColor); 420 menu->AddLine(BPoint(rect.left + 1, rect.top + 3), 421 BPoint(rect.left + 5, rect.top + 7), outlineColor); 422 menu->AddLine(BPoint(rect.left + 5, rect.top + 7), 423 BPoint(rect.right - 3, rect.top + 3), outlineColor); 424 425 menu->AddLine(BPoint(rect.left + 3, rect.top + 4), 426 BPoint(rect.right - 5, rect.top + 4), middleColor); 427 menu->AddLine(BPoint(rect.left + 4, rect.top + 5), 428 BPoint(rect.right - 6, rect.top + 5), middleColor); 429 menu->AddLine(BPoint(rect.left + 5, rect.top + 5), 430 BPoint(rect.left + 5, rect.top + 6), middleColor); 431 menu->EndLineArray(); 432 } 433 } 434 } 435 } 436 437 438 void 439 TTeamMenuItem::DrawContentLabel() 440 { 441 BMenu* menu = Menu(); 442 menu->MovePenBy(0, fLabelAscent); 443 444 float cachedWidth = menu->StringWidth(Label()); 445 if (Submenu() && fVertical) 446 cachedWidth += 18; 447 448 const char* label = Label(); 449 char* truncLabel = NULL; 450 float max = 0; 451 452 if (static_cast<TBarApp*>(be_app)->Settings()->superExpando && fVertical) 453 max = menu->MaxContentWidth() - kSwitchWidth; 454 else 455 max = menu->MaxContentWidth(); 456 457 if (max > 0) { 458 BPoint penloc = menu->PenLocation(); 459 BRect frame = Frame(); 460 float offset = penloc.x - frame.left; 461 if (cachedWidth + offset > max) { 462 truncLabel = (char*)malloc(strlen(label) + 4); 463 if (!truncLabel) 464 return; 465 TruncateLabel(max-offset, truncLabel); 466 label = truncLabel; 467 } 468 } 469 470 if (!label) 471 label = Label(); 472 473 TBarView* barview = (static_cast<TBarApp*>(be_app))->BarView(); 474 bool canHandle = !barview->Dragging() 475 || barview->AppCanHandleTypes(Signature()); 476 if (_IsSelected() && IsEnabled() && canHandle) 477 menu->SetLowColor(tint_color(menu->ViewColor(), 478 B_HIGHLIGHT_BACKGROUND_TINT)); 479 else 480 menu->SetLowColor(menu->ViewColor()); 481 482 menu->DrawString(label); 483 484 free(truncLabel); 485 } 486 487 488 bool 489 TTeamMenuItem::IsExpanded() 490 { 491 return fExpanded; 492 } 493 494 495 void 496 TTeamMenuItem::ToggleExpandState(bool resizeWindow) 497 { 498 fExpanded = !fExpanded; 499 500 if (fExpanded) { 501 // Populate Menu() with the stuff from SubMenu(). 502 TWindowMenu* sub = (static_cast<TWindowMenu*>(Submenu())); 503 if (sub) { 504 // force the menu to update it's contents. 505 bool locked = sub->LockLooper(); 506 // if locking the looper failed, the menu is just not visible 507 sub->AttachedToWindow(); 508 if (locked) 509 sub->UnlockLooper(); 510 511 if (sub->CountItems() > 1) { 512 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu()); 513 int myindex = parent->IndexOf(this) + 1; 514 515 TWindowMenuItem* windowItem = NULL; 516 int childIndex = 0; 517 int totalChildren = sub->CountItems() - 4; 518 // hide, show, close, separator. 519 for (; childIndex < totalChildren; childIndex++) { 520 windowItem = static_cast<TWindowMenuItem*> 521 (sub->RemoveItem((int32)0)); 522 parent->AddItem(windowItem, myindex + childIndex); 523 windowItem->ExpandedItem(true); 524 } 525 sub->SetExpanded(true, myindex + childIndex); 526 527 if (resizeWindow) 528 parent->SizeWindow(); 529 } 530 } 531 } else { 532 // Remove the goodies from the Menu() that should be in the SubMenu(); 533 TWindowMenu* sub = static_cast<TWindowMenu*>(Submenu()); 534 535 if (sub) { 536 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu()); 537 538 TWindowMenuItem* windowItem = NULL; 539 int childIndex = parent->IndexOf(this) + 1; 540 while (!parent->SubmenuAt(childIndex) && childIndex 541 < parent->CountItems()) { 542 windowItem = static_cast<TWindowMenuItem*> 543 (parent->RemoveItem(childIndex)); 544 sub->AddItem(windowItem, 0); 545 windowItem->ExpandedItem(false); 546 } 547 sub->SetExpanded(false, 0); 548 549 if (resizeWindow) 550 parent->SizeWindow(); 551 } 552 } 553 } 554 555 556 TWindowMenuItem* 557 TTeamMenuItem::ExpandedWindowItem(int32 id) 558 { 559 if (!fExpanded) { 560 // Paranoia 561 return NULL; 562 } 563 564 TExpandoMenuBar* parent = static_cast<TExpandoMenuBar*>(Menu()); 565 int childIndex = parent->IndexOf(this) + 1; 566 567 while (!parent->SubmenuAt(childIndex) 568 && childIndex < parent->CountItems()) { 569 TWindowMenuItem* item 570 = static_cast<TWindowMenuItem*>(parent->ItemAt(childIndex)); 571 if (item->ID() == id) 572 return item; 573 574 childIndex++; 575 } 576 return NULL; 577 } 578 579 580 BRect 581 TTeamMenuItem::ExpanderBounds() const 582 { 583 BRect bounds(Frame()); 584 bounds.left = bounds.right - kSwitchWidth; 585 return bounds; 586 } 587 588 589 bool 590 TTeamMenuItem::_IsSelected() const 591 { 592 return IsSelected() || fOverriddenSelected; 593 } 594 595