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 trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include "GeneralInfoView.h" 37 38 #include <algorithm> 39 40 #include <Application.h> 41 #include <Catalog.h> 42 #include <Locale.h> 43 #include <NodeMonitor.h> 44 #include <PopUpMenu.h> 45 #include <Region.h> 46 #include <Roster.h> 47 #include <Screen.h> 48 #include <StringFormat.h> 49 #include <SymLink.h> 50 #include <Volume.h> 51 #include <VolumeRoster.h> 52 #include <Window.h> 53 54 #include "Attributes.h" 55 #include "Commands.h" 56 #include "FSUtils.h" 57 #include "IconMenuItem.h" 58 #include "InfoWindow.h" 59 #include "Model.h" 60 #include "NavMenu.h" 61 #include "PoseView.h" 62 #include "StringForSize.h" 63 #include "Tracker.h" 64 #include "WidgetAttributeText.h" 65 66 67 #undef B_TRANSLATION_CONTEXT 68 #define B_TRANSLATION_CONTEXT "InfoWindow" 69 70 71 namespace BPrivate { 72 73 class TrackingView : public BControl { 74 public: 75 TrackingView(BRect, const char* str, BMessage* message); 76 77 virtual void MouseDown(BPoint); 78 virtual void MouseMoved(BPoint where, uint32 transit, const BMessage*); 79 virtual void MouseUp(BPoint); 80 virtual void Draw(BRect); 81 82 private: 83 bool fMouseDown; 84 bool fMouseInView; 85 }; 86 87 } // namespace BPrivate 88 89 90 const float kBorderMargin = 15.0f; 91 const float kDrawMargin = 3.0f; 92 93 94 const uint32 kSetPreferredApp = 'setp'; 95 const uint32 kSelectNewSymTarget = 'snew'; 96 const uint32 kOpenLinkSource = 'opls'; 97 const uint32 kOpenLinkTarget = 'oplt'; 98 99 100 static void 101 OpenParentAndSelectOriginal(const entry_ref* ref) 102 { 103 BEntry entry(ref); 104 node_ref node; 105 entry.GetNodeRef(&node); 106 107 BEntry parent; 108 entry.GetParent(&parent); 109 entry_ref parentRef; 110 parent.GetRef(&parentRef); 111 112 BMessage message(B_REFS_RECEIVED); 113 message.AddRef("refs", &parentRef); 114 message.AddData("nodeRefToSelect", B_RAW_TYPE, &node, sizeof(node_ref)); 115 116 be_app->PostMessage(&message); 117 } 118 119 120 static BWindow* 121 OpenToolTipWindow(BScreen& screen, BRect rect, const char* name, 122 const char* string, BMessenger target, BMessage* message) 123 { 124 font_height fontHeight; 125 be_plain_font->GetHeight(&fontHeight); 126 float height = ceilf(fontHeight.ascent + fontHeight.descent); 127 rect.top = floorf(rect.top + (rect.Height() - height) / 2.0f); 128 rect.bottom = rect.top + height; 129 130 rect.right = rect.left + ceilf(be_plain_font->StringWidth(string)) + 4; 131 if (rect.left < 0) 132 rect.OffsetBy(-rect.left, 0); 133 else if (rect.right > screen.Frame().right) 134 rect.OffsetBy(screen.Frame().right - rect.right, 0); 135 136 BWindow* window = new BWindow(rect, name, B_BORDERED_WINDOW_LOOK, 137 B_FLOATING_ALL_WINDOW_FEEL, 138 B_NOT_MOVABLE | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE 139 | B_NOT_RESIZABLE | B_AVOID_FOCUS | B_NO_WORKSPACE_ACTIVATION 140 | B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS); 141 142 TrackingView* trackingView = new TrackingView(window->Bounds(), 143 string, message); 144 trackingView->SetTarget(target); 145 window->AddChild(trackingView); 146 147 window->Sync(); 148 window->Show(); 149 150 return window; 151 } 152 153 154 GeneralInfoView::GeneralInfoView(Model* model) 155 : 156 BGroupView(B_VERTICAL), 157 fDivider(0), 158 fPreferredAppMenu(NULL), 159 fModel(model), 160 fMouseDown(false), 161 fTrackingState(no_track), 162 fPathWindow(NULL), 163 fLinkWindow(NULL), 164 fDescWindow(NULL), 165 fCurrentLinkColorWhich(B_LINK_TEXT_COLOR), 166 fCurrentPathColorWhich(fCurrentLinkColorWhich) 167 { 168 const char* fieldNames[] = { 169 B_TRANSLATE("Description:"), 170 B_TRANSLATE("Location:"), 171 B_TRANSLATE("Opens with:"), 172 B_TRANSLATE("Capacity:"), 173 B_TRANSLATE("Size:"), 174 B_TRANSLATE("Created:"), 175 B_TRANSLATE("Modified:"), 176 B_TRANSLATE("Kind:"), 177 B_TRANSLATE("Link to:"), 178 B_TRANSLATE("Version:"), 179 NULL 180 }; 181 182 183 SetFlags(Flags() | B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS); 184 SetName(B_TRANSLATE("Information")); 185 // Set view color to standard background grey 186 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 187 SetFont(be_plain_font); 188 fFreeBytes = -1; 189 fSizeString = ""; 190 fSizeRect.Set(0, 0, 0, 0); 191 192 // Find offset for attributes, might be overiden below if there 193 // is a prefered handle menu displayed 194 BFont currentFont; 195 GetFont(¤tFont); 196 currentFont.SetSize(currentFont.Size() - 2); 197 198 // The widest string depends on the locale. We should check them all, this 199 // is only an approximation that works for English and French. 200 float width = 0; 201 for (int i = 0; fieldNames[i] != 0; i++) 202 width = std::max(width, StringWidth(fieldNames[i])); 203 fDivider = width + kBorderMargin + 1; 204 205 // Keep some free space for the stuff we print ourselves 206 float lineHeight = CurrentFontHeight(); 207 int lineCount = 7; 208 if (model->IsSymLink()) 209 lineCount += 1; // Add space for "Link to" line 210 if (model->IsExecutable()) 211 lineCount += 2; // Add space for "Version" and "Description" lines 212 GroupLayout()->SetInsets(kBorderMargin, lineHeight * lineCount, 213 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING); 214 215 // Add a preferred handler pop-up menu if this item 216 // is a file...This goes in place of the Link To: 217 // string... 218 if (model->IsFile()) { 219 BMimeType mime(fModel->MimeType()); 220 BNodeInfo nodeInfo(fModel->Node()); 221 222 // But don't add the menu if the file is executable 223 if (!fModel->IsExecutable()) { 224 fPreferredAppMenu = new BMenuField("", "", new BPopUpMenu("")); 225 currentFont.SetSize(currentFont.Size() + 2); 226 fDivider = currentFont.StringWidth(B_TRANSLATE("Opens with:")) 227 + 5; 228 fPreferredAppMenu->SetDivider(fDivider); 229 fDivider += (kBorderMargin - 2); 230 fPreferredAppMenu->SetFont(¤tFont); 231 fPreferredAppMenu->SetHighUIColor(B_PANEL_TEXT_COLOR); 232 fPreferredAppMenu->SetLabel(B_TRANSLATE("Opens with:")); 233 234 char prefSignature[B_MIME_TYPE_LENGTH]; 235 nodeInfo.GetPreferredApp(prefSignature); 236 237 BMessage supportingAppList; 238 mime.GetSupportingApps(&supportingAppList); 239 240 // Add the default menu item and set it to marked 241 BMenuItem* result; 242 result = new BMenuItem(B_TRANSLATE("Default application"), 243 new BMessage(kSetPreferredApp)); 244 result->SetTarget(this); 245 fPreferredAppMenu->Menu()->AddItem(result); 246 result->SetMarked(true); 247 248 for (int32 index = 0; ; index++) { 249 const char* signature; 250 if (supportingAppList.FindString("applications", index, 251 &signature) != B_OK) { 252 break; 253 } 254 255 // Only add separator item if there are more items 256 if (index == 0) 257 fPreferredAppMenu->Menu()->AddSeparatorItem(); 258 259 BMessage* itemMessage = new BMessage(kSetPreferredApp); 260 itemMessage->AddString("signature", signature); 261 262 status_t err = B_ERROR; 263 entry_ref entry; 264 265 if (signature && signature[0]) 266 err = be_roster->FindApp(signature, &entry); 267 268 if (err != B_OK) 269 result = new BMenuItem(signature, itemMessage); 270 else 271 result = new BMenuItem(entry.name, itemMessage); 272 273 result->SetTarget(this); 274 fPreferredAppMenu->Menu()->AddItem(result); 275 if (strcmp(signature, prefSignature) == 0) 276 result->SetMarked(true); 277 } 278 279 AddChild(fPreferredAppMenu); 280 } 281 } 282 } 283 284 285 GeneralInfoView::~GeneralInfoView() 286 { 287 if (fPathWindow->Lock()) 288 fPathWindow->Quit(); 289 290 if (fLinkWindow->Lock()) 291 fLinkWindow->Quit(); 292 293 if (fDescWindow->Lock()) 294 fDescWindow->Quit(); 295 } 296 297 298 void 299 GeneralInfoView::InitStrings(const Model* model) 300 { 301 BMimeType mime; 302 char kind[B_MIME_TYPE_LENGTH]; 303 304 ASSERT(model->IsNodeOpen()); 305 306 BRect drawBounds(Bounds()); 307 drawBounds.left = fDivider; 308 309 // We'll do our own truncation later on in Draw() 310 WidgetAttributeText::AttrAsString(model, &fCreatedStr, kAttrStatCreated, 311 B_TIME_TYPE, drawBounds.Width() - kBorderMargin, this); 312 WidgetAttributeText::AttrAsString(model, &fModifiedStr, kAttrStatModified, 313 B_TIME_TYPE, drawBounds.Width() - kBorderMargin, this); 314 WidgetAttributeText::AttrAsString(model, &fPathStr, kAttrPath, 315 B_STRING_TYPE, 0, this); 316 317 // Use the same method as used to resolve fIconModel, which handles 318 // both absolute and relative symlinks. if the link is broken, try to 319 // get a little more information. 320 if (model->IsSymLink()) { 321 bool linked = false; 322 323 Model resolvedModel(model->EntryRef(), true, true); 324 if (resolvedModel.InitCheck() == B_OK) { 325 // Get the path of the link 326 BPath traversedPath; 327 resolvedModel.GetPath(&traversedPath); 328 329 // If the BPath is initialized, then check the file for existence 330 if (traversedPath.InitCheck() == B_OK) { 331 BEntry entry(traversedPath.Path(), false); 332 // look at the target itself 333 if (entry.InitCheck() == B_OK && entry.Exists()) 334 linked = true; 335 } 336 } 337 338 // always show the target as it is: absolute or relative! 339 BSymLink symLink(model->EntryRef()); 340 char linkToPath[B_PATH_NAME_LENGTH]; 341 symLink.ReadLink(linkToPath, B_PATH_NAME_LENGTH); 342 fLinkToStr = linkToPath; 343 if (!linked) { 344 // link points to missing object 345 fLinkToStr += B_TRANSLATE(" (broken)"); 346 } 347 } else if (model->IsExecutable()) { 348 if (((Model*)model)->GetLongVersionString(fDescStr, 349 B_APP_VERSION_KIND) == B_OK) { 350 // we want a flat string, so replace all newlines and tabs 351 // with spaces 352 fDescStr.ReplaceAll('\n', ' '); 353 fDescStr.ReplaceAll('\t', ' '); 354 } else 355 fDescStr = "-"; 356 } 357 358 if (mime.SetType(model->MimeType()) == B_OK 359 && mime.GetShortDescription(kind) == B_OK) 360 fKindStr = kind; 361 362 if (fKindStr.Length() == 0) 363 fKindStr = model->MimeType(); 364 } 365 366 367 void 368 GeneralInfoView::AttachedToWindow() 369 { 370 BFont font(be_plain_font); 371 372 font.SetSpacing(B_BITMAP_SPACING); 373 SetFont(&font); 374 375 CheckAndSetSize(); 376 if (fPreferredAppMenu) 377 fPreferredAppMenu->Menu()->SetTargetForItems(this); 378 379 _inherited::AttachedToWindow(); 380 } 381 382 383 void 384 GeneralInfoView::Pulse() 385 { 386 CheckAndSetSize(); 387 _inherited::Pulse(); 388 } 389 390 391 void 392 GeneralInfoView::ModelChanged(Model* model, BMessage* message) 393 { 394 BRect drawBounds(Bounds()); 395 drawBounds.left = fDivider; 396 397 switch (message->FindInt32("opcode")) { 398 case B_ENTRY_MOVED: 399 { 400 node_ref dirNode; 401 node_ref itemNode; 402 dirNode.device = itemNode.device = message->FindInt32("device"); 403 message->FindInt64("to directory", &dirNode.node); 404 message->FindInt64("node", &itemNode.node); 405 406 const char* name; 407 if (message->FindString("name", &name) != B_OK) 408 return; 409 410 // ensure notification is for us 411 if (*model->NodeRef() == itemNode 412 // For volumes, the device ID is obviously not handled in a 413 // consistent way; the node monitor sends us the ID of the 414 // parent device, while the model is set to the device of the 415 // volume directly - this hack works for volumes that are 416 // mounted in the root directory 417 || (model->IsVolume() 418 && itemNode.device == 1 419 && itemNode.node == model->NodeRef()->node)) { 420 model->UpdateEntryRef(&dirNode, name); 421 BString title; 422 title.SetToFormat(B_TRANSLATE_COMMENT("%s info", 423 "window title"), name); 424 Window()->SetTitle(title.String()); 425 WidgetAttributeText::AttrAsString(model, &fPathStr, kAttrPath, 426 B_STRING_TYPE, 0, this); 427 Invalidate(); 428 } 429 break; 430 } 431 432 case B_STAT_CHANGED: 433 if (model->OpenNode() == B_OK) { 434 WidgetAttributeText::AttrAsString(model, &fCreatedStr, 435 kAttrStatCreated, B_TIME_TYPE, drawBounds.Width() 436 - kBorderMargin, this); 437 WidgetAttributeText::AttrAsString(model, &fModifiedStr, 438 kAttrStatModified, B_TIME_TYPE, drawBounds.Width() 439 - kBorderMargin, this); 440 441 // don't change the size if it's a directory 442 if (!model->IsDirectory()) { 443 fLastSize = model->StatBuf()->st_size; 444 fSizeString = ""; 445 BInfoWindow::GetSizeString(fSizeString, fLastSize, 0); 446 } 447 model->CloseNode(); 448 } 449 break; 450 451 case B_ATTR_CHANGED: 452 { 453 // watch for icon updates 454 const char* attrName; 455 if (message->FindString("attr", &attrName) == B_OK) { 456 if (strcmp(attrName, kAttrLargeIcon) == 0 457 || strcmp(attrName, kAttrIcon) == 0) { 458 IconCache::sIconCache->IconChanged(model->ResolveIfLink()); 459 Invalidate(); 460 } else if (strcmp(attrName, kAttrMIMEType) == 0) { 461 if (model->OpenNode() == B_OK) { 462 model->AttrChanged(attrName); 463 InitStrings(model); 464 model->CloseNode(); 465 } 466 Invalidate(); 467 } 468 } 469 break; 470 } 471 472 default: 473 break; 474 } 475 476 fModel = model; 477 if (fModel->IsSymLink()) { 478 InitStrings(model); 479 Invalidate(); 480 } 481 482 drawBounds.left = fDivider; 483 Invalidate(drawBounds); 484 } 485 486 487 // This only applies to symlinks. If the target of the symlink 488 // was changed, then we have to update the entire model. 489 // (Since in order to re-target a symlink, we had to delete 490 // the old model and create a new one; BSymLink::SetTarget(), 491 // would be nice) 492 493 void 494 GeneralInfoView::ReLinkTargetModel(Model* model) 495 { 496 fModel = model; 497 InitStrings(model); 498 Invalidate(Bounds()); 499 } 500 501 502 void 503 GeneralInfoView::MouseDown(BPoint where) 504 { 505 // Start tracking the mouse if we are in any of the hotspots 506 if (fLinkRect.Contains(where)) { 507 InvertRect(fLinkRect); 508 fTrackingState = link_track; 509 } else if (fPathRect.Contains(where)) { 510 InvertRect(fPathRect); 511 fTrackingState = path_track; 512 } else if (fSizeRect.Contains(where)) { 513 if (fModel->IsDirectory() && !fModel->IsVolume() 514 && !fModel->IsRoot()) { 515 InvertRect(fSizeRect); 516 fTrackingState = size_track; 517 } else 518 fTrackingState = no_track; 519 } 520 521 fMouseDown = true; 522 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 523 } 524 525 526 void 527 GeneralInfoView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage) 528 { 529 fCurrentLinkColorWhich = B_LINK_TEXT_COLOR; 530 fCurrentPathColorWhich = fCurrentLinkColorWhich; 531 532 switch (fTrackingState) { 533 case link_track: 534 if (fLinkRect.Contains(where) != fMouseDown) { 535 fMouseDown = !fMouseDown; 536 InvertRect(fLinkRect); 537 } 538 break; 539 540 case path_track: 541 if (fPathRect.Contains(where) != fMouseDown) { 542 fMouseDown = !fMouseDown; 543 InvertRect(fPathRect); 544 } 545 break; 546 547 case size_track: 548 if (fSizeRect.Contains(where) != fMouseDown) { 549 fMouseDown = !fMouseDown; 550 InvertRect(fSizeRect); 551 } 552 break; 553 554 default: 555 { 556 // Only consider this if the window is the active window. 557 // We have to manually get the mouse here in the event that the 558 // mouse is over a pop-up window 559 uint32 buttons; 560 BPoint point; 561 GetMouse(&point, &buttons); 562 if (Window()->IsActive() && !buttons) { 563 // If we are down here, then that means that we're tracking 564 // the mouse but not from a mouse down. In this case, we're 565 // just interested in knowing whether or not we need to 566 // display the "pop-up" version of the path or link text. 567 BScreen screen(Window()); 568 BFont font; 569 GetFont(&font); 570 float maxWidth = (Bounds().Width() 571 - (fDivider + kBorderMargin)); 572 573 if (fPathRect.Contains(point)) { 574 if (fCurrentPathColorWhich != B_LINK_HOVER_COLOR) 575 fCurrentPathColorWhich = B_LINK_HOVER_COLOR; 576 577 if (font.StringWidth(fPathStr.String()) > maxWidth) { 578 fTrackingState = no_track; 579 BRect rect = ConvertToScreen(fPathRect); 580 581 if (fPathWindow == NULL 582 || BMessenger(fPathWindow).IsValid() == false) { 583 fPathWindow = OpenToolTipWindow(screen, rect, 584 "fPathWindow", fPathStr.String(), 585 BMessenger(this), 586 new BMessage(kOpenLinkSource)); 587 } 588 } 589 } else if (fLinkRect.Contains(point)) { 590 591 if (fCurrentLinkColorWhich != B_LINK_HOVER_COLOR) 592 fCurrentLinkColorWhich = B_LINK_HOVER_COLOR; 593 594 if (font.StringWidth(fLinkToStr.String()) > maxWidth) { 595 fTrackingState = no_track; 596 BRect rect = ConvertToScreen(fLinkRect); 597 598 if (!fLinkWindow 599 || BMessenger(fLinkWindow).IsValid() == false) { 600 fLinkWindow = OpenToolTipWindow(screen, rect, 601 "fLinkWindow", fLinkToStr.String(), 602 BMessenger(this), 603 new BMessage(kOpenLinkTarget)); 604 } 605 } 606 } else if (fDescRect.Contains(point) 607 && font.StringWidth(fDescStr.String()) > maxWidth) { 608 fTrackingState = no_track; 609 BRect rect = ConvertToScreen(fDescRect); 610 611 if (!fDescWindow 612 || BMessenger(fDescWindow).IsValid() == false) { 613 fDescWindow = OpenToolTipWindow(screen, rect, 614 "fDescWindow", fDescStr.String(), 615 BMessenger(this), NULL); 616 } 617 } 618 } 619 break; 620 } 621 } 622 623 DelayedInvalidate(16666, fPathRect); 624 DelayedInvalidate(16666, fLinkRect); 625 } 626 627 628 void 629 GeneralInfoView::OpenLinkSource() 630 { 631 OpenParentAndSelectOriginal(fModel->EntryRef()); 632 } 633 634 635 void 636 GeneralInfoView::OpenLinkTarget() 637 { 638 Model resolvedModel(fModel->EntryRef(), true, true); 639 BEntry entry; 640 if (resolvedModel.InitCheck() == B_OK) { 641 // Get the path of the link 642 BPath traversedPath; 643 resolvedModel.GetPath(&traversedPath); 644 645 // If the BPath is initialized, then check the file for existence 646 if (traversedPath.InitCheck() == B_OK) 647 entry.SetTo(traversedPath.Path()); 648 } 649 if (entry.InitCheck() != B_OK || !entry.Exists()) { 650 // Open a file dialog panel to allow the user to relink. 651 BInfoWindow* window = dynamic_cast<BInfoWindow*>(Window()); 652 if (window != NULL) 653 window->OpenFilePanel(fModel->EntryRef()); 654 } else { 655 entry_ref ref; 656 entry.GetRef(&ref); 657 BPath path(&ref); 658 printf("Opening link target: %s\n", path.Path()); 659 OpenParentAndSelectOriginal(&ref); 660 } 661 } 662 663 664 void 665 GeneralInfoView::MouseUp(BPoint where) 666 { 667 // Are we in the link rect? 668 if (fTrackingState == link_track && fLinkRect.Contains(where)) { 669 InvertRect(fLinkRect); 670 OpenLinkTarget(); 671 } else if (fTrackingState == path_track && fPathRect.Contains(where)) { 672 InvertRect(fPathRect); 673 OpenLinkSource(); 674 } else if (fTrackingState == size_track && fSizeRect.Contains(where)) { 675 // Recalculate size 676 Window()->PostMessage(kRecalculateSize); 677 } 678 679 // End mouse tracking 680 fMouseDown = false; 681 fTrackingState = no_track; 682 } 683 684 685 void 686 GeneralInfoView::CheckAndSetSize() 687 { 688 if (fModel->IsVolume() || fModel->IsRoot()) { 689 off_t freeBytes = 0; 690 off_t capacity = 0; 691 692 if (fModel->IsVolume()) { 693 BVolume volume(fModel->NodeRef()->device); 694 freeBytes = volume.FreeBytes(); 695 capacity = volume.Capacity(); 696 } else { 697 // iterate over all volumes 698 BVolumeRoster volumeRoster; 699 BVolume volume; 700 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 701 freeBytes += volume.FreeBytes(); 702 capacity += volume.Capacity(); 703 } 704 } 705 706 if (fFreeBytes == freeBytes) 707 return; 708 709 fFreeBytes = freeBytes; 710 711 fSizeString.SetTo(B_TRANSLATE("%capacity (%used used -- %free free)")); 712 713 char sizeStr[128]; 714 string_for_size(capacity, sizeStr, sizeof(sizeStr)); 715 fSizeString.ReplaceFirst("%capacity", sizeStr); 716 string_for_size(capacity - fFreeBytes, sizeStr, sizeof(sizeStr)); 717 fSizeString.ReplaceFirst("%used", sizeStr); 718 string_for_size(fFreeBytes, sizeStr, sizeof(sizeStr)); 719 fSizeString.ReplaceFirst("%free", sizeStr); 720 721 } else if (fModel->IsFile()) { 722 // poll for size changes because they do not get node monitored 723 // until a file gets closed (with the old BFS) 724 StatStruct statBuf; 725 BModelOpener opener(fModel); 726 727 if (fModel->InitCheck() != B_OK 728 || fModel->Node()->GetStat(&statBuf) != B_OK) { 729 return; 730 } 731 732 if (fLastSize == statBuf.st_size) 733 return; 734 735 fLastSize = statBuf.st_size; 736 fSizeString = ""; 737 BInfoWindow::GetSizeString(fSizeString, fLastSize, 0); 738 } else 739 return; 740 741 SetSizeString(fSizeString); 742 } 743 744 745 void 746 GeneralInfoView::MessageReceived(BMessage* message) 747 { 748 switch (message->what) { 749 case kSetPreferredApp: 750 { 751 BNode node(fModel->EntryRef()); 752 BNodeInfo nodeInfo(&node); 753 754 const char* newSignature; 755 if (message->FindString("signature", &newSignature) != B_OK) 756 newSignature = NULL; 757 758 fModel->SetPreferredAppSignature(newSignature); 759 nodeInfo.SetPreferredApp(newSignature); 760 break; 761 } 762 763 case kOpenLinkSource: 764 OpenLinkSource(); 765 break; 766 767 case kOpenLinkTarget: 768 OpenLinkTarget(); 769 break; 770 771 default: 772 _inherited::MessageReceived(message); 773 break; 774 } 775 } 776 777 778 void 779 GeneralInfoView::FrameResized(float, float) 780 { 781 BModelOpener opener(fModel); 782 783 // Truncate the strings according to the new width 784 InitStrings(fModel); 785 } 786 787 788 void 789 GeneralInfoView::Draw(BRect) 790 { 791 // Set the low color for anti-aliasing 792 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 793 794 // Clear the old contents 795 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 796 FillRect(Bounds()); 797 798 rgb_color labelColor = ui_color(B_PANEL_TEXT_COLOR); 799 rgb_color attributeColor = mix_color(HighColor(), labelColor, 192); 800 801 // Font information 802 font_height fontMetrics; 803 float lineHeight = 0; 804 float lineBase = 0; 805 // Draw the attribute font stuff 806 SetFont(be_plain_font); 807 GetFontHeight(&fontMetrics); 808 lineHeight = CurrentFontHeight() + 5; 809 810 // Starting base line for the first string 811 lineBase = lineHeight; 812 813 // Capacity/size 814 SetHighColor(labelColor); 815 if (fModel->IsVolume() || fModel->IsRoot()) { 816 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Capacity:"))), 817 lineBase)); 818 DrawString(B_TRANSLATE("Capacity:")); 819 } else { 820 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Size:"))), 821 lineBase)); 822 fSizeRect.left = fDivider + 2; 823 fSizeRect.top = lineBase - fontMetrics.ascent; 824 fSizeRect.bottom = lineBase + fontMetrics.descent; 825 DrawString(B_TRANSLATE("Size:")); 826 } 827 828 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 829 SetHighColor(attributeColor); 830 // Check for possible need of truncation 831 if (StringWidth(fSizeString.String()) 832 > (Bounds().Width() - (fDivider + kBorderMargin))) { 833 BString tmpString(fSizeString.String()); 834 TruncateString(&tmpString, B_TRUNCATE_MIDDLE, 835 Bounds().Width() - (fDivider + kBorderMargin)); 836 DrawString(tmpString.String()); 837 fSizeRect.right = fSizeRect.left + StringWidth(tmpString.String()) 838 + 3; 839 } else { 840 DrawString(fSizeString.String()); 841 fSizeRect.right = fSizeRect.left + StringWidth(fSizeString.String()) + 3; 842 } 843 lineBase += lineHeight; 844 845 // Created 846 SetHighColor(labelColor); 847 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Created:"))), 848 lineBase)); 849 DrawString(B_TRANSLATE("Created:")); 850 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 851 SetHighColor(attributeColor); 852 DrawString(fCreatedStr.String()); 853 lineBase += lineHeight; 854 855 // Modified 856 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Modified:"))), 857 lineBase)); 858 SetHighColor(labelColor); 859 DrawString(B_TRANSLATE("Modified:")); 860 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 861 SetHighColor(attributeColor); 862 DrawString(fModifiedStr.String()); 863 lineBase += lineHeight; 864 865 // Kind 866 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Kind:"))), 867 lineBase)); 868 SetHighColor(labelColor); 869 DrawString(B_TRANSLATE("Kind:")); 870 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 871 SetHighColor(attributeColor); 872 DrawString(fKindStr.String()); 873 lineBase += lineHeight; 874 875 BFont normalFont; 876 GetFont(&normalFont); 877 878 // Path 879 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Location:"))), 880 lineBase)); 881 SetHighColor(labelColor); 882 DrawString(B_TRANSLATE("Location:")); 883 884 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 885 SetHighUIColor(fCurrentPathColorWhich); 886 887 // Check for truncation 888 if (StringWidth(fPathStr.String()) > (Bounds().Width() 889 - (fDivider + kBorderMargin))) { 890 BString nameString(fPathStr.String()); 891 TruncateString(&nameString, B_TRUNCATE_MIDDLE, 892 Bounds().Width() - (fDivider + kBorderMargin)); 893 DrawString(nameString.String()); 894 } else 895 DrawString(fPathStr.String()); 896 897 // Cache the position of the path 898 fPathRect.top = lineBase - fontMetrics.ascent; 899 fPathRect.bottom = lineBase + fontMetrics.descent; 900 fPathRect.left = fDivider + 2; 901 fPathRect.right = fPathRect.left + StringWidth(fPathStr.String()) + 3; 902 903 lineBase += lineHeight; 904 905 // Link to/version 906 if (fModel->IsSymLink()) { 907 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Link to:"))), 908 lineBase)); 909 SetHighColor(labelColor); 910 DrawString(B_TRANSLATE("Link to:")); 911 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 912 SetHighUIColor(fCurrentLinkColorWhich); 913 914 // Check for truncation 915 if (StringWidth(fLinkToStr.String()) > (Bounds().Width() 916 - (fDivider + kBorderMargin))) { 917 BString nameString(fLinkToStr.String()); 918 TruncateString(&nameString, B_TRUNCATE_MIDDLE, 919 Bounds().Width() - (fDivider + kBorderMargin)); 920 DrawString(nameString.String()); 921 } else 922 DrawString(fLinkToStr.String()); 923 924 // Cache the position of the link field 925 fLinkRect.top = lineBase - fontMetrics.ascent; 926 fLinkRect.bottom = lineBase + fontMetrics.descent; 927 fLinkRect.left = fDivider + 2; 928 fLinkRect.right = fLinkRect.left + StringWidth(fLinkToStr.String()) 929 + 3; 930 931 // No description field 932 fDescRect = BRect(-1, -1, -1, -1); 933 } else if (fModel->IsExecutable()) { 934 //Version 935 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Version:"))), 936 lineBase)); 937 SetHighColor(labelColor); 938 DrawString(B_TRANSLATE("Version:")); 939 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 940 SetHighColor(attributeColor); 941 BString nameString; 942 if (fModel->GetVersionString(nameString, B_APP_VERSION_KIND) == B_OK) 943 DrawString(nameString.String()); 944 else 945 DrawString("-"); 946 lineBase += lineHeight; 947 948 // Description 949 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Description:"))), 950 lineBase)); 951 SetHighColor(labelColor); 952 DrawString(B_TRANSLATE("Description:")); 953 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 954 SetHighColor(attributeColor); 955 // Check for truncation 956 if (StringWidth(fDescStr.String()) > (Bounds().Width() 957 - (fDivider + kBorderMargin))) { 958 BString nameString(fDescStr.String()); 959 TruncateString(&nameString, B_TRUNCATE_MIDDLE, 960 Bounds().Width() - (fDivider + kBorderMargin)); 961 DrawString(nameString.String()); 962 } else 963 DrawString(fDescStr.String()); 964 965 // Cache the position of the description field 966 fDescRect.top = lineBase - fontMetrics.ascent; 967 fDescRect.bottom = lineBase + fontMetrics.descent; 968 fDescRect.left = fDivider + 2; 969 fDescRect.right = fDescRect.left + StringWidth(fDescStr.String()) + 3; 970 971 // No link field 972 fLinkRect = BRect(-1, -1, -1, -1); 973 } 974 } 975 976 977 void 978 GeneralInfoView::WindowActivated(bool active) 979 { 980 if (active) 981 return; 982 983 if (fPathWindow->Lock()) { 984 fPathWindow->Quit(); 985 fPathWindow = NULL; 986 } 987 988 if (fLinkWindow->Lock()) { 989 fLinkWindow->Quit(); 990 fLinkWindow = NULL; 991 } 992 993 if (fDescWindow->Lock()) { 994 fDescWindow->Quit(); 995 fDescWindow = NULL; 996 } 997 } 998 999 1000 float 1001 GeneralInfoView::CurrentFontHeight() 1002 { 1003 BFont font; 1004 GetFont(&font); 1005 font_height fontHeight; 1006 font.GetHeight(&fontHeight); 1007 1008 return fontHeight.ascent + fontHeight.descent + fontHeight.leading + 2; 1009 } 1010 1011 1012 off_t 1013 GeneralInfoView::LastSize() const 1014 { 1015 return fLastSize; 1016 } 1017 1018 1019 void 1020 GeneralInfoView::SetLastSize(off_t lastSize) 1021 { 1022 fLastSize = lastSize; 1023 } 1024 1025 1026 void 1027 GeneralInfoView::SetSizeString(const char* sizeString) 1028 { 1029 fSizeString = sizeString; 1030 1031 float lineHeight = CurrentFontHeight() + 6; 1032 BRect bounds(fDivider, 0, Bounds().right, lineHeight); 1033 Invalidate(bounds); 1034 } 1035 1036 1037 // #pragma mark - 1038 1039 1040 TrackingView::TrackingView(BRect frame, const char* str, BMessage* message) 1041 : BControl(frame, "trackingView", str, message, B_FOLLOW_ALL, 1042 B_WILL_DRAW), 1043 fMouseDown(false), 1044 fMouseInView(false) 1045 { 1046 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1047 SetEventMask(B_POINTER_EVENTS, 0); 1048 } 1049 1050 1051 void 1052 TrackingView::MouseDown(BPoint) 1053 { 1054 if (Message() != NULL) { 1055 fMouseDown = true; 1056 fMouseInView = true; 1057 InvertRect(Bounds()); 1058 } 1059 } 1060 1061 1062 void 1063 TrackingView::MouseMoved(BPoint, uint32 transit, const BMessage*) 1064 { 1065 if ((transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW) && fMouseDown) 1066 InvertRect(Bounds()); 1067 1068 fMouseInView = (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW); 1069 DelayedInvalidate(16666, Bounds()); 1070 if (!fMouseInView && !fMouseDown) 1071 Window()->Close(); 1072 } 1073 1074 1075 void 1076 TrackingView::MouseUp(BPoint) 1077 { 1078 if (Message() != NULL) { 1079 if (fMouseInView) 1080 Invoke(); 1081 1082 fMouseDown = false; 1083 Window()->Close(); 1084 } 1085 } 1086 1087 1088 void 1089 TrackingView::Draw(BRect) 1090 { 1091 if (Message() != NULL) 1092 SetHighUIColor(fMouseInView ? B_LINK_HOVER_COLOR 1093 : B_LINK_TEXT_COLOR); 1094 else 1095 SetHighUIColor(B_PANEL_TEXT_COLOR); 1096 SetLowColor(ViewColor()); 1097 1098 font_height fontHeight; 1099 GetFontHeight(&fontHeight); 1100 1101 DrawString(Label(), BPoint(3, Bounds().Height() - fontHeight.descent)); 1102 } 1103