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