diff --git a/src/kits/tracker/Jamfile b/src/kits/tracker/Jamfile index edb5b2767c..875c96b19e 100644 --- a/src/kits/tracker/Jamfile +++ b/src/kits/tracker/Jamfile @@ -46,6 +46,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { FindPanel.cpp GeneralInfoView.cpp GroupedMenu.cpp + HeaderView.cpp IconCache.cpp IconMenuItem.cpp InfoWindow.cpp diff --git a/src/kits/tracker/infowindow/FilePermissionsView.cpp b/src/kits/tracker/infowindow/FilePermissionsView.cpp index baf2b007c6..cd2ca3c397 100644 --- a/src/kits/tracker/infowindow/FilePermissionsView.cpp +++ b/src/kits/tracker/infowindow/FilePermissionsView.cpp @@ -74,7 +74,7 @@ public: FilePermissionsView::FilePermissionsView(BRect rect, Model* model) : - BView(rect, "FilePermissionsView", B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW), + BView(rect, B_TRANSLATE("Permissions"), B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW), fModel(model) { SetViewUIColor(B_PANEL_BACKGROUND_COLOR); diff --git a/src/kits/tracker/infowindow/GeneralInfoView.cpp b/src/kits/tracker/infowindow/GeneralInfoView.cpp index f77ec73146..6373cb380b 100644 --- a/src/kits/tracker/infowindow/GeneralInfoView.cpp +++ b/src/kits/tracker/infowindow/GeneralInfoView.cpp @@ -35,7 +35,8 @@ All rights reserved. #include "GeneralInfoView.h" -#include +#include + #include #include #include @@ -44,7 +45,6 @@ All rights reserved. #include #include #include -#include #include #include #include @@ -87,16 +87,9 @@ private: } // namespace BPrivate -// Offsets taken from TAlertView::Draw in BAlert.cpp -const float kIconHorizOffset = 18.0f; -const float kIconVertOffset = 6.0f; - const float kBorderMargin = 15.0f; const float kDrawMargin = 3.0f; -// Amount you have to move the mouse before a drag starts -const float kDragSlop = 3.0f; - const uint32 kSetPreferredApp = 'setp'; const uint32 kSelectNewSymTarget = 'snew'; @@ -158,76 +151,46 @@ OpenToolTipWindow(BScreen& screen, BRect rect, const char* name, } -GeneralInfoView::GeneralInfoView(BRect rect, Model* model) +GeneralInfoView::GeneralInfoView(Model* model) : - BView(rect, "attr_view", B_FOLLOW_ALL_SIDES, - B_WILL_DRAW | B_PULSE_NEEDED), + BGroupView(B_VERTICAL), fDivider(0), fPreferredAppMenu(NULL), fModel(model), - fIconModel(model), fMouseDown(false), - fDragging(false), - fDoubleClick(false), fTrackingState(no_track), - fIsDropTarget(false), - fTitleEditView(NULL), fPathWindow(NULL), fLinkWindow(NULL), fDescWindow(NULL), fCurrentLinkColorWhich(B_LINK_TEXT_COLOR), fCurrentPathColorWhich(fCurrentLinkColorWhich) { + SetFlags(Flags() | B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS); + SetName(B_TRANSLATE("Information")); // Set view color to standard background grey SetViewUIColor(B_PANEL_BACKGROUND_COLOR); SetFont(be_plain_font); - // If the model is a symlink, then we deference the model to - // get the targets icon - if (fModel->IsSymLink()) { - Model* resolvedModel = new Model(model->EntryRef(), true, true); - if (resolvedModel->InitCheck() == B_OK) - fIconModel = resolvedModel; - // broken link, just show the symlink - else - delete resolvedModel; - } - - // Create the rect for displaying the icon - fIconRect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); - // Offset taken from BAlert - fIconRect.OffsetBy(kIconHorizOffset, kIconVertOffset); - - // The title rect - // The magic numbers are used to properly calculate the rect so that - // when the editing text view is displayed, the position of the text - // does not change. - BFont currentFont; - font_height fontMetrics; - GetFont(¤tFont); - currentFont.GetHeight(&fontMetrics); - - fTitleRect.left = fIconRect.right + 5; - fTitleRect.top = 0; - fTitleRect.bottom = fontMetrics.ascent + 1; - fTitleRect.right = min_c( - fTitleRect.left + currentFont.StringWidth(fModel->Name()), - Bounds().Width() - 5); - // Offset so that it centers with the icon - fTitleRect.OffsetBy(0, - fIconRect.top + ((fIconRect.Height() - fTitleRect.Height()) / 2)); - // Make some room for the border for when we are in edit mode - // (Negative numbers increase the size of the rect) - fTitleRect.InsetBy(-1, -2); - fFreeBytes = -1; fSizeString = ""; fSizeRect.Set(0, 0, 0, 0); // Find offset for attributes, might be overiden below if there // is a prefered handle menu displayed + BFont currentFont; + GetFont(¤tFont); currentFont.SetSize(currentFont.Size() - 2); - fDivider = currentFont.StringWidth(B_TRANSLATE("Description:")) - + kBorderMargin + kBorderWidth + 1; + + // The widest string depends on the locale. We should check them all, this + // is only an approximation that works for English and French. + float width = currentFont.StringWidth(B_TRANSLATE("Description:")); + width = std::max(width, currentFont.StringWidth(B_TRANSLATE("Location:"))); + fDivider = width + kBorderMargin + 1; + + // Keep some free space for the stuff we print ourselves + float lineHeight = CurrentFontHeight(); + GroupLayout()->SetInsets(kBorderMargin, lineHeight * 7, + B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING); + // Add a preferred handler pop-up menu if this item // is a file...This goes in place of the Link To: // string... @@ -237,18 +200,12 @@ GeneralInfoView::GeneralInfoView(BRect rect, Model* model) // But don't add the menu if the file is executable if (!fModel->IsExecutable()) { - float lineHeight = CurrentFontHeight(); - - BRect preferredAppRect(kBorderWidth + kBorderMargin, - fTitleRect.bottom + (lineHeight * 7), - Bounds().Width() - 5, fTitleRect.bottom + (lineHeight * 8)); - fPreferredAppMenu = new BMenuField(preferredAppRect, "", "", - new BPopUpMenu("")); + fPreferredAppMenu = new BMenuField("", "", new BPopUpMenu("")); currentFont.SetSize(currentFont.Size() + 2); fDivider = currentFont.StringWidth(B_TRANSLATE("Opens with:")) + 5; fPreferredAppMenu->SetDivider(fDivider); - fDivider += (preferredAppRect.left - 2); + fDivider += (kBorderMargin - 2); fPreferredAppMenu->SetFont(¤tFont); fPreferredAppMenu->SetHighUIColor(B_PANEL_TEXT_COLOR); fPreferredAppMenu->SetLabel(B_TRANSLATE("Opens with:")); @@ -301,16 +258,6 @@ GeneralInfoView::GeneralInfoView(BRect rect, Model* model) AddChild(fPreferredAppMenu); } } - - fPermissionsSwitch = new PaneSwitch(BRect(), "Permissions"); - fPermissionsSwitch->SetMessage(new BMessage(kPermissionsSelected)); - fPermissionsSwitch->SetLabels(NULL, B_TRANSLATE("Permissions")); - AddChild(fPermissionsSwitch); - fPermissionsSwitch->ResizeToPreferred(); - fPermissionsSwitch->MoveTo(kBorderWidth + 3, - Bounds().bottom - 3 - fPermissionsSwitch->Bounds().Height()); - - InitStrings(model); } @@ -324,9 +271,6 @@ GeneralInfoView::~GeneralInfoView() if (fDescWindow->Lock()) fDescWindow->Quit(); - - if (fIconModel != fModel) - delete fIconModel; } @@ -507,25 +451,8 @@ GeneralInfoView::ModelChanged(Model* model, BMessage* message) break; } - // Update the icon stuff - if (fIconModel != fModel) { - delete fIconModel; - fIconModel = NULL; - } - fModel = model; if (fModel->IsSymLink()) { - // if we are looking at a symlink, deference the model and look - // at the target - Model* resolvedModel = new Model(model->EntryRef(), true, true); - if (resolvedModel->InitCheck() == B_OK) { - if (fIconModel != fModel) - delete fIconModel; - fIconModel = resolvedModel; - } else { - fIconModel = model; - delete resolvedModel; - } InitStrings(model); Invalidate(); } @@ -545,17 +472,6 @@ void GeneralInfoView::ReLinkTargetModel(Model* model) { fModel = model; - if (fModel->IsSymLink()) { - Model* resolvedModel = new Model(model->EntryRef(), true, true); - if (resolvedModel->InitCheck() == B_OK) { - if (fIconModel != fModel) - delete fIconModel; - fIconModel = resolvedModel; - } else { - fIconModel = fModel; - delete resolvedModel; - } - } InitStrings(model); Invalidate(Bounds()); } @@ -564,12 +480,6 @@ GeneralInfoView::ReLinkTargetModel(Model* model) void GeneralInfoView::MouseDown(BPoint where) { - BEntry entry; - fModel->GetEntry(&entry); - - // Assume this isn't part of a double click - fDoubleClick = false; - // Start tracking the mouse if we are in any of the hotspots if (fLinkRect.Contains(where)) { InvertRect(fLinkRect); @@ -577,67 +487,14 @@ GeneralInfoView::MouseDown(BPoint where) } else if (fPathRect.Contains(where)) { InvertRect(fPathRect); fTrackingState = path_track; - } else if (fTitleRect.Contains(where)) { - if (!fModel->HasLocalizedName() - && ConfirmChangeIfWellKnownDirectory(&entry, kRename, true)) { - BeginEditingTitle(); - } - } else if (fTitleEditView) { - FinishEditingTitle(true); - } else if (fSizeRect.Contains(where)) { if (fModel->IsDirectory() && !fModel->IsVolume() && !fModel->IsRoot()) { InvertRect(fSizeRect); fTrackingState = size_track; } else fTrackingState = no_track; - } else if (fIconRect.Contains(where)) { - uint32 buttons; - Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); - if (SecondaryMouseButtonDown(modifiers(), buttons)) { - // Show contextual menu - BPopUpMenu* contextMenu - = new BPopUpMenu("FileContext", false, false); - if (contextMenu) { - BuildContextMenu(contextMenu); - contextMenu->SetAsyncAutoDestruct(true); - contextMenu->Go(ConvertToScreen(where), true, true, - ConvertToScreen(fIconRect)); - } - } else { - // Check to see if the point is actually on part of the icon, - // versus just in the container rect. The icons are always - // the large version - BPoint offsetPoint; - offsetPoint.x = where.x - fIconRect.left; - offsetPoint.y = where.y - fIconRect.top; - if (IconCache::sIconCache->IconHitTest(offsetPoint, fIconModel, - kNormalIcon, B_LARGE_ICON)) { - // Can't drag the trash anywhere.. - fTrackingState = fModel->IsTrash() - ? open_only_track : icon_track; - - // Check for possible double click - if (abs((int32)(fClickPoint.x - where.x)) < kDragSlop - && abs((int32)(fClickPoint.y - where.y)) < kDragSlop) { - int32 clickCount; - Window()->CurrentMessage()->FindInt32("clicks", - &clickCount); - - // This checks the* previous* click point - if (clickCount == 2) { - offsetPoint.x = fClickPoint.x - fIconRect.left; - offsetPoint.y = fClickPoint.y - fIconRect.top; - fDoubleClick - = IconCache::sIconCache->IconHitTest(offsetPoint, - fIconModel, kNormalIcon, B_LARGE_ICON); - } - } - } - } } - fClickPoint = where; fMouseDown = true; SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); } @@ -646,20 +503,6 @@ GeneralInfoView::MouseDown(BPoint where) void GeneralInfoView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage) { - if (dragMessage != NULL && dragMessage->ReturnAddress() != BMessenger(this) - && dragMessage->what == B_SIMPLE_DATA - && BPoseView::CanHandleDragSelection(fModel, dragMessage, - (modifiers() & B_CONTROL_KEY) != 0)) { - // highlight drag target - bool overTarget = fIconRect.Contains(where); - SetDrawingMode(B_OP_OVER); - if (overTarget != fIsDropTarget) { - IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(), - overTarget ? kSelectedIcon : kNormalIcon, B_LARGE_ICON, true); - fIsDropTarget = overTarget; - } - } - fCurrentLinkColorWhich = B_LINK_TEXT_COLOR; fCurrentPathColorWhich = fCurrentLinkColorWhich; @@ -685,88 +528,6 @@ GeneralInfoView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage) } break; - case icon_track: - if (fMouseDown && !fDragging - && (abs((int32)(where.x - fClickPoint.x)) > kDragSlop - || abs((int32)(where.y - fClickPoint.y)) > kDragSlop)) { - // Find the required height - BFont font; - GetFont(&font); - - float height = CurrentFontHeight() - + fIconRect.Height() + 8; - BRect rect(0, 0, min_c(fIconRect.Width() - + font.StringWidth(fModel->Name()) + 4, - fIconRect.Width() * 3), height); - BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true); - dragBitmap->Lock(); - BView* view = new BView(dragBitmap->Bounds(), "", - B_FOLLOW_NONE, 0); - dragBitmap->AddChild(view); - view->SetOrigin(0, 0); - BRect clipRect(view->Bounds()); - BRegion newClip; - newClip.Set(clipRect); - view->ConstrainClippingRegion(&newClip); - - // Transparent draw magic - view->SetHighColor(0, 0, 0, 0); - view->FillRect(view->Bounds()); - view->SetDrawingMode(B_OP_ALPHA); - rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR); - textColor.alpha = 128; - // set transparency by value - view->SetHighColor(textColor); - view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); - - // Draw the icon - float hIconOffset = (rect.Width() - fIconRect.Width()) / 2; - IconCache::sIconCache->Draw(fIconModel, view, - BPoint(hIconOffset, 0), kNormalIcon, B_LARGE_ICON, true); - - // See if we need to truncate the string - BString nameString(fModel->Name()); - if (view->StringWidth(fModel->Name()) > rect.Width()) { - view->TruncateString(&nameString, B_TRUNCATE_END, - rect.Width() - 5); - } - - // Draw the label - font_height fontHeight; - font.GetHeight(&fontHeight); - float leftText = (view->StringWidth(nameString.String()) - - fIconRect.Width()) / 2; - view->MovePenTo(BPoint(hIconOffset - leftText + 2, - fIconRect.Height() + (fontHeight.ascent + 2))); - view->DrawString(nameString.String()); - - view->Sync(); - dragBitmap->Unlock(); - - BMessage dragMessage(B_REFS_RECEIVED); - dragMessage.AddPoint("click_pt", fClickPoint); - BPoint tmpLoc; - uint32 button; - GetMouse(&tmpLoc, &button); - if (button) - dragMessage.AddInt32("buttons", (int32)button); - - dragMessage.AddInt32("be:actions", - (modifiers() & B_OPTION_KEY) != 0 - ? B_COPY_TARGET : B_MOVE_TARGET); - dragMessage.AddRef("refs", fModel->EntryRef()); - DragMessage(&dragMessage, dragBitmap, B_OP_ALPHA, - BPoint((fClickPoint.x - fIconRect.left) - + hIconOffset, fClickPoint.y - fIconRect.top), this); - fDragging = true; - } - break; - - case open_only_track : - // Special type of entry that can't be renamed or drag and dropped - // It can only be opened by double clicking on the icon - break; - default: { // Only consider this if the window is the active window. @@ -893,23 +654,6 @@ GeneralInfoView::MouseUp(BPoint where) } else if (fTrackingState == path_track && fPathRect.Contains(where)) { InvertRect(fPathRect); OpenLinkSource(); - } else if ((fTrackingState == icon_track - || fTrackingState == open_only_track) - && fIconRect.Contains(where)) { - // If it was a double click, then tell Tracker to open the item - // The CurrentMessage() here does* not* have a "clicks" field, - // which is why we are tracking the clicks with this temp var - if (fDoubleClick) { - // Double click, launch. - BMessage message(B_REFS_RECEIVED); - message.AddRef("refs", fModel->EntryRef()); - - // add a messenger to the launch message that will be used to - // dispatch scripting calls from apps to the PoseView - message.AddMessenger("TrackerViewToken", BMessenger(this)); - be_app->PostMessage(&message); - fDoubleClick = false; - } } else if (fTrackingState == size_track && fSizeRect.Contains(where)) { // Recalculate size Window()->PostMessage(kRecalculateSize); @@ -917,9 +661,7 @@ GeneralInfoView::MouseUp(BPoint where) // End mouse tracking fMouseDown = false; - fDragging = false; fTrackingState = no_track; - } @@ -981,8 +723,7 @@ GeneralInfoView::CheckAndSetSize() BRect bounds(Bounds()); float lineHeight = CurrentFontHeight() + 2; - bounds.Set(fDivider, fIconRect.bottom, bounds.right, - fIconRect.bottom + lineHeight); + bounds.Set(fDivider, 0, bounds.right, lineHeight); Invalidate(bounds); } @@ -990,18 +731,6 @@ GeneralInfoView::CheckAndSetSize() void GeneralInfoView::MessageReceived(BMessage* message) { - if (message->WasDropped() - && message->what == B_SIMPLE_DATA - && message->ReturnAddress() != BMessenger(this) - && fIconRect.Contains(ConvertFromScreen(message->DropPoint())) - && BPoseView::CanHandleDragSelection(fModel, message, - (modifiers() & B_CONTROL_KEY) != 0)) { - BPoseView::HandleDropCommon(message, fModel, 0, this, - message->DropPoint()); - Invalidate(fIconRect); - return; - } - switch (message->what) { case kSetPreferredApp: { @@ -1032,6 +761,16 @@ GeneralInfoView::MessageReceived(BMessage* message) } +void +GeneralInfoView::FrameResized(float, float) +{ + BModelOpener opener(fModel); + + // Truncate the strings according to the new width + InitStrings(fModel); +} + + void GeneralInfoView::Draw(BRect) { @@ -1045,54 +784,17 @@ GeneralInfoView::Draw(BRect) rgb_color labelColor = ui_color(B_PANEL_TEXT_COLOR); rgb_color attributeColor = mix_color(HighColor(), labelColor, 192); - // Draw the dark grey area on the left - BRect drawBounds(Bounds()); - drawBounds.right = kBorderWidth; - SetHighUIColor(B_PANEL_BACKGROUND_COLOR, B_DARKEN_2_TINT); - FillRect(drawBounds); - - // Draw the icon, straddling the border - SetDrawingMode(B_OP_OVER); - IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(), - kNormalIcon, B_LARGE_ICON, true); - SetDrawingMode(B_OP_COPY); // Font information font_height fontMetrics; - BFont currentFont; float lineHeight = 0; float lineBase = 0; - // Draw the main title if the user is not currently editing it - if (fTitleEditView == NULL) { - SetFont(be_bold_font); - SetFontSize(be_bold_font->Size()); - GetFont(¤tFont); - currentFont.GetHeight(&fontMetrics); - lineHeight = CurrentFontHeight() + 5; - lineBase = fTitleRect.bottom - fontMetrics.descent; - SetHighColor(labelColor); - MovePenTo(BPoint(fIconRect.right + 6, lineBase)); - - // Recalculate the rect width - fTitleRect.right = min_c( - fTitleRect.left + currentFont.StringWidth(fModel->Name()), - Bounds().Width() - 5); - // Check for possible need of truncation - if (StringWidth(fModel->Name()) > fTitleRect.Width()) { - BString nameString(fModel->Name()); - TruncateString(&nameString, B_TRUNCATE_END, - fTitleRect.Width() - 2); - DrawString(nameString.String()); - } else - DrawString(fModel->Name()); - } - // Draw the attribute font stuff SetFont(be_plain_font); GetFontHeight(&fontMetrics); lineHeight = CurrentFontHeight() + 5; // Starting base line for the first string - lineBase = fTitleRect.bottom + lineHeight; + lineBase = lineHeight; // Capacity/size SetHighColor(labelColor); @@ -1258,128 +960,12 @@ GeneralInfoView::Draw(BRect) } -void -GeneralInfoView::BeginEditingTitle() -{ - if (fTitleEditView != NULL) - return; - - BFont font(be_plain_font); - font.SetSize(font.Size() + 2); - BRect textFrame(fTitleRect); - textFrame.right = Bounds().Width() - 5; - BRect textRect(textFrame); - textRect.OffsetTo(0, 0); - textRect.InsetBy(1, 1); - - // Just make it some really large size, since we don't do any line - // wrapping. The text filter will make sure to scroll the cursor - // into position - - textRect.right = 2000; - fTitleEditView = new BTextView(textFrame, "text_editor", - textRect, &font, 0, B_FOLLOW_ALL, B_WILL_DRAW); - fTitleEditView->SetText(fModel->Name()); - DisallowFilenameKeys(fTitleEditView); - - // Reset the width of the text rect - textRect = fTitleEditView->TextRect(); - textRect.right = fTitleEditView->LineWidth() + 20; - fTitleEditView->SetTextRect(textRect); - fTitleEditView->SetWordWrap(false); - // Add filter for catching B_RETURN and B_ESCAPE key's - fTitleEditView->AddFilter( - new BMessageFilter(B_KEY_DOWN, GeneralInfoView::TextViewFilter)); - - BScrollView* scrollView = new BScrollView("BorderView", fTitleEditView, - 0, 0, false, false, B_PLAIN_BORDER); - AddChild(scrollView); - fTitleEditView->SelectAll(); - fTitleEditView->MakeFocus(); - - Window()->UpdateIfNeeded(); -} - - -void -GeneralInfoView::FinishEditingTitle(bool commit) -{ - if (fTitleEditView == NULL) - return; - - bool reopen = false; - - const char* text = fTitleEditView->Text(); - uint32 length = strlen(text); - if (commit && strcmp(text, fModel->Name()) != 0 - && length < B_FILE_NAME_LENGTH) { - BEntry entry(fModel->EntryRef()); - BDirectory parent; - if (entry.InitCheck() == B_OK - && entry.GetParent(&parent) == B_OK) { - if (parent.Contains(text)) { - BAlert* alert = new BAlert("", - B_TRANSLATE("That name is already taken. " - "Please type another one."), - B_TRANSLATE("OK"), - 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); - alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); - alert->Go(); - reopen = true; - } else { - if (fModel->IsVolume()) { - BVolume volume(fModel->NodeRef()->device); - if (volume.InitCheck() == B_OK) - volume.SetName(text); - } else - entry.Rename(text); - - // Adjust the size of the text rect - BFont currentFont(be_plain_font); - currentFont.SetSize(currentFont.Size() + 2); - fTitleRect.right = min_c(fTitleRect.left - + currentFont.StringWidth(fTitleEditView->Text()), - Bounds().Width() - 5); - } - } - } else if (length >= B_FILE_NAME_LENGTH) { - BAlert* alert = new BAlert("", - B_TRANSLATE("That name is too long. Please type another one."), - B_TRANSLATE("OK"), - 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); - alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); - alert->Go(); - reopen = true; - } - - // Remove view - BView* scrollView = fTitleEditView->Parent(); - RemoveChild(scrollView); - delete scrollView; - fTitleEditView = NULL; - - if (reopen) - BeginEditingTitle(); -} - - -void -GeneralInfoView::MakeFocus(bool focus) -{ - if (!focus && fTitleEditView != NULL) - FinishEditingTitle(true); -} - - void GeneralInfoView::WindowActivated(bool active) { if (active) return; - if (fTitleEditView != NULL) - FinishEditingTitle(true); - if (fPathWindow->Lock()) { fPathWindow->Quit(); fPathWindow = NULL; @@ -1409,148 +995,6 @@ GeneralInfoView::CurrentFontHeight() } -status_t -GeneralInfoView::BuildContextMenu(BMenu* parent) -{ - if (parent == NULL) - return B_BAD_VALUE; - - // Add navigation menu if this is not a symlink - // Symlink's to directories are OK however! - BEntry entry(fModel->EntryRef()); - entry_ref ref; - entry.GetRef(&ref); - Model model(&entry); - bool navigate = false; - if (model.InitCheck() == B_OK) { - if (model.IsSymLink()) { - // Check if it's to a directory - if (entry.SetTo(model.EntryRef(), true) == B_OK) { - navigate = entry.IsDirectory(); - entry.GetRef(&ref); - } - } else if (model.IsDirectory() || model.IsVolume()) - navigate = true; - } - ModelMenuItem* navigationItem = NULL; - if (navigate) { - navigationItem = new ModelMenuItem(new Model(model), - new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, Window())); - - // setup a navigation menu item which will dynamically load items - // as menu items are traversed - BNavMenu* navMenu = dynamic_cast(navigationItem->Submenu()); - if (navMenu != NULL) - navMenu->SetNavDir(&ref); - - navigationItem->SetLabel(model.Name()); - navigationItem->SetEntry(&entry); - - parent->AddItem(navigationItem, 0); - parent->AddItem(new BSeparatorItem(), 1); - - BMessage* message = new BMessage(B_REFS_RECEIVED); - message->AddRef("refs", &ref); - navigationItem->SetMessage(message); - navigationItem->SetTarget(be_app); - } - - parent->AddItem(new BMenuItem(B_TRANSLATE("Open"), - new BMessage(kOpenSelection), 'O')); - - if (!model.IsDesktop() && !model.IsRoot() && !model.IsTrash() - && !fModel->HasLocalizedName()) { - parent->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), - new BMessage(kEditItem), 'E')); - parent->AddSeparatorItem(); - - if (fModel->IsVolume()) { - BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"), - new BMessage(kUnmountVolume), 'U'); - parent->AddItem(item); - // volume model, enable/disable the Unmount item - BVolume boot; - BVolumeRoster().GetBootVolume(&boot); - BVolume volume; - volume.SetTo(fModel->NodeRef()->device); - if (volume == boot) - item->SetEnabled(false); - } - } - - if (!model.IsRoot() && !model.IsVolume() && !model.IsTrash()) - parent->AddItem(new BMenuItem(B_TRANSLATE("Identify"), - new BMessage(kIdentifyEntry))); - - if (model.IsTrash()) - parent->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), - new BMessage(kEmptyTrash))); - - BMenuItem* sizeItem = NULL; - if (model.IsDirectory() && !model.IsVolume() && !model.IsRoot()) { - parent->AddItem(sizeItem - = new BMenuItem(B_TRANSLATE("Recalculate folder size"), - new BMessage(kRecalculateSize))); - } - - if (model.IsSymLink()) { - parent->AddItem(sizeItem - = new BMenuItem(B_TRANSLATE("Set new link target"), - new BMessage(kSetLinkTarget))); - } - - parent->AddItem(new BSeparatorItem()); - parent->AddItem(new BMenuItem(B_TRANSLATE("Permissions"), - new BMessage(kPermissionsSelected), 'P')); - - parent->SetFont(be_plain_font); - parent->SetTargetForItems(this); - - // Reset the nav menu to be_app - if (navigate) - navigationItem->SetTarget(be_app); - if (sizeItem) - sizeItem->SetTarget(Window()); - - return B_OK; -} - - -void -GeneralInfoView::SetPermissionsSwitchState(int32 state) -{ - fPermissionsSwitch->SetValue(state); - fPermissionsSwitch->Invalidate(); -} - - -filter_result -GeneralInfoView::TextViewFilter(BMessage* message, BHandler**, - BMessageFilter* filter) -{ - uchar key; - GeneralInfoView* attribView = static_cast( - static_cast(filter->Looper())->FindView("attr_view")); - - // Adjust the size of the text rect - BRect nuRect(attribView->TextView()->TextRect()); - nuRect.right = attribView->TextView()->LineWidth() + 20; - attribView->TextView()->SetTextRect(nuRect); - - // Make sure the cursor is in view - attribView->TextView()->ScrollToSelection(); - if (message->FindInt8("byte", (int8*)&key) != B_OK) - return B_DISPATCH_MESSAGE; - - if (key == B_RETURN || key == B_ESCAPE) { - attribView->FinishEditingTitle(key == B_RETURN); - return B_SKIP_MESSAGE; - } - - return B_DISPATCH_MESSAGE; -} - - off_t GeneralInfoView::LastSize() const { @@ -1572,8 +1016,7 @@ GeneralInfoView::SetSizeString(const char* sizeString) BRect bounds(Bounds()); float lineHeight = CurrentFontHeight() + 6; - bounds.Set(fDivider, fIconRect.bottom, bounds.right, - fIconRect.bottom + lineHeight); + bounds.Set(fDivider, 0, bounds.right, lineHeight); Invalidate(bounds); } diff --git a/src/kits/tracker/infowindow/GeneralInfoView.h b/src/kits/tracker/infowindow/GeneralInfoView.h index 82d19f274c..c1f9bd0209 100644 --- a/src/kits/tracker/infowindow/GeneralInfoView.h +++ b/src/kits/tracker/infowindow/GeneralInfoView.h @@ -36,11 +36,11 @@ All rights reserved. #define GENERALINFOVIEW_H +#include #include #include #include #include -#include #include "DialogPane.h" #include "Model.h" @@ -53,48 +53,33 @@ enum track_state { no_track = 0, link_track, path_track, - icon_track, size_track, - open_only_track - // This is for items that can be opened, but can't be - // drag and dropped or renamed (Trash, Desktop Folder...) }; -class GeneralInfoView : public BView { +class GeneralInfoView : public BGroupView { public: - GeneralInfoView(BRect, Model*); + GeneralInfoView(Model*); ~GeneralInfoView(); void ModelChanged(Model*, BMessage*); void ReLinkTargetModel(Model*); - void BeginEditingTitle(); - void FinishEditingTitle(bool); float CurrentFontHeight(); - BTextView* TextView() const { return fTitleEditView; } - - static filter_result TextViewFilter(BMessage*, BHandler**, - BMessageFilter*); - off_t LastSize() const; void SetLastSize(off_t); void SetSizeString(const char*); - status_t BuildContextMenu(BMenu* parent); - - void SetPermissionsSwitchState(int32 state); - protected: virtual void MouseDown(BPoint where); virtual void MouseMoved(BPoint where, uint32, const BMessage* dragMessage); virtual void MouseUp(BPoint where); virtual void MessageReceived(BMessage* message); virtual void AttachedToWindow(); + virtual void FrameResized(float, float); virtual void Draw(BRect); virtual void Pulse(); - virtual void MakeFocus(bool focus); virtual void WindowActivated(bool active); private: @@ -117,30 +102,20 @@ private: BRect fPathRect; BRect fLinkRect; BRect fDescRect; - BRect fTitleRect; - BRect fIconRect; BRect fSizeRect; - BPoint fClickPoint; float fDivider; BMenuField* fPreferredAppMenu; Model* fModel; - Model* fIconModel; - BBitmap* fIcon; bool fMouseDown; - bool fDragging; - bool fDoubleClick; track_state fTrackingState; - bool fIsDropTarget; - BTextView* fTitleEditView; - PaneSwitch* fPermissionsSwitch; BWindow* fPathWindow; BWindow* fLinkWindow; BWindow* fDescWindow; color_which fCurrentLinkColorWhich; color_which fCurrentPathColorWhich; - typedef BView _inherited; + typedef BGroupView _inherited; }; } // namespace BPrivate @@ -150,7 +125,5 @@ const uint32 kPermissionsSelected = 'sepe'; const uint32 kRecalculateSize = 'resz'; const uint32 kSetLinkTarget = 'link'; -const float kBorderWidth = 32.0f; - #endif /* !GENERALINFOVIEW_H */ diff --git a/src/kits/tracker/infowindow/HeaderView.cpp b/src/kits/tracker/infowindow/HeaderView.cpp new file mode 100644 index 0000000000..f97d6589c8 --- /dev/null +++ b/src/kits/tracker/infowindow/HeaderView.cpp @@ -0,0 +1,724 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + + +#include "HeaderView.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Commands.h" +#include "FSUtils.h" +#include "GeneralInfoView.h" +#include "IconMenuItem.h" +#include "Model.h" +#include "NavMenu.h" +#include "PoseView.h" +#include "Tracker.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "InfoWindow" + + +// Offsets taken from TAlertView::Draw in BAlert.cpp +const float kIconHorizOffset = 18.0f; +const float kIconVertOffset = 6.0f; +const float kBorderWidth = 32.0f; + +// Amount you have to move the mouse before a drag starts +const float kDragSlop = 3.0f; + + +HeaderView::HeaderView(Model* model) + : + BView("header", B_WILL_DRAW), + fModel(model), + fIconModel(model), + fTitleEditView(NULL), + fTrackingState(no_track), + fMouseDown(false), + fIsDropTarget(false), + fDoubleClick(false), + fDragging(false) +{ + // Create the rect for displaying the icon + fIconRect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); + // Offset taken from BAlert + fIconRect.OffsetBy(kIconHorizOffset, kIconVertOffset); + SetExplicitMinSize(BSize(B_SIZE_UNSET, B_LARGE_ICON + 2 * kIconVertOffset)); + + // The title rect + // The magic numbers are used to properly calculate the rect so that + // when the editing text view is displayed, the position of the text + // does not change. + BFont currentFont; + font_height fontMetrics; + GetFont(¤tFont); + currentFont.GetHeight(&fontMetrics); + + fTitleRect.left = fIconRect.right + 5; + fTitleRect.top = 0; + fTitleRect.bottom = fontMetrics.ascent + 1; + fTitleRect.right = min_c( + fTitleRect.left + currentFont.StringWidth(fModel->Name()), + Bounds().Width() - 5); + // Offset so that it centers with the icon + fTitleRect.OffsetBy(0, + fIconRect.top + ((fIconRect.Height() - fTitleRect.Height()) / 2)); + // Make some room for the border for when we are in edit mode + // (Negative numbers increase the size of the rect) + fTitleRect.InsetBy(-1, -2); + + // If the model is a symlink, then we deference the model to + // get the targets icon + if (fModel->IsSymLink()) { + Model* resolvedModel = new Model(model->EntryRef(), true, true); + if (resolvedModel->InitCheck() == B_OK) + fIconModel = resolvedModel; + // broken link, just show the symlink + else + delete resolvedModel; + } + +} + + +HeaderView::~HeaderView() +{ + if (fIconModel != fModel) + delete fIconModel; +} + + +void +HeaderView::ModelChanged(Model* model, BMessage* message) +{ + // Update the icon stuff + if (fIconModel != fModel) { + delete fIconModel; + fIconModel = NULL; + } + + fModel = model; + if (fModel->IsSymLink()) { + // if we are looking at a symlink, deference the model and look + // at the target + Model* resolvedModel = new Model(model->EntryRef(), true, true); + if (resolvedModel->InitCheck() == B_OK) { + if (fIconModel != fModel) + delete fIconModel; + fIconModel = resolvedModel; + } else { + fIconModel = model; + delete resolvedModel; + } + } + + Invalidate(); +} + + +void +HeaderView::ReLinkTargetModel(Model* model) +{ + fModel = model; + if (fModel->IsSymLink()) { + Model* resolvedModel = new Model(model->EntryRef(), true, true); + if (resolvedModel->InitCheck() == B_OK) { + if (fIconModel != fModel) + delete fIconModel; + fIconModel = resolvedModel; + } else { + fIconModel = fModel; + delete resolvedModel; + } + } + Invalidate(); +} + + +void +HeaderView::BeginEditingTitle() +{ + if (fTitleEditView != NULL) + return; + + BFont font(be_plain_font); + font.SetSize(font.Size() + 2); + BRect textFrame(fTitleRect); + textFrame.right = Bounds().Width() - 5; + BRect textRect(textFrame); + textRect.OffsetTo(0, 0); + textRect.InsetBy(1, 1); + + // Just make it some really large size, since we don't do any line + // wrapping. The text filter will make sure to scroll the cursor + // into position + + textRect.right = 2000; + fTitleEditView = new BTextView(textFrame, "text_editor", + textRect, &font, 0, B_FOLLOW_ALL, B_WILL_DRAW); + fTitleEditView->SetText(fModel->Name()); + DisallowFilenameKeys(fTitleEditView); + + // Reset the width of the text rect + textRect = fTitleEditView->TextRect(); + textRect.right = fTitleEditView->LineWidth() + 20; + fTitleEditView->SetTextRect(textRect); + fTitleEditView->SetWordWrap(false); + // Add filter for catching B_RETURN and B_ESCAPE key's + fTitleEditView->AddFilter( + new BMessageFilter(B_KEY_DOWN, HeaderView::TextViewFilter)); + + BScrollView* scrollView = new BScrollView("BorderView", fTitleEditView, + 0, 0, false, false, B_PLAIN_BORDER); + AddChild(scrollView); + fTitleEditView->SelectAll(); + fTitleEditView->MakeFocus(); + + Window()->UpdateIfNeeded(); +} + + +void +HeaderView::FinishEditingTitle(bool commit) +{ + if (fTitleEditView == NULL) + return; + + bool reopen = false; + + const char* text = fTitleEditView->Text(); + uint32 length = strlen(text); + if (commit && strcmp(text, fModel->Name()) != 0 + && length < B_FILE_NAME_LENGTH) { + BEntry entry(fModel->EntryRef()); + BDirectory parent; + if (entry.InitCheck() == B_OK + && entry.GetParent(&parent) == B_OK) { + if (parent.Contains(text)) { + BAlert* alert = new BAlert("", + B_TRANSLATE("That name is already taken. " + "Please type another one."), + B_TRANSLATE("OK"), + 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); + alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); + alert->Go(); + reopen = true; + } else { + if (fModel->IsVolume()) { + BVolume volume(fModel->NodeRef()->device); + if (volume.InitCheck() == B_OK) + volume.SetName(text); + } else + entry.Rename(text); + + // Adjust the size of the text rect + BFont currentFont(be_plain_font); + currentFont.SetSize(currentFont.Size() + 2); + fTitleRect.right = min_c(fTitleRect.left + + currentFont.StringWidth(fTitleEditView->Text()), + Bounds().Width() - 5); + } + } + } else if (length >= B_FILE_NAME_LENGTH) { + BAlert* alert = new BAlert("", + B_TRANSLATE("That name is too long. Please type another one."), + B_TRANSLATE("OK"), + 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); + alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); + alert->Go(); + reopen = true; + } + + // Remove view + BView* scrollView = fTitleEditView->Parent(); + RemoveChild(scrollView); + delete scrollView; + fTitleEditView = NULL; + + if (reopen) + BeginEditingTitle(); +} + + +void +HeaderView::Draw(BRect) +{ + // Set the low color for anti-aliasing + SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + // Clear the old contents + SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + FillRect(Bounds()); + + rgb_color labelColor = ui_color(B_PANEL_TEXT_COLOR); + + // Draw the icon, straddling the border + SetDrawingMode(B_OP_OVER); + IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(), + kNormalIcon, B_LARGE_ICON, true); + SetDrawingMode(B_OP_COPY); + + // Font information + font_height fontMetrics; + BFont currentFont; + float lineHeight = 0; + float lineBase = 0; + + // Draw the main title if the user is not currently editing it + if (fTitleEditView == NULL) { + SetFont(be_bold_font); + SetFontSize(be_bold_font->Size()); + GetFont(¤tFont); + currentFont.GetHeight(&fontMetrics); + lineHeight = CurrentFontHeight() + 5; + lineBase = fTitleRect.bottom - fontMetrics.descent; + SetHighColor(labelColor); + MovePenTo(BPoint(fIconRect.right + 6, lineBase)); + + // Recalculate the rect width + fTitleRect.right = min_c( + fTitleRect.left + currentFont.StringWidth(fModel->Name()), + Bounds().Width() - 5); + // Check for possible need of truncation + if (StringWidth(fModel->Name()) > fTitleRect.Width()) { + BString nameString(fModel->Name()); + TruncateString(&nameString, B_TRUNCATE_END, + fTitleRect.Width() - 2); + DrawString(nameString.String()); + } else + DrawString(fModel->Name()); + } + +} + + +void +HeaderView::MakeFocus(bool focus) +{ + if (!focus && fTitleEditView != NULL) + FinishEditingTitle(true); +} + + +void +HeaderView::WindowActivated(bool active) +{ + if (active) + return; + + if (fTitleEditView != NULL) + FinishEditingTitle(true); +} + + +void +HeaderView::MouseDown(BPoint where) +{ + // Assume this isn't part of a double click + fDoubleClick = false; + + BEntry entry; + fModel->GetEntry(&entry); + + if (fTitleRect.Contains(where)) { + if (!fModel->HasLocalizedName() + && ConfirmChangeIfWellKnownDirectory(&entry, kRename, true)) { + BeginEditingTitle(); + } + } else if (fTitleEditView) { + FinishEditingTitle(true); + } else if (fIconRect.Contains(where)) { + uint32 buttons; + Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); + if (SecondaryMouseButtonDown(modifiers(), buttons)) { + // Show contextual menu + BPopUpMenu* contextMenu + = new BPopUpMenu("FileContext", false, false); + if (contextMenu) { + BuildContextMenu(contextMenu); + contextMenu->SetAsyncAutoDestruct(true); + contextMenu->Go(ConvertToScreen(where), true, true, + ConvertToScreen(fIconRect)); + } + } else { + // Check to see if the point is actually on part of the icon, + // versus just in the container rect. The icons are always + // the large version + BPoint offsetPoint; + offsetPoint.x = where.x - fIconRect.left; + offsetPoint.y = where.y - fIconRect.top; + if (IconCache::sIconCache->IconHitTest(offsetPoint, fIconModel, + kNormalIcon, B_LARGE_ICON)) { + // Can't drag the trash anywhere.. + fTrackingState = fModel->IsTrash() + ? open_only_track : icon_track; + + // Check for possible double click + if (abs((int32)(fClickPoint.x - where.x)) < kDragSlop + && abs((int32)(fClickPoint.y - where.y)) < kDragSlop) { + int32 clickCount; + Window()->CurrentMessage()->FindInt32("clicks", + &clickCount); + + // This checks the* previous* click point + if (clickCount == 2) { + offsetPoint.x = fClickPoint.x - fIconRect.left; + offsetPoint.y = fClickPoint.y - fIconRect.top; + fDoubleClick + = IconCache::sIconCache->IconHitTest(offsetPoint, + fIconModel, kNormalIcon, B_LARGE_ICON); + } + } + } + } + } + + fClickPoint = where; + fMouseDown = true; + SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); +} + + +void +HeaderView::MouseMoved(BPoint where, uint32, const BMessage* dragMessage) +{ + if (dragMessage != NULL && dragMessage->ReturnAddress() != BMessenger(this) + && dragMessage->what == B_SIMPLE_DATA + && BPoseView::CanHandleDragSelection(fModel, dragMessage, + (modifiers() & B_CONTROL_KEY) != 0)) { + // highlight drag target + bool overTarget = fIconRect.Contains(where); + SetDrawingMode(B_OP_OVER); + if (overTarget != fIsDropTarget) { + IconCache::sIconCache->Draw(fIconModel, this, fIconRect.LeftTop(), + overTarget ? kSelectedIcon : kNormalIcon, B_LARGE_ICON, true); + fIsDropTarget = overTarget; + } + } + + switch (fTrackingState) { + case icon_track: + if (fMouseDown && !fDragging + && (abs((int32)(where.x - fClickPoint.x)) > kDragSlop + || abs((int32)(where.y - fClickPoint.y)) > kDragSlop)) { + // Find the required height + BFont font; + GetFont(&font); + + float height = CurrentFontHeight() + + fIconRect.Height() + 8; + BRect rect(0, 0, min_c(fIconRect.Width() + + font.StringWidth(fModel->Name()) + 4, + fIconRect.Width() * 3), height); + BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true); + dragBitmap->Lock(); + BView* view = new BView(dragBitmap->Bounds(), "", + B_FOLLOW_NONE, 0); + dragBitmap->AddChild(view); + view->SetOrigin(0, 0); + BRect clipRect(view->Bounds()); + BRegion newClip; + newClip.Set(clipRect); + view->ConstrainClippingRegion(&newClip); + + // Transparent draw magic + view->SetHighColor(0, 0, 0, 0); + view->FillRect(view->Bounds()); + view->SetDrawingMode(B_OP_ALPHA); + rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR); + textColor.alpha = 128; + // set transparency by value + view->SetHighColor(textColor); + view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); + + // Draw the icon + float hIconOffset = (rect.Width() - fIconRect.Width()) / 2; + IconCache::sIconCache->Draw(fIconModel, view, + BPoint(hIconOffset, 0), kNormalIcon, B_LARGE_ICON, true); + + // See if we need to truncate the string + BString nameString(fModel->Name()); + if (view->StringWidth(fModel->Name()) > rect.Width()) { + view->TruncateString(&nameString, B_TRUNCATE_END, + rect.Width() - 5); + } + + // Draw the label + font_height fontHeight; + font.GetHeight(&fontHeight); + float leftText = (view->StringWidth(nameString.String()) + - fIconRect.Width()) / 2; + view->MovePenTo(BPoint(hIconOffset - leftText + 2, + fIconRect.Height() + (fontHeight.ascent + 2))); + view->DrawString(nameString.String()); + + view->Sync(); + dragBitmap->Unlock(); + + BMessage dragMessage(B_REFS_RECEIVED); + dragMessage.AddPoint("click_pt", fClickPoint); + BPoint tmpLoc; + uint32 button; + GetMouse(&tmpLoc, &button); + if (button) + dragMessage.AddInt32("buttons", (int32)button); + + dragMessage.AddInt32("be:actions", + (modifiers() & B_OPTION_KEY) != 0 + ? B_COPY_TARGET : B_MOVE_TARGET); + dragMessage.AddRef("refs", fModel->EntryRef()); + DragMessage(&dragMessage, dragBitmap, B_OP_ALPHA, + BPoint((fClickPoint.x - fIconRect.left) + + hIconOffset, fClickPoint.y - fIconRect.top), this); + fDragging = true; + } + break; + + case open_only_track : + // Special type of entry that can't be renamed or drag and dropped + // It can only be opened by double clicking on the icon + break; + + case no_track: + // No mouse tracking, do nothing + break; + } +} + + +void +HeaderView::MouseUp(BPoint where) +{ + if ((fTrackingState == icon_track + || fTrackingState == open_only_track) + && fIconRect.Contains(where)) { + // If it was a double click, then tell Tracker to open the item + // The CurrentMessage() here does* not* have a "clicks" field, + // which is why we are tracking the clicks with this temp var + if (fDoubleClick) { + // Double click, launch. + BMessage message(B_REFS_RECEIVED); + message.AddRef("refs", fModel->EntryRef()); + + // add a messenger to the launch message that will be used to + // dispatch scripting calls from apps to the PoseView + message.AddMessenger("TrackerViewToken", BMessenger(this)); + be_app->PostMessage(&message); + fDoubleClick = false; + } + } + + // End mouse tracking + fMouseDown = false; + fDragging = false; + fTrackingState = no_track; +} + + +void +HeaderView::MessageReceived(BMessage* message) +{ + if (message->WasDropped() + && message->what == B_SIMPLE_DATA + && message->ReturnAddress() != BMessenger(this) + && fIconRect.Contains(ConvertFromScreen(message->DropPoint())) + && BPoseView::CanHandleDragSelection(fModel, message, + (modifiers() & B_CONTROL_KEY) != 0)) { + BPoseView::HandleDropCommon(message, fModel, 0, this, + message->DropPoint()); + Invalidate(fIconRect); + return; + } + + BView::MessageReceived(message); +} + + + +status_t +HeaderView::BuildContextMenu(BMenu* parent) +{ + if (parent == NULL) + return B_BAD_VALUE; + + // Add navigation menu if this is not a symlink + // Symlink's to directories are OK however! + BEntry entry(fModel->EntryRef()); + entry_ref ref; + entry.GetRef(&ref); + Model model(&entry); + bool navigate = false; + if (model.InitCheck() == B_OK) { + if (model.IsSymLink()) { + // Check if it's to a directory + if (entry.SetTo(model.EntryRef(), true) == B_OK) { + navigate = entry.IsDirectory(); + entry.GetRef(&ref); + } + } else if (model.IsDirectory() || model.IsVolume()) + navigate = true; + } + ModelMenuItem* navigationItem = NULL; + if (navigate) { + navigationItem = new ModelMenuItem(new Model(model), + new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, Window())); + + // setup a navigation menu item which will dynamically load items + // as menu items are traversed + BNavMenu* navMenu = dynamic_cast(navigationItem->Submenu()); + if (navMenu != NULL) + navMenu->SetNavDir(&ref); + + navigationItem->SetLabel(model.Name()); + navigationItem->SetEntry(&entry); + + parent->AddItem(navigationItem, 0); + parent->AddItem(new BSeparatorItem(), 1); + + BMessage* message = new BMessage(B_REFS_RECEIVED); + message->AddRef("refs", &ref); + navigationItem->SetMessage(message); + navigationItem->SetTarget(be_app); + } + + parent->AddItem(new BMenuItem(B_TRANSLATE("Open"), + new BMessage(kOpenSelection), 'O')); + + if (!model.IsDesktop() && !model.IsRoot() && !model.IsTrash() + && !fModel->HasLocalizedName()) { + parent->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), + new BMessage(kEditItem), 'E')); + parent->AddSeparatorItem(); + + if (fModel->IsVolume()) { + BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"), + new BMessage(kUnmountVolume), 'U'); + parent->AddItem(item); + // volume model, enable/disable the Unmount item + BVolume boot; + BVolumeRoster().GetBootVolume(&boot); + BVolume volume; + volume.SetTo(fModel->NodeRef()->device); + if (volume == boot) + item->SetEnabled(false); + } + } + + if (!model.IsRoot() && !model.IsVolume() && !model.IsTrash()) + parent->AddItem(new BMenuItem(B_TRANSLATE("Identify"), + new BMessage(kIdentifyEntry))); + + if (model.IsTrash()) + parent->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), + new BMessage(kEmptyTrash))); + + BMenuItem* sizeItem = NULL; + if (model.IsDirectory() && !model.IsVolume() && !model.IsRoot()) { + parent->AddItem(sizeItem + = new BMenuItem(B_TRANSLATE("Recalculate folder size"), + new BMessage(kRecalculateSize))); + } + + if (model.IsSymLink()) { + parent->AddItem(sizeItem + = new BMenuItem(B_TRANSLATE("Set new link target"), + new BMessage(kSetLinkTarget))); + } + + parent->AddItem(new BSeparatorItem()); + parent->AddItem(new BMenuItem(B_TRANSLATE("Permissions"), + new BMessage(kPermissionsSelected), 'P')); + + parent->SetFont(be_plain_font); + parent->SetTargetForItems(this); + + // Reset the nav menu to be_app + if (navigate) + navigationItem->SetTarget(be_app); + if (sizeItem) + sizeItem->SetTarget(Window()); + + return B_OK; +} + + +filter_result +HeaderView::TextViewFilter(BMessage* message, BHandler**, + BMessageFilter* filter) +{ + uchar key; + HeaderView* attribView = static_cast( + static_cast(filter->Looper())->FindView("header")); + + // Adjust the size of the text rect + BRect nuRect(attribView->TextView()->TextRect()); + nuRect.right = attribView->TextView()->LineWidth() + 20; + attribView->TextView()->SetTextRect(nuRect); + + // Make sure the cursor is in view + attribView->TextView()->ScrollToSelection(); + if (message->FindInt8("byte", (int8*)&key) != B_OK) + return B_DISPATCH_MESSAGE; + + if (key == B_RETURN || key == B_ESCAPE) { + attribView->FinishEditingTitle(key == B_RETURN); + return B_SKIP_MESSAGE; + } + + return B_DISPATCH_MESSAGE; +} + + +float +HeaderView::CurrentFontHeight() +{ + BFont font; + GetFont(&font); + font_height fontHeight; + font.GetHeight(&fontHeight); + + return fontHeight.ascent + fontHeight.descent + fontHeight.leading + 2; +} + + diff --git a/src/kits/tracker/infowindow/HeaderView.h b/src/kits/tracker/infowindow/HeaderView.h new file mode 100644 index 0000000000..0feab5faaf --- /dev/null +++ b/src/kits/tracker/infowindow/HeaderView.h @@ -0,0 +1,110 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + + +#ifndef HEADERVIEW_H +#define HEADERVIEW_H + + +#include +#include +#include +#include +#include +#include +#include + +#include "Model.h" + + +class HeaderView: public BView { +public: + HeaderView(Model*); + ~HeaderView(); + + void ModelChanged(Model*, BMessage*); + void ReLinkTargetModel(Model*); + void BeginEditingTitle(); + void FinishEditingTitle(bool); + virtual void Draw(BRect); + virtual void MakeFocus(bool focus); + virtual void WindowActivated(bool active); + + BTextView* TextView() const { return fTitleEditView; } + +protected: + virtual void MouseDown(BPoint where); + virtual void MouseMoved(BPoint where, uint32, const BMessage* dragMessage); + virtual void MouseUp(BPoint where); + virtual void MessageReceived(BMessage* message); + + status_t BuildContextMenu(BMenu* parent); + static filter_result TextViewFilter(BMessage*, BHandler**, + BMessageFilter*); + + float CurrentFontHeight(); + +private: + // States for tracking the mouse + enum track_state { + no_track = 0, + icon_track, + open_only_track + // This is for items that can be opened, but can't be + // drag and dropped or renamed (Trash, Desktop Folder...) + }; + + // Layouting + BRect fTitleRect; + BRect fIconRect; + BPoint fClickPoint; + + // Model data + Model* fModel; + Model* fIconModel; + BBitmap* fIcon; + BTextView* fTitleEditView; + + // Mouse tracking + track_state fTrackingState; + bool fMouseDown; + bool fIsDropTarget; + bool fDoubleClick; + bool fDragging; + + typedef BView _inherited; +}; + + +#endif /* !HEADERVIEW_H */ diff --git a/src/kits/tracker/infowindow/InfoWindow.cpp b/src/kits/tracker/infowindow/InfoWindow.cpp index 18b0e436a3..a86b7f18f1 100644 --- a/src/kits/tracker/infowindow/InfoWindow.cpp +++ b/src/kits/tracker/infowindow/InfoWindow.cpp @@ -58,6 +58,7 @@ All rights reserved. #include #include #include +#include #include #include #include @@ -96,7 +97,8 @@ BInfoWindow::BInfoWindow(Model* model, int32 group_index, : BWindow(BInfoWindow::InfoWindowRect(), "InfoWindow", B_TITLED_WINDOW, - B_NOT_RESIZABLE | B_NOT_ZOOMABLE, B_CURRENT_WORKSPACE), + B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS, + B_CURRENT_WORKSPACE), fModel(model), fStopCalc(false), fIndex(group_index), @@ -120,6 +122,31 @@ BInfoWindow::BInfoWindow(Model* model, int32 group_index, AddShortcut('U', 0, new BMessage(kUnmountVolume)); AddShortcut('P', 0, new BMessage(kPermissionsSelected)); + BGroupLayout* layout = new BGroupLayout(B_VERTICAL, 0); + SetLayout(layout); + + BModelOpener modelOpener(TargetModel()); + if (TargetModel()->InitCheck() != B_OK) + return; + + AddChild(new HeaderView(TargetModel())); + BTabView* tabView = new BTabView("tabs"); + tabView->SetBorder(B_NO_BORDER); + AddChild(tabView); + + fGeneralInfoView = new GeneralInfoView(TargetModel()); + tabView->AddTab(fGeneralInfoView); + + BRect permissionsBounds(0, + fGeneralInfoView->Bounds().bottom, + fGeneralInfoView->Bounds().right, + fGeneralInfoView->Bounds().bottom + 103); + + fPermissionsView = new FilePermissionsView( + permissionsBounds, fModel); + tabView->AddTab(fPermissionsView); + // This window accepts messages before being shown, so let's start the + // looper immediately. Run(); } @@ -170,7 +197,6 @@ BInfoWindow::IsShowing(const node_ref* node) const void BInfoWindow::Show() { - BModelOpener modelOpener(TargetModel()); if (TargetModel()->InitCheck() != B_OK) { Close(); return; @@ -178,22 +204,6 @@ BInfoWindow::Show() AutoLock lock(this); - const BFont* font = be_plain_font; - float width = font->StringWidth("This is a really long string which we" - "will use to find the window width"); - - // window height depends on file type - int lines = 9; - if (fModel->IsExecutable()) - lines++; - float height = font->Size() * (lines * 2 + 1); - - ResizeTo(width, height); - - BRect attrRect(Bounds()); - fGeneralInfoView = new GeneralInfoView(attrRect, TargetModel()); - AddChild(fGeneralInfoView); - // position window appropriately based on index BRect windRect(InfoWindowRect()); if ((fIndex + 2) % 2 == 1) { @@ -261,7 +271,7 @@ BInfoWindow::MessageReceived(BMessage* message) BEntry entry(fModel->EntryRef()); if (!fModel->HasLocalizedName() && ConfirmChangeIfWellKnownDirectory(&entry, kRename)) { - fGeneralInfoView->BeginEditingTitle(); + fHeaderView->BeginEditingTitle(); } break; } @@ -372,6 +382,7 @@ BInfoWindow::MessageReceived(BMessage* message) // Tell the attribute view about this new model fGeneralInfoView->ReLinkTargetModel(TargetModel()); + fHeaderView->ReLinkTargetModel(TargetModel()); } break; } @@ -429,6 +440,7 @@ BInfoWindow::MessageReceived(BMessage* message) // FilePermissionView::ModelChanged() // call, because it changes the model... // (bad style!) + fHeaderView->ModelChanged(TargetModel(), message); if (fPermissionsView != NULL) fPermissionsView->ModelChanged(TargetModel()); @@ -453,28 +465,8 @@ BInfoWindow::MessageReceived(BMessage* message) case kPermissionsSelected: { - BRect permissionsBounds(kBorderWidth + 1, - fGeneralInfoView->Bounds().bottom, - fGeneralInfoView->Bounds().right, - fGeneralInfoView->Bounds().bottom + 103); - - if (fPermissionsView == NULL) { - // Only true on first call. - fPermissionsView = new FilePermissionsView( - permissionsBounds, fModel); - ResizeBy(0, permissionsBounds.Height()); - fGeneralInfoView->AddChild(fPermissionsView); - fGeneralInfoView->SetPermissionsSwitchState(kPaneSwitchOpen); - } else if (fPermissionsView->IsHidden()) { - fPermissionsView->ModelChanged(fModel); - fPermissionsView->Show(); - ResizeBy(0, permissionsBounds.Height()); - fGeneralInfoView->SetPermissionsSwitchState(kPaneSwitchOpen); - } else { - fPermissionsView->Hide(); - ResizeBy(0, -permissionsBounds.Height()); - fGeneralInfoView->SetPermissionsSwitchState(kPaneSwitchClosed); - } + BTabView* tabView = (BTabView*)FindView("tabs"); + tabView->Select(1); break; } @@ -594,10 +586,7 @@ BInfoWindow::CalcSize(void* castToWindow) void BInfoWindow::SetSizeString(const char* sizeString) { - GeneralInfoView* view - = dynamic_cast(FindView("attr_view")); - if (view != NULL) - view->SetSizeString(sizeString); + fGeneralInfoView->SetSizeString(sizeString); } diff --git a/src/kits/tracker/infowindow/InfoWindow.h b/src/kits/tracker/infowindow/InfoWindow.h index b0cc067475..6b2a077982 100644 --- a/src/kits/tracker/infowindow/InfoWindow.h +++ b/src/kits/tracker/infowindow/InfoWindow.h @@ -40,6 +40,7 @@ All rights reserved. #include #include "FilePermissionsView.h" +#include "HeaderView.h" #include "LockingList.h" #include "Utilities.h" @@ -85,6 +86,7 @@ private: LockingList* fWindowList; FilePermissionsView* fPermissionsView; GeneralInfoView* fGeneralInfoView; + HeaderView* fHeaderView; BFilePanel* fFilePanel; bool fFilePanelOpen;