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