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 << name << B_TRANSLATE(" info"); 423 Window()->SetTitle(title.String()); 424 WidgetAttributeText::AttrAsString(model, &fPathStr, kAttrPath, 425 B_STRING_TYPE, 0, this); 426 Invalidate(); 427 } 428 break; 429 } 430 431 case B_STAT_CHANGED: 432 if (model->OpenNode() == B_OK) { 433 WidgetAttributeText::AttrAsString(model, &fCreatedStr, 434 kAttrStatCreated, B_TIME_TYPE, drawBounds.Width() 435 - kBorderMargin, this); 436 WidgetAttributeText::AttrAsString(model, &fModifiedStr, 437 kAttrStatModified, B_TIME_TYPE, drawBounds.Width() 438 - kBorderMargin, this); 439 440 // don't change the size if it's a directory 441 if (!model->IsDirectory()) { 442 fLastSize = model->StatBuf()->st_size; 443 fSizeString = ""; 444 BInfoWindow::GetSizeString(fSizeString, fLastSize, 0); 445 } 446 model->CloseNode(); 447 } 448 break; 449 450 case B_ATTR_CHANGED: 451 { 452 // watch for icon updates 453 const char* attrName; 454 if (message->FindString("attr", &attrName) == B_OK) { 455 if (strcmp(attrName, kAttrLargeIcon) == 0 456 || strcmp(attrName, kAttrIcon) == 0) { 457 IconCache::sIconCache->IconChanged(model->ResolveIfLink()); 458 Invalidate(); 459 } else if (strcmp(attrName, kAttrMIMEType) == 0) { 460 if (model->OpenNode() == B_OK) { 461 model->AttrChanged(attrName); 462 InitStrings(model); 463 model->CloseNode(); 464 } 465 Invalidate(); 466 } 467 } 468 break; 469 } 470 471 default: 472 break; 473 } 474 475 fModel = model; 476 if (fModel->IsSymLink()) { 477 InitStrings(model); 478 Invalidate(); 479 } 480 481 drawBounds.left = fDivider; 482 Invalidate(drawBounds); 483 } 484 485 486 // This only applies to symlinks. If the target of the symlink 487 // was changed, then we have to update the entire model. 488 // (Since in order to re-target a symlink, we had to delete 489 // the old model and create a new one; BSymLink::SetTarget(), 490 // would be nice) 491 492 void 493 GeneralInfoView::ReLinkTargetModel(Model* model) 494 { 495 fModel = model; 496 InitStrings(model); 497 Invalidate(Bounds()); 498 } 499 500 501 void 502 GeneralInfoView::MouseDown(BPoint where) 503 { 504 // Start tracking the mouse if we are in any of the hotspots 505 if (fLinkRect.Contains(where)) { 506 InvertRect(fLinkRect); 507 fTrackingState = link_track; 508 } else if (fPathRect.Contains(where)) { 509 InvertRect(fPathRect); 510 fTrackingState = path_track; 511 } else if (fSizeRect.Contains(where)) { 512 if (fModel->IsDirectory() && !fModel->IsVolume() 513 && !fModel->IsRoot()) { 514 InvertRect(fSizeRect); 515 fTrackingState = size_track; 516 } else 517 fTrackingState = no_track; 518 } 519 520 fMouseDown = true; 521 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); 522 } 523 524 525 void 526 GeneralInfoView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage) 527 { 528 fCurrentLinkColorWhich = B_LINK_TEXT_COLOR; 529 fCurrentPathColorWhich = fCurrentLinkColorWhich; 530 531 switch (fTrackingState) { 532 case link_track: 533 if (fLinkRect.Contains(where) != fMouseDown) { 534 fMouseDown = !fMouseDown; 535 InvertRect(fLinkRect); 536 } 537 break; 538 539 case path_track: 540 if (fPathRect.Contains(where) != fMouseDown) { 541 fMouseDown = !fMouseDown; 542 InvertRect(fPathRect); 543 } 544 break; 545 546 case size_track: 547 if (fSizeRect.Contains(where) != fMouseDown) { 548 fMouseDown = !fMouseDown; 549 InvertRect(fSizeRect); 550 } 551 break; 552 553 default: 554 { 555 // Only consider this if the window is the active window. 556 // We have to manually get the mouse here in the event that the 557 // mouse is over a pop-up window 558 uint32 buttons; 559 BPoint point; 560 GetMouse(&point, &buttons); 561 if (Window()->IsActive() && !buttons) { 562 // If we are down here, then that means that we're tracking 563 // the mouse but not from a mouse down. In this case, we're 564 // just interested in knowing whether or not we need to 565 // display the "pop-up" version of the path or link text. 566 BScreen screen(Window()); 567 BFont font; 568 GetFont(&font); 569 float maxWidth = (Bounds().Width() 570 - (fDivider + kBorderMargin)); 571 572 if (fPathRect.Contains(point)) { 573 if (fCurrentPathColorWhich != B_LINK_HOVER_COLOR) 574 fCurrentPathColorWhich = B_LINK_HOVER_COLOR; 575 576 if (font.StringWidth(fPathStr.String()) > maxWidth) { 577 fTrackingState = no_track; 578 BRect rect = ConvertToScreen(fPathRect); 579 580 if (fPathWindow == NULL 581 || BMessenger(fPathWindow).IsValid() == false) { 582 fPathWindow = OpenToolTipWindow(screen, rect, 583 "fPathWindow", fPathStr.String(), 584 BMessenger(this), 585 new BMessage(kOpenLinkSource)); 586 } 587 } 588 } else if (fLinkRect.Contains(point)) { 589 590 if (fCurrentLinkColorWhich != B_LINK_HOVER_COLOR) 591 fCurrentLinkColorWhich = B_LINK_HOVER_COLOR; 592 593 if (font.StringWidth(fLinkToStr.String()) > maxWidth) { 594 fTrackingState = no_track; 595 BRect rect = ConvertToScreen(fLinkRect); 596 597 if (!fLinkWindow 598 || BMessenger(fLinkWindow).IsValid() == false) { 599 fLinkWindow = OpenToolTipWindow(screen, rect, 600 "fLinkWindow", fLinkToStr.String(), 601 BMessenger(this), 602 new BMessage(kOpenLinkTarget)); 603 } 604 } 605 } else if (fDescRect.Contains(point) 606 && font.StringWidth(fDescStr.String()) > maxWidth) { 607 fTrackingState = no_track; 608 BRect rect = ConvertToScreen(fDescRect); 609 610 if (!fDescWindow 611 || BMessenger(fDescWindow).IsValid() == false) { 612 fDescWindow = OpenToolTipWindow(screen, rect, 613 "fDescWindow", fDescStr.String(), 614 BMessenger(this), NULL); 615 } 616 } 617 } 618 break; 619 } 620 } 621 622 DelayedInvalidate(16666, fPathRect); 623 DelayedInvalidate(16666, fLinkRect); 624 } 625 626 627 void 628 GeneralInfoView::OpenLinkSource() 629 { 630 OpenParentAndSelectOriginal(fModel->EntryRef()); 631 } 632 633 634 void 635 GeneralInfoView::OpenLinkTarget() 636 { 637 Model resolvedModel(fModel->EntryRef(), true, true); 638 BEntry entry; 639 if (resolvedModel.InitCheck() == B_OK) { 640 // Get the path of the link 641 BPath traversedPath; 642 resolvedModel.GetPath(&traversedPath); 643 644 // If the BPath is initialized, then check the file for existence 645 if (traversedPath.InitCheck() == B_OK) 646 entry.SetTo(traversedPath.Path()); 647 } 648 if (entry.InitCheck() != B_OK || !entry.Exists()) { 649 // Open a file dialog panel to allow the user to relink. 650 BInfoWindow* window = dynamic_cast<BInfoWindow*>(Window()); 651 if (window != NULL) 652 window->OpenFilePanel(fModel->EntryRef()); 653 } else { 654 entry_ref ref; 655 entry.GetRef(&ref); 656 BPath path(&ref); 657 printf("Opening link target: %s\n", path.Path()); 658 OpenParentAndSelectOriginal(&ref); 659 } 660 } 661 662 663 void 664 GeneralInfoView::MouseUp(BPoint where) 665 { 666 // Are we in the link rect? 667 if (fTrackingState == link_track && fLinkRect.Contains(where)) { 668 InvertRect(fLinkRect); 669 OpenLinkTarget(); 670 } else if (fTrackingState == path_track && fPathRect.Contains(where)) { 671 InvertRect(fPathRect); 672 OpenLinkSource(); 673 } else if (fTrackingState == size_track && fSizeRect.Contains(where)) { 674 // Recalculate size 675 Window()->PostMessage(kRecalculateSize); 676 } 677 678 // End mouse tracking 679 fMouseDown = false; 680 fTrackingState = no_track; 681 } 682 683 684 void 685 GeneralInfoView::CheckAndSetSize() 686 { 687 if (fModel->IsVolume() || fModel->IsRoot()) { 688 off_t freeBytes = 0; 689 off_t capacity = 0; 690 691 if (fModel->IsVolume()) { 692 BVolume volume(fModel->NodeRef()->device); 693 freeBytes = volume.FreeBytes(); 694 capacity = volume.Capacity(); 695 } else { 696 // iterate over all volumes 697 BVolumeRoster volumeRoster; 698 BVolume volume; 699 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 700 freeBytes += volume.FreeBytes(); 701 capacity += volume.Capacity(); 702 } 703 } 704 705 if (fFreeBytes == freeBytes) 706 return; 707 708 fFreeBytes = freeBytes; 709 710 fSizeString.SetTo(B_TRANSLATE("%capacity (%used used -- %free free)")); 711 712 char sizeStr[128]; 713 string_for_size(capacity, sizeStr, sizeof(sizeStr)); 714 fSizeString.ReplaceFirst("%capacity", sizeStr); 715 string_for_size(capacity - fFreeBytes, sizeStr, sizeof(sizeStr)); 716 fSizeString.ReplaceFirst("%used", sizeStr); 717 string_for_size(fFreeBytes, sizeStr, sizeof(sizeStr)); 718 fSizeString.ReplaceFirst("%free", sizeStr); 719 720 } else if (fModel->IsFile()) { 721 // poll for size changes because they do not get node monitored 722 // until a file gets closed (with the old BFS) 723 StatStruct statBuf; 724 BModelOpener opener(fModel); 725 726 if (fModel->InitCheck() != B_OK 727 || fModel->Node()->GetStat(&statBuf) != B_OK) { 728 return; 729 } 730 731 if (fLastSize == statBuf.st_size) 732 return; 733 734 fLastSize = statBuf.st_size; 735 fSizeString = ""; 736 BInfoWindow::GetSizeString(fSizeString, fLastSize, 0); 737 } else 738 return; 739 740 SetSizeString(fSizeString); 741 } 742 743 744 void 745 GeneralInfoView::MessageReceived(BMessage* message) 746 { 747 switch (message->what) { 748 case kSetPreferredApp: 749 { 750 BNode node(fModel->EntryRef()); 751 BNodeInfo nodeInfo(&node); 752 753 const char* newSignature; 754 if (message->FindString("signature", &newSignature) != B_OK) 755 newSignature = NULL; 756 757 fModel->SetPreferredAppSignature(newSignature); 758 nodeInfo.SetPreferredApp(newSignature); 759 break; 760 } 761 762 case kOpenLinkSource: 763 OpenLinkSource(); 764 break; 765 766 case kOpenLinkTarget: 767 OpenLinkTarget(); 768 break; 769 770 default: 771 _inherited::MessageReceived(message); 772 break; 773 } 774 } 775 776 777 void 778 GeneralInfoView::FrameResized(float, float) 779 { 780 BModelOpener opener(fModel); 781 782 // Truncate the strings according to the new width 783 InitStrings(fModel); 784 } 785 786 787 void 788 GeneralInfoView::Draw(BRect) 789 { 790 // Set the low color for anti-aliasing 791 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 792 793 // Clear the old contents 794 SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 795 FillRect(Bounds()); 796 797 rgb_color labelColor = ui_color(B_PANEL_TEXT_COLOR); 798 rgb_color attributeColor = mix_color(HighColor(), labelColor, 192); 799 800 // Font information 801 font_height fontMetrics; 802 float lineHeight = 0; 803 float lineBase = 0; 804 // Draw the attribute font stuff 805 SetFont(be_plain_font); 806 GetFontHeight(&fontMetrics); 807 lineHeight = CurrentFontHeight() + 5; 808 809 // Starting base line for the first string 810 lineBase = lineHeight; 811 812 // Capacity/size 813 SetHighColor(labelColor); 814 if (fModel->IsVolume() || fModel->IsRoot()) { 815 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Capacity:"))), 816 lineBase)); 817 DrawString(B_TRANSLATE("Capacity:")); 818 } else { 819 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Size:"))), 820 lineBase)); 821 fSizeRect.left = fDivider + 2; 822 fSizeRect.top = lineBase - fontMetrics.ascent; 823 fSizeRect.bottom = lineBase + fontMetrics.descent; 824 DrawString(B_TRANSLATE("Size:")); 825 } 826 827 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 828 SetHighColor(attributeColor); 829 // Check for possible need of truncation 830 if (StringWidth(fSizeString.String()) 831 > (Bounds().Width() - (fDivider + kBorderMargin))) { 832 BString tmpString(fSizeString.String()); 833 TruncateString(&tmpString, B_TRUNCATE_MIDDLE, 834 Bounds().Width() - (fDivider + kBorderMargin)); 835 DrawString(tmpString.String()); 836 fSizeRect.right = fSizeRect.left + StringWidth(tmpString.String()) 837 + 3; 838 } else { 839 DrawString(fSizeString.String()); 840 fSizeRect.right = fSizeRect.left + StringWidth(fSizeString.String()) + 3; 841 } 842 lineBase += lineHeight; 843 844 // Created 845 SetHighColor(labelColor); 846 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Created:"))), 847 lineBase)); 848 DrawString(B_TRANSLATE("Created:")); 849 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 850 SetHighColor(attributeColor); 851 DrawString(fCreatedStr.String()); 852 lineBase += lineHeight; 853 854 // Modified 855 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Modified:"))), 856 lineBase)); 857 SetHighColor(labelColor); 858 DrawString(B_TRANSLATE("Modified:")); 859 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 860 SetHighColor(attributeColor); 861 DrawString(fModifiedStr.String()); 862 lineBase += lineHeight; 863 864 // Kind 865 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Kind:"))), 866 lineBase)); 867 SetHighColor(labelColor); 868 DrawString(B_TRANSLATE("Kind:")); 869 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 870 SetHighColor(attributeColor); 871 DrawString(fKindStr.String()); 872 lineBase += lineHeight; 873 874 BFont normalFont; 875 GetFont(&normalFont); 876 877 // Path 878 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Location:"))), 879 lineBase)); 880 SetHighColor(labelColor); 881 DrawString(B_TRANSLATE("Location:")); 882 883 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 884 SetHighUIColor(fCurrentPathColorWhich); 885 886 // Check for truncation 887 if (StringWidth(fPathStr.String()) > (Bounds().Width() 888 - (fDivider + kBorderMargin))) { 889 BString nameString(fPathStr.String()); 890 TruncateString(&nameString, B_TRUNCATE_MIDDLE, 891 Bounds().Width() - (fDivider + kBorderMargin)); 892 DrawString(nameString.String()); 893 } else 894 DrawString(fPathStr.String()); 895 896 // Cache the position of the path 897 fPathRect.top = lineBase - fontMetrics.ascent; 898 fPathRect.bottom = lineBase + fontMetrics.descent; 899 fPathRect.left = fDivider + 2; 900 fPathRect.right = fPathRect.left + StringWidth(fPathStr.String()) + 3; 901 902 lineBase += lineHeight; 903 904 // Link to/version 905 if (fModel->IsSymLink()) { 906 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Link to:"))), 907 lineBase)); 908 SetHighColor(labelColor); 909 DrawString(B_TRANSLATE("Link to:")); 910 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 911 SetHighUIColor(fCurrentLinkColorWhich); 912 913 // Check for truncation 914 if (StringWidth(fLinkToStr.String()) > (Bounds().Width() 915 - (fDivider + kBorderMargin))) { 916 BString nameString(fLinkToStr.String()); 917 TruncateString(&nameString, B_TRUNCATE_MIDDLE, 918 Bounds().Width() - (fDivider + kBorderMargin)); 919 DrawString(nameString.String()); 920 } else 921 DrawString(fLinkToStr.String()); 922 923 // Cache the position of the link field 924 fLinkRect.top = lineBase - fontMetrics.ascent; 925 fLinkRect.bottom = lineBase + fontMetrics.descent; 926 fLinkRect.left = fDivider + 2; 927 fLinkRect.right = fLinkRect.left + StringWidth(fLinkToStr.String()) 928 + 3; 929 930 // No description field 931 fDescRect = BRect(-1, -1, -1, -1); 932 } else if (fModel->IsExecutable()) { 933 //Version 934 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Version:"))), 935 lineBase)); 936 SetHighColor(labelColor); 937 DrawString(B_TRANSLATE("Version:")); 938 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 939 SetHighColor(attributeColor); 940 BString nameString; 941 if (fModel->GetVersionString(nameString, B_APP_VERSION_KIND) == B_OK) 942 DrawString(nameString.String()); 943 else 944 DrawString("-"); 945 lineBase += lineHeight; 946 947 // Description 948 MovePenTo(BPoint(fDivider - (StringWidth(B_TRANSLATE("Description:"))), 949 lineBase)); 950 SetHighColor(labelColor); 951 DrawString(B_TRANSLATE("Description:")); 952 MovePenTo(BPoint(fDivider + kDrawMargin, lineBase)); 953 SetHighColor(attributeColor); 954 // Check for truncation 955 if (StringWidth(fDescStr.String()) > (Bounds().Width() 956 - (fDivider + kBorderMargin))) { 957 BString nameString(fDescStr.String()); 958 TruncateString(&nameString, B_TRUNCATE_MIDDLE, 959 Bounds().Width() - (fDivider + kBorderMargin)); 960 DrawString(nameString.String()); 961 } else 962 DrawString(fDescStr.String()); 963 964 // Cache the position of the description field 965 fDescRect.top = lineBase - fontMetrics.ascent; 966 fDescRect.bottom = lineBase + fontMetrics.descent; 967 fDescRect.left = fDivider + 2; 968 fDescRect.right = fDescRect.left + StringWidth(fDescStr.String()) + 3; 969 970 // No link field 971 fLinkRect = BRect(-1, -1, -1, -1); 972 } 973 } 974 975 976 void 977 GeneralInfoView::WindowActivated(bool active) 978 { 979 if (active) 980 return; 981 982 if (fPathWindow->Lock()) { 983 fPathWindow->Quit(); 984 fPathWindow = NULL; 985 } 986 987 if (fLinkWindow->Lock()) { 988 fLinkWindow->Quit(); 989 fLinkWindow = NULL; 990 } 991 992 if (fDescWindow->Lock()) { 993 fDescWindow->Quit(); 994 fDescWindow = NULL; 995 } 996 } 997 998 999 float 1000 GeneralInfoView::CurrentFontHeight() 1001 { 1002 BFont font; 1003 GetFont(&font); 1004 font_height fontHeight; 1005 font.GetHeight(&fontHeight); 1006 1007 return fontHeight.ascent + fontHeight.descent + fontHeight.leading + 2; 1008 } 1009 1010 1011 off_t 1012 GeneralInfoView::LastSize() const 1013 { 1014 return fLastSize; 1015 } 1016 1017 1018 void 1019 GeneralInfoView::SetLastSize(off_t lastSize) 1020 { 1021 fLastSize = lastSize; 1022 } 1023 1024 1025 void 1026 GeneralInfoView::SetSizeString(const char* sizeString) 1027 { 1028 fSizeString = sizeString; 1029 1030 float lineHeight = CurrentFontHeight() + 6; 1031 BRect bounds(fDivider, 0, Bounds().right, lineHeight); 1032 Invalidate(bounds); 1033 } 1034 1035 1036 // #pragma mark - 1037 1038 1039 TrackingView::TrackingView(BRect frame, const char* str, BMessage* message) 1040 : BControl(frame, "trackingView", str, message, B_FOLLOW_ALL, 1041 B_WILL_DRAW), 1042 fMouseDown(false), 1043 fMouseInView(false) 1044 { 1045 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1046 SetEventMask(B_POINTER_EVENTS, 0); 1047 } 1048 1049 1050 void 1051 TrackingView::MouseDown(BPoint) 1052 { 1053 if (Message() != NULL) { 1054 fMouseDown = true; 1055 fMouseInView = true; 1056 InvertRect(Bounds()); 1057 } 1058 } 1059 1060 1061 void 1062 TrackingView::MouseMoved(BPoint, uint32 transit, const BMessage*) 1063 { 1064 if ((transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW) && fMouseDown) 1065 InvertRect(Bounds()); 1066 1067 fMouseInView = (transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW); 1068 DelayedInvalidate(16666, Bounds()); 1069 if (!fMouseInView && !fMouseDown) 1070 Window()->Close(); 1071 } 1072 1073 1074 void 1075 TrackingView::MouseUp(BPoint) 1076 { 1077 if (Message() != NULL) { 1078 if (fMouseInView) 1079 Invoke(); 1080 1081 fMouseDown = false; 1082 Window()->Close(); 1083 } 1084 } 1085 1086 1087 void 1088 TrackingView::Draw(BRect) 1089 { 1090 if (Message() != NULL) 1091 SetHighUIColor(fMouseInView ? B_LINK_HOVER_COLOR 1092 : B_LINK_TEXT_COLOR); 1093 else 1094 SetHighUIColor(B_PANEL_TEXT_COLOR); 1095 SetLowColor(ViewColor()); 1096 1097 font_height fontHeight; 1098 GetFontHeight(&fontHeight); 1099 1100 DrawString(Label(), BPoint(3, Bounds().Height() - fontHeight.descent)); 1101 } 1102