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