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 *id = 999; 1029 if (icon->what == B_ARCHIVED_OBJECT) 1030 icon->what = 0; 1031 1032 // !! check for name collisions? 1033 status_t err = fShelf->AddReplicant(icon, BPoint(1, 1)); 1034 if (err != B_OK) 1035 return err; 1036 1037 float oldWidth = Bounds().Width(); 1038 float oldHeight = Bounds().Height(); 1039 float width, height; 1040 GetPreferredSize(&width, &height); 1041 if (oldWidth != width || oldHeight != height) 1042 AdjustPlacement(); 1043 1044 int32 count = fShelf->CountReplicants(); 1045 BView *view; 1046 fShelf->ReplicantAt(count-1, &view, (uint32 *)id, NULL); 1047 1048 // add the item to the add-on list 1049 entry_ref ref; 1050 if (addOn) { 1051 // Use it if we got it 1052 ref = *addOn; 1053 } else { 1054 const char *appsig; 1055 icon->FindString("add_on", &appsig); 1056 BRoster roster; 1057 roster.FindApp(appsig, &ref); 1058 } 1059 1060 BFile file(&ref, B_READ_ONLY); 1061 node_ref nodeRef; 1062 file.GetNodeRef(&nodeRef); 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 AddItem(*id, nodeRef, entry, addOn != NULL); 1069 1070 return B_OK; 1071 } 1072 1073 1074 void 1075 TReplicantTray::RemoveIcon(int32 target, bool byIndex) 1076 { 1077 if (target < 0) 1078 return; 1079 1080 int32 index, id; 1081 BView *view = ViewAt(&index, &id, target, byIndex); 1082 if (view && index >= 0) { 1083 // remove the reference from the item list & the shelf 1084 RemoveItem(id); 1085 fShelf->DeleteReplicant(index); 1086 1087 // force a placement update, !! need to fix BShelf 1088 RealReplicantAdjustment(index); 1089 } 1090 } 1091 1092 1093 void 1094 TReplicantTray::RemoveIcon(const char *name) 1095 { 1096 if (!name || strlen(name) <= 0) 1097 return; 1098 1099 int32 id, index; 1100 BView *view = ViewAt(&index, &id, name); 1101 if (view && index >= 0) { 1102 // remove the reference from the item list & shelf 1103 RemoveItem(id); 1104 fShelf->DeleteReplicant(index); 1105 1106 // force a placement update, !! need to fix BShelf 1107 RealReplicantAdjustment(index); 1108 } 1109 } 1110 1111 1112 void 1113 TReplicantTray::RealReplicantAdjustment(int32 startIndex) 1114 { 1115 if (startIndex < 0) 1116 return; 1117 1118 if (startIndex == fLastReplicant) 1119 startIndex = 0; 1120 1121 // reset the locations of all replicants after the one deleted 1122 RealignReplicants(startIndex); 1123 1124 float oldWidth = Bounds().Width(); 1125 float oldHeight = Bounds().Height(); 1126 float width, height; 1127 GetPreferredSize(&width, &height); 1128 if (oldWidth != width || oldHeight != height) { 1129 // resize view to accomodate the replicants 1130 // redraw as necessary 1131 AdjustPlacement(); 1132 } 1133 } 1134 1135 1136 /** looking for a replicant by id/index 1137 * return the view and index 1138 */ 1139 1140 BView * 1141 TReplicantTray::ViewAt(int32 *index, int32 *id, int32 target, bool byIndex) 1142 { 1143 *index = -1; 1144 1145 BView *view; 1146 if (byIndex){ 1147 if (fShelf->ReplicantAt(target, &view, (uint32 *)id)) { 1148 if (view) { 1149 *index = target; 1150 return view; 1151 } 1152 } 1153 } else { 1154 int32 count = fShelf->CountReplicants()-1; 1155 int32 localid; 1156 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) { 1157 fShelf->ReplicantAt(repIndex, &view, (uint32 *)&localid); 1158 if (localid == target && view) { 1159 *index = repIndex; 1160 *id = localid; 1161 return view; 1162 } 1163 } 1164 } 1165 1166 return NULL; 1167 } 1168 1169 1170 /** looking for a replicant with a view by name 1171 * return the view, index and the id of the replicant 1172 */ 1173 1174 BView * 1175 TReplicantTray::ViewAt(int32 *index, int32 *id, const char *name) 1176 { 1177 *index = -1; 1178 *id = -1; 1179 1180 BView *view; 1181 int32 count = fShelf->CountReplicants()-1; 1182 for (int32 repIndex = count ; repIndex >= 0 ; repIndex--) { 1183 fShelf->ReplicantAt(repIndex, &view, (uint32 *)id); 1184 if (view && view->Name() && strcmp(name, view->Name()) == 0) { 1185 *index = repIndex; 1186 return view; 1187 } 1188 } 1189 1190 return NULL; 1191 } 1192 1193 1194 /** Shelf will call to determine where and if 1195 * the replicant is to be added 1196 */ 1197 1198 bool 1199 TReplicantTray::AcceptAddon(BRect replicantFrame, BMessage *message) 1200 { 1201 if (!message) 1202 return false; 1203 1204 if (replicantFrame.Height() > kMaxReplicantHeight) 1205 return false; 1206 1207 alignment align = B_ALIGN_LEFT; 1208 if (fAlignmentSupport && message->HasBool("deskbar:dynamic_align")) { 1209 if (!fBarView->Vertical()) 1210 align = B_ALIGN_RIGHT; 1211 else 1212 align = fBarView->Left() ? B_ALIGN_LEFT : B_ALIGN_RIGHT; 1213 } else if (message->HasInt32("deskbar:align")) 1214 message->FindInt32("deskbar:align", (int32 *)&align); 1215 1216 if (message->HasInt32("deskbar:private_align")) 1217 message->FindInt32("deskbar:private_align", (int32 *)&align); 1218 else 1219 align = B_ALIGN_LEFT; 1220 1221 BPoint loc = LocationForReplicant(fShelf->CountReplicants(), 1222 replicantFrame.Width()); 1223 1224 message->AddPoint("_pjp_loc", loc); 1225 return true; 1226 } 1227 1228 1229 /** based on the previous (index - 1) replicant in the list 1230 * calculate where the left point should be for this 1231 * replicant. replicant will flow to the right on its own 1232 */ 1233 1234 BPoint 1235 TReplicantTray::LocationForReplicant(int32 index, float width) 1236 { 1237 BPoint loc(kIconGap + 1, kGutter + 1); 1238 1239 if (fMultiRowMode) { 1240 // try to find free space in every row 1241 for (int32 row = 0; ; loc.y += kMaxReplicantHeight + kIconGap, row++) { 1242 // determine free space in this row 1243 BRect rect(loc.x, loc.y, loc.x + kMinimumTrayWidth + 2, kMaxReplicantHeight); 1244 if (row == 0 && fBarView->ShowingClock()) 1245 rect.right -= fClock->Frame().Width() + kIconGap; 1246 1247 for (int32 i = 0; i < index; i++) { 1248 BView *view = NULL; 1249 fShelf->ReplicantAt(i, &view); 1250 if (view == NULL || view->Frame().top != rect.top) 1251 continue; 1252 1253 rect.left = view->Frame().right + kIconGap + 1; 1254 } 1255 1256 if (rect.Width() >= width) { 1257 // the icon fits in this row 1258 loc = rect.LeftTop(); 1259 break; 1260 } 1261 } 1262 } else { 1263 if (index > 0) { 1264 // get the last replicant added for placement reference 1265 BView *view = NULL; 1266 fShelf->ReplicantAt(index - 1, &view); 1267 if (view) { 1268 // push this rep placement past the last one 1269 loc.x = view->Frame().right + kIconGap + 1; 1270 loc.y = view->Frame().top; 1271 } 1272 } 1273 } 1274 1275 if ((loc.y == fRightBottomReplicant.top && loc.x > fRightBottomReplicant.left) 1276 || loc.y > fRightBottomReplicant.top) { 1277 fRightBottomReplicant.Set(loc.x, loc.y, loc.x + width, loc.y + kMaxReplicantHeight); 1278 fLastReplicant = index; 1279 } 1280 1281 return loc; 1282 } 1283 1284 1285 BRect 1286 TReplicantTray::IconFrame(int32 target, bool byIndex) 1287 { 1288 int32 index, id; 1289 BView *view = ViewAt(&index, &id, target, byIndex); 1290 if (view) 1291 return view->Frame(); 1292 1293 return BRect(0, 0, 0, 0); 1294 } 1295 1296 1297 BRect 1298 TReplicantTray::IconFrame(const char *name) 1299 { 1300 if (!name) 1301 return BRect(0, 0, 0, 0); 1302 1303 int32 id, index; 1304 BView *view = ViewAt(&index, &id, name); 1305 if (view) 1306 return view->Frame(); 1307 1308 return BRect(0, 0, 0, 0); 1309 } 1310 1311 1312 /** Scan from the startIndex and reset the location 1313 * as defined in LocationForReplicant() 1314 */ 1315 1316 void 1317 TReplicantTray::RealignReplicants(int32 startIndex) 1318 { 1319 if (startIndex < 0) 1320 startIndex = 0; 1321 1322 int32 count = fShelf->CountReplicants(); 1323 if (count <= 0) 1324 return; 1325 1326 if (startIndex == 0) 1327 fRightBottomReplicant.Set(0, 0, 0, 0); 1328 1329 BView *view = NULL; 1330 for (int32 i = startIndex ; i < count ; i++){ 1331 fShelf->ReplicantAt(i, &view); 1332 BPoint loc = LocationForReplicant(i, view->Frame().Width()); 1333 if (view && (view->Frame().LeftTop() != loc)) { 1334 view->MoveTo(loc); 1335 } 1336 } 1337 } 1338 1339 1340 void 1341 TReplicantTray::SetMultiRow(bool state) 1342 { 1343 fMultiRowMode = state; 1344 1345 // in multi-row state, we only want the short date 1346 1347 if (fClock != NULL) 1348 fClock->AllowFullDate(!state); 1349 } 1350 1351 1352 // #pragma mark - 1353 1354 1355 /** draggable region that is asynchronous so that 1356 * dragging does not block other activities 1357 */ 1358 1359 TDragRegion::TDragRegion(TBarView *parent, BView *child) 1360 : BControl(BRect(0, 0, 0, 0), "", "", NULL, B_FOLLOW_NONE, 1361 B_WILL_DRAW | B_FRAME_EVENTS), 1362 fBarView(parent), 1363 fChild(child), 1364 fDragLocation(kAutoPlaceDragRegion) 1365 { 1366 } 1367 1368 1369 void 1370 TDragRegion::AttachedToWindow() 1371 { 1372 BView::AttachedToWindow(); 1373 SetViewColor(ui_color(B_MENU_BACKGROUND_COLOR)); 1374 ResizeToPreferred(); 1375 } 1376 1377 1378 void 1379 TDragRegion::GetPreferredSize(float *width, float *height) 1380 { 1381 fChild->ResizeToPreferred(); 1382 *width = fChild->Bounds().Width(); 1383 *height = fChild->Bounds().Height(); 1384 1385 if (fDragLocation != kNoDragRegion) 1386 *width += 7; 1387 else 1388 *width += 6; 1389 1390 *height += 3; 1391 } 1392 1393 1394 void 1395 TDragRegion::FrameMoved(BPoint) 1396 { 1397 if (fBarView->Left() && fBarView->Vertical() && fDragLocation != kNoDragRegion) 1398 fChild->MoveTo(5,2); 1399 else 1400 fChild->MoveTo(2,2); 1401 } 1402 1403 1404 void 1405 TDragRegion::Draw(BRect) 1406 { 1407 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR); 1408 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 1409 rgb_color vdark = tint_color(menuColor, B_DARKEN_3_TINT); 1410 rgb_color vvdark = tint_color(menuColor, B_DARKEN_4_TINT); 1411 rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT); 1412 1413 BRect frame(Bounds()); 1414 BeginLineArray(4); 1415 1416 if (fBarView->Vertical()) { 1417 AddLine(frame.LeftTop(), frame.RightTop(), light); 1418 AddLine(frame.LeftTop(), frame.LeftBottom(), light); 1419 AddLine(frame.RightBottom(), frame.RightTop(), hilite); 1420 } else if (fBarView->AcrossTop()) { 1421 AddLine(frame.LeftTop()+BPoint(0, 1), frame.RightTop()+BPoint(-1, 1), 1422 light); 1423 AddLine(frame.RightTop(), frame.RightBottom(), vvdark); 1424 AddLine(frame.RightTop()+BPoint(-1, 2),frame.RightBottom()+BPoint(-1, -1), 1425 hilite); 1426 AddLine(frame.LeftBottom(), frame.RightBottom()+BPoint(-1, 0), hilite); 1427 } else if (fBarView->AcrossBottom()) { 1428 AddLine(frame.LeftTop()+BPoint(0, 1), frame.RightTop()+BPoint(-1, 1), light); 1429 AddLine(frame.LeftBottom(), frame.RightBottom(), hilite); 1430 AddLine(frame.RightTop(), frame.RightBottom(), vvdark); 1431 AddLine(frame.RightTop()+BPoint(-1, 1),frame.RightBottom()+BPoint(-1, -1), 1432 hilite); 1433 } 1434 1435 EndLineArray(); 1436 1437 if (fDragLocation != kDontDrawDragRegion || fDragLocation != kNoDragRegion) 1438 DrawDragRegion(); 1439 } 1440 1441 1442 void 1443 TDragRegion::DrawDragRegion() 1444 { 1445 rgb_color menuColor = ui_color(B_MENU_BACKGROUND_COLOR); 1446 rgb_color menuHilite = tint_color(menuColor, B_HIGHLIGHT_BACKGROUND_TINT); 1447 rgb_color vdark = tint_color(menuColor, B_DARKEN_3_TINT); 1448 rgb_color light = tint_color(menuColor, B_LIGHTEN_2_TINT); 1449 1450 BRect dragRegion(DragRegion()); 1451 1452 BeginLineArray(dragRegion.IntegerHeight()); 1453 BPoint pt = dragRegion.LeftTop() + BPoint(1,1); 1454 1455 // Draw drag region highlighted if tracking mouse 1456 if (IsTracking()) { 1457 SetHighColor(menuHilite); 1458 FillRect(dragRegion); 1459 while (pt.y + 1 <= dragRegion.bottom) { 1460 AddLine(pt, pt, light); 1461 AddLine(pt+BPoint(1,1), pt+BPoint(1,1), vdark); 1462 1463 pt.y += 3; 1464 } 1465 } else { 1466 while (pt.y + 1 <= dragRegion.bottom) { 1467 AddLine(pt, pt, vdark); 1468 AddLine(pt+BPoint(1,1), pt+BPoint(1,1), light); 1469 1470 pt.y += 3; 1471 } 1472 } 1473 EndLineArray(); 1474 } 1475 1476 1477 BRect 1478 TDragRegion::DragRegion() const 1479 { 1480 BRect dragRegion(Bounds()); 1481 dragRegion.top += 2; 1482 dragRegion.bottom -= 2; 1483 1484 bool placeOnLeft=false; 1485 if (fDragLocation == kAutoPlaceDragRegion) { 1486 if (fBarView->Vertical() && fBarView->Left()) 1487 placeOnLeft = true; 1488 else 1489 placeOnLeft = false; 1490 } else if (fDragLocation == kDragRegionLeft) 1491 placeOnLeft = true; 1492 else if (fDragLocation == kDragRegionRight) 1493 placeOnLeft = false; 1494 1495 if (placeOnLeft) { 1496 dragRegion.left += 1; 1497 dragRegion.right = dragRegion.left + 3; 1498 } else { 1499 dragRegion.right -= 1; 1500 dragRegion.left = dragRegion.right - 3; 1501 } 1502 1503 return dragRegion; 1504 } 1505 1506 1507 void 1508 TDragRegion::MouseDown(BPoint thePoint) 1509 { 1510 ulong buttons; 1511 BPoint where; 1512 BRect dragRegion(DragRegion()); 1513 1514 dragRegion.InsetBy(-2.0f, -2.0f); 1515 // DragRegion() is designed for drawing, not clicking 1516 1517 if (!dragRegion.Contains(thePoint)) 1518 return; 1519 1520 while(true) { 1521 GetMouse(&where, &buttons); 1522 if (!buttons) 1523 break; 1524 1525 if ((Window()->Flags() & B_ASYNCHRONOUS_CONTROLS) != 0) { 1526 fPreviousPosition = thePoint; 1527 SetTracking(true); 1528 SetMouseEventMask(B_POINTER_EVENTS, 1529 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 1530 Invalidate(DragRegion()); 1531 break; 1532 } 1533 1534 snooze(25000); 1535 } 1536 } 1537 1538 1539 void 1540 TDragRegion::MouseUp(BPoint pt) 1541 { 1542 if (IsTracking()) { 1543 SetTracking(false); 1544 Invalidate(DragRegion()); 1545 } else 1546 BControl::MouseUp(pt); 1547 } 1548 1549 1550 bool 1551 TDragRegion::SwitchModeForRect(BPoint mouse, BRect rect, 1552 bool newVertical, bool newLeft, bool newTop, int32 newState) 1553 { 1554 if (!rect.Contains(mouse)) 1555 // not our rect 1556 return false; 1557 1558 if (newVertical == fBarView->Vertical() 1559 && newLeft == fBarView->Left() 1560 && newTop == fBarView->Top() 1561 && newState == fBarView->State()) 1562 // already in the correct mode 1563 return true; 1564 1565 fBarView->ChangeState(newState, newVertical, newLeft, newTop); 1566 return true; 1567 } 1568 1569 1570 void 1571 TDragRegion::MouseMoved(BPoint where, uint32 code, const BMessage *message) 1572 { 1573 if (IsTracking()) { 1574 BScreen screen; 1575 BRect frame = screen.Frame(); 1576 1577 float hDivider = frame.Width() / 6; 1578 hDivider = (hDivider < kMinimumWindowWidth + 10.0f) ? kMinimumWindowWidth + 10.0f : hDivider; 1579 float miniDivider = frame.top + kMiniHeight + 10.0f; 1580 float vDivider = frame.Height() / 2; 1581 #ifdef FULL_MODE 1582 float thirdScreen = frame.Height() / 3; 1583 #endif 1584 BRect topLeft(frame.left, frame.top, frame.left + hDivider, miniDivider); 1585 BRect topMiddle(frame.left + hDivider, frame.top, frame.right - hDivider, vDivider); 1586 BRect topRight(frame.right - hDivider, frame.top, frame.right, miniDivider); 1587 1588 #ifdef FULL_MODE 1589 vDivider = miniDivider + thirdScreen; 1590 #endif 1591 BRect middleLeft(frame.left, miniDivider, frame.left + hDivider, vDivider); 1592 BRect middleRight(frame.right - hDivider, miniDivider, frame.right, vDivider); 1593 1594 #ifdef FULL_MODE 1595 BRect leftSide(frame.left, vDivider, frame.left + hDivider, frame.bottom - thirdScreen); 1596 BRect rightSide(frame.right - hDivider, vDivider, frame.right, frame.bottom - thirdScreen); 1597 1598 vDivider = frame.bottom - thirdScreen; 1599 #endif 1600 BRect bottomLeft(frame.left, vDivider, frame.left + hDivider, frame.bottom); 1601 BRect bottomMiddle(frame.left + hDivider, vDivider, frame.right - hDivider, frame.bottom); 1602 BRect bottomRight(frame.right - hDivider, vDivider, frame.right, frame.bottom); 1603 1604 if (where != fPreviousPosition) { 1605 fPreviousPosition = where; 1606 ConvertToScreen(&where); 1607 1608 // use short circuit evaluation for convenience 1609 if (SwitchModeForRect(where, topLeft, true, true, true, kMiniState) 1610 || SwitchModeForRect(where, topMiddle, false, true, true, kExpandoState) 1611 || SwitchModeForRect(where, topRight, true, false, true, kMiniState) 1612 1613 || SwitchModeForRect(where, middleLeft, true, true, true, kExpandoState) 1614 || SwitchModeForRect(where, middleRight, true, false, true, kExpandoState) 1615 1616 #ifdef FULL_MODE 1617 || SwitchModeForRect(where, leftSide, true, true, true, kFullState) 1618 || SwitchModeForRect(where, rightSide, true, false, true, kFullState) 1619 #endif 1620 || SwitchModeForRect(where, bottomLeft, true, true, false, kMiniState) 1621 || SwitchModeForRect(where, bottomMiddle, false, true, false, kExpandoState) 1622 || SwitchModeForRect(where, bottomRight, true, false, false, kMiniState)) 1623 ; 1624 } 1625 } else 1626 BControl::MouseMoved(where, code, message); 1627 } 1628 1629 1630 int32 1631 TDragRegion::DragRegionLocation() const 1632 { 1633 return fDragLocation; 1634 } 1635 1636 1637 void 1638 TDragRegion::SetDragRegionLocation(int32 location) 1639 { 1640 if (location == fDragLocation) 1641 return; 1642 1643 fDragLocation = location; 1644 Invalidate(); 1645 } 1646 1647