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