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