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 BVolume volume(device); 719 RunAddOnQuery(&volume, kEnabledPredicate); 720 break; 721 } 722 723 case B_DEVICE_UNMOUNTED: 724 { 725 // remove all items associated with the device 726 // unmounted 727 // contrary to what the BeBook says, the item is called "device", 728 // not "new device" like it is for B_DEVICE_MOUNTED 729 dev_t device; 730 if (message->FindInt32("device", &device) != B_OK) 731 break; 732 733 UnloadAddOn(NULL, &device, false, true); 734 break; 735 } 736 } 737 } 738 739 740 /*! 741 The add-ons must support the exported C function API 742 if they do, they will be loaded and added to deskbar 743 primary function is the Instantiate function 744 */ 745 status_t 746 TReplicantTray::LoadAddOn(BEntry* entry, int32* id, bool force) 747 { 748 if (!entry) 749 return B_ERROR; 750 751 node_ref nodeRef; 752 entry->GetNodeRef(&nodeRef); 753 // no duplicates 754 if (NodeExists(nodeRef)) 755 return B_ERROR; 756 757 BNode node(entry); 758 if (!force) { 759 status_t error = node.InitCheck(); 760 if (error != B_OK) 761 return error; 762 763 uint64 deskbarID; 764 ssize_t size = node.ReadAttr(kDeskbarSecurityCodeAttr, B_UINT64_TYPE, 765 0, &deskbarID, sizeof(fDeskbarSecurityCode)); 766 if (size != sizeof(fDeskbarSecurityCode) 767 || deskbarID != fDeskbarSecurityCode) { 768 // no code or code doesn't match 769 return B_ERROR; 770 } 771 } 772 773 BPath path; 774 status_t status = entry->GetPath(&path); 775 if (status < B_OK) 776 return status; 777 778 // load the add-on 779 image_id image = load_add_on(path.Path()); 780 if (image < B_OK) 781 return image; 782 783 // get the view loading function symbol 784 // we first look for a symbol that takes an image_id 785 // and entry_ref pointer, if not found, go with normal 786 // instantiate function 787 BView* (*entryFunction)(image_id, const entry_ref*); 788 BView* (*itemFunction)(void); 789 BView* view = NULL; 790 791 entry_ref ref; 792 entry->GetRef(&ref); 793 794 if (get_image_symbol(image, kInstantiateEntryCFunctionName, 795 B_SYMBOL_TYPE_TEXT, (void**)&entryFunction) >= B_OK) { 796 view = (*entryFunction)(image, &ref); 797 } else if (get_image_symbol(image, kInstantiateItemCFunctionName, 798 B_SYMBOL_TYPE_TEXT, (void**)&itemFunction) >= B_OK) { 799 view = (*itemFunction)(); 800 } else { 801 unload_add_on(image); 802 return B_ERROR; 803 } 804 805 if (view == NULL || IconExists(view->Name())) { 806 delete view; 807 unload_add_on(image); 808 return B_ERROR; 809 } 810 811 BMessage* data = new BMessage; 812 view->Archive(data); 813 delete view; 814 815 AddIcon(data, id, &ref); 816 // add the rep; adds info to list 817 818 node.WriteAttr(kDeskbarSecurityCodeAttr, B_UINT64_TYPE, 0, 819 &fDeskbarSecurityCode, sizeof(fDeskbarSecurityCode)); 820 821 return B_OK; 822 } 823 824 825 status_t 826 TReplicantTray::AddItem(int32 id, node_ref nodeRef, BEntry& entry, bool isAddOn) 827 { 828 DeskbarItemInfo* item = new DeskbarItemInfo; 829 if (item == NULL) 830 return B_NO_MEMORY; 831 832 item->id = id; 833 item->isAddOn = isAddOn; 834 835 if (entry.GetRef(&item->entryRef) < B_OK) { 836 item->entryRef.device = -1; 837 item->entryRef.directory = -1; 838 item->entryRef.name = NULL; 839 } 840 item->nodeRef = nodeRef; 841 842 fItemList->AddItem(item); 843 844 if (isAddOn) 845 watch_node(&nodeRef, B_WATCH_NAME | B_WATCH_ATTR, this, Window()); 846 847 return B_OK; 848 } 849 850 851 /** from entry_removed message, when attribute removed 852 * or when a device is unmounted (use removeall, by device) 853 */ 854 855 void 856 TReplicantTray::UnloadAddOn(node_ref* nodeRef, dev_t* device, 857 bool which, bool removeAll) 858 { 859 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 860 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 861 if (!item) 862 continue; 863 864 if ((which && nodeRef && item->nodeRef == *nodeRef) 865 || (device && item->nodeRef.device == *device)) { 866 867 if (device && be_roster->IsRunning(&item->entryRef)) 868 continue; 869 870 RemoveIcon(item->id); 871 872 if (!removeAll) 873 break; 874 } 875 } 876 } 877 878 879 void 880 TReplicantTray::RemoveItem(int32 id) 881 { 882 DeskbarItemInfo* item = DeskbarItemFor(id); 883 if (item == NULL) 884 return; 885 886 // attribute was added via Deskbar API (AddItem(entry_ref*, int32*) 887 if (item->isAddOn) { 888 BNode node(&item->entryRef); 889 node.RemoveAttr(kStatusPredicate); 890 watch_node(&item->nodeRef, B_STOP_WATCHING, this, Window()); 891 } 892 893 fItemList->RemoveItem(item); 894 delete item; 895 } 896 897 898 /** ENTRY_MOVED message, moving only occurs on a device 899 * copying will occur (ENTRY_CREATED) between devices 900 */ 901 902 void 903 TReplicantTray::MoveItem(entry_ref* ref, ino_t toDirectory) 904 { 905 if (!ref) 906 return; 907 908 // scan for a matching entry_ref and update it 909 // 910 // don't need to change node info as it does not change 911 912 for (int32 i = fItemList->CountItems(); i-- > 0 ;) { 913 DeskbarItemInfo* item = (DeskbarItemInfo*)fItemList->ItemAt(i); 914 if (!item) 915 continue; 916 917 if (!strcmp(item->entryRef.name, ref->name) 918 && item->entryRef.device == ref->device 919 && item->entryRef.directory == ref->directory) { 920 item->entryRef.directory = toDirectory; 921 break; 922 } 923 } 924 } 925 926 #endif // add-on support 927 928 // external add-on API routines 929 // called using the new BDeskbar class 930 931 // existence of icon/replicant by name or ID 932 // returns opposite 933 // note: name and id are semi-private limiting 934 // the ability of non-host apps to remove 935 // icons without a little bit of work 936 937 /** for a specific id 938 * return the name of the replicant (name of view) 939 */ 940 941 status_t 942 TReplicantTray::ItemInfo(int32 id, const char** name) 943 { 944 if (id < 0) 945 return B_ERROR; 946 947 int32 index, temp; 948 BView* view = ViewAt(&index, &temp, id, false); 949 if (view) { 950 *name = view->Name(); 951 return B_OK; 952 } 953 954 return B_ERROR; 955 } 956 957 958 /** for a specific name 959 * return the id (internal to Deskbar) 960 */ 961 962 status_t 963 TReplicantTray::ItemInfo(const char* name, int32* id) 964 { 965 if (!name || strlen(name) <= 0) 966 return B_ERROR; 967 968 int32 index; 969 BView* view = ViewAt(&index, id, name); 970 if (view) 971 return B_OK; 972 973 return B_ERROR; 974 } 975 976 977 /** at a specific index 978 * return both the name and the id of the replicant 979 */ 980 981 status_t 982 TReplicantTray::ItemInfo(int32 index, const char** name, int32* id) 983 { 984 if (index < 0) 985 return B_ERROR; 986 987 BView* view; 988 fShelf->ReplicantAt(index, &view, (uint32*)id, NULL); 989 if (view) { 990 *name = view->Name(); 991 return B_OK; 992 } 993 994 return B_ERROR; 995 } 996 997 998 /** replicant exists, by id/index */ 999 1000 bool 1001 TReplicantTray::IconExists(int32 target, bool byIndex) 1002 { 1003 int32 index, id; 1004 BView* view = ViewAt(&index, &id, target, byIndex); 1005 1006 return view && index >= 0; 1007 } 1008 1009 1010 /** replicant exists, by name */ 1011 1012 bool 1013 TReplicantTray::IconExists(const char* name) 1014 { 1015 if (!name || strlen(name) == 0) 1016 return false; 1017 1018 int32 index, id; 1019 BView* view = ViewAt(&index, &id, name); 1020 1021 return view && index >= 0; 1022 } 1023 1024 1025 int32 1026 TReplicantTray::IconCount() const 1027 { 1028 return fShelf->CountReplicants(); 1029 } 1030 1031 1032 /*! Message must contain an archivable view for later rehydration. 1033 This function takes over ownership of the provided message on success 1034 only. 1035 Returns the current replicant ID. 1036 */ 1037 status_t 1038 TReplicantTray::AddIcon(BMessage* archive, int32* id, const entry_ref* addOn) 1039 { 1040 if (archive == NULL || id == NULL) 1041 return B_ERROR; 1042 1043 // find entry_ref 1044 1045 entry_ref ref; 1046 if (addOn) { 1047 // Use it if we got it 1048 ref = *addOn; 1049 } else { 1050 const char* signature; 1051 status_t status = archive->FindString("add_on", &signature); 1052 if (status == B_OK) { 1053 BRoster roster; 1054 status = roster.FindApp(signature, &ref); 1055 } 1056 if (status < B_OK) 1057 return status; 1058 } 1059 1060 BFile file; 1061 status_t status = file.SetTo(&ref, B_READ_ONLY); 1062 if (status < B_OK) 1063 return status; 1064 1065 node_ref nodeRef; 1066 status = file.GetNodeRef(&nodeRef); 1067 if (status < B_OK) 1068 return status; 1069 1070 BEntry entry(&ref, true); 1071 // ToDo: this resolves an eventual link for the item 1072 // being added - this is okay for now, but in multi-user 1073 // environments, one might want to have links that 1074 // carry the be:deskbar_item_status attribute 1075 status = entry.InitCheck(); 1076 if (status != B_OK) 1077 return status; 1078 1079 *id = 999; 1080 if (archive->what == B_ARCHIVED_OBJECT) 1081 archive->what = 0; 1082 1083 BRect originalBounds = archive->FindRect("_frame"); 1084 // this is a work-around for buggy replicants that change their 1085 // size in AttachedToWindow() (such as "SVM") 1086 1087 // !! check for name collisions? 1088 status = fShelf->AddReplicant(archive, BPoint(1, 1)); 1089 if (status != B_OK) 1090 return status; 1091 1092 int32 count = fShelf->CountReplicants(); 1093 BView* view; 1094 fShelf->ReplicantAt(count - 1, &view, (uint32*)id, NULL); 1095 1096 if (originalBounds != view->Bounds()) { 1097 // The replicant changed its size when added to the window, so we need 1098 // to recompute all over again (it's already done once via 1099 // BShelf::AddReplicant() and TReplicantShelf::CanAcceptReplicantView()) 1100 RealignReplicants(); 1101 } 1102 1103 float oldWidth = Bounds().Width(); 1104 float oldHeight = Bounds().Height(); 1105 float width, height; 1106 GetPreferredSize(&width, &height); 1107 if (oldWidth != width || oldHeight != height) 1108 AdjustPlacement(); 1109 1110 // add the item to the add-on list 1111 1112 AddItem(*id, nodeRef, entry, addOn != NULL); 1113 return B_OK; 1114 } 1115 1116 1117 void 1118 TReplicantTray::RemoveIcon(int32 target, bool byIndex) 1119 { 1120 if (target < 0) 1121 return; 1122 1123 int32 index, id; 1124 BView* view = ViewAt(&index, &id, target, byIndex); 1125 if (view && index >= 0) { 1126 // remove the reference from the item list & the shelf 1127 RemoveItem(id); 1128 fShelf->DeleteReplicant(index); 1129 1130 // force a placement update, !! need to fix BShelf 1131 RealReplicantAdjustment(index); 1132 } 1133 } 1134 1135 1136 void 1137 TReplicantTray::RemoveIcon(const char* name) 1138 { 1139 if (!name || strlen(name) <= 0) 1140 return; 1141 1142 int32 id, index; 1143 BView* view = ViewAt(&index, &id, name); 1144 if (view && index >= 0) { 1145 // remove the reference from the item list & shelf 1146 RemoveItem(id); 1147 fShelf->DeleteReplicant(index); 1148 1149 // force a placement update, !! need to fix BShelf 1150 RealReplicantAdjustment(index); 1151 } 1152 } 1153 1154 1155 void 1156 TReplicantTray::RealReplicantAdjustment(int32 startIndex) 1157 { 1158 if (startIndex < 0) 1159 return; 1160 1161 if (startIndex == fLastReplicant) 1162 startIndex = 0; 1163 1164 // reset the locations of all replicants after the one deleted 1165 RealignReplicants(startIndex); 1166 1167 float oldWidth = Bounds().Width(); 1168 float oldHeight = Bounds().Height(); 1169 float width, height; 1170 GetPreferredSize(&width, &height); 1171 if (oldWidth != width || oldHeight != height) { 1172 // resize view to accomodate the replicants 1173 // redraw as necessary 1174 AdjustPlacement(); 1175 } 1176 } 1177 1178 1179 /** looking for a replicant by id/index 1180 * return the view and index 1181 */ 1182 1183 BView* 1184 TReplicantTray::ViewAt(int32* index, int32* id, int32 target, bool byIndex) 1185 { 1186 *index = -1; 1187 1188 BView* view; 1189 if (byIndex) { 1190 if (fShelf->ReplicantAt(target, &view, (uint32*)id)) { 1191 if (view) { 1192 *index = target; 1193 return view; 1194 } 1195 } 1196 } else { 1197 int32 count = fShelf->CountReplicants()-1; 1198 int32 localid; 1199 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) { 1200 fShelf->ReplicantAt(repIndex, &view, (uint32*)&localid); 1201 if (localid == target && view) { 1202 *index = repIndex; 1203 *id = localid; 1204 return view; 1205 } 1206 } 1207 } 1208 1209 return NULL; 1210 } 1211 1212 1213 /** looking for a replicant with a view by name 1214 * return the view, index and the id of the replicant 1215 */ 1216 1217 BView* 1218 TReplicantTray::ViewAt(int32* index, int32* id, const char* name) 1219 { 1220 *index = -1; 1221 *id = -1; 1222 1223 BView* view; 1224 int32 count = fShelf->CountReplicants()-1; 1225 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) { 1226 fShelf->ReplicantAt(repIndex, &view, (uint32*)id); 1227 if (view && view->Name() && strcmp(name, view->Name()) == 0) { 1228 *index = repIndex; 1229 return view; 1230 } 1231 } 1232 1233 return NULL; 1234 } 1235 1236 1237 /** Shelf will call to determine where and if 1238 * the replicant is to be added 1239 */ 1240 1241 bool 1242 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage* message) 1243 { 1244 if (!message) 1245 return false; 1246 1247 if (replicantFrame.Height() > kMaxReplicantHeight) 1248 return false; 1249 1250 alignment align = B_ALIGN_LEFT; 1251 if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) { 1252 if (!fBarView->Vertical()) 1253 align = B_ALIGN_RIGHT; 1254 else 1255 align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT; 1256 } else if (message->HasInt32("deskbar:align")) 1257 message->FindInt32("deskbar:align", (int32*)&align); 1258 1259 if (message->HasInt32("deskbar:private_align")) 1260 message->FindInt32("deskbar:private_align", (int32*)&align); 1261 else 1262 align = B_ALIGN_LEFT; 1263 1264 BPoint loc = LocationForReplicant(fShelf->CountReplicants(), 1265 replicantFrame.Width()); 1266 1267 message->AddPoint("_pjp_loc", loc); 1268 return true; 1269 } 1270 1271 1272 /** based on the previous (index - 1) replicant in the list 1273 * calculate where the left point should be for this 1274 * replicant. replicant will flow to the right on its own 1275 */ 1276 1277 BPoint 1278 TReplicantTray::LocationForReplicant(int32 index, float width) 1279 { 1280 BPoint loc(kIconGap + 1, kGutter + 1); 1281 1282 if (fMultiRowMode) { 1283 // try to find free space in every row 1284 for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) { 1285 // determine free space in this row 1286 BRect rect(loc.x, loc.y, loc.x + fMinimumTrayWidth - kIconGap - 2.0, 1287 loc.y + kMaxReplicantHeight); 1288 if (row == 0 && fBarView->ShowingClock()) 1289 rect.right -= fClock->Frame().Width() + kIconGap; 1290 1291 for (int32 i = 0; i < index; i++) { 1292 BView* view = NULL; 1293 fShelf->ReplicantAt(i, &view); 1294 if (view == NULL || view->Frame().top != rect.top) 1295 continue; 1296 1297 rect.left = view->Frame().right + kIconGap + 1; 1298 } 1299 1300 if (rect.Width() >= width) { 1301 // the icon fits in this row 1302 loc = rect.LeftTop(); 1303 break; 1304 } 1305 } 1306 } else { 1307 if (index > 0) { 1308 // get the last replicant added for placement reference 1309 BView* view = NULL; 1310 fShelf->ReplicantAt(index - 1, &view); 1311 if (view) { 1312 // push this rep placement past the last one 1313 loc.x = view->Frame().right + kIconGap + 1; 1314 loc.y = view->Frame().top; 1315 } 1316 } 1317 } 1318 1319 if ((loc.y == fRightBottomReplicant.top && loc.x 1320 > fRightBottomReplicant.left) || loc.y > fRightBottomReplicant.top) { 1321 fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y 1322 + kMaxReplicantHeight); 1323 fLastReplicant = index; 1324 } 1325 1326 return loc; 1327 } 1328 1329 1330 BRect 1331 TReplicantTray::IconFrame(int32 target, bool byIndex) 1332 { 1333 int32 index, id; 1334 BView* view = ViewAt(&index, &id, target, byIndex); 1335 if (view) 1336 return view->Frame(); 1337 1338 return BRect(0, 0, 0, 0); 1339 } 1340 1341 1342 BRect 1343 TReplicantTray::IconFrame(const char* name) 1344 { 1345 if (!name) 1346 return BRect(0, 0, 0, 0); 1347 1348 int32 id, index; 1349 BView* view = ViewAt(&index, &id, name); 1350 if (view) 1351 return view->Frame(); 1352 1353 return BRect(0, 0, 0, 0); 1354 } 1355 1356 1357 /** Scan from the startIndex and reset the location 1358 * as defined in LocationForReplicant() 1359 */ 1360 1361 void 1362 TReplicantTray::RealignReplicants(int32 startIndex) 1363 { 1364 if (startIndex < 0) 1365 startIndex = 0; 1366 1367 int32 count = fShelf->CountReplicants(); 1368 if (count <= 0) 1369 return; 1370 1371 if (startIndex == 0) 1372 fRightBottomReplicant.Set(0, 0, 0, 0); 1373 1374 BView* view = NULL; 1375 for (int32 i = startIndex ; i < count ; i++) { 1376 fShelf->ReplicantAt(i, &view); 1377 if (view != NULL) { 1378 BPoint loc = LocationForReplicant(i, view->Frame().Width()); 1379 if (view->Frame().LeftTop() != loc) 1380 view->MoveTo(loc); 1381 } 1382 } 1383 } 1384 1385 1386 void 1387 TReplicantTray::SetMultiRow(bool state) 1388 { 1389 fMultiRowMode = state; 1390 1391 // in multi-row state, we only want the short date 1392 1393 if (fClock != NULL) 1394 fClock->AllowFullDate(!state); 1395 } 1396 1397 1398 // #pragma mark - 1399 1400 1401 /*! Draggable region that is asynchronous so that dragging does not block 1402 other activities. 1403 */ 1404 TDragRegion::TDragRegion(TBarView* parent, BView* child) 1405 : 1406 BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE, 1407 B_WILL_DRAW | B_FRAME_EVENTS), 1408 fBarView(parent), 1409 fChild(child), 1410 fDragLocation(kAutoPlaceDragRegion) 1411 { 1412 } 1413 1414 1415 void 1416 TDragRegion::AttachedToWindow() 1417 { 1418 BView::AttachedToWindow(); 1419 if (be_control_look != NULL) 1420 SetViewColor(tint_color(ui_color(B_MENU_BACKGROUND_COLOR), 1.1)); 1421 else 1422 SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 1423 ResizeToPreferred(); 1424 } 1425 1426 1427 void 1428 TDragRegion::GetPreferredSize(float* width, float* height) 1429 { 1430 fChild->ResizeToPreferred(); 1431 *width = fChild->Bounds().Width(); 1432 *height = fChild->Bounds().Height(); 1433 1434 if (fDragLocation != kNoDragRegion) 1435 *width += 7; 1436 else 1437 *width += 6; 1438 1439 *height += 3; 1440 } 1441 1442 1443 void 1444 TDragRegion::FrameMoved(BPoint) 1445 { 1446 if (fBarView->Left() && fBarView->Vertical() 1447 && fDragLocation != kNoDragRegion) 1448 fChild->MoveTo(5, 2); 1449 else 1450 fChild->MoveTo(2, 2); 1451 } 1452 1453 1454 void 1455 TDragRegion::Draw(BRect) 1456 { 1457 rgb_color menuColor = ViewColor(); 1458 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 1459 rgb_color ldark = tint_color(menuColor, 1.02); 1460 rgb_color dark = tint_color(menuColor, B_DARKEN_2_TINT); 1461 rgb_color vdark = tint_color(menuColor, B_DARKEN_3_TINT); 1462 rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT); 1463 rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT); 1464 1465 BRect frame(Bounds()); 1466 BeginLineArray(4); 1467 1468 if (be_control_look != NULL) { 1469 if (fBarView->Vertical()) { 1470 AddLine(frame.LeftTop(), frame.RightTop(), dark); 1471 AddLine(BPoint(frame.left, frame.top + 1), 1472 BPoint(frame.right, frame.top + 1), ldark); 1473 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite); 1474 } else if (fBarView->AcrossTop() || fBarView->AcrossBottom()) { 1475 AddLine(frame.LeftTop(), 1476 BPoint(frame.left, frame.bottom), dark); 1477 AddLine(BPoint(frame.left + 1, frame.top + 1), 1478 BPoint(frame.right - 1, frame.top + 1), light); 1479 AddLine(BPoint(frame.right, frame.top + 2), 1480 BPoint(frame.right, frame.bottom), hilite); 1481 AddLine(BPoint(frame.left + 1, frame.bottom), 1482 BPoint(frame.right - 1, frame.bottom), hilite); 1483 } 1484 } else { 1485 if (fBarView->Vertical()) { 1486 AddLine(frame.LeftTop(), frame.RightTop(), light); 1487 AddLine(frame.LeftTop(), frame.LeftBottom(), light); 1488 AddLine(frame.RightBottom(), frame.RightTop(), hilite); 1489 } else if (fBarView->AcrossTop()) { 1490 AddLine(BPoint(frame.left, frame.top + 1), 1491 BPoint(frame.right - 1, frame.top + 1), light); 1492 AddLine(frame.RightTop(), frame.RightBottom(), vvdark); 1493 AddLine(BPoint(frame.right - 1, frame.top + 2), 1494 BPoint(frame.right - 1, frame.bottom - 1), hilite); 1495 AddLine(frame.LeftBottom(), 1496 BPoint(frame.right - 1, frame.bottom), hilite); 1497 } else if (fBarView->AcrossBottom()) { 1498 AddLine(BPoint(frame.left, frame.top + 1), 1499 BPoint(frame.right - 1, frame.top + 1), light); 1500 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite); 1501 AddLine(frame.RightTop(), frame.RightBottom(), vvdark); 1502 AddLine(BPoint(frame.right - 1, frame.top + 1), 1503 BPoint(frame.right - 1, frame.bottom - 1), hilite); 1504 } 1505 } 1506 1507 EndLineArray(); 1508 1509 if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion) 1510 DrawDragRegion(); 1511 } 1512 1513 1514 void 1515 TDragRegion::DrawDragRegion() 1516 { 1517 BRect dragRegion(DragRegion()); 1518 1519 rgb_color menuColor = ViewColor(); 1520 rgb_color menuHilite = menuColor; 1521 if (IsTracking()) { 1522 // Draw drag region highlighted if tracking mouse 1523 menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT); 1524 SetHighColor(menuHilite); 1525 FillRect(dragRegion); 1526 } 1527 rgb_color vdark = tint_color(menuHilite, B_DARKEN_3_TINT); 1528 rgb_color light = tint_color(menuHilite, B_LIGHTEN_2_TINT); 1529 1530 BeginLineArray(dragRegion.IntegerHeight()); 1531 BPoint pt; 1532 pt.x = floorf((dragRegion.left + dragRegion.right) / 2 + 0.5) - 1; 1533 pt.y = dragRegion.top + 2; 1534 1535 while (pt.y + 1 <= dragRegion.bottom) { 1536 AddLine(pt, pt, vdark); 1537 AddLine(pt + BPoint(1, 1), pt + BPoint(1, 1), light); 1538 1539 pt.y += 3; 1540 } 1541 EndLineArray(); 1542 } 1543 1544 1545 BRect 1546 TDragRegion::DragRegion() const 1547 { 1548 float kTopBottomInset = 2; 1549 float kLeftRightInset = 1; 1550 float kDragWidth = 3; 1551 if (be_control_look != NULL) { 1552 kTopBottomInset = 1; 1553 kLeftRightInset = 0; 1554 kDragWidth = 4; 1555 } 1556 1557 BRect dragRegion(Bounds()); 1558 dragRegion.top += kTopBottomInset; 1559 dragRegion.bottom -= kTopBottomInset; 1560 1561 bool placeOnLeft = false; 1562 if (fDragLocation == kAutoPlaceDragRegion) { 1563 if (fBarView->Vertical() && fBarView->Left()) 1564 placeOnLeft = true; 1565 else 1566 placeOnLeft = false; 1567 } else if (fDragLocation == kDragRegionLeft) 1568 placeOnLeft = true; 1569 else if (fDragLocation == kDragRegionRight) 1570 placeOnLeft = false; 1571 1572 if (placeOnLeft) { 1573 dragRegion.left += kLeftRightInset; 1574 dragRegion.right = dragRegion.left + kDragWidth; 1575 } else { 1576 dragRegion.right -= kLeftRightInset; 1577 dragRegion.left = dragRegion.right - kDragWidth; 1578 } 1579 1580 return dragRegion; 1581 } 1582 1583 1584 void 1585 TDragRegion::MouseDown(BPoint thePoint) 1586 { 1587 ulong buttons; 1588 BPoint where; 1589 BRect dragRegion(DragRegion()); 1590 1591 dragRegion.InsetBy(-2.0f, -2.0f); 1592 // DragRegion() is designed for drawing, not clicking 1593 1594 if (!dragRegion.Contains(thePoint)) 1595 return; 1596 1597 while (true) { 1598 GetMouse(&where, &buttons); 1599 if (!buttons) 1600 break; 1601 1602 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) { 1603 fPreviousPosition = thePoint; 1604 SetTracking(true); 1605 SetMouseEventMask(B_POINTER_EVENTS, 1606 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 1607 Invalidate(DragRegion()); 1608 break; 1609 } 1610 1611 snooze(25000); 1612 } 1613 } 1614 1615 1616 void 1617 TDragRegion::MouseUp(BPoint pt) 1618 { 1619 if (IsTracking()) { 1620 SetTracking(false); 1621 Invalidate(DragRegion()); 1622 } else 1623 BControl::MouseUp(pt); 1624 } 1625 1626 1627 bool 1628 TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect, 1629 bool newVertical, bool newLeft, bool newTop, int32 newState) 1630 { 1631 if (!rect.Contains(mouse)) 1632 // not our rect 1633 return false; 1634 1635 if (newVertical == fBarView->Vertical() 1636 && newLeft == fBarView->Left() 1637 && newTop == fBarView->Top() 1638 && newState == fBarView->State()) 1639 // already in the correct mode 1640 return true; 1641 1642 fBarView->ChangeState(newState, newVertical, newLeft, newTop); 1643 return true; 1644 } 1645 1646 1647 void 1648 TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage* message) 1649 { 1650 if (IsTracking()) { 1651 BScreen screen; 1652 BRect frame = screen.Frame(); 1653 1654 float hDivider = frame.Width() / 6; 1655 hDivider = (hDivider < sMinimumWindowWidth + 10.0f) 1656 ? sMinimumWindowWidth + 10.0f : hDivider; 1657 float miniDivider = frame.top + kMiniHeight + 10.0f; 1658 float vDivider = frame.Height() / 2; 1659 #ifdef FULL_MODE 1660 float thirdScreen = frame.Height() / 3; 1661 #endif 1662 BRect topLeft(frame.left, frame.top, frame.left + hDivider, 1663 miniDivider); 1664 BRect topMiddle(frame.left + hDivider, frame.top, frame.right 1665 - hDivider, vDivider); 1666 BRect topRight(frame.right - hDivider, frame.top, frame.right, 1667 miniDivider); 1668 1669 #ifdef FULL_MODE 1670 vDivider = miniDivider + thirdScreen; 1671 #endif 1672 BRect middleLeft(frame.left, miniDivider, frame.left + hDivider, 1673 vDivider); 1674 BRect middleRight(frame.right - hDivider, miniDivider, frame.right, 1675 vDivider); 1676 1677 #ifdef FULL_MODE 1678 BRect leftSide(frame.left, vDivider, frame.left + hDivider, 1679 frame.bottom - thirdScreen); 1680 BRect rightSide(frame.right - hDivider, vDivider, frame.right, 1681 frame.bottom - thirdScreen); 1682 1683 vDivider = frame.bottom - thirdScreen; 1684 #endif 1685 BRect bottomLeft(frame.left, vDivider, frame.left + hDivider, 1686 frame.bottom); 1687 BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right 1688 - hDivider, frame.bottom); 1689 BRect bottomRight(frame.right - hDivider, vDivider, frame.right, 1690 frame.bottom); 1691 1692 if (where != fPreviousPosition) { 1693 fPreviousPosition = where; 1694 ConvertToScreen(&where); 1695 1696 // use short circuit evaluation for convenience 1697 if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState) 1698 || SwitchModeForRect(where, topMiddle, false, true, true, 1699 kExpandoState) 1700 || SwitchModeForRect(where, topRight, true, false, true, 1701 kMiniState) 1702 || SwitchModeForRect(where, middleLeft, true, true, true, 1703 kExpandoState) 1704 || SwitchModeForRect(where, middleRight, true, false, true, 1705 kExpandoState) 1706 1707 #ifdef FULL_MODE 1708 || SwitchModeForRect(where, leftSide, true, true, true, 1709 kFullState) 1710 || SwitchModeForRect(where, rightSide, true, false, true, 1711 kFullState) 1712 #endif 1713 || SwitchModeForRect(where, bottomLeft, true, true, false, 1714 kMiniState) 1715 || SwitchModeForRect(where, bottomMiddle, false, true, false, 1716 kExpandoState) 1717 || SwitchModeForRect(where, bottomRight, true, false, false, 1718 kMiniState)) 1719 ; 1720 } 1721 } else 1722 BControl::MouseMoved(where, code, message); 1723 } 1724 1725 1726 int32 1727 TDragRegion::DragRegionLocation() const 1728 { 1729 return fDragLocation; 1730 } 1731 1732 1733 void 1734 TDragRegion::SetDragRegionLocation(int32 location) 1735 { 1736 if (location == fDragLocation) 1737 return; 1738 1739 fDragLocation = location; 1740 Invalidate(); 1741 } 1742 1743