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