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