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