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 <errno.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <time.h> 42 #include <unistd.h> 43 #include <algorithm> 44 45 #include <fs_index.h> 46 #include <fs_info.h> 47 48 #include <Application.h> 49 #include <Beep.h> 50 #include <Bitmap.h> 51 #include <Catalog.h> 52 #include <ControlLook.h> 53 #include <Directory.h> 54 #include <FindDirectory.h> 55 #include <Locale.h> 56 #include <MenuItem.h> 57 #include <NodeInfo.h> 58 #include <NodeMonitor.h> 59 #include <Path.h> 60 #include <PopUpMenu.h> 61 #include <Roster.h> 62 #include <Screen.h> 63 #include <Volume.h> 64 #include <VolumeRoster.h> 65 #include <Window.h> 66 67 #include "icons_logo.h" 68 #include "BarApp.h" 69 #include "DeskBarUtils.h" 70 #include "ResourceSet.h" 71 #include "StatusView.h" 72 #include "StatusViewShelf.h" 73 #include "TimeView.h" 74 75 using std::max; 76 77 #ifdef DB_ADDONS 78 // Add-on support 79 // 80 // Item - internal item list (node, eref, etc) 81 // Icon - physical replicant handed to the DeskbarClass class 82 // AddOn - attribute based add-on 83 84 const char* const kInstantiateItemCFunctionName = "instantiate_deskbar_item"; 85 const char* const kInstantiateEntryCFunctionName = "instantiate_deskbar_entry"; 86 const char* const kDeskbarSecurityCodeFile = "Deskbar_security_code"; 87 const char* const kDeskbarSecurityCodeAttr = "be:deskbar_security_code"; 88 const char* const kStatusPredicate = "be:deskbar_item_status"; 89 const char* const kEnabledPredicate = "be:deskbar_item_status = enabled"; 90 const char* const kDisabledPredicate = "be:deskbar_item_status = disabled"; 91 92 float sMinimumWindowWidth = kGutter + kMinimumTrayWidth + kDragRegionWidth; 93 94 95 static void 96 DumpItem(DeskbarItemInfo* item) 97 { 98 printf("is addon: %i, id: %li\n", item->isAddOn, item->id); 99 printf("entry_ref: %ld, %Ld, %s\n", item->entryRef.device, 100 item->entryRef.directory, item->entryRef.name); 101 printf("node_ref: %ld, %Ld\n", item->nodeRef.device, item->nodeRef.node); 102 } 103 104 105 static void 106 DumpList(BList* itemlist) 107 { 108 int32 count = itemlist->CountItems() - 1; 109 if (count < 0) { 110 printf("no items in list\n"); 111 return; 112 } 113 for (int32 i = count ; i >= 0 ; i--) { 114 DeskbarItemInfo* item = (DeskbarItemInfo*)itemlist->ItemAt(i); 115 if (!item) 116 continue; 117 118 DumpItem(item); 119 } 120 } 121 #endif /* DB_ADDONS */ 122 123 124 #undef B_TRANSLATE_CONTEXT 125 #define B_TRANSLATE_CONTEXT "Tray" 126 127 // don't change the name of this view to anything other than "Status"! 128 129 TReplicantTray::TReplicantTray(TBarView* parent, bool vertical) 130 : BView(BRect(0, 0, 1, 1), "Status", B_FOLLOW_LEFT | B_FOLLOW_TOP, 131 B_WILL_DRAW | B_FRAME_EVENTS), 132 fClock(NULL), 133 fBarView(parent), 134 fShelf(new TReplicantShelf(this)), 135 fMultiRowMode(vertical), 136 fMinimumTrayWidth(kMinimumTrayWidth), 137 fAlignmentSupport(false) 138 { 139 // init the minimum window width according to the logo. 140 const BBitmap* logoBitmap = AppResSet()->FindBitmap(B_MESSAGE_TYPE, 141 R_LeafLogoBitmap); 142 if (logoBitmap != NULL) { 143 sMinimumWindowWidth = max_c(sMinimumWindowWidth, 144 2 * (logoBitmap->Bounds().Width() + 8)); 145 fMinimumTrayWidth = sMinimumWindowWidth - kGutter - kDragRegionWidth; 146 } 147 } 148 149 150 TReplicantTray::~TReplicantTray() 151 { 152 delete fShelf; 153 } 154 155 156 void 157 TReplicantTray::AttachedToWindow() 158 { 159 BView::AttachedToWindow(); 160 161 if (be_control_look != NULL) { 162 SetViewColor(Parent()->ViewColor()); 163 } else { 164 SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 165 B_DARKEN_1_TINT)); 166 } 167 SetDrawingMode(B_OP_COPY); 168 169 Window()->SetPulseRate(1000000); 170 DealWithClock(fBarView->ShowingClock()); 171 172 173 #ifdef DB_ADDONS 174 // load addons and rehydrate archives 175 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST) 176 InitAddOnSupport(); 177 #endif 178 #endif 179 ResizeToPreferred(); 180 } 181 182 183 void 184 TReplicantTray::DetachedFromWindow() 185 { 186 #ifdef DB_ADDONS 187 // clean up add-on support 188 #if !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST) 189 DeleteAddOnSupport(); 190 #endif 191 #endif 192 BView::DetachedFromWindow(); 193 } 194 195 196 void 197 TReplicantTray::RememberClockSettings() 198 { 199 if (fClock) { 200 desk_settings* settings = ((TBarApp*)be_app)->Settings(); 201 202 settings->timeShowSeconds = fClock->ShowingSeconds(); 203 } 204 } 205 206 207 bool 208 TReplicantTray::ShowingSeconds() 209 { 210 if (fClock) 211 return fClock->ShowingSeconds(); 212 return false; 213 } 214 215 216 void 217 TReplicantTray::DealWithClock(bool showClock) 218 { 219 fBarView->ShowClock(showClock); 220 221 if (showClock) { 222 if (!fClock) { 223 desk_settings* settings = ((TBarApp*)be_app)->Settings(); 224 225 fClock = new TTimeView(fMinimumTrayWidth, kMaxReplicantHeight - 1.0, 226 settings->timeShowSeconds, 227 false); 228 AddChild(fClock); 229 230 fClock->MoveTo(Bounds().right - fClock->Bounds().Width() - 1, 2); 231 } 232 } else { 233 if (fClock) { 234 RememberClockSettings(); 235 236 fClock->RemoveSelf(); 237 delete fClock; 238 fClock = NULL; 239 } 240 } 241 } 242 243 244 /*! Width is set to a minimum of kMinimumReplicantCount by kMaxReplicantWidth 245 if not in multirowmode and greater than kMinimumReplicantCount 246 the width should be calculated based on the actual 247 replicant widths 248 */ 249 void 250 TReplicantTray::GetPreferredSize(float* preferredWidth, float* preferredHeight) 251 { 252 float width = 0, height = kMinimumTrayHeight; 253 254 if (fMultiRowMode) { 255 if (fShelf->CountReplicants() > 0) 256 height = fRightBottomReplicant.bottom; 257 258 // the height will be uniform for the number of rows necessary to show 259 // all the reps + any gutters necessary for spacing 260 int32 rowCount = (int32)(height / kMaxReplicantHeight); 261 height = kGutter + (rowCount * kMaxReplicantHeight) 262 + ((rowCount - 1) * kIconGap) + kGutter; 263 height = max(kMinimumTrayHeight, height); 264 width = fMinimumTrayWidth; 265 } else { 266 // if last replicant overruns clock then resize to accomodate 267 if (fShelf->CountReplicants() > 0) { 268 if (fBarView->ShowingClock() 269 && fRightBottomReplicant.right + 6 >= fClock->Frame().left) { 270 width = fRightBottomReplicant.right + 6 271 + fClock->Frame().Width(); 272 } else 273 width = fRightBottomReplicant.right + 3; 274 } 275 276 // this view has a fixed minimum width 277 width = max(fMinimumTrayWidth, width); 278 } 279 280 *preferredWidth = width; 281 // add 1 for the border 282 *preferredHeight = height + 1; 283 } 284 285 286 void 287 TReplicantTray::AdjustPlacement() 288 { 289 // called when an add-on has been added or removed 290 // need to resize the parent of this accordingly 291 292 BRect bounds = Bounds(); 293 float width, height; 294 GetPreferredSize(&width, &height); 295 296 if (width == bounds.Width() && height == bounds.Height()) { 297 // no need to change anything 298 return; 299 } 300 301 Parent()->ResizeToPreferred(); 302 fBarView->UpdatePlacement(); 303 Parent()->Invalidate(); 304 Invalidate(); 305 } 306 307 308 void 309 TReplicantTray::MessageReceived(BMessage* message) 310 { 311 switch (message->what) { 312 case 'time': 313 // from context menu in clock and in this view 314 DealWithClock(!fBarView->ShowingClock()); 315 RealignReplicants(); 316 AdjustPlacement(); 317 break; 318 319 case 'Trfm': 320 // time string reformat -> realign 321 DealWithClock(fBarView->ShowingClock()); 322 RealignReplicants(); 323 AdjustPlacement(); 324 break; 325 326 case kShowSeconds: 327 case kFullDate: 328 if (fClock != NULL) 329 Window()->PostMessage(message, fClock); 330 break; 331 332 #ifdef DB_ADDONS 333 case B_NODE_MONITOR: 334 case B_QUERY_UPDATE: 335 HandleEntryUpdate(message); 336 break; 337 #endif 338 339 default: 340 BView::MessageReceived(message); 341 break; 342 } 343 } 344 345 346 void 347 TReplicantTray::ShowReplicantMenu(BPoint point) 348 { 349 BPopUpMenu* menu = new BPopUpMenu("", false, false); 350 menu->SetFont(be_plain_font); 351 352 // If clock is visible show the extended menu, otherwise show "Show Time" 353 354 if (fBarView->ShowingClock()) 355 fClock->ShowClockOptions(ConvertToScreen(point)); 356 else { 357 BMenuItem* item = new BMenuItem(B_TRANSLATE("Show Time"), 358 new BMessage('time')); 359 menu->AddItem(item); 360 menu->SetTargetForItems(this); 361 BPoint where = ConvertToScreen(point); 362 menu->Go(where, true, true, BRect(where - BPoint(4, 4), 363 where + BPoint(4, 4)), true); 364 } 365 } 366 367 368 void 369 TReplicantTray::MouseDown(BPoint where) 370 { 371 #ifdef DB_ADDONS 372 if (modifiers() & B_CONTROL_KEY) 373 DumpList(fItemList); 374 #endif 375 376 uint32 buttons; 377 378 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 379 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 380 ShowReplicantMenu(where); 381 } else { 382 BPoint save = where; 383 bigtime_t doubleClickSpeed; 384 bigtime_t start = system_time(); 385 uint32 buttons; 386 387 get_click_speed(&doubleClickSpeed); 388 389 do { 390 if (fabs(where.x - save.x) > 4 || fabs(where.y - save.y) > 4) 391 // user moved out of bounds of click area 392 break; 393 394 if ((system_time() - start) > (2 * doubleClickSpeed)) { 395 ShowReplicantMenu(where); 396 break; 397 } 398 399 snooze(50000); 400 GetMouse(&where, &buttons); 401 } while (buttons); 402 } 403 BView::MouseDown(where); 404 } 405 406 #ifdef DB_ADDONS 407 408 void 409 TReplicantTray::InitAddOnSupport() 410 { 411 // list to maintain refs to each rep added/deleted 412 fItemList = new BList(); 413 bool haveKey = false; 414 BPath path; 415 416 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { 417 path.Append(kDeskbarSecurityCodeFile); 418 419 BFile file(path.Path(), B_READ_ONLY); 420 if (file.InitCheck() == B_OK 421 && file.Read(&fDeskbarSecurityCode, sizeof(fDeskbarSecurityCode)) 422 == sizeof(fDeskbarSecurityCode)) 423 haveKey = true; 424 } 425 if (!haveKey) { 426 // create the security code 427 bigtime_t real = real_time_clock_usecs(); 428 bigtime_t boot = system_time(); 429 // two computers would have to have exactly matching clocks, and launch 430 // Deskbar at the exact same time into the bootsequence in order for 431 // their security-ID to be identical 432 fDeskbarSecurityCode = ((real & 0xffffffffULL) << 32) 433 | (boot & 0xffffffffULL); 434 435 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) == B_OK) { 436 path.Append(kDeskbarSecurityCodeFile); 437 BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE 438 | B_ERASE_FILE); 439 if (file.InitCheck() == B_OK) 440 file.Write(&fDeskbarSecurityCode, sizeof(fDeskbarSecurityCode)); 441 } 442 } 443 444 // for each volume currently mounted index the volume with our indices 445 BVolumeRoster roster; 446 BVolume volume; 447 while (roster.GetNextVolume(&volume) == B_OK) { 448 fs_create_index(volume.Device(), kStatusPredicate, B_STRING_TYPE, 0); 449 RunAddOnQuery(&volume, kEnabledPredicate); 450 } 451 452 // we also watch for volumes mounted and unmounted 453 watch_node(NULL, B_WATCH_MOUNT | B_WATCH_ATTR, this, Window()); 454 } 455 456 457 void 458 TReplicantTray::DeleteAddOnSupport() 459 { 460 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 461 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->RemoveItem(i); 462 if (item) { 463 if (item->isAddOn) 464 watch_node(&(item->nodeRef), B_STOP_WATCHING, this, Window()); 465 466 delete item; 467 } 468 } 469 delete fItemList; 470 471 // stop the volume mount/unmount watch 472 stop_watching(this, Window()); 473 } 474 475 476 void 477 TReplicantTray::RunAddOnQuery(BVolume* volume, const char* predicate) 478 { 479 // Since the new BFS supports querying for attributes without 480 // an index, we only run the query if the index exists (for 481 // newly mounted devices only - the Deskbar will automatically 482 // create an index for every device mounted at startup). 483 index_info info; 484 if (!volume->KnowsQuery() 485 || fs_stat_index(volume->Device(), kStatusPredicate, &info) != 0) 486 return; 487 488 // run a new query on a specific volume and make it live 489 BQuery query; 490 query.SetVolume(volume); 491 query.SetPredicate(predicate); 492 query.Fetch(); 493 494 int32 id; 495 BEntry entry; 496 while (query.GetNextEntry(&entry) == B_OK) { 497 // scan any entries returned 498 // attempt to load them as add-ons 499 // collisions are handled in LoadAddOn 500 LoadAddOn(&entry, &id); 501 } 502 } 503 504 505 bool 506 TReplicantTray::IsAddOn(entry_ref& ref) 507 { 508 BFile file(&ref, B_READ_ONLY); 509 510 char status[64]; 511 ssize_t size = file.ReadAttr(kStatusPredicate, B_STRING_TYPE, 0, &status, 512 sizeof(status)); 513 return size > 0; 514 } 515 516 517 DeskbarItemInfo* 518 TReplicantTray::DeskbarItemFor(node_ref& nodeRef) 519 { 520 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 521 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 522 if (item == NULL) 523 continue; 524 525 if (item->nodeRef == nodeRef) 526 return item; 527 } 528 529 return NULL; 530 } 531 532 533 DeskbarItemInfo* 534 TReplicantTray::DeskbarItemFor(int32 id) 535 { 536 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 537 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 538 if (item == NULL) 539 continue; 540 541 if (item->id == id) 542 return item; 543 } 544 545 return NULL; 546 } 547 548 549 bool 550 TReplicantTray::NodeExists(node_ref& nodeRef) 551 { 552 return DeskbarItemFor(nodeRef) != NULL; 553 } 554 555 556 /*! This handles B_NODE_MONITOR & B_QUERY_UPDATE messages received 557 for the registered add-ons. 558 */ 559 void 560 TReplicantTray::HandleEntryUpdate(BMessage* message) 561 { 562 int32 opcode; 563 if (message->FindInt32("opcode", &opcode) != B_OK) 564 return; 565 566 BPath path; 567 switch (opcode) { 568 case B_ENTRY_CREATED: 569 { 570 // entry was just listed, matches live query 571 const char* name; 572 ino_t directory; 573 dev_t device; 574 // received when an app adds a ref to the 575 // Deskbar add-ons folder 576 if (message->FindString("name", &name) == B_OK 577 && message->FindInt64("directory", &directory) == B_OK 578 && message->FindInt32("device", &device) == B_OK) { 579 entry_ref ref(device, directory, name); 580 // see if this item has the attribute 581 // that we expect 582 if (IsAddOn(ref)) { 583 int32 id; 584 BEntry entry(&ref); 585 LoadAddOn(&entry, &id); 586 } 587 } 588 break; 589 } 590 591 case B_ATTR_CHANGED: 592 { 593 // from node watch on individual items 594 // (node_watch added in LoadAddOn) 595 node_ref nodeRef; 596 if (message->FindInt32("device", &(nodeRef.device)) == B_OK 597 && message->FindInt64("node", &(nodeRef.node)) == B_OK) { 598 // get the add-on this is for 599 DeskbarItemInfo* item = DeskbarItemFor(nodeRef); 600 if (item == NULL) 601 break; 602 603 BFile file(&item->entryRef, B_READ_ONLY); 604 605 char status[255]; 606 ssize_t size = file.ReadAttr(kStatusPredicate, 607 B_STRING_TYPE, 0, status, sizeof(status) - 1); 608 status[sizeof(status) - 1] = '\0'; 609 610 // attribute was removed 611 if (size == B_ENTRY_NOT_FOUND) { 612 // cleans up and removes node_watch 613 UnloadAddOn(&nodeRef, NULL, true, false); 614 } else if (!strcmp(status, "enable")) { 615 int32 id; 616 BEntry entry(&item->entryRef, true); 617 LoadAddOn(&entry, &id); 618 } 619 } 620 break; 621 } 622 623 case B_ENTRY_MOVED: 624 { 625 entry_ref ref; 626 ino_t todirectory; 627 ino_t node; 628 const char* name; 629 if (message->FindString("name", &name) == B_OK 630 && message->FindInt64("from directory", &(ref.directory)) 631 == B_OK 632 && message->FindInt64("to directory", &todirectory) == B_OK 633 && message->FindInt32("device", &(ref.device)) == B_OK 634 && message->FindInt64("node", &node) == B_OK ) { 635 636 if (!name) 637 break; 638 639 ref.set_name(name); 640 // change the directory reference to 641 // the new directory 642 MoveItem(&ref, todirectory); 643 } 644 break; 645 } 646 647 case B_ENTRY_REMOVED: 648 { 649 // entry was rm'd from the device 650 node_ref nodeRef; 651 if (message->FindInt32("device", &(nodeRef.device)) == B_OK 652 && message->FindInt64("node", &(nodeRef.node)) == B_OK) { 653 DeskbarItemInfo* item = DeskbarItemFor(nodeRef); 654 if (item == NULL) 655 break; 656 657 // If there is a team running where the add-on comes from, 658 // we don't want to remove the icon yet. 659 if (be_roster->IsRunning(&item->entryRef)) 660 break; 661 662 UnloadAddOn(&nodeRef, NULL, true, false); 663 } 664 break; 665 } 666 667 case B_DEVICE_MOUNTED: 668 { 669 // run a new query on the new device 670 dev_t device; 671 if (message->FindInt32("new device", &device) != B_OK) 672 break; 673 674 BVolume volume(device); 675 RunAddOnQuery(&volume, kEnabledPredicate); 676 break; 677 } 678 679 case B_DEVICE_UNMOUNTED: 680 { 681 // remove all items associated with the device 682 // unmounted 683 // contrary to what the BeBook says, the item is called "device", 684 // not "new device" like it is for B_DEVICE_MOUNTED 685 dev_t device; 686 if (message->FindInt32("device", &device) != B_OK) 687 break; 688 689 UnloadAddOn(NULL, &device, false, true); 690 break; 691 } 692 } 693 } 694 695 696 /*! The add-ons must support the exported C function API 697 if they do, they will be loaded and added to deskbar 698 primary function is the Instantiate function 699 */ 700 status_t 701 TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool force) 702 { 703 if (!entry) 704 return B_ERROR; 705 706 node_ref nodeRef; 707 entry->GetNodeRef(&nodeRef); 708 // no duplicates 709 if (NodeExists(nodeRef)) 710 return B_ERROR; 711 712 BNode node(entry); 713 if (!force) { 714 status_t error = node.InitCheck(); 715 if (error != B_OK) 716 return error; 717 718 uint64 deskbarID; 719 ssize_t size = node.ReadAttr(kDeskbarSecurityCodeAttr, B_UINT64_TYPE, 720 0, &deskbarID, sizeof(fDeskbarSecurityCode)); 721 if (size != sizeof(fDeskbarSecurityCode) 722 || deskbarID != fDeskbarSecurityCode) { 723 // no code or code doesn't match 724 return B_ERROR; 725 } 726 } 727 728 BPath path; 729 status_t status = entry->GetPath(&path); 730 if (status < B_OK) 731 return status; 732 733 // load the add-on 734 image_id image = load_add_on(path.Path()); 735 if (image < B_OK) 736 return image; 737 738 // get the view loading function symbol 739 // we first look for a symbol that takes an image_id 740 // and entry_ref pointer, if not found, go with normal 741 // instantiate function 742 BView* (*entryFunction)(image_id, const entry_ref*); 743 BView* (*itemFunction)(void); 744 BView* view = NULL; 745 746 entry_ref ref; 747 entry->GetRef(&ref); 748 749 if (get_image_symbol(image, kInstantiateEntryCFunctionName, 750 B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) { 751 view = (*entryFunction)(image, &ref); 752 } else if (get_image_symbol(image, kInstantiateItemCFunctionName, 753 B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) { 754 view = (*itemFunction)(); 755 } else { 756 unload_add_on(image); 757 return B_ERROR; 758 } 759 760 if (view == NULL || IconExists(view->Name())) { 761 delete view; 762 unload_add_on(image); 763 return B_ERROR; 764 } 765 766 BMessage* data = new BMessage; 767 view->Archive(data); 768 delete view; 769 770 AddIcon(data, id, &ref); 771 // add the rep; adds info to list 772 773 node.WriteAttr(kDeskbarSecurityCodeAttr, B_UINT64_TYPE, 0, 774 &fDeskbarSecurityCode, sizeof(fDeskbarSecurityCode)); 775 776 return B_OK; 777 } 778 779 780 status_t 781 TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn) 782 { 783 DeskbarItemInfo* item = new DeskbarItemInfo; 784 if (item == NULL) 785 return B_NO_MEMORY; 786 787 item->id = id; 788 item->isAddOn = isAddOn; 789 790 if (entry.GetRef(&item->entryRef) < B_OK) { 791 item->entryRef.device = -1; 792 item->entryRef.directory = -1; 793 item->entryRef.name = NULL; 794 } 795 item->nodeRef = nodeRef; 796 797 fItemList->AddItem(item); 798 799 if (isAddOn) 800 watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window()); 801 802 return B_OK; 803 } 804 805 806 /** from entry_removed message, when attribute removed 807 * or when a device is unmounted (use removeall, by device) 808 */ 809 810 void 811 TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device, 812 bool which, bool removeAll) 813 { 814 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 815 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 816 if (!item) 817 continue; 818 819 if ((which && nodeRef && item->nodeRef == *nodeRef) 820 || (device && item->nodeRef.device == *device)) { 821 822 if (device && be_roster->IsRunning(&item->entryRef)) 823 continue; 824 825 RemoveIcon(item->id); 826 827 if (!removeAll) 828 break; 829 } 830 } 831 } 832 833 834 void 835 TReplicantTray::RemoveItem(int32 id) 836 { 837 DeskbarItemInfo* item = DeskbarItemFor(id); 838 if (item == NULL) 839 return; 840 841 // attribute was added via Deskbar API (AddItem(entry_ref*, int32*) 842 if (item->isAddOn) { 843 BNode node(&item->entryRef); 844 node.RemoveAttr(kStatusPredicate); 845 watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window()); 846 } 847 848 fItemList->RemoveItem(item); 849 delete item; 850 } 851 852 853 /** ENTRY_MOVED message, moving only occurs on a device 854 * copying will occur (ENTRY_CREATED) between devices 855 */ 856 857 void 858 TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory) 859 { 860 if (!ref) 861 return; 862 863 // scan for a matching entry_ref and update it 864 // 865 // don't need to change node info as it does not change 866 867 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 868 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 869 if (!item) 870 continue; 871 872 if (!strcmp(item->entryRef.name, ref->name) 873 && item->entryRef.device == ref->device 874 && item->entryRef.directory == ref->directory) { 875 item->entryRef.directory = toDirectory; 876 break; 877 } 878 } 879 } 880 881 #endif // add-on support 882 883 // external add-on API routines 884 // called using the new BDeskbar class 885 886 // existence of icon/replicant by name or ID 887 // returns opposite 888 // note: name and id are semi-private limiting 889 // the ability of non-host apps to remove 890 // icons without a little bit of work 891 892 /** for a specific id 893 * return the name of the replicant (name of view) 894 */ 895 896 status_t 897 TReplicantTray::ItemInfo(int32 id, const char** name) 898 { 899 if (id < 0) 900 return B_ERROR; 901 902 int32 index, temp; 903 BView* view = ViewAt(&index, &temp, id, false); 904 if (view) { 905 *name = view->Name(); 906 return B_OK; 907 } 908 909 return B_ERROR; 910 } 911 912 913 /** for a specific name 914 * return the id (internal to Deskbar) 915 */ 916 917 status_t 918 TReplicantTray::ItemInfo(const char* name, int32* id) 919 { 920 if (!name || strlen(name) <= 0) 921 return B_ERROR; 922 923 int32 index; 924 BView* view = ViewAt(&index, id, name); 925 if (view) 926 return B_OK; 927 928 return B_ERROR; 929 } 930 931 932 /** at a specific index 933 * return both the name and the id of the replicant 934 */ 935 936 status_t 937 TReplicantTray::ItemInfo(int32 index, const char** name, int32* id) 938 { 939 if (index < 0) 940 return B_ERROR; 941 942 BView* view; 943 fShelf->ReplicantAt(index, &view, (uint32*)id, NULL); 944 if (view) { 945 *name = view->Name(); 946 return B_OK; 947 } 948 949 return B_ERROR; 950 } 951 952 953 /** replicant exists, by id/index */ 954 955 bool 956 TReplicantTray::IconExists(int32 target, bool byIndex) 957 { 958 int32 index, id; 959 BView* view = ViewAt(&index, &id, target, byIndex); 960 961 return view && index >= 0; 962 } 963 964 965 /** replicant exists, by name */ 966 967 bool 968 TReplicantTray::IconExists(const char* name) 969 { 970 if (!name || strlen(name) == 0) 971 return false; 972 973 int32 index, id; 974 BView* view = ViewAt(&index, &id, name); 975 976 return view && index >= 0; 977 } 978 979 980 int32 981 TReplicantTray::IconCount() const 982 { 983 return fShelf->CountReplicants(); 984 } 985 986 987 /*! Message must contain an archivable view for later rehydration. 988 This function takes over ownership of the provided message on success 989 only. 990 Returns the current replicant ID. 991 */ 992 status_t 993 TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn) 994 { 995 if (archive == NULL || id == NULL) 996 return B_ERROR; 997 998 // find entry_ref 999 1000 entry_ref ref; 1001 if (addOn) { 1002 // Use it if we got it 1003 ref = *addOn; 1004 } else { 1005 const char* signature; 1006 1007 status_t status = archive->FindString("add_on", &signature); 1008 if (status == B_OK) { 1009 BRoster roster; 1010 status = roster.FindApp(signature, &ref); 1011 } 1012 if (status < B_OK) 1013 return status; 1014 } 1015 1016 BFile file; 1017 status_t status = file.SetTo(&ref, B_READ_ONLY); 1018 if (status < B_OK) 1019 return status; 1020 1021 node_ref nodeRef; 1022 status = file.GetNodeRef(&nodeRef); 1023 if (status < B_OK) 1024 return status; 1025 1026 BEntry entry(&ref, true); 1027 // TODO: this resolves an eventual link for the item being added - this 1028 // is okay for now, but in multi-user environments, one might want to 1029 // have links that carry the be:deskbar_item_status attribute 1030 status = entry.InitCheck(); 1031 if (status != B_OK) 1032 return status; 1033 1034 *id = 999; 1035 if (archive->what == B_ARCHIVED_OBJECT) 1036 archive->what = 0; 1037 1038 BRect originalBounds = archive->FindRect("_frame"); 1039 // this is a work-around for buggy replicants that change their size in 1040 // AttachedToWindow() (such as "SVM") 1041 1042 // TODO: check for name collisions? 1043 status = fShelf->AddReplicant(archive, BPoint(1, 1)); 1044 if (status != B_OK) 1045 return status; 1046 1047 int32 count = fShelf->CountReplicants(); 1048 BView* view; 1049 fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL); 1050 1051 if (originalBounds != view->Bounds()) { 1052 // The replicant changed its size when added to the window, so we need 1053 // to recompute all over again (it's already done once via 1054 // BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView()) 1055 RealignReplicants(); 1056 } 1057 1058 float oldWidth = Bounds().Width(); 1059 float oldHeight = Bounds().Height(); 1060 float width, height; 1061 GetPreferredSize(&width, &height); 1062 if (oldWidth != width || oldHeight != height) 1063 AdjustPlacement(); 1064 1065 // add the item to the add-on list 1066 1067 AddItem(*id, nodeRef, entry, addOn != NULL); 1068 return B_OK; 1069 } 1070 1071 1072 void 1073 TReplicantTray::RemoveIcon(int32 target, bool byIndex) 1074 { 1075 if (target < 0) 1076 return; 1077 1078 int32 index, id; 1079 BView* view = ViewAt(&index, &id, target, byIndex); 1080 if (view && index >= 0) { 1081 // remove the reference from the item list & the shelf 1082 RemoveItem(id); 1083 fShelf->DeleteReplicant(index); 1084 1085 // force a placement update, !! need to fix BShelf 1086 RealReplicantAdjustment(index); 1087 } 1088 } 1089 1090 1091 void 1092 TReplicantTray::RemoveIcon(const char* name) 1093 { 1094 if (!name || strlen(name) <= 0) 1095 return; 1096 1097 int32 id, index; 1098 BView* view = ViewAt(&index, &id, name); 1099 if (view && index >= 0) { 1100 // remove the reference from the item list & shelf 1101 RemoveItem(id); 1102 fShelf->DeleteReplicant(index); 1103 1104 // force a placement update, !! need to fix BShelf 1105 RealReplicantAdjustment(index); 1106 } 1107 } 1108 1109 1110 void 1111 TReplicantTray::RealReplicantAdjustment(int32 startIndex) 1112 { 1113 if (startIndex < 0) 1114 return; 1115 1116 if (startIndex == fLastReplicant) 1117 startIndex = 0; 1118 1119 // reset the locations of all replicants after the one deleted 1120 RealignReplicants(startIndex); 1121 1122 float oldWidth = Bounds().Width(); 1123 float oldHeight = Bounds().Height(); 1124 float width, height; 1125 GetPreferredSize(&width, &height); 1126 if (oldWidth != width || oldHeight != height) { 1127 // resize view to accomodate the replicants, redraw as necessary 1128 AdjustPlacement(); 1129 } 1130 } 1131 1132 1133 /** looking for a replicant by id/index 1134 * return the view and index 1135 */ 1136 1137 BView* 1138 TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex) 1139 { 1140 *index = -1; 1141 1142 BView* view; 1143 if (byIndex) { 1144 if (fShelf->ReplicantAt(target, &view, (uint32*)id)) { 1145 if (view) { 1146 *index = target; 1147 return view; 1148 } 1149 } 1150 } else { 1151 int32 count = fShelf->CountReplicants() - 1; 1152 int32 localid; 1153 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) { 1154 fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid); 1155 if (localid == target && view) { 1156 *index = repIndex; 1157 *id = localid; 1158 return view; 1159 } 1160 } 1161 } 1162 return NULL; 1163 } 1164 1165 1166 /** looking for a replicant with a view by name 1167 * return the view, index and the id of the replicant 1168 */ 1169 1170 BView* 1171 TReplicantTray::ViewAt(int32* index, int32* id, const char* name) 1172 { 1173 *index = -1; 1174 *id = -1; 1175 1176 BView* view; 1177 int32 count = fShelf->CountReplicants()-1; 1178 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) { 1179 fShelf->ReplicantAt(repIndex, &view, (uint32*)id); 1180 if (view && view->Name() && strcmp(name, view->Name()) == 0) { 1181 *index = repIndex; 1182 return view; 1183 } 1184 } 1185 return NULL; 1186 } 1187 1188 1189 /** Shelf will call to determine where and if 1190 * the replicant is to be added 1191 */ 1192 1193 bool 1194 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message) 1195 { 1196 if (!message) 1197 return false; 1198 1199 if (replicantFrame.Height() > kMaxReplicantHeight) 1200 return false; 1201 1202 alignment align = B_ALIGN_LEFT; 1203 if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) { 1204 if (!fBarView->Vertical()) 1205 align = B_ALIGN_RIGHT; 1206 else 1207 align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT; 1208 } else if (message->HasInt32("deskbar:align")) 1209 message->FindInt32("deskbar:align", (int32*)&align); 1210 1211 if (message->HasInt32("deskbar:private_align")) 1212 message->FindInt32("deskbar:private_align", (int32*)&align); 1213 else 1214 align = B_ALIGN_LEFT; 1215 1216 BPoint loc = LocationForReplicant(fShelf->CountReplicants(), 1217 replicantFrame.Width()); 1218 1219 message->AddPoint("_pjp_loc", loc); 1220 return true; 1221 } 1222 1223 1224 /** based on the previous (index - 1) replicant in the list 1225 * calculate where the left point should be for this 1226 * replicant. replicant will flow to the right on its own 1227 */ 1228 1229 BPoint 1230 TReplicantTray::LocationForReplicant(int32 index, float width) 1231 { 1232 BPoint loc(kIconGap + 1, kGutter + 1); 1233 1234 if (fMultiRowMode) { 1235 // try to find free space in every row 1236 for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) { 1237 // determine free space in this row 1238 BRect rect(loc.x, loc.y, loc.x + fMinimumTrayWidth - kIconGap - 2.0, 1239 loc.y + kMaxReplicantHeight); 1240 if (row == 0 && fBarView->ShowingClock()) 1241 rect.right -= fClock->Frame().Width() + kIconGap; 1242 1243 for (int32 i = 0; i < index; i++) { 1244 BView* view = NULL; 1245 fShelf->ReplicantAt(i, &view); 1246 if (view == NULL || view->Frame().top != rect.top) 1247 continue; 1248 1249 rect.left = view->Frame().right + kIconGap + 1; 1250 } 1251 1252 if (rect.Width() >= width) { 1253 // the icon fits in this row 1254 loc = rect.LeftTop(); 1255 break; 1256 } 1257 } 1258 } else { 1259 if (index > 0) { 1260 // get the last replicant added for placement reference 1261 BView* view = NULL; 1262 fShelf->ReplicantAt(index - 1, &view); 1263 if (view) { 1264 // push this rep placement past the last one 1265 loc.x = view->Frame().right + kIconGap + 1; 1266 loc.y = view->Frame().top; 1267 } 1268 } 1269 } 1270 1271 if ((loc.y == fRightBottomReplicant.top && loc.x 1272 > fRightBottomReplicant.left) || loc.y > fRightBottomReplicant.top) { 1273 fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y 1274 + kMaxReplicantHeight); 1275 fLastReplicant = index; 1276 } 1277 1278 return loc; 1279 } 1280 1281 1282 BRect 1283 TReplicantTray::IconFrame(int32 target, bool byIndex) 1284 { 1285 int32 index, id; 1286 BView* view = ViewAt(&index, &id, target, byIndex); 1287 if (view) 1288 return view->Frame(); 1289 1290 return BRect(0, 0, 0, 0); 1291 } 1292 1293 1294 BRect 1295 TReplicantTray::IconFrame(const char* name) 1296 { 1297 if (!name) 1298 return BRect(0, 0, 0, 0); 1299 1300 int32 id, index; 1301 BView* view = ViewAt(&index, &id, name); 1302 if (view) 1303 return view->Frame(); 1304 1305 return BRect(0, 0, 0, 0); 1306 } 1307 1308 1309 /** Scan from the startIndex and reset the location 1310 * as defined in LocationForReplicant() 1311 */ 1312 1313 void 1314 TReplicantTray::RealignReplicants(int32 startIndex) 1315 { 1316 if (startIndex < 0) 1317 startIndex = 0; 1318 1319 int32 count = fShelf->CountReplicants(); 1320 if (count <= 0) 1321 return; 1322 1323 if (startIndex == 0) 1324 fRightBottomReplicant.Set(0, 0, 0, 0); 1325 1326 BView* view = NULL; 1327 for (int32 i = startIndex ; i < count ; i++) { 1328 fShelf->ReplicantAt(i, &view); 1329 if (view != NULL) { 1330 BPoint loc = LocationForReplicant(i, view->Frame().Width()); 1331 if (view->Frame().LeftTop() != loc) 1332 view->MoveTo(loc); 1333 } 1334 } 1335 } 1336 1337 1338 void 1339 TReplicantTray::SetMultiRow(bool state) 1340 { 1341 fMultiRowMode = state; 1342 } 1343 1344 1345 // #pragma mark - 1346 1347 1348 /*! Draggable region that is asynchronous so that dragging does not block 1349 other activities. 1350 */ 1351 TDragRegion::TDragRegion(TBarView* parent, BView* child) 1352 : 1353 BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE, 1354 B_WILL_DRAW | B_FRAME_EVENTS), 1355 fBarView(parent), 1356 fChild(child), 1357 fDragLocation(kAutoPlaceDragRegion) 1358 { 1359 } 1360 1361 1362 void 1363 TDragRegion::AttachedToWindow() 1364 { 1365 BView::AttachedToWindow(); 1366 if (be_control_look != NULL) 1367 SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 1.1)); 1368 else 1369 SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 1370 ResizeToPreferred(); 1371 } 1372 1373 1374 void 1375 TDragRegion::GetPreferredSize(float* width, float* height) 1376 { 1377 fChild->ResizeToPreferred(); 1378 *width = fChild->Bounds().Width(); 1379 *height = fChild->Bounds().Height(); 1380 1381 if (fDragLocation != kNoDragRegion) 1382 *width += 7; 1383 else 1384 *width += 6; 1385 1386 *height += 3; 1387 } 1388 1389 1390 void 1391 TDragRegion::FrameMoved(BPoint) 1392 { 1393 if (fBarView->Left() && fBarView->Vertical() 1394 && fDragLocation != kNoDragRegion) 1395 fChild->MoveTo(5, 2); 1396 else 1397 fChild->MoveTo(2, 2); 1398 } 1399 1400 1401 void 1402 TDragRegion::Draw(BRect) 1403 { 1404 rgb_color menuColor = ViewColor(); 1405 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 1406 rgb_color ldark = tint_color(menuColor, 1.02); 1407 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT); 1408 rgb_color vdark = tint_color(menuColor, B_DARKEN_3_TINT); 1409 rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT); 1410 rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT); 1411 1412 BRect frame(Bounds()); 1413 BeginLineArray(4); 1414 1415 if (be_control_look != NULL) { 1416 if (fBarView->Vertical()) { 1417 AddLine(frame.LeftTop(), frame.RightTop(), dark); 1418 AddLine(BPoint(frame.left, frame.top + 1), 1419 BPoint(frame.right, frame.top + 1), ldark); 1420 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite); 1421 } else if (fBarView->AcrossTop() || fBarView->AcrossBottom()) { 1422 AddLine(frame.LeftTop(), 1423 BPoint(frame.left, frame.bottom), dark); 1424 AddLine(BPoint(frame.left + 1, frame.top + 1), 1425 BPoint(frame.right - 1, frame.top + 1), light); 1426 AddLine(BPoint(frame.right, frame.top + 2), 1427 BPoint(frame.right, frame.bottom), hilite); 1428 AddLine(BPoint(frame.left + 1, frame.bottom), 1429 BPoint(frame.right - 1, frame.bottom), hilite); 1430 } 1431 } else { 1432 if (fBarView->Vertical()) { 1433 AddLine(frame.LeftTop(), frame.RightTop(), light); 1434 AddLine(frame.LeftTop(), frame.LeftBottom(), light); 1435 AddLine(frame.RightBottom(), frame.RightTop(), hilite); 1436 } else if (fBarView->AcrossTop()) { 1437 AddLine(BPoint(frame.left, frame.top + 1), 1438 BPoint(frame.right - 1, frame.top + 1), light); 1439 AddLine(frame.RightTop(), frame.RightBottom(), vvdark); 1440 AddLine(BPoint(frame.right - 1, frame.top + 2), 1441 BPoint(frame.right - 1, frame.bottom - 1), hilite); 1442 AddLine(frame.LeftBottom(), 1443 BPoint(frame.right - 1, frame.bottom), hilite); 1444 } else if (fBarView->AcrossBottom()) { 1445 AddLine(BPoint(frame.left, frame.top + 1), 1446 BPoint(frame.right - 1, frame.top + 1), light); 1447 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite); 1448 AddLine(frame.RightTop(), frame.RightBottom(), vvdark); 1449 AddLine(BPoint(frame.right - 1, frame.top + 1), 1450 BPoint(frame.right - 1, frame.bottom - 1), hilite); 1451 } 1452 } 1453 1454 EndLineArray(); 1455 1456 if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion) 1457 DrawDragRegion(); 1458 } 1459 1460 1461 void 1462 TDragRegion::DrawDragRegion() 1463 { 1464 BRect dragRegion(DragRegion()); 1465 1466 rgb_color menuColor = ViewColor(); 1467 rgb_color menuHilite = menuColor; 1468 if (IsTracking()) { 1469 // Draw drag region highlighted if tracking mouse 1470 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT); 1471 SetHighColor(menuHilite); 1472 FillRect(dragRegion); 1473 } 1474 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT); 1475 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT); 1476 1477 BeginLineArray(dragRegion.IntegerHeight()); 1478 BPoint pt; 1479 pt.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1; 1480 pt.y = dragRegion.top + 2; 1481 1482 while (pt.y + 1 <= dragRegion.bottom) { 1483 AddLine(pt, pt, vdark); 1484 AddLine(pt + BPoint(1, 1), pt + BPoint(1, 1), light); 1485 1486 pt.y += 3; 1487 } 1488 EndLineArray(); 1489 } 1490 1491 1492 BRect 1493 TDragRegion::DragRegion() const 1494 { 1495 float kTopBottomInset = 2; 1496 float kLeftRightInset = 1; 1497 float kDragWidth = 3; 1498 if (be_control_look != NULL) { 1499 kTopBottomInset = 1; 1500 kLeftRightInset = 0; 1501 kDragWidth = 4; 1502 } 1503 1504 BRect dragRegion(Bounds()); 1505 dragRegion.top += kTopBottomInset; 1506 dragRegion.bottom -= kTopBottomInset; 1507 1508 bool placeOnLeft = false; 1509 if (fDragLocation == kAutoPlaceDragRegion) { 1510 if (fBarView->Vertical() && fBarView->Left()) 1511 placeOnLeft = true; 1512 else 1513 placeOnLeft = false; 1514 } else if (fDragLocation == kDragRegionLeft) 1515 placeOnLeft = true; 1516 else if (fDragLocation == kDragRegionRight) 1517 placeOnLeft = false; 1518 1519 if (placeOnLeft) { 1520 dragRegion.left += kLeftRightInset; 1521 dragRegion.right = dragRegion.left + kDragWidth; 1522 } else { 1523 dragRegion.right -= kLeftRightInset; 1524 dragRegion.left = dragRegion.right - kDragWidth; 1525 } 1526 1527 return dragRegion; 1528 } 1529 1530 1531 void 1532 TDragRegion::MouseDown(BPoint thePoint) 1533 { 1534 ulong buttons; 1535 BPoint where; 1536 BRect dragRegion(DragRegion()); 1537 1538 dragRegion.InsetBy(-2.0f, -2.0f); 1539 // DragRegion() is designed for drawing, not clicking 1540 1541 if (!dragRegion.Contains(thePoint)) 1542 return; 1543 1544 while (true) { 1545 GetMouse(&where, &buttons); 1546 if (!buttons) 1547 break; 1548 1549 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) { 1550 fPreviousPosition = thePoint; 1551 SetTracking(true); 1552 SetMouseEventMask(B_POINTER_EVENTS, 1553 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 1554 Invalidate(DragRegion()); 1555 break; 1556 } 1557 1558 snooze(25000); 1559 } 1560 } 1561 1562 1563 void 1564 TDragRegion::MouseUp(BPoint pt) 1565 { 1566 if (IsTracking()) { 1567 SetTracking(false); 1568 Invalidate(DragRegion()); 1569 } else 1570 BControl::MouseUp(pt); 1571 } 1572 1573 1574 bool 1575 TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect, 1576 bool newVertical, bool newLeft, bool newTop, int32 newState) 1577 { 1578 if (!rect.Contains(mouse)) { 1579 // not our rect 1580 return false; 1581 } 1582 1583 if (newVertical == fBarView->Vertical() && newLeft == fBarView->Left() 1584 && newTop == fBarView->Top() && newState == fBarView->State()) { 1585 // already in the correct mode 1586 return true; 1587 } 1588 1589 fBarView->ChangeState(newState, newVertical, newLeft, newTop); 1590 return true; 1591 } 1592 1593 1594 void 1595 TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage* message) 1596 { 1597 if (IsTracking()) { 1598 BScreen screen; 1599 BRect frame = screen.Frame(); 1600 1601 float hDivider = frame.Width() / 6; 1602 hDivider = (hDivider < sMinimumWindowWidth + 10.0f) 1603 ? sMinimumWindowWidth + 10.0f : hDivider; 1604 float miniDivider = frame.top + kMiniHeight + 10.0f; 1605 float vDivider = frame.Height() / 2; 1606 #ifdef FULL_MODE 1607 float thirdScreen = frame.Height() / 3; 1608 #endif 1609 BRect topLeft(frame.left, frame.top, frame.left + hDivider, 1610 miniDivider); 1611 BRect topMiddle(frame.left + hDivider, frame.top, frame.right 1612 - hDivider, vDivider); 1613 BRect topRight(frame.right - hDivider, frame.top, frame.right, 1614 miniDivider); 1615 1616 #ifdef FULL_MODE 1617 vDivider = miniDivider + thirdScreen; 1618 #endif 1619 BRect middleLeft(frame.left, miniDivider, frame.left + hDivider, 1620 vDivider); 1621 BRect middleRight(frame.right - hDivider, miniDivider, frame.right, 1622 vDivider); 1623 1624 #ifdef FULL_MODE 1625 BRect leftSide(frame.left, vDivider, frame.left + hDivider, 1626 frame.bottom - thirdScreen); 1627 BRect rightSide(frame.right - hDivider, vDivider, frame.right, 1628 frame.bottom - thirdScreen); 1629 1630 vDivider = frame.bottom - thirdScreen; 1631 #endif 1632 BRect bottomLeft(frame.left, vDivider, frame.left + hDivider, 1633 frame.bottom); 1634 BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right 1635 - hDivider, frame.bottom); 1636 BRect bottomRight(frame.right - hDivider, vDivider, frame.right, 1637 frame.bottom); 1638 1639 if (where != fPreviousPosition) { 1640 fPreviousPosition = where; 1641 ConvertToScreen(&where); 1642 1643 // use short circuit evaluation for convenience 1644 if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState) 1645 || SwitchModeForRect(where, topMiddle, false, true, true, 1646 kExpandoState) 1647 || SwitchModeForRect(where, topRight, true, false, true, 1648 kMiniState) 1649 || SwitchModeForRect(where, middleLeft, true, true, true, 1650 kExpandoState) 1651 || SwitchModeForRect(where, middleRight, true, false, true, 1652 kExpandoState) 1653 1654 #ifdef FULL_MODE 1655 || SwitchModeForRect(where, leftSide, true, true, true, 1656 kFullState) 1657 || SwitchModeForRect(where, rightSide, true, false, true, 1658 kFullState) 1659 #endif 1660 || SwitchModeForRect(where, bottomLeft, true, true, false, 1661 kMiniState) 1662 || SwitchModeForRect(where, bottomMiddle, false, true, false, 1663 kExpandoState) 1664 || SwitchModeForRect(where, bottomRight, true, false, false, 1665 kMiniState)) 1666 ; 1667 } 1668 } else 1669 BControl::MouseMoved(where, code, message); 1670 } 1671 1672 1673 int32 1674 TDragRegion::DragRegionLocation() const 1675 { 1676 return fDragLocation; 1677 } 1678 1679 1680 void 1681 TDragRegion::SetDragRegionLocation(int32 location) 1682 { 1683 if (location == fDragLocation) 1684 return; 1685 1686 fDragLocation = location; 1687 Invalidate(); 1688 } 1689 1690