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