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