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