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