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