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