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