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