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 "StatusView.h" 38 39 #include <errno.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <time.h> 43 #include <unistd.h> 44 #include <algorithm> 45 46 #include <fs_index.h> 47 #include <fs_info.h> 48 49 #include <Application.h> 50 #include <Beep.h> 51 #include <Bitmap.h> 52 #include <Catalog.h> 53 #include <ControlLook.h> 54 #include <Debug.h> 55 #include <Directory.h> 56 #include <FindDirectory.h> 57 #include <Locale.h> 58 #include <MenuItem.h> 59 #include <NodeInfo.h> 60 #include <NodeMonitor.h> 61 #include <Path.h> 62 #include <PopUpMenu.h> 63 #include <Roster.h> 64 #include <Screen.h> 65 #include <Volume.h> 66 #include <VolumeRoster.h> 67 #include <Window.h> 68 69 #include "icons.h" 70 71 #include "BarApp.h" 72 #include "BarMenuBar.h" 73 #include "DeskbarUtils.h" 74 #include "ExpandoMenuBar.h" 75 #include "ResourceSet.h" 76 #include "StatusViewShelf.h" 77 #include "TimeView.h" 78 79 80 static const float kVerticalMiniMultiplier = 2.9f; 81 82 float sIconGap = 0.0f; 83 float gDragWidth, gDragRegionWidth = 0.0f; 84 float gMinReplicantHeight, gMinReplicantWidth = 0.0f; 85 float gMinimumTrayWidth, gMinimumWindowWidth, gMaximumWindowWidth = 0.0f; 86 87 88 #ifdef DB_ADDONS 89 // Add-on support 90 // 91 // Item - internal item list (node, eref, etc) 92 // Icon - physical replicant handed to the DeskbarClass class 93 // AddOn - attribute based add-on 94 95 const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item"; 96 const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry"; 97 const char* const kReplicantSettingsFile = "replicants"; 98 const char* const kReplicantPathField = "replicant_path"; 99 100 101 static void 102 DumpItem(DeskbarItemInfo* item) 103 { 104 printf("is addon: %i, id: %" B_PRId32 "\n", item->isAddOn, item->id); 105 printf("entry_ref: %" B_PRIdDEV ", %" B_PRIdINO ", %s\n", 106 item->entryRef.device, item->entryRef.directory, item->entryRef.name); 107 printf("node_ref: %" B_PRIdDEV ", %" B_PRIdINO "\n", item->nodeRef.device, 108 item->nodeRef.node); 109 } 110 111 112 static void 113 DumpList(BList* itemlist) 114 { 115 int32 count = itemlist->CountItems() - 1; 116 if (count < 0) { 117 printf("no items in list\n"); 118 return; 119 } 120 for (int32 i = count; i >= 0; i--) { 121 DeskbarItemInfo* item = (DeskbarItemInfo*)itemlist->ItemAt(i); 122 if (!item) 123 continue; 124 125 DumpItem(item); 126 } 127 } 128 #endif /* DB_ADDONS */ 129 130 131 #undef B_TRANSLATION_CONTEXT 132 #define B_TRANSLATION_CONTEXT "Tray" 133 134 // don't change the name of this view to anything other than "Status"! 135 136 TReplicantTray::TReplicantTray(TBarView* barView) 137 : 138 BView(BRect(0, 0, 1, 1), "Status", B_FOLLOW_LEFT | B_FOLLOW_TOP, 139 B_WILL_DRAW | B_FRAME_EVENTS), 140 fTime(NULL), 141 fBarView(barView), 142 fShelf(new TReplicantShelf(this)), 143 fMinimumTrayWidth(gMinimumTrayWidth), 144 fTrayPadding(3.0f), 145 fClockMargin(12.0f), 146 fAlignmentSupport(false) 147 { 148 // scale replicants by font size 149 fMaxReplicantHeight = std::max(gMinReplicantHeight, 150 float(((TBarApp*)be_app)->IconSize())); 151 // but not bigger than TabHeight which depends on be_bold_font 152 // TODO this should only apply to mini-mode but we set it once here for all 153 fMaxReplicantHeight = std::min(fMaxReplicantHeight, 154 fBarView->TabHeight() - 4); 155 // TODO: depends on window size... (so use something like 156 // max(129, height * 3), and restrict the minimum window width for it) 157 // Use bold font because it depends on the window tab height. 158 fMaxReplicantWidth = 129; 159 160 fMinTrayHeight = kGutter + fMaxReplicantHeight + kGutter; 161 if (fBarView != NULL && fBarView->Vertical() 162 && (fBarView->ExpandoState() || fBarView->FullState())) { 163 fMinimumTrayWidth = gMinimumWindowWidth - kGutter - gDragRegionWidth; 164 } 165 166 // Create the time view 167 fTime = new TTimeView(fMinimumTrayWidth, fMaxReplicantHeight - 1.0, 168 fBarView); 169 } 170 171 172 TReplicantTray::~TReplicantTray() 173 { 174 delete fShelf; 175 delete fTime; 176 } 177 178 179 void 180 TReplicantTray::AttachedToWindow() 181 { 182 BView::AttachedToWindow(); 183 184 if (be_control_look != NULL) { 185 AdoptParentColors(); 186 } else { 187 SetViewUIColor(B_MENU_BACKGROUND_COLOR, B_DARKEN_1_TINT); 188 } 189 SetDrawingMode(B_OP_COPY); 190 191 Window()->SetPulseRate(1000000); 192 193 fTrayPadding = ceilf(be_control_look->ComposeSpacing(kTrayPadding) / 2); 194 fClockMargin = fTrayPadding * 4; 195 196 clock_settings* clock = ((TBarApp*)be_app)->ClockSettings(); 197 fTime->SetShowSeconds(clock->showSeconds); 198 fTime->SetShowDayOfWeek(clock->showDayOfWeek); 199 fTime->SetShowTimeZone(clock->showTimeZone); 200 201 AddChild(fTime); 202 203 fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - fTrayPadding, 2); 204 // will be moved into place later 205 206 if (!((TBarApp*)be_app)->Settings()->showClock) 207 fTime->Hide(); 208 209 #ifdef DB_ADDONS 210 // load addons and rehydrate archives 211 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST) 212 InitAddOnSupport(); 213 #endif 214 #endif 215 ResizeToPreferred(); 216 } 217 218 219 void 220 TReplicantTray::DetachedFromWindow() 221 { 222 #ifdef DB_ADDONS 223 // clean up add-on support 224 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST) 225 DeleteAddOnSupport(); 226 #endif 227 #endif 228 BView::DetachedFromWindow(); 229 } 230 231 232 /*! Width is set to a minimum of kMinimumReplicantCount by kMaxReplicantWidth 233 if not in multirowmode and greater than kMinimumReplicantCount 234 the width should be calculated based on the actual replicant widths 235 */ 236 void 237 TReplicantTray::GetPreferredSize(float* preferredWidth, float* preferredHeight) 238 { 239 float width = 0; 240 float height = fMinTrayHeight; 241 242 if (fBarView->Vertical()) { 243 width = static_cast<TBarApp*>(be_app)->Settings()->width 244 - gDragWidth - kGutter; 245 width = std::max(gMinimumTrayWidth, width); 246 247 if (fRightBottomReplicant.IsValid()) 248 height = fRightBottomReplicant.bottom; 249 else if (ReplicantCount() > 0) { 250 // The height will be uniform for the number of rows necessary 251 // to show all the replicants and gutters. 252 int32 rowCount = (int32)(height / fMaxReplicantHeight); 253 height = kGutter + (rowCount * fMaxReplicantHeight) 254 + ((rowCount - 1) * sIconGap) + kGutter; 255 height = std::max(fMinTrayHeight, height); 256 } else 257 height = fMinTrayHeight; 258 } else { 259 // if last replicant overruns clock then resize to accomodate 260 if (ReplicantCount() > 0) { 261 if (!fTime->IsHidden(fTime) && Bounds().right - fTrayPadding - 2 262 - fTime->Frame().Width() - fClockMargin 263 < fRightBottomReplicant.right + fClockMargin) { 264 width = fRightBottomReplicant.right + fClockMargin 265 + fTime->Frame().Width() + fTrayPadding + 2; 266 } else 267 width = fRightBottomReplicant.right + sIconGap + kGutter; 268 } 269 270 // this view has a fixed minimum width 271 width = std::max(gMinimumTrayWidth, width); 272 273 // if mini-mode set to tab height 274 // else if horizontal mode set to team menu item height 275 if (fBarView->MiniState()) 276 height = std::max(fMinTrayHeight, fBarView->TabHeight()); 277 else 278 height = fBarView->TeamMenuItemHeight(); 279 } 280 281 *preferredWidth = width; 282 // add 1 for the border 283 *preferredHeight = height + 1; 284 } 285 286 287 void 288 TReplicantTray::AdjustPlacement() 289 { 290 // called when an add-on has been added or removed 291 // need to resize the parent of this accordingly 292 293 BRect bounds = Bounds(); 294 float width, height; 295 GetPreferredSize(&width, &height); 296 297 if (width == bounds.Width() && height == bounds.Height()) { 298 // no need to change anything 299 return; 300 } 301 302 Parent()->ResizeToPreferred(); 303 fBarView->UpdatePlacement(); 304 Parent()->Invalidate(); 305 Invalidate(); 306 } 307 308 309 void 310 TReplicantTray::MessageReceived(BMessage* message) 311 { 312 switch (message->what) { 313 case B_LOCALE_CHANGED: 314 if (fTime == NULL) 315 return; 316 317 fTime->UpdateTimeFormat(); 318 fTime->Update(); 319 // time string reformat -> realign 320 goto realignReplicants; 321 322 case kShowHideTime: 323 // from context menu in clock and in this view 324 ShowHideTime(); 325 break; 326 327 case kShowSeconds: 328 if (fTime == NULL) 329 return; 330 331 fTime->SetShowSeconds(!fTime->ShowSeconds()); 332 333 // time string reformat -> realign 334 goto realignReplicants; 335 336 case kShowDayOfWeek: 337 if (fTime == NULL) 338 return; 339 340 fTime->SetShowDayOfWeek(!fTime->ShowDayOfWeek()); 341 342 // time string reformat -> realign 343 goto realignReplicants; 344 345 case kShowTimeZone: 346 if (fTime == NULL) 347 return; 348 349 fTime->SetShowTimeZone(!fTime->ShowTimeZone()); 350 351 // time string reformat -> realign 352 goto realignReplicants; 353 354 case kGetClockSettings: 355 { 356 if (fTime == NULL) 357 return; 358 359 bool showClock = !fTime->IsHidden(fTime); 360 bool showSeconds = fTime->ShowSeconds(); 361 bool showDayOfWeek = fTime->ShowDayOfWeek(); 362 bool showTimeZone = fTime->ShowTimeZone(); 363 364 BMessage reply(kGetClockSettings); 365 reply.AddBool("showClock", showClock); 366 reply.AddBool("showSeconds", showSeconds); 367 reply.AddBool("showDayOfWeek", showDayOfWeek); 368 reply.AddBool("showTimeZone", showTimeZone); 369 message->SendReply(&reply); 370 break; 371 } 372 373 #ifdef DB_ADDONS 374 case B_NODE_MONITOR: 375 HandleEntryUpdate(message); 376 break; 377 #endif 378 379 case kRealignReplicants: 380 realignReplicants: 381 RealignReplicants(); 382 AdjustPlacement(); 383 break; 384 385 default: 386 BView::MessageReceived(message); 387 break; 388 } 389 } 390 391 392 void 393 TReplicantTray::MouseDown(BPoint where) 394 { 395 #ifdef DB_ADDONS 396 if (modifiers() & B_CONTROL_KEY) 397 DumpList(fItemList); 398 #endif 399 400 uint32 buttons; 401 402 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 403 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 404 ShowReplicantMenu(where); 405 } else { 406 BPoint save = where; 407 bigtime_t doubleClickSpeed; 408 bigtime_t start = system_time(); 409 uint32 buttons; 410 411 get_click_speed(&doubleClickSpeed); 412 413 do { 414 if (fabs(where.x - save.x) > 4 || fabs(where.y - save.y) > 4) 415 // user moved out of bounds of click area 416 break; 417 418 if ((system_time() - start) > (2 * doubleClickSpeed)) { 419 ShowReplicantMenu(where); 420 break; 421 } 422 423 snooze(50000); 424 GetMouse(&where, &buttons); 425 } while (buttons); 426 } 427 BView::MouseDown(where); 428 } 429 430 431 void 432 TReplicantTray::ShowReplicantMenu(BPoint point) 433 { 434 BPopUpMenu* menu = new BPopUpMenu("", false, false); 435 menu->SetFont(be_plain_font); 436 437 // If clock is visible show the extended menu, otherwise show "Show clock" 438 439 if (!fTime->IsHidden(fTime)) 440 fTime->ShowTimeOptions(ConvertToScreen(point)); 441 else { 442 BMenuItem* item = new BMenuItem(B_TRANSLATE("Show clock"), 443 new BMessage(kShowHideTime)); 444 menu->AddItem(item); 445 menu->SetTargetForItems(this); 446 BPoint where = ConvertToScreen(point); 447 menu->Go(where, true, true, BRect(where - BPoint(4, 4), 448 where + BPoint(4, 4)), true); 449 } 450 } 451 452 453 void 454 TReplicantTray::ShowHideTime() 455 { 456 if (fTime == NULL) 457 return; 458 459 // Check from the point of view of fTime because we need to ignore 460 // whether or not the parent window is hidden. 461 if (fTime->IsHidden(fTime)) 462 fTime->Show(); 463 else 464 fTime->Hide(); 465 466 RealignReplicants(); 467 AdjustPlacement(); 468 469 // Check from the point of view of fTime ignoring parent's state. 470 bool showClock = !fTime->IsHidden(fTime); 471 472 // Update showClock setting that gets saved to disk on quit 473 static_cast<TBarApp*>(be_app)->Settings()->showClock = showClock; 474 475 // Send a message to Time preferences telling it to update 476 BMessenger messenger("application/x-vnd.Haiku-Time"); 477 BMessage message(kShowHideTime); 478 message.AddBool("showClock", showClock); 479 messenger.SendMessage(&message); 480 } 481 482 483 #ifdef DB_ADDONS 484 485 486 void 487 TReplicantTray::InitAddOnSupport() 488 { 489 // list to maintain refs to each rep added/deleted 490 fItemList = new BList(); 491 BPath path; 492 493 if (GetDeskbarSettingsDirectory(path, true) == B_OK) { 494 path.Append(kReplicantSettingsFile); 495 496 BFile file(path.Path(), B_READ_ONLY); 497 if (file.InitCheck() == B_OK) { 498 status_t result; 499 BEntry entry; 500 int32 id; 501 BString path; 502 if (fAddOnSettings.Unflatten(&file) == B_OK) { 503 for (int32 i = 0; fAddOnSettings.FindString(kReplicantPathField, 504 i, &path) == B_OK; i++) { 505 if (entry.SetTo(path.String()) == B_OK && entry.Exists()) { 506 result = LoadAddOn(&entry, &id, false); 507 } else 508 result = B_ENTRY_NOT_FOUND; 509 510 if (result != B_OK) { 511 fAddOnSettings.RemoveData(kReplicantPathField, i); 512 --i; 513 } 514 } 515 } 516 } 517 } 518 } 519 520 521 void 522 TReplicantTray::DeleteAddOnSupport() 523 { 524 _SaveSettings(); 525 526 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) { 527 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->RemoveItem(i); 528 if (item) { 529 if (item->isAddOn) 530 watch_node(&(item->nodeRef), B_STOP_WATCHING, this, Window()); 531 532 delete item; 533 } 534 } 535 delete fItemList; 536 537 // stop the volume mount/unmount watch 538 stop_watching(this, Window()); 539 } 540 541 542 DeskbarItemInfo* 543 TReplicantTray::DeskbarItemFor(node_ref& nodeRef) 544 { 545 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) { 546 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 547 if (item == NULL) 548 continue; 549 550 if (item->nodeRef == nodeRef) 551 return item; 552 } 553 554 return NULL; 555 } 556 557 558 DeskbarItemInfo* 559 TReplicantTray::DeskbarItemFor(int32 id) 560 { 561 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) { 562 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 563 if (item == NULL) 564 continue; 565 566 if (item->id == id) 567 return item; 568 } 569 570 return NULL; 571 } 572 573 574 bool 575 TReplicantTray::NodeExists(node_ref& nodeRef) 576 { 577 return DeskbarItemFor(nodeRef) != NULL; 578 } 579 580 581 /*! This handles B_NODE_MONITOR & B_QUERY_UPDATE messages received 582 for the registered add-ons. 583 */ 584 void 585 TReplicantTray::HandleEntryUpdate(BMessage* message) 586 { 587 int32 opcode; 588 if (message->FindInt32("opcode", &opcode) != B_OK) 589 return; 590 591 BPath path; 592 switch (opcode) { 593 case B_ENTRY_MOVED: 594 { 595 entry_ref ref; 596 ino_t todirectory; 597 ino_t node; 598 const char* name; 599 if (message->FindString("name", &name) == B_OK 600 && message->FindInt64("from directory", &(ref.directory)) 601 == B_OK 602 && message->FindInt64("to directory", &todirectory) == B_OK 603 && message->FindInt32("device", &(ref.device)) == B_OK 604 && message->FindInt64("node", &node) == B_OK ) { 605 606 if (name == NULL) 607 break; 608 609 ref.set_name(name); 610 // change the directory reference to 611 // the new directory 612 MoveItem(&ref, todirectory); 613 } 614 break; 615 } 616 617 case B_ENTRY_REMOVED: 618 { 619 // entry was rm'd from the device 620 node_ref nodeRef; 621 if (message->FindInt32("device", &(nodeRef.device)) == B_OK 622 && message->FindInt64("node", &(nodeRef.node)) == B_OK) { 623 DeskbarItemInfo* item = DeskbarItemFor(nodeRef); 624 if (item == NULL) 625 break; 626 627 // If there is a team running where the add-on comes from, 628 // we don't want to remove the icon yet. 629 if (be_roster->IsRunning(&item->entryRef)) 630 break; 631 632 UnloadAddOn(&nodeRef, NULL, true, false); 633 } 634 break; 635 } 636 } 637 } 638 639 640 /*! The add-ons must support the exported C function API 641 if they do, they will be loaded and added to deskbar 642 primary function is the Instantiate function 643 */ 644 status_t 645 TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool addToSettings) 646 { 647 if (entry == NULL) 648 return B_BAD_VALUE; 649 650 node_ref nodeRef; 651 entry->GetNodeRef(&nodeRef); 652 // no duplicates 653 if (NodeExists(nodeRef)) 654 return B_ERROR; 655 656 BNode node(entry); 657 BPath path; 658 status_t status = entry->GetPath(&path); 659 if (status != B_OK) 660 return status; 661 662 // load the add-on 663 image_id image = load_add_on(path.Path()); 664 if (image < B_OK) 665 return image; 666 667 // get the view loading function symbol 668 // we first look for a symbol that takes an image_id 669 // and entry_ref pointer, if not found, go with normal 670 // instantiate function 671 BView* (*entryFunction)(image_id, const entry_ref*, float, float); 672 BView* (*itemFunction)(float, float); 673 BView* view = NULL; 674 675 entry_ref ref; 676 entry->GetRef(&ref); 677 678 if (get_image_symbol(image, kInstantiateEntryCFunctionName, 679 B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) { 680 view = (*entryFunction)(image, &ref, fMaxReplicantWidth, 681 fMaxReplicantHeight); 682 } else if (get_image_symbol(image, kInstantiateItemCFunctionName, 683 B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) { 684 view = (*itemFunction)(fMaxReplicantWidth, fMaxReplicantHeight); 685 } else { 686 unload_add_on(image); 687 return B_ERROR; 688 } 689 690 if (view == NULL || IconExists(view->Name())) { 691 delete view; 692 unload_add_on(image); 693 return B_ERROR; 694 } 695 696 BMessage* data = new BMessage; 697 view->Archive(data); 698 delete view; 699 700 // add the rep; adds info to list 701 if (AddIcon(data, id, &ref) != B_OK) 702 delete data; 703 704 if (addToSettings) { 705 fAddOnSettings.AddString(kReplicantPathField, path.Path()); 706 _SaveSettings(); 707 } 708 709 return B_OK; 710 } 711 712 713 status_t 714 TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn) 715 { 716 DeskbarItemInfo* item = new DeskbarItemInfo; 717 if (item == NULL) 718 return B_NO_MEMORY; 719 720 item->id = id; 721 item->isAddOn = isAddOn; 722 723 if (entry.GetRef(&item->entryRef) != B_OK) { 724 item->entryRef.device = -1; 725 item->entryRef.directory = -1; 726 item->entryRef.name = NULL; 727 } 728 item->nodeRef = nodeRef; 729 730 fItemList->AddItem(item); 731 732 if (isAddOn) 733 watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window()); 734 735 return B_OK; 736 } 737 738 739 /** from entry_removed message, when attribute removed 740 * or when a device is unmounted (use removeall, by device) 741 */ 742 743 void 744 TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device, bool which, 745 bool removeAll) 746 { 747 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) { 748 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 749 if (item == NULL) 750 continue; 751 752 if ((which && nodeRef != NULL && item->nodeRef == *nodeRef) 753 || (device != NULL && item->nodeRef.device == *device)) { 754 755 if (device != NULL && be_roster->IsRunning(&item->entryRef)) 756 continue; 757 758 RemoveIcon(item->id); 759 760 if (!removeAll) 761 break; 762 } 763 } 764 } 765 766 767 void 768 TReplicantTray::RemoveItem(int32 id) 769 { 770 DeskbarItemInfo* item = DeskbarItemFor(id); 771 if (item == NULL) 772 return; 773 774 // attribute was added via Deskbar API (AddItem(entry_ref*, int32*) 775 if (item->isAddOn) { 776 BPath path(&item->entryRef); 777 BString storedPath; 778 for (int32 i = 0; 779 fAddOnSettings.FindString(kReplicantPathField, i, &storedPath) 780 == B_OK; i++) { 781 if (storedPath == path.Path()) { 782 fAddOnSettings.RemoveData(kReplicantPathField, i); 783 break; 784 } 785 } 786 _SaveSettings(); 787 788 BNode node(&item->entryRef); 789 watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window()); 790 } 791 792 fItemList->RemoveItem(item); 793 delete item; 794 } 795 796 797 /** ENTRY_MOVED message, moving only occurs on a device 798 * copying will occur (ENTRY_CREATED) between devices 799 */ 800 801 void 802 TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory) 803 { 804 if (ref == NULL) 805 return; 806 807 // scan for a matching entry_ref and update it 808 // 809 // don't need to change node info as it does not change 810 811 for (int32 i = fItemList->CountItems() - 1; i >= 0; i--) { 812 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 813 if (item == NULL) 814 continue; 815 816 if (strcmp(item->entryRef.name, ref->name) == 0 817 && item->entryRef.device == ref->device 818 && item->entryRef.directory == ref->directory) { 819 item->entryRef.directory = toDirectory; 820 break; 821 } 822 } 823 } 824 825 #endif // add-on support 826 827 // external add-on API routines 828 // called using the new BDeskbar class 829 830 // existence of icon/replicant by name or ID 831 // returns opposite 832 // note: name and id are semi-private limiting 833 // the ability of non-host apps to remove 834 // icons without a little bit of work 835 836 /** for a specific id 837 * return the name of the replicant (name of view) 838 */ 839 840 status_t 841 TReplicantTray::ItemInfo(int32 id, const char** name) 842 { 843 if (id < 0) 844 return B_BAD_VALUE; 845 846 int32 index; 847 int32 temp; 848 BView* view = ViewAt(&index, &temp, id, false); 849 if (view != NULL) { 850 *name = view->Name(); 851 return B_OK; 852 } 853 854 return B_ERROR; 855 } 856 857 858 /** for a specific name 859 * return the id (internal to Deskbar) 860 */ 861 862 status_t 863 TReplicantTray::ItemInfo(const char* name, int32* id) 864 { 865 if (name == NULL || *name == '\0') 866 return B_BAD_VALUE; 867 868 int32 index; 869 BView* view = ViewAt(&index, id, name); 870 871 return view != NULL ? B_OK : B_ERROR; 872 } 873 874 875 /** at a specific index 876 * return both the name and the id of the replicant 877 */ 878 879 status_t 880 TReplicantTray::ItemInfo(int32 index, const char** name, int32* id) 881 { 882 if (index < 0) 883 return B_BAD_VALUE; 884 885 BView* view; 886 fShelf->ReplicantAt(index, &view, (uint32*)id, NULL); 887 if (view != NULL) { 888 *name = view->Name(); 889 return B_OK; 890 } 891 892 return B_ERROR; 893 } 894 895 896 /** replicant exists, by id/index */ 897 898 bool 899 TReplicantTray::IconExists(int32 target, bool byIndex) 900 { 901 int32 index; 902 int32 id; 903 BView* view = ViewAt(&index, &id, target, byIndex); 904 905 return view && index >= 0; 906 } 907 908 909 /** replicant exists, by name */ 910 911 bool 912 TReplicantTray::IconExists(const char* name) 913 { 914 if (name == NULL || *name == '\0') 915 return false; 916 917 int32 index; 918 int32 id; 919 BView* view = ViewAt(&index, &id, name); 920 921 return view != NULL && index >= 0; 922 } 923 924 925 int32 926 TReplicantTray::ReplicantCount() const 927 { 928 return fShelf->CountReplicants(); 929 } 930 931 932 /*! Message must contain an archivable view for later rehydration. 933 This function takes over ownership of the provided message on success 934 only. 935 Returns the current replicant ID. 936 */ 937 status_t 938 TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn) 939 { 940 if (archive == NULL || id == NULL) 941 return B_BAD_VALUE; 942 943 // find entry_ref 944 945 entry_ref ref; 946 if (addOn != NULL) { 947 // Use it if we got it 948 ref = *addOn; 949 } else { 950 const char* signature; 951 952 status_t status = archive->FindString("add_on", &signature); 953 if (status == B_OK) { 954 BRoster roster; 955 status = roster.FindApp(signature, &ref); 956 } 957 if (status != B_OK) 958 return status; 959 } 960 961 BFile file; 962 status_t status = file.SetTo(&ref, B_READ_ONLY); 963 if (status != B_OK) 964 return status; 965 966 node_ref nodeRef; 967 status = file.GetNodeRef(&nodeRef); 968 if (status != B_OK) 969 return status; 970 971 BEntry entry(&ref, true); 972 // TODO: this resolves an eventual link for the item being added - this 973 // is okay for now, but in multi-user environments, one might want to 974 // have links that carry the be:deskbar_item_status attribute 975 status = entry.InitCheck(); 976 if (status != B_OK) 977 return status; 978 979 *id = 999; 980 if (archive->what == B_ARCHIVED_OBJECT) 981 archive->what = 0; 982 983 BRect originalBounds = archive->FindRect("_frame"); 984 // this is a work-around for buggy replicants that change their size in 985 // AttachedToWindow() (such as "SVM") 986 987 // TODO: check for name collisions? 988 status = fShelf->AddReplicant(archive, BPoint(1, 1)); 989 if (status != B_OK) 990 return status; 991 992 int32 count = ReplicantCount(); 993 BView* view; 994 fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL); 995 996 if (view != NULL && originalBounds != view->Bounds()) { 997 // The replicant changed its size when added to the window, so we need 998 // to recompute all over again (it's already done once via 999 // BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView()) 1000 RealignReplicants(); 1001 } 1002 1003 float oldWidth = Bounds().Width(); 1004 float oldHeight = Bounds().Height(); 1005 float width, height; 1006 GetPreferredSize(&width, &height); 1007 if (oldWidth != width || oldHeight != height) 1008 AdjustPlacement(); 1009 1010 // add the item to the add-on list 1011 1012 AddItem(*id, nodeRef, entry, addOn != NULL); 1013 return B_OK; 1014 } 1015 1016 1017 void 1018 TReplicantTray::RemoveIcon(int32 target, bool byIndex) 1019 { 1020 if (target < 0) 1021 return; 1022 1023 int32 index; 1024 int32 id; 1025 BView* view = ViewAt(&index, &id, target, byIndex); 1026 if (view != NULL && index >= 0) { 1027 // remove the reference from the item list & the shelf 1028 RemoveItem(id); 1029 fShelf->DeleteReplicant(index); 1030 1031 // force a placement update, !! need to fix BShelf 1032 RealReplicantAdjustment(index); 1033 } 1034 } 1035 1036 1037 void 1038 TReplicantTray::RemoveIcon(const char* name) 1039 { 1040 if (name == NULL || *name == '\0') 1041 return; 1042 1043 int32 index; 1044 int32 id; 1045 BView* view = ViewAt(&index, &id, name); 1046 if (view != NULL && index >= 0) { 1047 // remove the reference from the item list & shelf 1048 RemoveItem(id); 1049 fShelf->DeleteReplicant(index); 1050 1051 // force a placement update, !! need to fix BShelf 1052 RealReplicantAdjustment(index); 1053 } 1054 } 1055 1056 1057 void 1058 TReplicantTray::RealReplicantAdjustment(int32 startIndex) 1059 { 1060 if (startIndex < 0) 1061 return; 1062 1063 if (startIndex == fLastReplicant) 1064 startIndex = 0; 1065 1066 // reset the locations of all replicants after the one deleted 1067 RealignReplicants(startIndex); 1068 1069 float oldWidth = Bounds().Width(); 1070 float oldHeight = Bounds().Height(); 1071 float width, height; 1072 GetPreferredSize(&width, &height); 1073 if (oldWidth != width || oldHeight != height) { 1074 // resize view to accomodate the replicants, redraw as necessary 1075 AdjustPlacement(); 1076 } 1077 } 1078 1079 1080 /** looking for a replicant by id/index 1081 * return the view and index 1082 */ 1083 1084 BView* 1085 TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex) 1086 { 1087 *index = -1; 1088 1089 BView* view; 1090 if (byIndex) { 1091 if (fShelf->ReplicantAt(target, &view, (uint32*)id)) { 1092 if (view != NULL) { 1093 *index = target; 1094 1095 return view; 1096 } 1097 } 1098 } else { 1099 int32 count = ReplicantCount() - 1; 1100 int32 localid; 1101 for (int32 repIndex = count; repIndex >= 0; repIndex--) { 1102 fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid); 1103 if (localid == target && view != NULL) { 1104 *index = repIndex; 1105 *id = localid; 1106 1107 return view; 1108 } 1109 } 1110 } 1111 1112 return NULL; 1113 } 1114 1115 1116 /** looking for a replicant with a view by name 1117 * return the view, index and the id of the replicant 1118 */ 1119 1120 BView* 1121 TReplicantTray::ViewAt(int32* index, int32* id, const char* name) 1122 { 1123 *index = -1; 1124 *id = -1; 1125 1126 BView* view; 1127 int32 count = ReplicantCount() - 1; 1128 for (int32 repIndex = count; repIndex >= 0; repIndex--) { 1129 fShelf->ReplicantAt(repIndex, &view, (uint32*)id); 1130 if (view != NULL && view->Name() != NULL 1131 && strcmp(name, view->Name()) == 0) { 1132 *index = repIndex; 1133 1134 return view; 1135 } 1136 } 1137 1138 return NULL; 1139 } 1140 1141 1142 /** Shelf will call to determine where and if 1143 * the replicant is to be added 1144 */ 1145 1146 bool 1147 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message) 1148 { 1149 if (message == NULL) 1150 return false; 1151 1152 if (replicantFrame.Height() > fMaxReplicantHeight) 1153 return false; 1154 1155 alignment align = B_ALIGN_LEFT; 1156 if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) { 1157 if (!fBarView->Vertical() && !fBarView->MiniState()) 1158 align = B_ALIGN_RIGHT; 1159 else 1160 align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT; 1161 } else if (message->HasInt32("deskbar:align")) 1162 message->FindInt32("deskbar:align", (int32*)&align); 1163 1164 if (message->HasInt32("deskbar:private_align")) 1165 message->FindInt32("deskbar:private_align", (int32*)&align); 1166 else 1167 align = B_ALIGN_LEFT; 1168 1169 BPoint loc = LocationForReplicant(ReplicantCount(), 1170 replicantFrame.Width()); 1171 message->AddPoint("_pjp_loc", loc); 1172 1173 return true; 1174 } 1175 1176 1177 /** based on the previous (index - 1) replicant in the list 1178 * calculate where the left point should be for this 1179 * replicant. replicant will flow to the right on its own 1180 */ 1181 1182 BPoint 1183 TReplicantTray::LocationForReplicant(int32 index, float replicantWidth) 1184 { 1185 BPoint loc(fTrayPadding, 0); 1186 if (fBarView->Vertical() || fBarView->MiniState()) { 1187 if (fBarView->Vertical() && !fBarView->Left()) 1188 loc.x += gDragWidth; // move past dragger on left 1189 1190 loc.y = floorf((fBarView->TabHeight() - fMaxReplicantHeight) / 2) - 1; 1191 } else { 1192 loc.x -= 2; // keeps everything lined up nicely 1193 const int32 iconSize = static_cast<TBarApp*>(be_app)->IconSize(); 1194 float yOffset = iconSize > B_MINI_ICON ? 3 : 2; 1195 // squeeze icons in there at 16x16, reduce border by 1px 1196 1197 if (fBarView->Top()) { 1198 // align top 1199 loc.y = yOffset; 1200 } else { 1201 // align bottom 1202 loc.y = (fBarView->TeamMenuItemHeight() + 1) 1203 - fMaxReplicantHeight - yOffset; 1204 } 1205 } 1206 1207 // move clock vertically centered in first row next to replicants 1208 fTime->MoveTo(Bounds().right - fTime->Bounds().Width() - fTrayPadding, 1209 loc.y + floorf((fMaxReplicantHeight - fTime->fHeight) / 2)); 1210 1211 if (fBarView->Vertical()) { 1212 // try to find free space in every row 1213 for (int32 row = 0; ; loc.y += fMaxReplicantHeight + sIconGap, row++) { 1214 // determine free space in this row 1215 BRect rowRect(loc.x, loc.y, 1216 loc.x + Bounds().Width() - fTrayPadding, 1217 loc.y + fMaxReplicantHeight); 1218 if (row == 0 && !fTime->IsHidden(fTime)) 1219 rowRect.right -= fClockMargin + fTime->Frame().Width(); 1220 1221 BRect replicantRect = rowRect; 1222 for (int32 i = 0; i < index; i++) { 1223 BView* view = NULL; 1224 fShelf->ReplicantAt(i, &view); 1225 if (view == NULL || view->Frame().top != rowRect.top) 1226 continue; 1227 1228 // push this replicant placement past the last one 1229 replicantRect.left = view->Frame().right + sIconGap + 1; 1230 } 1231 1232 // calculated left position, add replicantWidth to get the 1233 // right position 1234 replicantRect.right = replicantRect.left + replicantWidth; 1235 1236 // check if replicant fits in this row 1237 if (replicantRect.right < rowRect.right) { 1238 // replicant fits in this row 1239 loc = replicantRect.LeftTop(); 1240 break; 1241 } 1242 1243 // check next row 1244 } 1245 } else { 1246 // horizontal 1247 if (index > 0) { 1248 // get the last replicant added for placement reference 1249 BView* view = NULL; 1250 fShelf->ReplicantAt(index - 1, &view); 1251 if (view != NULL) { 1252 // push this replicant placement past the last one 1253 loc.x = view->Frame().right + sIconGap + 1; 1254 } 1255 } 1256 } 1257 1258 if (loc.y > fRightBottomReplicant.top 1259 || (loc.y == fRightBottomReplicant.top 1260 && loc.x > fRightBottomReplicant.left)) { 1261 fRightBottomReplicant.Set(loc.x, loc.y, loc.x + replicantWidth, 1262 loc.y + fMaxReplicantHeight); 1263 fLastReplicant = index; 1264 } 1265 1266 return loc; 1267 } 1268 1269 1270 BRect 1271 TReplicantTray::IconFrame(int32 target, bool byIndex) 1272 { 1273 int32 index; 1274 int32 id; 1275 BView* view = ViewAt(&index, &id, target, byIndex); 1276 1277 return view != NULL ? view->Frame() : BRect(0, 0, 0, 0); 1278 } 1279 1280 1281 BRect 1282 TReplicantTray::IconFrame(const char* name) 1283 { 1284 if (name == NULL) 1285 return BRect(0, 0, 0, 0); 1286 1287 int32 index; 1288 int32 id; 1289 BView* view = ViewAt(&index, &id, name); 1290 1291 return view != NULL ? view->Frame() : BRect(0, 0, 0, 0); 1292 } 1293 1294 1295 /** Scan from the startIndex and reset the location 1296 * as defined in LocationForReplicant() 1297 */ 1298 1299 void 1300 TReplicantTray::RealignReplicants(int32 startIndex) 1301 { 1302 if (startIndex < 0) 1303 startIndex = 0; 1304 1305 int32 replicantCount = ReplicantCount(); 1306 if (replicantCount <= 0) 1307 return; 1308 1309 if (startIndex == 0) 1310 fRightBottomReplicant.Set(0, 0, 0, 0); 1311 1312 BView* view = NULL; 1313 for (int32 index = startIndex; index < replicantCount; index++) { 1314 fShelf->ReplicantAt(index, &view); 1315 if (view == NULL) 1316 continue; 1317 1318 float replicantWidth = view->Frame().Width(); 1319 BPoint loc = LocationForReplicant(index, replicantWidth); 1320 if (view->Frame().LeftTop() != loc) 1321 view->MoveTo(loc); 1322 } 1323 } 1324 1325 1326 status_t 1327 TReplicantTray::_SaveSettings() 1328 { 1329 status_t result; 1330 BPath path; 1331 if ((result = GetDeskbarSettingsDirectory(path, true)) == B_OK) { 1332 path.Append(kReplicantSettingsFile); 1333 1334 BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE); 1335 if ((result = file.InitCheck()) == B_OK) 1336 result = fAddOnSettings.Flatten(&file); 1337 } 1338 1339 return result; 1340 } 1341 1342 1343 void 1344 TReplicantTray::SaveTimeSettings() 1345 { 1346 if (fTime == NULL) 1347 return; 1348 1349 clock_settings* settings = ((TBarApp*)be_app)->ClockSettings(); 1350 settings->showSeconds = fTime->ShowSeconds(); 1351 settings->showDayOfWeek = fTime->ShowDayOfWeek(); 1352 settings->showTimeZone = fTime->ShowTimeZone(); 1353 } 1354 1355 1356 // #pragma mark - TDragRegion 1357 1358 1359 /*! Draggable region that is asynchronous so that dragging does not block 1360 other activities. 1361 */ 1362 TDragRegion::TDragRegion(TBarView* barView, BView* replicantTray) 1363 : 1364 BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE, 1365 B_WILL_DRAW | B_DRAW_ON_CHILDREN | B_FRAME_EVENTS), 1366 fBarView(barView), 1367 fReplicantTray(replicantTray), 1368 fDragLocation(kAutoPlaceDragRegion) 1369 { 1370 } 1371 1372 1373 void 1374 TDragRegion::AttachedToWindow() 1375 { 1376 BView::AttachedToWindow(); 1377 1378 CalculateRegions(); 1379 1380 if (be_control_look != NULL) 1381 SetViewUIColor(B_MENU_BACKGROUND_COLOR, 1.1); 1382 else 1383 SetViewUIColor(B_MENU_BACKGROUND_COLOR); 1384 1385 ResizeToPreferred(); 1386 } 1387 1388 1389 void 1390 TDragRegion::GetPreferredSize(float* width, float* height) 1391 { 1392 fReplicantTray->ResizeToPreferred(); 1393 *width = fReplicantTray->Bounds().Width(); 1394 *height = fReplicantTray->Bounds().Height(); 1395 1396 if (fDragLocation != kNoDragRegion) 1397 *width += gDragWidth + kGutter; 1398 else 1399 *width += 6; 1400 1401 if (fBarView->Vertical() && !fBarView->MiniState()) 1402 *height += 3; // add a pixel for an extra border on top 1403 else 1404 *height += 2; // all other modes have a 1px border on top and bottom 1405 } 1406 1407 1408 void 1409 TDragRegion::Draw(BRect updateRect) 1410 { 1411 rgb_color menuColor = ViewColor(); 1412 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 1413 rgb_color ldark = tint_color(menuColor, 1.02); 1414 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT); 1415 1416 BRect frame(Bounds()); 1417 BeginLineArray(4); 1418 1419 if (fBarView->Vertical()) { 1420 // vertical expando full or mini state, draw 2 lines at the top 1421 AddLine(frame.LeftTop(), frame.RightTop(), dark); 1422 AddLine(BPoint(frame.left, frame.top + 1), 1423 BPoint(frame.right, frame.top + 1), ldark); 1424 // add hilight along bottom 1425 AddLine(BPoint(frame.left + 1, frame.bottom), 1426 BPoint(frame.right - 1, frame.bottom), hilite); 1427 } else { 1428 // mini-mode or horizontal, draw hilight along top left and bottom 1429 AddLine(frame.LeftTop(), frame.RightTop(), hilite); 1430 AddLine(BPoint(frame.left, frame.top + 1), frame.LeftBottom(), hilite); 1431 if (!fBarView->Vertical()) { 1432 // only draw bottom hilight in horizontal mode 1433 AddLine(BPoint(frame.left + 1, frame.bottom - 3), 1434 BPoint(frame.right - 1, frame.bottom - 3), hilite); 1435 } 1436 } 1437 1438 EndLineArray(); 1439 } 1440 1441 1442 void 1443 TDragRegion::DrawAfterChildren(BRect updateRect) 1444 { 1445 if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion) 1446 DrawDragger(); 1447 } 1448 1449 1450 void 1451 TDragRegion::DrawDragger() 1452 { 1453 BRect dragRegion(DragRegion()); 1454 1455 rgb_color menuColor = ViewColor(); 1456 rgb_color menuHilite = menuColor; 1457 if (IsTracking()) { 1458 // draw drag region highlighted if tracking mouse 1459 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT); 1460 SetHighColor(menuHilite); 1461 FillRect(dragRegion.InsetByCopy(0, -1)); 1462 } else { 1463 SetHighColor(menuColor); 1464 FillRect(dragRegion.InsetByCopy(0, 1)); 1465 } 1466 1467 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT); 1468 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT); 1469 1470 rgb_color dark = tint_color(menuHilite, B_DARKEN_2_TINT); 1471 1472 BeginLineArray(dragRegion.IntegerHeight() + 2); 1473 BPoint where; 1474 where.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1; 1475 where.y = dragRegion.top + 2; 1476 1477 while (where.y + 1 <= dragRegion.bottom - 2) { 1478 AddLine(where, where, vdark); 1479 AddLine(where + BPoint(1, 1), where + BPoint(1, 1), light); 1480 1481 where.y += 3; 1482 } 1483 1484 if (fBarView != NULL && fBarView->Vertical() && fBarView->MiniState() 1485 && !fBarView->Top()) { 1486 // extend bottom border in bottom mini-mode 1487 AddLine(BPoint(dragRegion.left, dragRegion.bottom - 2), 1488 BPoint(dragRegion.right, dragRegion.bottom - 2), 1489 IsTracking() ? menuHilite : dark); 1490 } 1491 1492 EndLineArray(); 1493 } 1494 1495 1496 BRect 1497 TDragRegion::DragRegion() const 1498 { 1499 BRect dragRegion(Bounds()); 1500 1501 bool placeOnLeft = false; 1502 if (fDragLocation == kAutoPlaceDragRegion) { 1503 placeOnLeft = fBarView->Left() 1504 && (fBarView->Vertical() || fBarView->MiniState()); 1505 } else 1506 placeOnLeft = fDragLocation == kDragRegionLeft; 1507 1508 if (placeOnLeft) 1509 dragRegion.right = dragRegion.left + gDragWidth; 1510 else 1511 dragRegion.left = dragRegion.right - gDragWidth; 1512 1513 return dragRegion; 1514 } 1515 1516 1517 void 1518 TDragRegion::MouseDown(BPoint where) 1519 { 1520 uint32 buttons; 1521 BPoint mouseLoc; 1522 1523 BRect dragRegion(DragRegion()); 1524 dragRegion.InsetBy(-2, -2); 1525 // DragRegion() is designed for drawing, not clicking 1526 1527 if (!dragRegion.Contains(where)) 1528 return; 1529 1530 while (true) { 1531 GetMouse(&mouseLoc, &buttons); 1532 if (buttons == 0) 1533 break; 1534 1535 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) { 1536 fPreviousPosition = where; 1537 SetTracking(true); 1538 SetMouseEventMask(B_POINTER_EVENTS, 1539 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 1540 Invalidate(DragRegion()); 1541 break; 1542 } 1543 1544 snooze(25000); 1545 } 1546 } 1547 1548 1549 void 1550 TDragRegion::MouseUp(BPoint where) 1551 { 1552 if (IsTracking()) { 1553 SetTracking(false); 1554 Invalidate(DragRegion()); 1555 } else 1556 BControl::MouseUp(where); 1557 } 1558 1559 1560 bool 1561 TDragRegion::SwitchModeForRegion(BPoint where, BRegion region, 1562 bool newVertical, bool newLeft, bool newTop, int32 newState) 1563 { 1564 if (!region.Contains(where)) { 1565 // not our region 1566 return false; 1567 } 1568 1569 if (newVertical == fBarView->Vertical() && newLeft == fBarView->Left() 1570 && newTop == fBarView->Top() && newState == fBarView->State()) { 1571 // already in the correct mode 1572 return true; 1573 } 1574 1575 fBarView->ChangeState(newState, newVertical, newLeft, newTop, true); 1576 1577 return true; 1578 } 1579 1580 1581 //! Deskbar regions 1582 // 1583 // ┌───────3──────────┬─────────────────────────────────┬──────────3───────┐ 1584 // ├────────────────┬─┘ └─┬────────────────┤ 1585 // │ 2 │ │ 2 │ 1586 // ├────────────────┤ ├────────────────┤ 1587 // │ │ │ │ 1588 // │ │ │ │ 1589 // │ │ 4 │ │ 1590 // │ │ │ │ 1591 // │ │ │ │ 1592 // │ 1 │ │ 1 │ 1593 // │ │ │ │ 1594 // │ │ │ │ 1595 // │ ├─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤ │ 1596 // │ │ │ │ 1597 // │ │ │ │ 1598 // │ │ │ │ 1599 // ├────────────────┘ └────────────────┤ 1600 // │ │ 1601 // │ 4 │ 1602 // │ │ 1603 // │ │ 1604 // ├────────────────┐ ┌────────────────┤ 1605 // │ 2 │ │ 2 │ 1606 // ├────────────────┴─┐ ┌─┴────────────────┤ 1607 // └───────3──────────┴─────────────────────────────────┴──────────3───────┘ 1608 // 1609 // 1. Vertical expando-mode, right (default) or left 1610 // 2. Vertical mini-mode, right-top left-top left-bottom or right-bottom 1611 // 3. Horizontal mini-mode, right-top left-top left-bottom or right-bottom 1612 // 4. Horizontal expando-mode top or bottom 1613 1614 void 1615 TDragRegion::CalculateRegions() 1616 { 1617 const BRect screenFrame((BScreen(Window())).Frame()); 1618 1619 float menuBarHeight = fBarView->BarMenuBar()->Frame().Height(); 1620 float hDivider = floorf(screenFrame.Width() / 4); 1621 float halfScreen = floorf(screenFrame.Height() / 2); 1622 1623 // corners 1624 fTopLeftVertical.Set(BRect(screenFrame.left, 1625 screenFrame.top + menuBarHeight, screenFrame.left + hDivider, 1626 screenFrame.top + floorf(menuBarHeight * kVerticalMiniMultiplier))); 1627 fTopRightVertical.Set(BRect(screenFrame.right - hDivider, 1628 screenFrame.top + menuBarHeight, screenFrame.right, 1629 screenFrame.top + floorf(menuBarHeight * kVerticalMiniMultiplier))); 1630 fBottomLeftVertical.Set(BRect(screenFrame.left, 1631 screenFrame.bottom - floorf(menuBarHeight * kVerticalMiniMultiplier), 1632 screenFrame.left + hDivider, screenFrame.bottom - menuBarHeight)); 1633 fBottomRightVertical.Set(BRect(screenFrame.right - hDivider, 1634 screenFrame.bottom - floorf(menuBarHeight * kVerticalMiniMultiplier), 1635 screenFrame.right, screenFrame.bottom - menuBarHeight)); 1636 1637 fTopLeftHorizontal.Set(BRect(screenFrame.left, screenFrame.top, 1638 screenFrame.left + hDivider, screenFrame.top + menuBarHeight)); 1639 fTopRightHorizontal.Set(BRect(screenFrame.right - hDivider, screenFrame.top, 1640 screenFrame.right, screenFrame.top + menuBarHeight)); 1641 fBottomLeftHorizontal.Set(BRect(screenFrame.left, screenFrame.bottom - menuBarHeight, 1642 screenFrame.left + hDivider, screenFrame.bottom)); 1643 fBottomRightHorizontal.Set(BRect(screenFrame.right - hDivider, 1644 screenFrame.bottom - menuBarHeight, screenFrame.right, 1645 screenFrame.bottom)); 1646 1647 // left/right expando 1648 fMiddleLeft.Set(BRect(screenFrame.left, screenFrame.top, 1649 screenFrame.left + hDivider, screenFrame.bottom)); 1650 fMiddleLeft.Exclude(&fTopLeftHorizontal); 1651 fMiddleLeft.Exclude(&fBottomLeftHorizontal); 1652 fMiddleLeft.Exclude(&fTopLeftVertical); 1653 fMiddleLeft.Exclude(&fBottomLeftVertical); 1654 1655 fMiddleRight.Set(BRect(screenFrame.right - hDivider, 1656 screenFrame.top, screenFrame.right, screenFrame.bottom)); 1657 fMiddleRight.Exclude(&fTopRightHorizontal); 1658 fMiddleRight.Exclude(&fBottomRightHorizontal); 1659 fMiddleRight.Exclude(&fTopRightVertical); 1660 fMiddleRight.Exclude(&fBottomRightVertical); 1661 1662 #ifdef FULL_MODE 1663 // left/right full 1664 fLeftSide.Set(BRect(screenFrame.left, screenFrame.bottom - halfScreen, 1665 screenFrame.left + hDivider, screenFrame.bottom)); 1666 fLeftSide.Exclude(&fBottomLeftHorizontal); 1667 fLeftSide.Exclude(&fBottomLeftVertical); 1668 fMiddleLeft.Exclude(&fLeftSide); 1669 1670 fRightSide.Set(BRect(screenFrame.right - hDivider, 1671 screenFrame.bottom - halfScreen, screenFrame.right, 1672 screenFrame.bottom)); 1673 fRightSide.Exclude(&fBottomRightHorizontal); 1674 fRightSide.Exclude(&fBottomRightVertical); 1675 fMiddleRight.Exclude(&fRightSide); 1676 #endif 1677 1678 // top/bottom 1679 BRect leftSideRect(screenFrame.left, screenFrame.top, 1680 screenFrame.left + hDivider, screenFrame.bottom); 1681 BRect rightSideRect(screenFrame.right - hDivider, screenFrame.top, 1682 screenFrame.right, screenFrame.bottom); 1683 1684 fTopHalf.Set(BRect(screenFrame.left, screenFrame.top, screenFrame.right, 1685 screenFrame.top + halfScreen)); 1686 fTopHalf.Exclude(leftSideRect); 1687 fTopHalf.Exclude(rightSideRect); 1688 1689 fBottomHalf.Set(BRect(screenFrame.left, screenFrame.bottom - halfScreen, 1690 screenFrame.right, screenFrame.bottom)); 1691 fBottomHalf.Exclude(leftSideRect); 1692 fBottomHalf.Exclude(rightSideRect); 1693 } 1694 1695 1696 void 1697 TDragRegion::MouseMoved(BPoint where, uint32 transit, 1698 const BMessage* dragMessage) 1699 { 1700 if (!IsTracking() || where == fPreviousPosition) 1701 return BControl::MouseMoved(where, transit, dragMessage); 1702 1703 fPreviousPosition = where; 1704 1705 // TODO: can't trust the passed in where param, get screen_where from 1706 // Window()->CurrentMessage() instead, why is this necessary? 1707 BPoint whereScreen; 1708 BMessage* currentMessage = Window()->CurrentMessage(); 1709 if (currentMessage == NULL || currentMessage->FindPoint("screen_where", 1710 &whereScreen) != B_OK) { 1711 whereScreen = ConvertToScreen(where); 1712 } 1713 1714 // use short circuit evaluation for convenience 1715 if (// vertical mini 1716 SwitchModeForRegion(whereScreen, fTopLeftVertical, true, 1717 true, true, kMiniState) 1718 || SwitchModeForRegion(whereScreen, fTopRightVertical, true, 1719 false, true, kMiniState) 1720 || SwitchModeForRegion(whereScreen, fBottomLeftVertical, true, 1721 true, false, kMiniState) 1722 || SwitchModeForRegion(whereScreen, fBottomRightVertical, true, 1723 false, false, kMiniState) 1724 // horizontal mini 1725 || SwitchModeForRegion(whereScreen, fTopLeftHorizontal, false, 1726 true, true, kMiniState) 1727 || SwitchModeForRegion(whereScreen, fTopRightHorizontal, false, 1728 false, true, kMiniState) 1729 || SwitchModeForRegion(whereScreen, fBottomLeftHorizontal, false, 1730 true, false, kMiniState) 1731 || SwitchModeForRegion(whereScreen, fBottomRightHorizontal, false, 1732 false, false, kMiniState) 1733 // expando 1734 || SwitchModeForRegion(whereScreen, fMiddleLeft, true, true, true, 1735 kExpandoState) 1736 || SwitchModeForRegion(whereScreen, fMiddleRight, true, false, true, 1737 kExpandoState) 1738 #ifdef FULL_MODE 1739 // full 1740 || SwitchModeForRegion(whereScreen, fLeftSide, true, true, true, 1741 kFullState) 1742 || SwitchModeForRegion(whereScreen, fRightSide, true, false, true, 1743 kFullState) 1744 #endif 1745 // horizontal 1746 || SwitchModeForRegion(whereScreen, fTopHalf, false, true, true, 1747 kExpandoState) 1748 || SwitchModeForRegion(whereScreen, fBottomHalf, false, true, false, 1749 kExpandoState) 1750 ); 1751 } 1752 1753 1754 int32 1755 TDragRegion::DragRegionLocation() const 1756 { 1757 return fDragLocation; 1758 } 1759 1760 1761 void 1762 TDragRegion::SetDragRegionLocation(int32 location) 1763 { 1764 if (location == fDragLocation) 1765 return; 1766 1767 fDragLocation = location; 1768 Invalidate(); 1769 } 1770 1771 1772 // #pragma mark - TResizeControl 1773 1774 1775 /*! Draggable region that is asynchronous so that resizing does not block. 1776 */ 1777 TResizeControl::TResizeControl(TBarView* barView) 1778 : 1779 BControl(BRect(0, gDragWidth, 0, kMenuBarHeight), "", "", NULL, 1780 B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS), 1781 fBarView(barView) 1782 { 1783 } 1784 1785 1786 TResizeControl::~TResizeControl() 1787 { 1788 } 1789 1790 1791 void 1792 TResizeControl::AttachedToWindow() 1793 { 1794 BView::AttachedToWindow(); 1795 1796 if (be_control_look != NULL) 1797 SetViewUIColor(B_MENU_BACKGROUND_COLOR, 1.1); 1798 else 1799 SetViewUIColor(B_MENU_BACKGROUND_COLOR); 1800 } 1801 1802 1803 void 1804 TResizeControl::Draw(BRect updateRect) 1805 { 1806 if (!fBarView->Vertical()) 1807 return; 1808 1809 BRect dragRegion(Bounds()); 1810 1811 int32 height = dragRegion.IntegerHeight(); 1812 if (height <= 0) 1813 return; 1814 1815 rgb_color menuColor = ViewColor(); 1816 rgb_color menuHilite = menuColor; 1817 if (IsTracking()) { 1818 // draw drag region highlighted if tracking mouse 1819 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT); 1820 SetHighColor(menuHilite); 1821 FillRect(dragRegion); 1822 } else { 1823 SetHighColor(menuColor); 1824 FillRect(dragRegion); 1825 } 1826 1827 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT); 1828 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT); 1829 1830 BeginLineArray(height); 1831 BPoint where; 1832 where.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1; 1833 where.y = dragRegion.top + 2; 1834 1835 while (where.y + 1 <= dragRegion.bottom) { 1836 AddLine(where, where, vdark); 1837 AddLine(where + BPoint(1, 1), where + BPoint(1, 1), light); 1838 1839 where.y += 3; 1840 } 1841 EndLineArray(); 1842 } 1843 1844 1845 void 1846 TResizeControl::MouseDown(BPoint where) 1847 { 1848 uint32 buttons; 1849 BPoint mouseLoc; 1850 1851 while (true) { 1852 GetMouse(&mouseLoc, &buttons); 1853 if (buttons == 0) 1854 break; 1855 1856 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) { 1857 SetTracking(true); 1858 SetMouseEventMask(B_POINTER_EVENTS, 1859 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 1860 Invalidate(); 1861 break; 1862 } 1863 1864 snooze(25000); 1865 } 1866 } 1867 1868 1869 void 1870 TResizeControl::MouseUp(BPoint where) 1871 { 1872 if (IsTracking()) { 1873 SetTracking(false); 1874 Invalidate(); 1875 } else 1876 BControl::MouseUp(where); 1877 } 1878 1879 1880 void 1881 TResizeControl::MouseMoved(BPoint where, uint32 code, 1882 const BMessage* dragMessage) 1883 { 1884 if (!fBarView->Vertical() || !IsResizing()) 1885 return BControl::MouseMoved(where, code, dragMessage); 1886 1887 float windowWidth = Window()->Frame().Width(); 1888 float delta = 0; 1889 BPoint whereScreen = ConvertToScreen(where); 1890 1891 if (fBarView->Left()) { 1892 delta = whereScreen.x - Window()->Frame().right; 1893 if (delta > 0 && windowWidth >= gMaximumWindowWidth) 1894 ; // do nothing 1895 else if (delta < 0 && windowWidth <= gMinimumWindowWidth) 1896 ; // do nothing 1897 else 1898 Window()->ResizeBy(delta, 0); 1899 } else { 1900 delta = Window()->Frame().left - whereScreen.x; 1901 if (delta > 0 && windowWidth >= gMaximumWindowWidth) 1902 ; // do nothing 1903 else if (delta < 0 && windowWidth <= gMinimumWindowWidth) 1904 ; // do nothing 1905 else { 1906 Window()->MoveBy(delta, 0); 1907 Window()->ResizeBy(delta, 0); 1908 } 1909 } 1910 1911 windowWidth = Window()->Frame().Width(); 1912 1913 BControl::MouseMoved(where, code, dragMessage); 1914 } 1915