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