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