Tracker InfoWindow: layoutify, use tabs

- Use layout kit to make a few things simpler
- Separate the view showing the header (icon/name) and the one showing
  the extra details, making two simpler files instead of a complex one
- Fix some layouting issues
- The permissions are now in a second tab, allowing to add a third one
  with other things (more on that later)

screenshot: http://pulkomandy.tk/drop/fileinfo.png

Change-Id: Ief80815eba749723664f40f1317f8aa4cf692162
Reviewed-on: https://review.haiku-os.org/c/haiku/+/1745
Reviewed-by: Ryan Leavengood <[email protected]>
This commit is contained in:
Adrien Destugues
2019-08-25 19:17:14 +00:00
committed by waddlesplash
parent ebb30434c9
commit 2140520f96
8 changed files with 912 additions and 670 deletions
+1
View File
@@ -46,6 +46,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
FindPanel.cpp
GeneralInfoView.cpp
GroupedMenu.cpp
HeaderView.cpp
IconCache.cpp
IconMenuItem.cpp
InfoWindow.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);
+35 -592
View File
@@ -35,7 +35,8 @@ All rights reserved.
#include "GeneralInfoView.h"
#include <Alert.h>
#include <algorithm>
#include <Application.h>
#include <Catalog.h>
#include <Locale.h>
@@ -44,7 +45,6 @@ All rights reserved.
#include <Region.h>
#include <Roster.h>
#include <Screen.h>
#include <ScrollView.h>
#include <StringFormat.h>
#include <SymLink.h>
#include <Volume.h>
@@ -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(&currentFont);
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(&currentFont);
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(&currentFont);
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(&currentFont);
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<BNavMenu*>(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<GeneralInfoView*>(
static_cast<BWindow*>(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);
}
+5 -32
View File
@@ -36,11 +36,11 @@ All rights reserved.
#define GENERALINFOVIEW_H
#include <GroupView.h>
#include <MenuField.h>
#include <Message.h>
#include <Rect.h>
#include <TextView.h>
#include <View.h>
#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 */
+724
View File
@@ -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 <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <Locale.h>
#include <PopUpMenu.h>
#include <ScrollView.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <Window.h>
#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(&currentFont);
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(&currentFont);
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<BNavMenu*>(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<HeaderView*>(
static_cast<BWindow*>(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;
}
+110
View File
@@ -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 <Bitmap.h>
#include <Handler.h>
#include <Message.h>
#include <MessageFilter.h>
#include <Rect.h>
#include <TextView.h>
#include <View.h>
#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 */
+34 -45
View File
@@ -58,6 +58,7 @@ All rights reserved.
#include <ScrollView.h>
#include <StringFormat.h>
#include <SymLink.h>
#include <TabView.h>
#include <TextView.h>
#include <Volume.h>
#include <VolumeRoster.h>
@@ -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<BWindow> 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<GeneralInfoView*>(FindView("attr_view"));
if (view != NULL)
view->SetSizeString(sizeString);
fGeneralInfoView->SetSizeString(sizeString);
}
+2
View File
@@ -40,6 +40,7 @@ All rights reserved.
#include <MessageFilter.h>
#include "FilePermissionsView.h"
#include "HeaderView.h"
#include "LockingList.h"
#include "Utilities.h"
@@ -85,6 +86,7 @@ private:
LockingList<BWindow>* fWindowList;
FilePermissionsView* fPermissionsView;
GeneralInfoView* fGeneralInfoView;
HeaderView* fHeaderView;
BFilePanel* fFilePanel;
bool fFilePanelOpen;