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