#include "ContainerWindow.h"
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <GridView.h>
#include <GroupLayout.h>
#include <Keymap.h>
#include <Locale.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <Screen.h>
#include <UnicodeChar.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <WindowPrivate.h>
#include <fs_attr.h>
#include <image.h>
#include <strings.h>
#include <stdlib.h>
#include "Attributes.h"
#include "AutoDeleter.h"
#include "AutoLock.h"
#include "BackgroundImage.h"
#include "Commands.h"
#include "CountView.h"
#include "DeskWindow.h"
#include "DraggableContainerIcon.h"
#include "FSClipboard.h"
#include "FSUndoRedo.h"
#include "FSUtils.h"
#include "FavoritesMenu.h"
#include "FindPanel.h"
#include "IconMenuItem.h"
#include "LiveMenu.h"
#include "MimeTypes.h"
#include "Model.h"
#include "MountMenu.h"
#include "NavMenu.h"
#include "Navigator.h"
#include "OpenWithWindow.h"
#include "PoseView.h"
#include "QueryContainerWindow.h"
#include "SelectionWindow.h"
#include "Shortcuts.h"
#include "TemplatesMenu.h"
#include "Thread.h"
#include "TitleView.h"
#include "Tracker.h"
#include "TrackerSettings.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ContainerWindow"
#ifdef _IMPEXP_BE
_IMPEXP_BE
#endif
void do_minimize_team(BRect zoomRect, team_id team, bool zoom);
struct AddOneAddOnParams {
BObjectList<BMenuItem>* primaryList;
BObjectList<BMenuItem>* secondaryList;
};
struct StaggerOneParams {
bool rectFromParent;
};
BRect BContainerWindow::sNewWindRect;
static int32 sWindowStaggerBy;
LockingList<AddOnInfo, true>* BContainerWindow::fAddOnsList
= new LockingList<struct AddOnInfo, true>(10);
namespace BPrivate {
int
CompareContainerWindowNodeRef(const BContainerWindow* item1, const BContainerWindow* item2)
{
const node_ref* ref1 = item1->TargetModel()->NodeRef();
const node_ref* ref2 = item2->TargetModel()->NodeRef();
if (*ref1 < *ref2)
return -1;
else if (*ref1 == *ref2)
return 0;
else
return 1;
}
filter_result
ActivateWindowFilter(BMessage*, BHandler** target, BMessageFilter*)
{
BView* view = dynamic_cast<BView*>(*target);
if (view != NULL
&& dynamic_cast<BPoseView*>(view) == NULL
&& dynamic_cast<DraggableContainerIcon*>(view) == NULL
&& view->Window() != NULL) {
view->Window()->Activate(true);
}
return B_DISPATCH_MESSAGE;
}
}
static int32
AddOnMenuGenerate(const struct AddOnInfo* info, BMenu* menu, BContainerWindow* window)
{
if (info->has_populate_menu != B_NO_INIT && info->has_populate_menu != B_OK)
return info->has_populate_menu;
const entry_ref* addOnRef = info->model->EntryRef();
BEntry entry(addOnRef);
status_t result = entry.InitCheck();
if (result != B_OK)
return result;
BPath path;
result = entry.GetPath(&path);
if (result != B_OK)
return result;
image_id addOnImage = load_add_on(path.Path());
if (addOnImage < 0) {
info->has_populate_menu = addOnImage;
return addOnImage;
}
void (*populateMenu)(BMessage*, BMenu*, BHandler*);
result = get_image_symbol(addOnImage, "populate_menu", 2, (void**)&populateMenu);
if (result < 0) {
PRINT(("Couldn't find populate_menu in %s\n", info->model->Name()));
info->has_populate_menu = result;
unload_add_on(addOnImage);
return result;
}
BMessage* message = window->AddOnMessage(B_TRACKER_ADDON_MESSAGE);
message->AddRef("addon_ref", addOnRef);
(*populateMenu)(message, menu, window->PoseView());
unload_add_on(addOnImage);
info->has_populate_menu = B_OK;
return B_OK;
}
static status_t
RunAddOnMessageThread(BMessage *message, void *)
{
entry_ref addOnRef;
BEntry entry;
BPath path;
status_t result = message->FindRef("addon_ref", &addOnRef);
image_id addOnImage;
if (result != B_OK)
goto end;
entry = BEntry(&addOnRef);
result = entry.InitCheck();
if (result != B_OK)
goto end;
result = entry.GetPath(&path);
if (result != B_OK)
goto end;
addOnImage = load_add_on(path.Path());
if (addOnImage < 0) {
result = addOnImage;
goto end;
}
void (*messageReceived)(BMessage*);
result = get_image_symbol(addOnImage, "message_received", 2,
(void**)&messageReceived);
if (result < 0) {
PRINT(("Couldn't find message_received\n"));
unload_add_on(addOnImage);
goto end;
}
(*messageReceived)(message);
unload_add_on(addOnImage);
return B_OK;
end:
BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
buffer.ReplaceFirst("%error", strerror(result));
buffer.ReplaceFirst("%name", addOnRef.name);
BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return result;
}
static void
AddOneAddOn(void* context, const struct AddOnInfo* info,
bool primary, BContainerWindow* window, BMenu* menu)
{
AddOneAddOnParams* params = (AddOneAddOnParams*)context;
BObjectList<BMenuItem>* list = primary ? params->primaryList : params->secondaryList;
if (list != NULL) {
ModelMenuItem* item;
try {
item = new ModelMenuItem(info->model, info->model->Name(), NULL,
info->key, info->modifiers);
} catch (...) {
return;
}
BMessage* message = new BMessage(kLoadAddOn);
message->AddRef("refs", info->model->EntryRef());
item->SetMessage(message);
list->AddItem(item);
}
if (menu != NULL)
AddOnMenuGenerate(info, menu, window);
}
static int32
AddOnThread(BMessage* refsMessage, entry_ref addOnRef, entry_ref directoryRef)
{
ObjectDeleter<BMessage> _(refsMessage);
BEntry entry(&addOnRef);
BPath path;
status_t result = entry.InitCheck();
if (result == B_OK)
result = entry.GetPath(&path);
if (result == B_OK) {
image_id addOnImage = load_add_on(path.Path());
if (addOnImage >= 0) {
void (*processRefs)(entry_ref, BMessage*, void*);
result = get_image_symbol(addOnImage, "process_refs", 2,
(void**)&processRefs);
if (result >= 0) {
(*processRefs)(directoryRef, refsMessage, NULL);
unload_add_on(addOnImage);
return B_OK;
} else
PRINT(("couldn't find process_refs\n"));
unload_add_on(addOnImage);
} else
result = addOnImage;
}
BString buffer(B_TRANSLATE("Error %error loading Add-On %name."));
buffer.ReplaceFirst("%error", strerror(result));
buffer.ReplaceFirst("%name", addOnRef.name);
BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return result;
}
static bool
NodeHasSavedState(const BNode* node)
{
attr_info info;
return node->GetAttrInfo(kAttrWindowFrame, &info) == B_OK;
}
static bool
OffsetFrameOne(const char* DEBUG_ONLY(name), uint32, off_t, void* castToRect,
void* castToParams)
{
ASSERT(strcmp(name, kAttrWindowFrame) == 0);
StaggerOneParams* params = (StaggerOneParams*)castToParams;
if (!params->rectFromParent)
return false;
if (!castToRect)
return false;
((BRect*)castToRect)->OffsetBy(sWindowStaggerBy, sWindowStaggerBy);
return true;
}
static void
AddMimeTypeString(BStringList& list, Model* model)
{
if (model == NULL)
return;
const char* modelMimeType = model->MimeType();
if (modelMimeType == NULL || *modelMimeType == '\0')
return;
BString type = BString(modelMimeType);
if (list.HasString(type, true))
return;
list.Add(type);
}
BContainerWindow::BContainerWindow(LockingList<BWindow>* list, uint32 openFlags, window_look look,
window_feel feel, uint32 windowFlags, uint32 workspace, bool useLayout)
:
BWindow(InitialWindowRect(feel), "TrackerWindow", look, feel, windowFlags, workspace),
fWindowList(list),
fOpenFlags(openFlags),
fUsesLayout(useLayout),
fMenuContainer(NULL),
fPoseContainer(NULL),
fBorderedView(NULL),
fVScrollBarContainer(NULL),
fCountContainer(NULL),
fShortcuts(NULL),
fContextMenu(NULL),
fPoseContextMenu(NULL),
fWindowContextMenu(NULL),
fDropContextMenu(NULL),
fVolumeContextMenu(NULL),
fTrashContextMenu(NULL),
fDragContextMenu(NULL),
fMoveToItem(NULL),
fCopyToItem(NULL),
fCreateLinkItem(NULL),
fOpenWithItem(NULL),
fEditQueryItem(NULL),
fMountItem(NULL),
fNavigationItem(NULL),
fNewTemplatesItem(NULL),
fMenuBar(NULL),
fDraggableIcon(NULL),
fNavigator(NULL),
fPoseView(NULL),
fAttrMenu(NULL),
fWindowMenu(NULL),
fFileMenu(NULL),
fArrangeByItem(NULL),
fSelectionWindow(NULL),
fTaskLoop(NULL),
fStateNeedsSaving(false),
fBackgroundImage(NULL),
fSavedZoomRect(0, 0, -1, -1),
fSaveStateIsEnabled(true),
fIsWatchingPath(false)
{
InitIconPreloader();
if (list != NULL) {
ASSERT(list->IsLocked());
list->AddItem(this);
}
if (fUsesLayout) {
SetFlags(Flags() | B_AUTO_UPDATE_SIZE_LIMITS);
fRootLayout = new BGroupLayout(B_VERTICAL, 0);
fRootLayout->SetInsets(0);
SetLayout(fRootLayout);
fRootLayout->Owner()->AdoptSystemColors();
fMenuContainer = new BGroupView(B_HORIZONTAL, 0);
fRootLayout->AddView(fMenuContainer);
fPoseContainer = new BGridView(0.0, 0.0);
fRootLayout->AddView(fPoseContainer);
fBorderedView = new BorderedView;
fPoseContainer->GridLayout()->AddView(fBorderedView, 0, 1);
fCountContainer = new BGroupView(B_HORIZONTAL, 0);
fPoseContainer->GridLayout()->AddView(fCountContainer, 0, 2);
}
AddCommonFilter(new BMessageFilter(B_MOUSE_DOWN, ActivateWindowFilter));
Run();
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL && tracker->Lock()) {
tracker->StartWatching(this, kWindowsShowFullPathChanged);
tracker->StartWatching(this, kSingleWindowBrowseChanged);
tracker->StartWatching(this, kShowNavigatorChanged);
tracker->Unlock();
}
AddShortcut('Z', B_COMMAND_KEY, new BMessage(B_UNDO), this);
AddShortcut('Z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO), this);
}
BContainerWindow::~BContainerWindow()
{
ASSERT(IsLocked());
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL && tracker->Lock()) {
tracker->StopWatching(this, kWindowsShowFullPathChanged);
tracker->StopWatching(this, kSingleWindowBrowseChanged);
tracker->StopWatching(this, kShowNavigatorChanged);
tracker->Unlock();
}
delete fTaskLoop;
delete fBackgroundImage;
delete fShortcuts;
if (fSelectionWindow != NULL && fSelectionWindow->Lock())
fSelectionWindow->Quit();
}
BRect
BContainerWindow::InitialWindowRect(window_feel feel)
{
if (!sNewWindRect.IsValid()) {
const float labelSpacing = be_control_look->DefaultLabelSpacing();
sNewWindRect = BRect(labelSpacing * 14, labelSpacing * 8,
labelSpacing * 91, labelSpacing * 46);
sWindowStaggerBy = (int32)(labelSpacing * 3.0f);
}
if (feel != kDesktopWindowFeel)
return sNewWindRect;
BRect result = sNewWindRect;
result.OffsetTo(0, 0);
return result;
}
void
BContainerWindow::Minimize(bool minimize)
{
if (minimize && (modifiers() & B_OPTION_KEY) != 0)
do_minimize_team(BRect(0, 0, 0, 0), be_app->Team(), true);
else
_inherited::Minimize(minimize);
}
bool
BContainerWindow::QuitRequested()
{
if (CurrentMessage() != NULL
&& ((CurrentMessage()->FindInt32("modifiers") & B_CONTROL_KEY)) != 0) {
be_app->PostMessage(kCloseAllWindows);
}
Hide();
return true;
}
void
BContainerWindow::Quit()
{
if (fCreateLinkItem != NULL && fCreateLinkItem->Menu() == NULL)
delete fCreateLinkItem;
if (fCopyToItem != NULL && fCopyToItem->Menu() == NULL)
delete fCopyToItem;
if (fMoveToItem != NULL && fMoveToItem->Menu() == NULL)
delete fMoveToItem;
if (fMountItem != NULL && fMountItem->Menu() == NULL)
delete fMountItem;
if (fOpenWithItem != NULL && fOpenWithItem->Menu() == NULL)
delete fOpenWithItem;
if (fEditQueryItem != NULL && fEditQueryItem->Menu() == NULL)
delete fEditQueryItem;
if (fNewTemplatesItem != NULL && fNewTemplatesItem->Menu() == NULL)
delete fNewTemplatesItem;
if (fNavigationItem != NULL && fNavigationItem->Menu() == NULL)
delete fNavigationItem;
if (fAttrMenu != NULL && fAttrMenu->Supermenu() == NULL)
delete fAttrMenu;
if (fArrangeByItem != NULL && fArrangeByItem->Menu() == NULL)
delete fArrangeByItem;
delete fPoseContextMenu;
delete fWindowContextMenu;
delete fDropContextMenu;
delete fVolumeContextMenu;
delete fDragContextMenu;
delete fTrashContextMenu;
int32 windowCount = 0;
if (fWindowList != NULL) {
AutoLock<LockingList<BWindow> > lock(fWindowList);
if (lock.IsLocked()) {
fWindowList->RemoveItem(this, false);
windowCount = fWindowList->CountItems();
}
}
if (StateNeedsSaving())
SaveState();
if (fWindowList != NULL && windowCount == 0)
be_app->PostMessage(B_QUIT_REQUESTED);
_inherited::Quit();
}
BPoseView*
BContainerWindow::NewPoseView(Model* model, uint32 viewMode)
{
return new BPoseView(model, viewMode);
}
void
BContainerWindow::CreatePoseView(Model* model)
{
fPoseView = NewPoseView(model, kListMode);
fBorderedView->GroupLayout()->AddView(fPoseView);
fBorderedView->GroupLayout()->SetInsets(1, 0, 1, 1);
fBorderedView->EnableBorderHighlight(false);
TrackerSettings settings;
if (settings.SingleWindowBrowse() && model->IsDirectory() && !PoseView()->IsFilePanel()) {
fNavigator = new BNavigator(model);
fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
if (!settings.ShowNavigator())
fNavigator->Hide();
}
SetPathWatchingEnabled(settings.ShowNavigator()
|| settings.ShowFullPathInTitleBar());
}
void
BContainerWindow::AddContextMenus()
{
fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false);
AddPoseContextMenu(fPoseContextMenu);
fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false);
AddVolumeContextMenu(fVolumeContextMenu);
fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false);
AddWindowContextMenu(fWindowContextMenu);
fDropContextMenu = new BPopUpMenu("DropContext", false, false);
AddDropContextMenu(fDropContextMenu);
fDragContextMenu = new BPopUpNavMenu("DragContext");
fTrashContextMenu = new BPopUpMenu("TrashContext", false, false);
AddTrashContextMenu(fTrashContextMenu);
}
void
BContainerWindow::DetachSubmenus()
{
if (fCreateLinkItem != NULL && fCreateLinkItem->Menu() != NULL)
fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
if (fCopyToItem != NULL && fCopyToItem->Menu() != NULL)
fCopyToItem->Menu()->RemoveItem(fCopyToItem);
if (fMoveToItem != NULL && fMoveToItem->Menu() != NULL)
fMoveToItem->Menu()->RemoveItem(fMoveToItem);
if (fMountItem != NULL && fMountItem->Menu() != NULL)
fMountItem = DetachMountMenu();
if (fOpenWithItem != NULL && fOpenWithItem->Menu() != NULL)
fOpenWithItem->Menu()->RemoveItem(fOpenWithItem);
if (fEditQueryItem != NULL && fEditQueryItem->Menu() != NULL)
fEditQueryItem->Menu()->RemoveItem(fEditQueryItem);
if (fNewTemplatesItem != NULL && fNewTemplatesItem->Menu() != NULL)
fNewTemplatesItem->Menu()->RemoveItem(fNewTemplatesItem);
if (fNavigationItem != NULL && fNavigationItem->Menu() != NULL) {
delete fNavigationItem->Menu()->RemoveItem(
fNavigationItem->Menu()->IndexOf(fNavigationItem) + 1);
fNavigationItem->Menu()->RemoveItem(fNavigationItem);
}
}
void
BContainerWindow::RepopulateMenus()
{
DetachSubmenus();
if (fMenuBar != NULL) {
if (fFileMenu != NULL) {
fMenuBar->RemoveItem(fFileMenu);
delete fFileMenu;
}
if (fWindowMenu != NULL) {
fMenuBar->RemoveItem(fWindowMenu);
delete fWindowMenu;
}
if (fAttrMenu != NULL) {
fMenuBar->RemoveItem(fAttrMenu);
delete fAttrMenu;
}
if (ShouldAddMenus()) {
AddMenus();
if (PoseView()->ViewMode() == kListMode)
fMenuBar->AddItem(fAttrMenu, 2);
}
}
delete fPoseContextMenu;
fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false);
AddPoseContextMenu(fPoseContextMenu);
delete fWindowContextMenu;
fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false);
AddWindowContextMenu(fWindowContextMenu);
}
void
BContainerWindow::Init(const BMessage* message)
{
if (PoseView() == NULL)
return;
if (NeedsDefaultStateSetup())
SetupDefaultState();
if (ShouldAddScrollBars())
PoseView()->AddScrollBars();
fShortcuts = new TShortcuts(this);
fEditQueryItem = Shortcuts()->EditQueryItem();
const char* name = Shortcuts()->MoveToLabel();
fMoveToItem = Shortcuts()->MoveToItem(new BNavMenu(name, kMoveSelectionTo, this));
name = Shortcuts()->CopyToLabel();
fCopyToItem = Shortcuts()->CopyToItem(new BNavMenu(name, kCopySelectionTo, this));
name = Shortcuts()->CreateLinkLabel();
fCreateLinkItem = Shortcuts()->CreateLinkItem(new BNavMenu(name, kCreateLink, this));
name = Shortcuts()->NewTemplatesLabel();
fNewTemplatesItem = Shortcuts()->NewTemplatesItem(new TemplatesMenu(PoseView(), name));
TrackerSettings settings;
if (ShouldAddMenus()) {
fMenuBar = new BMenuBar("MenuBar");
fMenuContainer->GroupLayout()->AddView(fMenuBar);
AddMenus();
if (ShouldHaveDraggableFolderIcon())
_AddFolderIcon();
} else {
AddShortcuts();
}
AddContextMenus();
AddShortcut(B_DELETE, B_NO_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kDeleteSelection),
PoseView());
AddShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kCleanupAll), PoseView());
AddShortcut('Q', B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY | B_CONTROL_KEY,
new BMessage(kQuitTracker));
AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenSelection), PoseView());
SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
#if DEBUG
AddShortcut('D', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dbug'),
PoseView());
AddShortcut('C', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpcc'),
PoseView());
AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpfl'),
PoseView());
AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY,
new BMessage('dpfL'), PoseView());
#endif
BKeymap keymap;
if (keymap.SetToCurrent() == B_OK) {
BStringList unmodified(3);
if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, unmodified)
== B_OK) {
int32 count = unmodified.CountStrings();
for (int32 i = 0; i < count; i++) {
uint32 key = BUnicodeChar::FromUTF8(unmodified.StringAt(i));
if (!HasShortcut(key, 0)) {
BMessage* increaseSize = new BMessage(kIconMode);
increaseSize->AddInt32("scale", 1);
AddShortcut(key, B_COMMAND_KEY, increaseSize, PoseView());
}
}
}
unmodified.MakeEmpty();
}
if (message != NULL)
RestoreState(*message);
else
RestoreState();
bool isListMode = PoseView()->ViewMode() == kListMode;
if (ShouldAddMenus() && isListMode) {
ShowAttributesMenu();
}
if (ShouldAddMenus() && ShouldHaveAddOnMenus())
BuildAddOnMenus(fMenuBar);
CheckScreenIntersect();
if (fBackgroundImage != NULL && !PoseView()->IsDesktopView() && !isListMode)
fBackgroundImage->Show(PoseView(), current_workspace());
Show();
SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION);
}
void
BContainerWindow::InitLayout()
{
fBorderedView->GroupLayout()->AddView(0, PoseView()->TitleView());
fCountContainer->GroupLayout()->AddView(PoseView()->CountView(), 0.25f);
bool forFilePanel = PoseView()->IsFilePanel();
if (!forFilePanel) {
fPoseContainer->GridLayout()->SetInsets(-1, 0, -1, -1);
fCountContainer->GroupLayout()->SetInsets(0, -1, 0, 0);
}
if (PoseView()->VScrollBar() != NULL) {
fVScrollBarContainer = new BGroupView(B_VERTICAL, 0);
fVScrollBarContainer->GroupLayout()->AddView(PoseView()->VScrollBar());
fVScrollBarContainer->GroupLayout()->SetInsets(-1, forFilePanel ? 0 : -1,
0, 0);
fPoseContainer->GridLayout()->AddView(fVScrollBarContainer, 1, 1);
}
if (PoseView()->HScrollBar() != NULL) {
BGroupView* hScrollBarContainer = new BGroupView(B_VERTICAL, 0);
hScrollBarContainer->GroupLayout()->AddView(PoseView()->HScrollBar());
hScrollBarContainer->GroupLayout()->SetInsets(0, -1, 0,
forFilePanel ? 0 : -1);
fCountContainer->GroupLayout()->AddView(hScrollBarContainer);
BSize size = PoseView()->HScrollBar()->MinSize();
if (forFilePanel) {
size.height -= 1;
}
PoseView()->CountView()->SetExplicitMinSize(size);
}
}
void
BContainerWindow::RestoreState()
{
UpdateTitle();
WindowStateNodeOpener opener(this, false);
RestoreWindowState(opener.StreamNode());
PoseView()->Init(opener.StreamNode());
RestoreStateCommon();
}
void
BContainerWindow::RestoreState(const BMessage &message)
{
UpdateTitle();
RestoreWindowState(message);
PoseView()->Init(message);
RestoreStateCommon();
}
void
BContainerWindow::RestoreStateCommon()
{
if (fUsesLayout)
InitLayout();
UpdateBackgroundImage();
}
void
BContainerWindow::OpenParent()
{
BEntry entry(TargetModel()->EntryRef());
if (entry.InitCheck() != B_OK)
return;
BEntry parentEntry;
if (FSGetParentVirtualDirectoryAware(entry, parentEntry) != B_OK)
return;
entry_ref setToRef;
parentEntry.GetRef(&setToRef);
const entry_ref* parent = &setToRef;
BMessage message(kSwitchDirectory);
message.AddRef("refs", parent);
MessageReceived(&message);
}
void
BContainerWindow::SwitchDirectory(const entry_ref* ref)
{
BEntry entry;
if (entry.SetTo(ref, true) != B_OK || entry.InitCheck() != B_OK)
return;
if (StateNeedsSaving())
SaveState(false);
bool wasInTrash = TargetModel()->IsTrash() || TargetModel()->InTrash();
bool wasRoot = TargetModel()->IsRoot();
bool wasVolume = TargetModel()->IsVolume();
WindowStateNodeOpener opener(this, false);
opener.SetTo(&entry, false);
UpdateBackgroundImage();
PoseView()->SwitchDir(ref, opener.StreamNode());
if (wasInTrash ^ (TargetModel()->IsTrash() || TargetModel()->InTrash())
|| wasRoot != TargetModel()->IsRoot() || wasVolume != TargetModel()->IsVolume()) {
RepopulateMenus();
}
if (PoseView()->IsFilePanel())
return;
RebuildAddOnMenus(fMenuBar);
TrackerSettings settings;
if (settings.ShowNavigator() || settings.ShowFullPathInTitleBar())
SetPathWatchingEnabled(true);
SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
if (ShouldHaveDraggableFolderIcon()) {
if (fDraggableIcon != NULL) {
IconCache::sIconCache->IconChanged(TargetModel());
if (fDraggableIcon->IsHidden())
fDraggableIcon->Show();
fDraggableIcon->Invalidate();
} else {
_AddFolderIcon();
}
} else if (fMenuBar != NULL && fDraggableIcon != NULL) {
fDraggableIcon->Hide();
}
UpdateTitle();
}
void
BContainerWindow::UpdateTitle()
{
if (TrackerSettings().ShowFullPathInTitleBar()) {
BPath path;
TargetModel()->GetPath(&path);
SetTitle(path.Path());
} else {
SetTitle(TargetModel()->Name());
}
if (Navigator() != NULL)
Navigator()->UpdateLocation(TargetModel(), kActionUpdatePath);
}
void
BContainerWindow::UpdateBackgroundImage()
{
if (BootedInSafeMode() || PoseView()->IsFilePanel())
return;
WindowStateNodeOpener opener(this, false);
bool isDesktop = PoseView()->IsDesktopView();
bool isAllowed = !((TargetModel()->IsDesktop() && !isDesktop) || TargetModel()->IsRoot());
if (isAllowed && opener.Node() != NULL) {
fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, opener.Node(),
isDesktop, PoseView());
}
BNode defaultingNode;
if (fBackgroundImage == NULL && !isDesktop
&& DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) {
fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, &defaultingNode,
isDesktop, PoseView());
}
}
void
BContainerWindow::FrameResized(float, float)
{
if (PoseView() != NULL && !PoseView()->IsDesktopView()) {
BRect extent = PoseView()->Extent();
float offsetX = extent.left - PoseView()->Bounds().left;
float offsetY = extent.top - PoseView()->Bounds().top;
BPoint scroll(B_ORIGIN);
if (offsetX < 0 && PoseView()->Bounds().right > extent.right
&& Bounds().Width() > fPreviousBounds.Width()) {
scroll.x = std::max(fPreviousBounds.Width() - Bounds().Width(),
offsetX);
}
if (offsetY < 0 && PoseView()->Bounds().bottom > extent.bottom
&& Bounds().Height() > fPreviousBounds.Height()) {
scroll.y = std::max(fPreviousBounds.Height() - Bounds().Height(),
offsetY);
}
if (scroll != B_ORIGIN)
PoseView()->ScrollBy(scroll.x, scroll.y);
PoseView()->UpdateScrollRange();
PoseView()->ResetPosePlacementHint();
}
fPreviousBounds = Bounds();
if (IsActive())
fStateNeedsSaving = true;
}
void
BContainerWindow::FrameMoved(BPoint)
{
if (IsActive())
fStateNeedsSaving = true;
}
void
BContainerWindow::WorkspacesChanged(uint32, uint32)
{
if (IsActive())
fStateNeedsSaving = true;
}
void
BContainerWindow::ViewModeChanged(uint32 oldMode, uint32 newMode)
{
if (fBackgroundImage == NULL)
return;
if (newMode == kListMode)
fBackgroundImage->Remove();
else if (oldMode == kListMode)
fBackgroundImage->Show(PoseView(), current_workspace());
}
void
BContainerWindow::CheckScreenIntersect()
{
BScreen screen(this);
BRect screenFrame(screen.Frame());
BRect frame(Frame());
if (sNewWindRect.bottom > screenFrame.bottom)
sNewWindRect.OffsetTo(85, 50);
if (sNewWindRect.right > screenFrame.right)
sNewWindRect.OffsetTo(85, 50);
if (!frame.Intersects(screenFrame))
MoveTo(sNewWindRect.LeftTop());
}
void
BContainerWindow::SaveState(bool hide)
{
if (SaveStateIsEnabled()) {
WindowStateNodeOpener opener(this, true);
if (opener.StreamNode() != NULL)
SaveWindowState(opener.StreamNode());
if (hide)
Hide();
if (opener.StreamNode())
PoseView()->SaveState(opener.StreamNode());
fStateNeedsSaving = false;
}
}
void
BContainerWindow::SaveState(BMessage& message) const
{
if (SaveStateIsEnabled()) {
SaveWindowState(message);
PoseView()->SaveState(message);
}
}
bool
BContainerWindow::StateNeedsSaving() const
{
return PoseView() != NULL && (fStateNeedsSaving || PoseView()->StateNeedsSaving());
}
status_t
BContainerWindow::GetLayoutState(BNode* node, BMessage* message)
{
if (node == NULL || message == NULL)
return B_BAD_VALUE;
status_t result = node->InitCheck();
if (result != B_OK)
return result;
node->RewindAttrs();
char attrName[256];
while (node->GetNextAttrName(attrName) == B_OK) {
attr_info info;
if (node->GetAttrInfo(attrName, &info) != B_OK)
continue;
if (strcmp(attrName, kAttrWindowFrame) != 0
&& strcmp(attrName, kAttrColumns) != 0
&& strcmp(attrName, kAttrViewState) != 0
&& strcmp(attrName, kAttrColumnsForeign) != 0
&& strcmp(attrName, kAttrViewStateForeign) != 0) {
continue;
}
char* buffer = new char[info.size];
if (node->ReadAttr(attrName, info.type, 0, buffer,
(size_t)info.size) == info.size) {
message->AddData(attrName, info.type, buffer, (ssize_t)info.size);
}
delete[] buffer;
}
return B_OK;
}
status_t
BContainerWindow::SetLayoutState(BNode* node, const BMessage* message)
{
status_t result = node->InitCheck();
if (result != B_OK)
return result;
for (int32 globalIndex = 0; ;) {
#if B_BEOS_VERSION_DANO
const char* name;
#else
char* name;
#endif
type_code type;
int32 count;
status_t result = message->GetInfo(B_ANY_TYPE, globalIndex, &name,
&type, &count);
if (result != B_OK)
break;
for (int32 index = 0; index < count; index++) {
const void* buffer;
ssize_t size;
result = message->FindData(name, type, index, &buffer, &size);
if (result != B_OK) {
PRINT(("error reading %s \n", name));
return result;
}
if (node->WriteAttr(name, type, 0, buffer,
(size_t)size) != size) {
PRINT(("error writing %s \n", name));
return result;
}
globalIndex++;
}
}
return B_OK;
}
bool
BContainerWindow::ShouldAddMenus() const
{
return true;
}
bool
BContainerWindow::ShouldAddScrollBars() const
{
return true;
}
Model*
BContainerWindow::TargetModel() const
{
return PoseView()->TargetModel();
}
void
BContainerWindow::SelectionChanged()
{
}
void
BContainerWindow::Zoom(BPoint, float, float)
{
BRect oldZoomRect(fSavedZoomRect);
fSavedZoomRect = Frame();
ResizeToFit();
if (fSavedZoomRect == Frame() && oldZoomRect.IsValid())
ResizeTo(oldZoomRect.Width(), oldZoomRect.Height());
}
void
BContainerWindow::ResizeToFit()
{
BScreen screen(this);
BRect screenFrame(screen.Frame());
screenFrame.InsetBy(5, 5);
BMessage decoratorSettings;
GetDecoratorSettings(&decoratorSettings);
float tabHeight = 15;
BRect tabRect;
if (decoratorSettings.FindRect("tab frame", &tabRect) == B_OK)
tabHeight = tabRect.Height();
screenFrame.top += tabHeight;
BRect frame(Frame());
float widthDiff = frame.Width() - PoseView()->Frame().Width();
float heightDiff = frame.Height() - PoseView()->Frame().Height();
BPoint leftTop(frame.LeftTop());
leftTop.ConstrainTo(screenFrame);
frame.OffsetTo(leftTop);
BRect extent(PoseView()->Extent());
frame.right = frame.left + extent.Width() + widthDiff;
frame.bottom = frame.top + extent.Height() + heightDiff;
frame = frame & screenFrame;
ResizeTo(frame.Width(), frame.Height());
MoveTo(frame.LeftTop());
PoseView()->DisableScrollBars();
PoseView()->ScrollBy(
extent.left - PoseView()->Bounds().left,
extent.top - PoseView()->Bounds().top);
PoseView()->UpdateScrollRange();
PoseView()->EnableScrollBars();
}
void
BContainerWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_CUT:
case B_COPY:
case B_PASTE:
case B_SELECT_ALL:
{
BView* view = CurrentFocus();
if (dynamic_cast<BTextView*>(view) == NULL) {
if (PoseView() != NULL)
PostMessage(message, PoseView());
} else {
PostMessage(message, view);
}
break;
}
case kCutMoreSelectionToClipboard:
case kCopyMoreSelectionToClipboard:
case kPasteLinksFromClipboard:
if (PoseView() != NULL)
PostMessage(message, PoseView());
break;
case B_UNDO: {
BView* view = CurrentFocus();
if (dynamic_cast<BTextView*>(view) == NULL) {
FSUndo();
} else {
view->MessageReceived(message);
}
break;
}
case B_REDO: {
BView* view = CurrentFocus();
if (dynamic_cast<BTextView*>(view) == NULL) {
FSRedo();
} else {
view->MessageReceived(message);
}
break;
}
case kOpenParentDir:
OpenParent();
break;
case kNewFolder:
PostMessage(message, PoseView());
break;
case kNewTemplateSubmenu:
{
entry_ref ref;
if (message->FindRef("refs", &ref) == B_OK)
_NewTemplateSubmenu(ref);
break;
}
case kRestoreState:
if (message->HasMessage("state")) {
BMessage state;
message->FindMessage("state", &state);
Init(&state);
} else
Init();
break;
case kResizeToFit:
ResizeToFit();
break;
case kLoadAddOn:
LoadAddOn(message);
break;
case kRebuildAddOnMenus:
RebuildAddOnMenus(fMenuBar);
break;
case kCopySelectionTo:
{
entry_ref ref;
if (message->FindRef("refs", &ref) != B_OK)
break;
BRoster().AddToRecentFolders(&ref);
Model model(&ref);
if (model.InitCheck() != B_OK)
break;
if (*model.NodeRef() == *TargetModel()->NodeRef())
PoseView()->DuplicateSelection();
else
PoseView()->MoveSelectionInto(&model, this, true);
break;
}
case kMoveSelectionTo:
{
entry_ref ref;
if (message->FindRef("refs", &ref) != B_OK)
break;
BRoster().AddToRecentFolders(&ref);
Model model(&ref);
if (model.InitCheck() != B_OK)
break;
PoseView()->MoveSelectionInto(&model, this, false, true);
break;
}
case kCreateLink:
case kCreateRelativeLink:
{
entry_ref ref;
if (message->FindRef("refs", &ref) == B_OK) {
BRoster().AddToRecentFolders(&ref);
Model model(&ref);
if (model.InitCheck() != B_OK)
break;
PoseView()->MoveSelectionInto(&model, this, false, false,
message->what == kCreateLink,
message->what == kCreateRelativeLink);
} else if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()) {
PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
message->what == kCreateLink,
message->what == kCreateRelativeLink);
}
break;
}
case kShowSelectionWindow:
ShowSelectionWindow();
break;
case kSelectMatchingEntries:
PoseView()->SelectMatchingEntries(message);
break;
case kFindButton:
(new FindWindow())->Show();
break;
case kQuitTracker:
be_app->PostMessage(B_QUIT_REQUESTED);
break;
case kRestoreBackgroundImage:
UpdateBackgroundImage();
break;
case kSwitchDirectory:
{
entry_ref ref;
if (message->FindRef("refs", &ref) != B_OK)
break;
if (!PoseView()->IsFilePanel() && !TrackerSettings().SingleWindowBrowse()) {
message->what = B_REFS_RECEIVED;
const node_ref* nodeRef = TargetModel()->NodeRef();
message->AddData("nodeRefToSelect", B_RAW_TYPE, nodeRef, sizeof(node_ref));
if ((modifiers() & B_OPTION_KEY) != 0) {
message->AddData("nodeRefsToClose", B_RAW_TYPE, nodeRef, sizeof(node_ref));
}
be_app->PostMessage(message);
} else {
SwitchDirectory(&ref);
if (Navigator() != NULL) {
int32 action = message->GetInt32("action", kActionSet);
Navigator()->UpdateLocation(TargetModel(), action);
}
}
break;
}
case B_REFS_RECEIVED:
{
if (PoseView() == NULL || !PoseView()->IsDragging())
break;
entry_ref ref;
if (message->FindRef("refs", &ref) == B_OK) {
PoseView()->SetWaitingForRefs(false);
BEntry entry(&ref, true);
if (entry.InitCheck() == B_OK && entry.IsDirectory() && !FSIsPrintersDir(&entry)) {
Model target(&entry, true, false);
BPoint where;
uint32 buttons;
PoseView()->GetMouse(&where, &buttons, true);
PoseView()->HandleDropCommon(PoseView()->DragMessage(), &target, NULL,
PoseView(), where);
}
}
PoseView()->DragStop();
break;
}
case B_TRACKER_ADDON_MESSAGE:
{
_PassMessageToAddOn(message);
break;
}
case B_OBSERVER_NOTICE_CHANGE:
{
int32 observerWhat;
if (message->FindInt32("be:observe_change_what", &observerWhat)
== B_OK) {
TrackerSettings settings;
switch (observerWhat) {
case kWindowsShowFullPathChanged:
UpdateTitle();
if (!IsPathWatchingEnabled()
&& settings.ShowFullPathInTitleBar()) {
SetPathWatchingEnabled(true);
}
if (IsPathWatchingEnabled()
&& !(settings.ShowNavigator() || settings.ShowFullPathInTitleBar())) {
SetPathWatchingEnabled(false);
}
break;
case kSingleWindowBrowseChanged:
if (PoseView()->IsFilePanel() || PoseView()->IsDesktopView()) {
SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
break;
}
if (settings.SingleWindowBrowse() && Navigator() == NULL
&& TargetModel()->IsDirectory()) {
fNavigator = new BNavigator(TargetModel());
fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
fNavigator->Hide();
SetPathWatchingEnabled(settings.ShowNavigator()
|| settings.ShowFullPathInTitleBar());
}
if (!settings.SingleWindowBrowse() && fWindowList != NULL) {
int32 windowCount = fWindowList->CountItems();
BObjectList<BContainerWindow> containerList(windowCount);
for (int32 index = 0; index < windowCount; index++) {
BContainerWindow* window
= dynamic_cast<BContainerWindow*>(fWindowList->ItemAt(index));
if (window != NULL && window->TargetModel() != NULL
&& window->TargetModel()->NodeRef() != NULL) {
containerList.AddItem(window);
}
}
windowCount = containerList.CountItems();
if (windowCount > 1) {
containerList.SortItems(CompareContainerWindowNodeRef);
for (int32 index = windowCount - 2; index >= 0; --index) {
BContainerWindow* window = containerList.ItemAt(index);
BContainerWindow* second = containerList.ItemAt(index + 1);
if (window == NULL || second == NULL)
continue;
const node_ref* windowRef = window->TargetModel()->NodeRef();
const node_ref* secondRef = second->TargetModel()->NodeRef();
if (*windowRef == *secondRef) {
BMessenger(second).SendMessage(B_QUIT_REQUESTED);
}
}
}
}
if (!settings.SingleWindowBrowse() && TargetModel()->IsDesktop()) {
this->Quit();
}
SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
break;
case kShowNavigatorChanged:
ShowNavigator(settings.ShowNavigator());
if (!IsPathWatchingEnabled() && settings.ShowNavigator())
SetPathWatchingEnabled(true);
if (IsPathWatchingEnabled()
&& !(settings.ShowNavigator() || settings.ShowFullPathInTitleBar())) {
SetPathWatchingEnabled(false);
}
SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse());
break;
default:
_inherited::MessageReceived(message);
break;
}
}
break;
}
case B_NODE_MONITOR:
UpdateTitle();
break;
default:
_inherited::MessageReceived(message);
break;
}
}
bool
BContainerWindow::IsShowing(const node_ref* node) const
{
return PoseView()->Represents(node);
}
bool
BContainerWindow::IsShowing(const entry_ref* entry) const
{
return PoseView()->Represents(entry);
}
void
BContainerWindow::AddMenus()
{
fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this);
AddFileMenu(fFileMenu);
fMenuBar->AddItem(fFileMenu, 0);
fWindowMenu = new TLiveWindowMenu(B_TRANSLATE("Window"), this);
fMenuBar->AddItem(fWindowMenu, 1);
AddWindowMenu(fWindowMenu);
fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
NewAttributesMenu(fAttrMenu);
fArrangeByItem = NewArrangeByMenu();
}
void
BContainerWindow::AddFileMenu(BMenu* menu)
{
if (TargetModel()->IsTrash()) {
menu->AddItem(Shortcuts()->EmptyTrashItem());
menu->AddItem(new BSeparatorItem());
} else if (TargetModel()->IsPrintersDir()) {
menu->AddItem(Shortcuts()->AddPrinterItem());
menu->AddItem(new BSeparatorItem());
}
menu->AddItem(Shortcuts()->FindItem());
if (ShouldHaveNewFolderItem())
menu->AddItem(Shortcuts()->NewFolderItem());
menu->AddSeparatorItem();
menu->AddItem(Shortcuts()->OpenItem());
menu->AddItem(Shortcuts()->GetInfoItem());
menu->AddItem(Shortcuts()->EditNameItem());
if (TargetModel()->IsPrintersDir()) {
menu->AddItem(Shortcuts()->MakeActivePrinterItem());
return;
}
if (!TargetModel()->IsRoot()) {
if (TargetModel()->IsTrash()) {
menu->AddItem(Shortcuts()->DeleteItem());
menu->AddItem(Shortcuts()->RestoreItem());
} else {
menu->AddItem(Shortcuts()->DuplicateItem());
menu->AddItem(Shortcuts()->MoveToTrashItem());
}
}
menu->AddSeparatorItem();
if (!TargetModel()->IsRoot() && !TargetModel()->IsTrash() && !TargetModel()->InTrash()) {
menu->AddItem(Shortcuts()->CutItem());
menu->AddItem(Shortcuts()->CopyItem());
menu->AddItem(Shortcuts()->PasteItem());
menu->AddSeparatorItem();
}
if (!TargetModel()->IsRoot())
menu->AddItem(Shortcuts()->IdentifyItem());
if (ShouldHaveAddOnMenus())
menu->AddItem(new BMenuItem(new BMenu(Shortcuts()->AddOnsLabel())));
}
void
BContainerWindow::AddWindowMenu(BMenu* menu)
{
AddIconSizeMenu(menu);
BMenuItem* item = new BMenuItem(B_TRANSLATE("List view"), new BMessage(kListMode), '3');
item->SetTarget(PoseView());
menu->AddItem(item);
menu->AddSeparatorItem();
menu->AddItem(Shortcuts()->ResizeToFitItem());
menu->AddItem(Shortcuts()->SelectItem());
menu->AddItem(Shortcuts()->SelectAllItem());
menu->AddItem(Shortcuts()->InvertSelectionItem());
menu->AddItem(Shortcuts()->OpenParentItem());
menu->AddItem(Shortcuts()->CloseItem());
menu->AddItem(Shortcuts()->CloseAllInWorkspaceItem());
menu->AddSeparatorItem();
item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS),
new BMessage(kShowSettingsWindow), ',');
item->SetTarget(be_app);
menu->AddItem(item);
}
void
BContainerWindow::AddIconSizeMenu(BMenu* menu)
{
if (menu == NULL)
return;
BMenuItem* item;
BMessage* message;
BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view"));
iconSizeMenu->SetRadioMode(true);
static const uint32 kIconSizes[] = { 32, 40, 48, 64, 96, 128 };
BString label;
const char* format;
const char* comment = "The '×' is the Unicode multiplication sign U+00D7";
uint32 iconSize;
for (uint32 i = 0; i < sizeof(kIconSizes) / sizeof(uint32); ++i) {
iconSize = kIconSizes[i];
message = new BMessage(kIconMode);
message->AddInt32("size", iconSize);
format = B_TRANSLATE_COMMENT("%" B_PRId32 " × %" B_PRId32, comment);
label.SetToFormat(format, iconSize, iconSize);
item = new BMenuItem(label, message);
item->SetTarget(PoseView());
iconSizeMenu->AddItem(item);
}
iconSizeMenu->AddSeparatorItem();
message = new BMessage(kIconMode);
message->AddInt32("scale", 0);
item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-');
item->SetTarget(PoseView());
iconSizeMenu->AddItem(item);
message = new BMessage(kIconMode);
message->AddInt32("scale", 1);
item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+');
item->SetTarget(PoseView());
iconSizeMenu->AddItem(item);
menu->AddItem(iconSizeMenu);
BMenuItem* iconSizeSuperItem = iconSizeMenu->Superitem();
if (iconSizeSuperItem != NULL) {
iconSizeSuperItem->SetShortcut('1', B_COMMAND_KEY);
iconSizeSuperItem->SetMessage(new BMessage(kIconMode));
iconSizeSuperItem->SetTarget(PoseView());
}
item = new BMenuItem(B_TRANSLATE("Mini icon view"), new BMessage(kMiniIconMode), '2');
item->SetTarget(PoseView());
menu->AddItem(item);
}
void
BContainerWindow::AddShortcuts()
{
ASSERT(!TargetModel()->IsTrash());
ASSERT(!PoseView()->IsFilePanel());
ASSERT(!TargetModel()->IsQuery());
ASSERT(!TargetModel()->IsVirtualDirectory());
AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
new BMessage(kCutMoreSelectionToClipboard), this);
AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY,
new BMessage(kCopyMoreSelectionToClipboard), this);
AddShortcut('F', B_COMMAND_KEY, new BMessage(kFindButton), PoseView());
AddShortcut('N', B_COMMAND_KEY, new BMessage(kNewFolder), PoseView());
AddShortcut('O', B_COMMAND_KEY, new BMessage(kOpenSelection), PoseView());
AddShortcut('I', B_COMMAND_KEY, new BMessage(kGetInfo), PoseView());
AddShortcut('E', B_COMMAND_KEY, new BMessage(kEditName), PoseView());
AddShortcut('D', B_COMMAND_KEY, new BMessage(kDuplicateSelection), PoseView());
AddShortcut(B_DELETE, B_NO_COMMAND_KEY, new BMessage(kMoveSelectionToTrash), PoseView());
AddShortcut('K', B_COMMAND_KEY, new BMessage(kCleanup), PoseView());
AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), PoseView());
AddShortcut('S', B_COMMAND_KEY, new BMessage(kInvertSelection), PoseView());
AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kShowSelectionWindow), PoseView());
AddShortcut('G', B_COMMAND_KEY, new BMessage(kEditQuery), PoseView());
AddShortcut('U', B_COMMAND_KEY, new BMessage(kUnmountVolume), PoseView());
AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(kOpenParentDir), PoseView());
AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage(kOpenSelectionWith), PoseView());
BMessage* decreaseSize = new BMessage(kIconMode);
decreaseSize->AddInt32("scale", 0);
AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView());
BMessage* increaseSize = new BMessage(kIconMode);
increaseSize->AddInt32("scale", 1);
AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView());
}
void
BContainerWindow::MenusBeginning()
{
if (fMenuBar == NULL)
return;
if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) {
PoseView()->CommitActivePose();
}
if (fFileMenu != NULL)
UpdateMenu(fFileMenu, kFileMenuContext);
if (fWindowMenu != NULL)
UpdateMenu(fWindowMenu, kWindowMenuContext);
}
void
BContainerWindow::MenusEnded()
{
DeleteSubmenu(fCreateLinkItem);
DeleteSubmenu(fCopyToItem);
DeleteSubmenu(fMoveToItem);
DeleteSubmenu(fOpenWithItem);
DeleteSubmenu(fNavigationItem);
DeleteSubmenu(fMountItem);
}
void
BContainerWindow::SetupNavigationMenu(BMenu* parent, const entry_ref* ref)
{
ASSERT(parent != NULL);
if (fNavigationItem != NULL && fNavigationItem->Menu() != NULL) {
delete fNavigationItem->Menu()->RemoveItem(
fNavigationItem->Menu()->IndexOf(fNavigationItem) + 1);
fNavigationItem->Menu()->RemoveItem(fNavigationItem);
}
if (ref == NULL)
ref = TargetModel()->EntryRef();
if (!ShouldHaveNavigationMenu(ref))
return;
BEntry entry;
if (entry.SetTo(ref) != B_OK)
return;
Model model(&entry);
entry_ref resolvedRef;
if (model.InitCheck() != B_OK)
return;
else if (!model.IsContainer() && !model.IsSymLink())
return;
if (model.IsSymLink()) {
if (entry.SetTo(model.EntryRef(), true) != B_OK)
return;
Model resolvedModel(&entry);
if (resolvedModel.InitCheck() != B_OK || !resolvedModel.IsContainer())
return;
entry.GetRef(&resolvedRef);
ref = &resolvedRef;
}
delete fNavigationItem;
fNavigationItem
= new ModelMenuItem(&model, new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, this));
BNavMenu* navMenu = dynamic_cast<BNavMenu*>(fNavigationItem->Submenu());
navMenu->SetNavDir(ref);
fNavigationItem->SetLabel(model.Name());
fNavigationItem->SetEntry(&entry);
parent->AddItem(fNavigationItem, 0);
parent->AddItem(new BSeparatorItem(), 1);
BMessage* message = new BMessage(B_REFS_RECEIVED);
message->AddRef("refs", ref);
fNavigationItem->SetMessage(message);
fNavigationItem->SetTarget(be_app);
if (!PoseView()->IsDragging())
parent->SetTrackingHook(NULL, NULL);
}
void
BContainerWindow::SetupEditQueryItem(BMenu* parent, const entry_ref* ref)
{
ASSERT(parent != NULL);
if (fEditQueryItem != NULL && fEditQueryItem->Menu() != NULL)
fEditQueryItem->Menu()->RemoveItem(fEditQueryItem);
if (ref == NULL)
ref = TargetModel()->EntryRef();
ASSERT(ref);
BMenuItem* openItem = parent->FindItem(kOpenSelection);
int32 openIndex = parent->IndexOf(openItem);
if (openItem == NULL || openIndex == B_ERROR)
return;
parent->AddItem(fEditQueryItem, openIndex + 1);
Shortcuts()->UpdateEditQueryItem(fEditQueryItem);
}
void
BContainerWindow::SetupOpenWithMenu(BMenu* parent, const entry_ref* ref)
{
ASSERT(parent != NULL);
if (fOpenWithItem != NULL && fOpenWithItem->Menu() != NULL)
fOpenWithItem->Menu()->RemoveItem(fOpenWithItem);
if (ref == NULL)
ref = TargetModel()->EntryRef();
ASSERT(ref != NULL);
if (!ShouldHaveOpenWithMenu(ref))
return;
BMenuItem* openItem = parent->FindItem(kOpenSelection);
if (openItem == NULL)
return;
int32 openIndex = parent->IndexOf(openItem);
if (openIndex == B_ERROR)
return;
BMessage message(B_REFS_RECEIVED);
BPose* pose;
int32 selectCount = PoseView()->CountSelected();
for (int32 index = 0; index < selectCount; index++) {
pose = PoseView()->SelectionList()->ItemAt(index);
message.AddRef("refs", pose->TargetModel()->EntryRef());
}
message.AddMessenger("TrackerViewToken", BMessenger(PoseView()));
delete fOpenWithItem;
fOpenWithItem = Shortcuts()->OpenWithItem(
new OpenWithMenu(Shortcuts()->OpenWithLabel(), &message, this, be_app));
parent->AddItem(fOpenWithItem, openIndex + 1);
Shortcuts()->UpdateOpenWithItem(fOpenWithItem);
}
void
BContainerWindow::SetupNewTemplatesMenu(BMenu* parent, MenuContext context)
{
ASSERT(parent != NULL);
if (fNewTemplatesItem != NULL && fNewTemplatesItem->Menu() != NULL)
fNewTemplatesItem->Menu()->RemoveItem(fNewTemplatesItem);
if (!ShouldHaveNewFolderItem())
return;
BMenuItem* newFolderItem = parent->FindItem(kNewFolder);
if (PoseView()->IsFilePanel())
return Shortcuts()->UpdateNewFolderItem(newFolderItem);
TemplatesMenu* newTemplatesMenu = (TemplatesMenu*)fNewTemplatesItem->Submenu();
ASSERT(newTemplatesMenu != NULL);
if (newTemplatesMenu->CountTemplates() == 0)
newTemplatesMenu->UpdateMenuState();
if (newTemplatesMenu->CountTemplates() == 0)
return Shortcuts()->UpdateNewFolderItem(newFolderItem);
int32 newFolderIndex = parent->IndexOf(newFolderItem);
if (newFolderItem != NULL && newFolderIndex != B_ERROR) {
parent->RemoveItem(newFolderItem);
parent->AddItem(fNewTemplatesItem, newFolderIndex);
delete newFolderItem;
} else {
if (context == kWindowPopUpContext)
parent->AddItem(fNewTemplatesItem, 2);
else if (context == kFileMenuContext)
parent->AddItem(fNewTemplatesItem, 1);
}
Shortcuts()->UpdateNewTemplatesItem(fNewTemplatesItem);
}
void
BContainerWindow::SetupMountMenu(BMenu* parent, MenuContext context, const entry_ref* ref)
{
ASSERT(parent != NULL);
if (fMountItem != NULL && fMountItem->Menu() != NULL)
fMountItem = DetachMountMenu();
if (ref == NULL)
ref = TargetModel()->EntryRef();
ASSERT(ref != NULL);
Model model(ref);
if (!(model.IsDesktop() || model.IsRoot() || model.IsVolume()))
return;
int32 mountIndex = parent->CountItems() - 1;
if (model.IsDesktop()) {
BMenuItem* selectAll = parent->FindItem(B_SELECT_ALL);
if (selectAll != NULL)
mountIndex = parent->IndexOf(selectAll) + 2;
} else if (model.IsRoot() || model.IsVolume()) {
BMenuItem* editName = parent->FindItem(kEditName);
if (editName != NULL)
mountIndex = parent->IndexOf(editName) + 2;
}
delete fMountItem;
fMountItem = Shortcuts()->MountItem(new MountMenu(Shortcuts()->MountLabel()));
parent->AddItem(fMountItem, mountIndex);
if (model.IsDesktop()
|| (model.IsRoot() && (context == kWindowPopUpContext || context == kPosePopUpContext))) {
parent->AddItem(new BSeparatorItem(), mountIndex + 1);
} else {
BMenuItem* unmountItem = Shortcuts()->UnmountItem();
parent->AddItem(unmountItem, mountIndex + 1);
Shortcuts()->UpdateUnmountItem(unmountItem);
parent->AddItem(new BSeparatorItem(), mountIndex + 2);
}
}
BMenuItem*
BContainerWindow::DetachMountMenu()
{
BMenu* menu = fMountItem->Menu();
int32 mountIndex = menu->IndexOf(fMountItem);
if (mountIndex == B_ERROR)
return NULL;
if (menu->ItemAt(mountIndex + 1) != NULL && menu->ItemAt(mountIndex + 1)->Shortcut() == 'U') {
BMenuItem* separator = menu->RemoveItem(mountIndex + 2);
if (dynamic_cast<BSeparatorItem*>(separator) != NULL)
delete separator;
delete menu->RemoveItem(mountIndex + 1);
} else {
BMenuItem* separator = menu->RemoveItem(mountIndex + 1);
if (dynamic_cast<BSeparatorItem*>(separator) != NULL)
delete separator;
}
return menu->RemoveItem(mountIndex);
}
void
BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what, const entry_ref* ref,
bool addLocalOnly)
{
BVolume volume;
BVolumeRoster volumeRoster;
BDirectory directory;
BEntry entry;
BPath path;
Model model;
dev_t device = ref->device;
int32 volumeCount = 0;
navMenu->RemoveItems(0, navMenu->CountItems(), true);
volumeRoster.Rewind();
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
if (!volume.IsReadOnly() && volume.IsPersistent())
volumeCount++;
}
if (entry.SetTo(ref) == B_OK
&& entry.GetParent(&entry) == B_OK
&& model.SetTo(&entry) == B_OK) {
BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what,
this);
menu->SetNavDir(model.EntryRef());
menu->SetShowParent(true);
BMenuItem* item = new SpecialModelMenuItem(&model, menu);
item->SetMessage(new BMessage((uint32)what));
navMenu->AddItem(item);
}
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
path.Append("Tracker");
if (entry.SetTo(path.Path()) == B_OK
&& model.SetTo(&entry) == B_OK) {
BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"),
kRecentFolders, what, this);
BMenuItem* item = new SpecialModelMenuItem(&model, menu);
item->SetMessage(new BMessage((uint32)what));
navMenu->AddItem(item);
}
}
FSGetBootDeskDir(&directory);
if (directory.InitCheck() == B_OK && directory.GetEntry(&entry) == B_OK
&& model.SetTo(&entry) == B_OK) {
navMenu->AddNavDir(&model, what, this, true);
}
if (find_directory(B_USER_DIRECTORY, &path) == B_OK
&& entry.SetTo(path.Path()) == B_OK && model.SetTo(&entry) == B_OK) {
navMenu->AddNavDir(&model, what, this, true);
}
navMenu->AddSeparatorItem();
if (addLocalOnly || volumeCount < 2) {
if (volume.SetTo(device) == B_OK
&& volume.GetRootDirectory(&directory) == B_OK
&& directory.GetEntry(&entry) == B_OK
&& model.SetTo(&entry) == B_OK) {
navMenu->AddNavDir(&model, what, this, false);
navMenu->SetNavDir(model.EntryRef());
}
} else {
volumeRoster.Rewind();
while (volumeRoster.GetNextVolume(&volume) == B_OK) {
if (volume.IsReadOnly() || !volume.IsPersistent())
continue;
if (volume.GetRootDirectory(&directory) == B_OK
&& directory.GetEntry(&entry) == B_OK
&& model.SetTo(&entry) == B_OK) {
navMenu->AddNavDir(&model, what, this, true);
}
}
}
}
void
BContainerWindow::SetupMoveCopyMenus(BMenu* parent, const entry_ref* ref)
{
if (fMoveToItem == NULL || fCopyToItem == NULL || fCreateLinkItem == NULL)
return;
if (fMoveToItem->Menu() != NULL)
fMoveToItem->Menu()->RemoveItem(fMoveToItem);
if (fCopyToItem->Menu() != NULL)
fCopyToItem->Menu()->RemoveItem(fCopyToItem);
if (fCreateLinkItem->Menu() != NULL) {
delete fCreateLinkItem->Menu()->RemoveItem(
fCreateLinkItem->Menu()->IndexOf(fCreateLinkItem) + 1);
fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem);
}
if (ref == NULL)
ref = TargetModel()->EntryRef();
if (!ShouldHaveMoveCopyMenus(ref))
return;
BMenuItem* moveToTrashItem
= Shortcuts()->FindItem(parent, kMoveSelectionToTrash, kDeleteSelection);
int32 index = parent->IndexOf(moveToTrashItem);
if (moveToTrashItem == NULL || index == B_ERROR)
return;
index += 2;
parent->AddItem(fMoveToItem, index++);
parent->AddItem(fCopyToItem, index++);
parent->AddItem(fCreateLinkItem, index++);
parent->AddItem(new BSeparatorItem(), index);
Shortcuts()->UpdateCreateLinkItem(fCreateLinkItem);
fMoveToItem->SetEnabled(false);
fCopyToItem->SetEnabled(false);
fCreateLinkItem->SetEnabled(false);
Model model(ref);
if (model.IsRoot() || model.IsTrash() || model.InTrash())
return;
PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()), kMoveSelectionTo, ref,
true);
PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()), kCopySelectionTo, ref,
false);
PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCreateLinkItem->Submenu()),
(modifiers() & B_SHIFT_KEY) != 0 ? kCreateRelativeLink : kCreateLink, ref, false);
Shortcuts()->UpdateMoveToItem(parent->FindItem(kMoveSelectionTo));
Shortcuts()->UpdateCopyToItem(parent->FindItem(kCopySelectionTo));
Shortcuts()->UpdateCreateLinkItem(parent->FindItem(Shortcuts()->CreateLinkCommand()));
}
uint32
BContainerWindow::ShowDropContextMenu(BPoint where, BPoseView* source)
{
BPoint global(where);
PoseView()->ConvertToScreen(&global);
PoseView()->CommitActivePose();
Shortcuts()->UpdateCreateLinkHereItem(
fDropContextMenu->FindItem(Shortcuts()->CreateLinkHereCommand()));
BMenuItem* item;
int32 itemCount = fDropContextMenu->CountItems();
for (int32 index = 0; index < itemCount - 2; index++) {
item = fDropContextMenu->ItemAt(index);
if (item == NULL)
break;
if (item->Command() == kMoveSelectionTo && source != NULL) {
item->SetEnabled(PoseView()->TargetVolumeIsReadOnly() == false
&& source->SelectedVolumeIsReadOnly() == false);
} else {
item->SetEnabled(PoseView()->TargetVolumeIsReadOnly() == false);
}
}
item = fDropContextMenu->Go(global, true, true);
if (item != NULL)
return item->Command();
return 0;
}
void
BContainerWindow::ShowContextMenu(BPoint where, const entry_ref* ref)
{
ASSERT(IsLocked());
BPoint global(where);
PoseView()->ConvertToScreen(&global);
PoseView()->CommitActivePose();
if (ref != NULL) {
Model model(ref);
if (model.InitCheck() != B_OK)
return;
if (PoseView()->IsDragging()) {
fContextMenu = NULL;
BEntry entry;
model.GetEntry(&entry);
if (!FSIsPrintersDir(&entry) && !fDragContextMenu->IsShowing()) {
fDragContextMenu->ClearMenu();
BEntry resolvedEntry(ref, true);
if (!resolvedEntry.IsDirectory())
return;
entry_ref resolvedRef;
resolvedEntry.GetRef(&resolvedRef);
fDragContextMenu->SetNavDir(&resolvedRef);
fDragContextMenu->SetTypesList(PoseView()->CachedTypesList());
fDragContextMenu->SetTarget(BMessenger(this));
BPoseView* poseView = PoseView();
if (poseView != NULL) {
BMessenger target(poseView);
fDragContextMenu->InitTrackingHook(&BPoseView::MenuTrackingHook, &target,
poseView->DragMessage());
}
fDragContextMenu->Go(global);
}
return;
}
if (model.IsTrash())
fContextMenu = fTrashContextMenu;
else if (model.IsVolume() || model.IsRoot())
fContextMenu = fVolumeContextMenu;
else
fContextMenu = fPoseContextMenu;
if (fContextMenu->Window() != NULL)
return;
MenusEnded();
SetupNavigationMenu(fContextMenu, ref);
UpdateMenu(fContextMenu, kPosePopUpContext, ref);
} else if (fWindowContextMenu != NULL) {
fContextMenu = fWindowContextMenu;
if (fContextMenu->Window() != NULL)
return;
MenusEnded();
SetupNavigationMenu(fContextMenu, TargetModel()->EntryRef());
UpdateMenu(fContextMenu, kWindowPopUpContext);
}
fContextMenu->Go(global, true, true, true);
fContextMenu = NULL;
}
void
BContainerWindow::AddPoseContextMenu(BMenu* menu)
{
menu->AddItem(Shortcuts()->OpenItem());
menu->AddItem(Shortcuts()->GetInfoItem());
menu->AddItem(Shortcuts()->EditNameItem());
if (TargetModel()->InTrash()) {
menu->AddItem(Shortcuts()->DeleteItem());
menu->AddItem(Shortcuts()->RestoreItem());
} else if (!TargetModel()->InTrash()) {
menu->AddItem(Shortcuts()->DuplicateItem());
menu->AddItem(Shortcuts()->MoveToTrashItem());
}
menu->AddSeparatorItem();
if (!TargetModel()->IsPrintersDir() && !TargetModel()->IsRoot() && !TargetModel()->IsTrash()
&& !TargetModel()->InTrash()) {
menu->AddItem(Shortcuts()->CutItem());
menu->AddItem(Shortcuts()->CopyItem());
menu->AddItem(Shortcuts()->PasteItem());
menu->AddSeparatorItem();
}
menu->AddItem(Shortcuts()->IdentifyItem());
if (ShouldHaveAddOnMenus())
menu->AddItem(new BMenuItem(new BMenu(Shortcuts()->AddOnsLabel())));
}
void
BContainerWindow::AddVolumeContextMenu(BMenu* menu)
{
menu->AddItem(Shortcuts()->OpenItem());
menu->AddItem(Shortcuts()->GetInfoItem());
menu->AddItem(Shortcuts()->EditNameItem());
menu->AddSeparatorItem();
if (ShouldHaveAddOnMenus())
menu->AddItem(new BMenuItem(new BMenu(Shortcuts()->AddOnsLabel())));
}
void
BContainerWindow::AddWindowContextMenu(BMenu* menu)
{
if (TargetModel()->IsTrash()) {
menu->AddItem(Shortcuts()->EmptyTrashItem());
menu->AddSeparatorItem();
}
if (ShouldHaveNewFolderItem()) {
menu->AddItem(Shortcuts()->NewFolderItem());
menu->AddSeparatorItem();
}
if (PoseView()->IsDesktopView()) {
AddIconSizeMenu(menu);
menu->AddSeparatorItem();
}
if (!(TargetModel()->IsPrintersDir() || TargetModel()->IsRoot()
|| TargetModel()->IsTrash() || TargetModel()->InTrash())) {
menu->AddItem(Shortcuts()->PasteItem());
menu->AddSeparatorItem();
}
if (PoseView()->IsDesktopView())
menu->AddItem(Shortcuts()->CleanupItem());
menu->AddItem(Shortcuts()->SelectItem());
menu->AddItem(Shortcuts()->SelectAllItem());
if (!PoseView()->IsDesktopView())
menu->AddItem(Shortcuts()->OpenParentItem());
menu->AddSeparatorItem();
if (ShouldHaveAddOnMenus())
menu->AddItem(new BMenuItem(new BMenu(Shortcuts()->AddOnsLabel())));
#if DEBUG
menu->AddSeparatorItem();
BMenuItem* testing = new BMenuItem("Test icon cache",
new BMessage(kTestIconCache));
menu->AddItem(testing);
testing->SetTarget(PoseView());
#endif
}
void
BContainerWindow::AddDropContextMenu(BMenu* menu)
{
menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"), new BMessage(kMoveSelectionTo)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"), new BMessage(kCopySelectionTo)));
menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"), new BMessage(kCreateLink)));
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"), new BMessage(kCancelButton)));
}
void
BContainerWindow::AddTrashContextMenu(BMenu* menu)
{
menu->AddItem(Shortcuts()->EmptyTrashItem());
menu->AddItem(Shortcuts()->OpenItem());
menu->AddItem(Shortcuts()->GetInfoItem());
}
void
BContainerWindow::EachAddOn(void (*eachAddOn)(void* context,
const struct AddOnInfo*, bool primary, BContainerWindow* window, BMenu* menu),
void* passThru, BStringList& mimeTypes, BMenu* parent)
{
AutoLock<LockingList<AddOnInfo, true> > lock(fAddOnsList);
if (!lock.IsLocked())
return;
for (int i = fAddOnsList->CountItems() - 1; i >= 0; i--) {
struct AddOnInfo* item = fAddOnsList->ItemAt(i);
bool primary = false;
if (mimeTypes.CountStrings() > 0 && !item->supportedTypes.IsEmpty()) {
bool secondary = false;
for (int32 i = mimeTypes.CountStrings(); !primary && i-- > 0;) {
BMimeType mimeType(mimeTypes.StringAt(i));
for (int32 j = 0; j < item->supportedTypes.CountStrings(); j++) {
BString supportedType = item->supportedTypes.StringAt(j);
if (BMimeType(supportedType).Contains(&mimeType))
primary = true;
else if (supportedType == B_FILE_MIME_TYPE)
secondary = true;
}
}
if (!secondary && !primary)
continue;
}
((eachAddOn)(passThru, item, primary, this, parent));
}
}
void
BContainerWindow::BuildMimeTypeList(BStringList& mimeTypes)
{
int32 selectCount = PoseView()->CountSelected();
if (selectCount <= 0) {
AddMimeTypeString(mimeTypes, TargetModel());
} else {
_UpdateSelectionMIMEInfo();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = PoseView()->SelectionList()->ItemAt(index);
AddMimeTypeString(mimeTypes, pose->TargetModel());
if (pose->TargetModel()->IsSymLink()) {
Model* resolved = new Model(
pose->TargetModel()->EntryRef(), true, true);
if (resolved->InitCheck() == B_OK)
AddMimeTypeString(mimeTypes, resolved);
delete resolved;
}
}
}
}
void
BContainerWindow::BuildAddOnMenus(BMenuBar* parent)
{
AddOneAddOnParams params;
params.primaryList = NULL;
params.secondaryList = NULL;
BStringList mimeTypes;
EachAddOn(AddOneAddOn, ¶ms, mimeTypes, parent);
}
void
BContainerWindow::RebuildAddOnMenus(BMenuBar* parent)
{
if (parent == NULL || !ShouldHaveAddOnMenus())
return;
int32 addOnsIndex = (PoseView()->ViewMode() == kListMode) ? 3 : 2;
if (parent->CountItems() >= addOnsIndex) {
BMenuItem* addOnsItem;
while ((addOnsItem = parent->ItemAt(addOnsIndex)) != NULL) {
parent->RemoveItem(addOnsItem);
delete addOnsItem;
}
}
if (ShouldAddMenus())
BuildAddOnMenus(parent);
}
void
BContainerWindow::BuildAddOnsMenu(BMenu* parent)
{
BMenuItem* addOnsItem = parent->FindItem(Shortcuts()->AddOnsLabel());
if (parent->IndexOf(addOnsItem) == 0) {
#if DEBUG
addOnsItem = parent->ItemAt(parent->CountItems() - 3);
#else
addOnsItem = parent->ItemAt(parent->CountItems() - 1);
#endif
}
if (addOnsItem == NULL)
return;
BMenu* addOnsMenu = addOnsItem->Submenu();
if (addOnsMenu == NULL)
return;
{
BFont font;
AutoLock<BLooper> _(parent->Looper());
parent->GetFont(&font);
addOnsMenu->SetFont(&font);
}
BMenuItem* item;
for (;;) {
item = addOnsMenu->RemoveItem((int32)0);
if (item == NULL)
break;
delete item;
}
BObjectList<BMenuItem> primaryList;
BObjectList<BMenuItem> secondaryList;
BStringList mimeTypes(10);
BuildMimeTypeList(mimeTypes);
AddOneAddOnParams params;
params.primaryList = &primaryList;
params.secondaryList = &secondaryList;
EachAddOn(AddOneAddOn, ¶ms, mimeTypes, parent);
primaryList.SortItems(CompareLabels);
secondaryList.SortItems(CompareLabels);
int32 count = primaryList.CountItems();
for (int32 index = 0; index < count; index++)
addOnsMenu->AddItem(primaryList.ItemAt(index));
if (count > 0)
addOnsMenu->AddSeparatorItem();
count = secondaryList.CountItems();
for (int32 index = 0; index < count; index++)
addOnsMenu->AddItem(secondaryList.ItemAt(index));
Shortcuts()->UpdateAddOnsItem(addOnsItem);
}
void
BContainerWindow::UpdateMenu(BMenu* menu, MenuContext context, const entry_ref* ref)
{
Shortcuts()->Update(menu);
if (context == kFileMenuContext)
UpdateFileMenu(menu);
else if (context == kWindowMenuContext)
UpdateWindowMenu(menu);
else if (context == kPosePopUpContext)
UpdatePoseContextMenu(menu, ref);
else if (context == kWindowPopUpContext)
UpdateWindowContextMenu(menu);
}
void
BContainerWindow::UpdateFileMenu(BMenu* menu)
{
SetupNewTemplatesMenu(menu, kFileMenuContext);
UpdateFileMenuOrPoseContextMenu(menu, kFileMenuContext);
}
void
BContainerWindow::UpdatePoseContextMenu(BMenu* menu, const entry_ref* ref)
{
ASSERT(ref != NULL);
UpdateFileMenuOrPoseContextMenu(menu, kPosePopUpContext, ref);
}
void
BContainerWindow::UpdateFileMenuOrPoseContextMenu(BMenu* menu, MenuContext context,
const entry_ref* ref)
{
if (context == kPosePopUpContext)
ASSERT(ref != NULL);
if (ref == NULL)
ref = TargetModel()->EntryRef();
if (ShouldHaveOpenWithMenu(ref)
&& (context == kPosePopUpContext || context == kFileMenuContext)) {
SetupOpenWithMenu(menu, ref);
}
if (context == kPosePopUpContext) {
Model model(ref);
if (model.IsRoot() || model.IsVolume())
SetupMountMenu(menu, kPosePopUpContext, ref);
} else if (context == kFileMenuContext && TargetModel()->IsRoot()) {
SetupMountMenu(menu, kFileMenuContext, ref);
}
if (ShouldHaveEditQueryItem(ref))
SetupEditQueryItem(menu, ref);
if (ShouldHaveMoveCopyMenus(ref))
SetupMoveCopyMenus(menu, ref);
if (ShouldHaveAddOnMenus())
BuildAddOnsMenu(menu);
}
void
BContainerWindow::UpdateWindowMenu(BMenu* menu)
{
UpdateWindowMenuOrWindowContextMenu(menu, kWindowMenuContext);
}
void
BContainerWindow::UpdateWindowContextMenu(BMenu* menu)
{
SetupNewTemplatesMenu(menu, kWindowPopUpContext);
UpdateWindowMenuOrWindowContextMenu(menu, kWindowPopUpContext);
if (PoseView()->IsDesktopView() || TargetModel()->IsRoot())
SetupMountMenu(menu, kWindowPopUpContext, TargetModel()->EntryRef());
if (ShouldHaveAddOnMenus())
BuildAddOnsMenu(menu);
}
void
BContainerWindow::UpdateWindowMenuOrWindowContextMenu(BMenu* menu, MenuContext context)
{
SetupArrangeByMenu(menu);
uint32 viewMode = PoseView()->ViewMode();
BMenuItem* iconModeItem = menu->FindItem(kIconMode);
if (iconModeItem != NULL) {
BMenu* iconSizeMenu = iconModeItem->Submenu();
if (iconSizeMenu != NULL) {
if (viewMode == kIconMode) {
BMenuItem* item;
BMessage* message;
int32 iconSize = PoseView()->UnscaledIconSizeInt();
int32 itemCount = iconSizeMenu->CountItems();
for (int32 index = 0; index < itemCount; index++) {
item = iconSizeMenu->ItemAt(index);
if (item == NULL)
continue;
message = item->Message();
if (message == NULL) {
item->SetMarked(false);
continue;
}
bool sizeMatches = (iconSize == message->GetInt32("size", -1));
item->SetMarked(sizeMatches);
if (sizeMatches)
break;
}
} else {
BMenuItem* marked = iconSizeMenu->FindMarked();
if (marked != NULL)
marked->SetMarked(false);
}
}
}
MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode);
MarkNamedMenuItem(menu, kListMode, viewMode == kListMode);
MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode);
}
BMessage*
BContainerWindow::AddOnMessage(int32 what)
{
BMessage* message = new BMessage(what);
PoseList* selectionList = PoseView()->SelectionList();
int32 index = 0;
BPose* pose;
while ((pose = selectionList->ItemAt(index++)) != NULL)
message->AddRef("refs", pose->TargetModel()->EntryRef());
message->AddRef("dir_ref", TargetModel()->EntryRef());
message->AddMessenger("TrackerViewToken", BMessenger(PoseView()));
return message;
}
void
BContainerWindow::LoadAddOn(BMessage* message)
{
UpdateIfNeeded();
entry_ref addOnRef;
status_t result = message->FindRef("refs", &addOnRef);
if (result != B_OK) {
BString buffer(B_TRANSLATE("Error %error loading add-On %name."));
buffer.ReplaceFirst("%error", strerror(result));
buffer.ReplaceFirst("%name", addOnRef.name);
BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return;
}
BMessage* refs = AddOnMessage(B_REFS_RECEIVED);
LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs,
addOnRef, *TargetModel()->EntryRef());
}
bool
BContainerWindow::ShouldHaveNavigationMenu(const entry_ref* ref)
{
if (ref == NULL) {
if (PoseView()->CountSelected() > 0)
ref = PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef();
else
ref = TargetModel()->EntryRef();
}
return !PoseView()->IsFilePanel() && !Model(ref).IsQuery();
}
bool
BContainerWindow::ShouldHaveOpenWithMenu(const entry_ref* ref)
{
if (PoseView()->IsFilePanel())
return false;
if (ref == NULL)
ref = TargetModel()->EntryRef();
Model model(ref);
if (model.IsPrintersDir())
return false;
return !(model.InTrash() || model.IsTrash() || model.IsRoot() || model.IsVolume());
}
bool
BContainerWindow::ShouldHaveEditQueryItem(const entry_ref* ref)
{
if (ref == NULL)
return FSIsQueriesDir(TargetModel()->EntryRef());
Model model(ref);
return model.IsQuery() || model.IsQueryTemplate();
}
bool
BContainerWindow::ShouldHaveMoveCopyMenus(const entry_ref* ref)
{
if (PoseView()->IsFilePanel())
return false;
if (ref == NULL)
ref = TargetModel()->EntryRef();
Model model(ref);
return !(model.IsPrintersDir() || model.IsRoot() || model.IsTrash() || model.InTrash());
}
bool
BContainerWindow::ShouldHaveNewFolderItem()
{
return !(TargetModel()->IsQuery() || TargetModel()->IsPrintersDir()
|| TargetModel()->IsRoot() || TargetModel()->IsTrash() || TargetModel()->InTrash()
|| TargetModel()->IsVirtualDirectory());
}
bool
BContainerWindow::ShouldHaveAddOnMenus()
{
return !PoseView()->IsFilePanel();
}
bool
BContainerWindow::ShouldHaveDraggableFolderIcon()
{
return fMenuBar != NULL;
}
void
BContainerWindow::_UpdateSelectionMIMEInfo()
{
BPose* pose;
int32 index = 0;
while ((pose = PoseView()->SelectionList()->ItemAt(index++)) != NULL) {
BString mimeType(pose->TargetModel()->MimeType());
if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
pose->TargetModel()->Mimeset(true);
if (pose->TargetModel()->IsSymLink()) {
Model* resolved = new Model(pose->TargetModel()->EntryRef(),
true, true);
if (resolved->InitCheck() == B_OK) {
mimeType.SetTo(resolved->MimeType());
if (!mimeType.Length()
|| mimeType.ICompare(B_FILE_MIMETYPE) == 0) {
resolved->Mimeset(true);
}
}
delete resolved;
}
}
}
}
void
BContainerWindow::_AddFolderIcon()
{
if (fMenuBar == NULL) {
return;
}
float baseIconSize = be_control_look->ComposeIconSize(16).Height() + 1,
iconSize = fMenuBar->Bounds().Height() - 2;
if (iconSize < baseIconSize)
iconSize = baseIconSize;
fDraggableIcon = new(std::nothrow) DraggableContainerIcon(BSize(iconSize - 1, iconSize - 1));
if (fDraggableIcon != NULL) {
fMenuContainer->GroupLayout()->AddView(fDraggableIcon);
fMenuBar->SetBorders(
BControlLook::B_ALL_BORDERS & ~BControlLook::B_RIGHT_BORDER);
}
}
void
BContainerWindow::_PassMessageToAddOn(BMessage* message)
{
LaunchInNewThread("Add-on-Pass-Message", B_NORMAL_PRIORITY,
&RunAddOnMessageThread, new BMessage(*message), (void*)NULL);
}
void
BContainerWindow::_NewTemplateSubmenu(entry_ref dirRef)
{
entry_ref submenuRef;
BPath path(&dirRef);
path.Append(B_TRANSLATE_COMMENT("New submenu", "Folder name of New-template submenu"));
get_ref_for_path(path.Path(), &submenuRef);
if (FSCreateNewFolder(&submenuRef) != B_OK)
return;
BNode node(&submenuRef);
if (node.InitCheck() != B_OK)
return;
bool flag = true;
node.WriteAttr(kAttrTemplateSubMenu, B_BOOL_TYPE, 0, &flag, sizeof(bool));
BEntry entry(&submenuRef);
node_ref nref;
if (entry.GetNodeRef(&nref) != B_OK)
return;
BMessage message(B_REFS_RECEIVED);
message.AddRef("refs", &dirRef);
message.AddData("nodeRefToSelect", B_RAW_TYPE, (void*)&nref, sizeof(node_ref));
be_app->PostMessage(&message);
}
BMenuItem*
BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
int32 type, float width, int32 align, bool editable, bool statField)
{
return NewAttributeMenuItem(label, name, type, NULL, width, align,
editable, statField);
}
BMenuItem*
BContainerWindow::NewAttributeMenuItem(const char* label, const char* name,
int32 type, const char* displayAs, float width, int32 align,
bool editable, bool statField)
{
BMessage* message = new BMessage(kAttributeItem);
message->AddString("attr_name", name);
message->AddInt32("attr_type", type);
message->AddInt32("attr_hash", (int32)AttrHashString(name, (uint32)type));
message->AddFloat("attr_width", width);
message->AddInt32("attr_align", align);
if (displayAs != NULL)
message->AddString("attr_display_as", displayAs);
message->AddBool("attr_editable", editable);
message->AddBool("attr_statfield", statField);
BMenuItem* menuItem = new BMenuItem(label, message);
menuItem->SetTarget(PoseView());
return menuItem;
}
void
BContainerWindow::NewAttributesMenu()
{
if (fAttrMenu != NULL)
delete fAttrMenu;
fAttrMenu = new BMenu(B_TRANSLATE("Attributes"));
NewAttributesMenu(fAttrMenu);
}
void
BContainerWindow::NewAttributesMenu(BMenu* menu)
{
ASSERT(PoseView() != NULL);
BMenuItem* item;
while ((item = menu->RemoveItem((int32)0)) != NULL)
delete item;
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy layout"),
new BMessage(kCopyAttributes)));
item->SetTarget(PoseView());
menu->AddItem(item = new BMenuItem(B_TRANSLATE("Paste layout"),
new BMessage(kPasteAttributes)));
item->SetTarget(PoseView());
menu->AddSeparatorItem();
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Name"),
kAttrStatName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
if (gLocalizedNamePreferred) {
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Real name"),
kAttrRealName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true));
}
menu->AddItem(NewAttributeMenuItem (B_TRANSLATE("Size"), kAttrStatSize,
B_OFF_T_TYPE, 80, B_ALIGN_RIGHT, false, true));
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Modified"),
kAttrStatModified, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Created"),
kAttrStatCreated, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true));
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Kind"),
kAttrMIMEType, B_MIME_STRING_TYPE, 145, B_ALIGN_LEFT, false, false));
if (TargetModel()->IsTrash() || TargetModel()->InTrash()) {
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Original name"),
kAttrOriginalPath, B_STRING_TYPE, 225, B_ALIGN_LEFT, false,
false));
} else {
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Location"), kAttrPath,
B_STRING_TYPE, 225, B_ALIGN_LEFT, false, false));
}
#ifdef OWNER_GROUP_ATTRIBUTES
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Owner"), kAttrStatOwner,
B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Group"), kAttrStatGroup,
B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true));
#endif
menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"),
kAttrStatMode, B_STRING_TYPE, 80, B_ALIGN_LEFT, false, true));
MarkAttributesMenu(menu);
}
void
BContainerWindow::ShowAttributesMenu()
{
ASSERT(fAttrMenu != NULL);
fMenuBar->AddItem(fAttrMenu, 2);
}
void
BContainerWindow::HideAttributesMenu()
{
ASSERT(fAttrMenu != NULL);
fMenuBar->RemoveItem(fAttrMenu);
}
void
BContainerWindow::MarkAttributesMenu()
{
MarkAttributesMenu(fAttrMenu);
}
void
BContainerWindow::MarkAttributesMenu(BMenu* menu)
{
if (menu == NULL)
return;
BMenuItem* item;
BMenu* submenu;
int32 submenuCount;
int32 itemCount = menu->CountItems();
for (int32 index = 0; index < itemCount; index++) {
item = menu->ItemAt(index);
uint32 attrHash;
if (item->Message() != NULL) {
if (item->Message()->FindInt32("attr_hash", (int32*)&attrHash) == B_OK)
item->SetMarked(PoseView()->ColumnFor(attrHash) != 0);
else
item->SetMarked(false);
}
submenu = item->Submenu();
if (submenu == NULL)
continue;
submenuCount = submenu->CountItems();
for (int32 subindex = 0; subindex < submenuCount; subindex++) {
item = submenu->ItemAt(subindex);
if (item == NULL || item->Message() == NULL)
continue;
if (item->Message()->FindInt32("attr_hash", (int32*)&attrHash) == B_OK)
item->SetMarked(PoseView()->ColumnFor(attrHash) != 0);
else
item->SetMarked(false);
}
}
}
BMenu*
BContainerWindow::AddMimeMenu(const BMimeType& mimeType, bool isSuperType,
BMenu* menu, int32 start)
{
AutoLock<BLooper> _(menu->Looper());
if (!mimeType.IsValid())
return NULL;
for (int32 i = start; BMenuItem* item = menu->ItemAt(i); i++) {
BMessage* message = item->Message();
if (message == NULL)
continue;
const char* type;
if (message->FindString("mimetype", &type) == B_OK
&& !strcmp(mimeType.Type(), type)) {
return item->Submenu();
}
}
BMessage attrInfo;
char description[B_MIME_TYPE_LENGTH];
const char* label = mimeType.Type();
if (!mimeType.IsInstalled())
return NULL;
if (mimeType.GetAttrInfo(&attrInfo) != B_OK)
return NULL;
if (mimeType.GetShortDescription(description) == B_OK && description[0])
label = description;
BMenu* mimeMenu = NULL;
if (isSuperType) {
mimeMenu = new BMenu(label);
BFont font;
menu->GetFont(&font);
mimeMenu->SetFont(&font);
}
int32 index = -1;
const char* publicName;
while (attrInfo.FindString("attr:public_name", ++index, &publicName)
== B_OK) {
if (!attrInfo.FindBool("attr:viewable", index)) {
continue;
}
int32 type;
int32 align;
int32 width;
bool editable;
const char* attrName;
if (attrInfo.FindString("attr:name", index, &attrName) != B_OK
|| attrInfo.FindInt32("attr:type", index, &type) != B_OK
|| attrInfo.FindBool("attr:editable", index, &editable) != B_OK
|| attrInfo.FindInt32("attr:width", index, &width) != B_OK
|| attrInfo.FindInt32("attr:alignment", index, &align) != B_OK)
continue;
BString displayAs;
attrInfo.FindString("attr:display_as", index, &displayAs);
if (mimeMenu == NULL) {
mimeMenu = new BMenu(label);
BFont font;
menu->GetFont(&font);
mimeMenu->SetFont(&font);
}
mimeMenu->AddItem(NewAttributeMenuItem(publicName, attrName, type,
displayAs.String(), width, align, editable, false));
}
if (mimeMenu == NULL)
return NULL;
BMessage* message = new BMessage(kMIMETypeItem);
message->AddString("mimetype", mimeType.Type());
menu->AddItem(new IconMenuItem(mimeMenu, message, mimeType.Type()));
return mimeMenu;
}
BMenuItem*
BContainerWindow::NewArrangeByMenu()
{
ASSERT(fAttrMenu);
TLiveArrangeByMenu* menu = new TLiveArrangeByMenu(Shortcuts()->ArrangeByLabel(), this);
BMenuItem* item;
int32 attrCount = fAttrMenu->CountItems();
for (int32 i = 3; i < attrCount; i++) {
item = fAttrMenu->ItemAt(i);
if (item == NULL || item->Message() == NULL)
continue;
item = new BMenuItem(item->Label(), new BMessage(*item->Message()));
item->Message()->what = kArrangeBy;
menu->AddItem(item);
}
menu->AddSeparatorItem();
menu->AddItem(Shortcuts()->ReverseOrderItem());
menu->AddSeparatorItem();
menu->AddItem(Shortcuts()->CleanupItem());
return new BMenuItem(menu);
}
void
BContainerWindow::SetupArrangeByMenu(BMenu* parent)
{
if (fArrangeByItem != NULL && fArrangeByItem->Menu() != NULL)
fArrangeByItem->Menu()->RemoveItem(fArrangeByItem);
if (parent == NULL)
return;
if (PoseView()->IsDesktopView()) {
Shortcuts()->UpdateCleanupItem(Shortcuts()->FindItem(parent, kCleanup, kCleanupAll));
return;
}
int32 selectIndex = parent->IndexOf(parent->FindItem(kShowSelectionWindow));
if (selectIndex == B_ERROR)
return;
parent->AddItem(fArrangeByItem, selectIndex);
uint32 attrHash;
int32 itemCount = fArrangeByItem->Submenu()->CountItems();
for (int32 index = 0; index < itemCount; index++) {
BMenuItem* item = fArrangeByItem->Submenu()->ItemAt(index);
if (item == NULL || item->Message() == NULL)
continue;
if (item->Message()->FindInt32("attr_hash", (int32*)&attrHash) == B_OK)
item->SetMarked(PoseView()->PrimarySort() == attrHash);
else if (item->Message()->what == kArrangeReverseOrder)
item->SetMarked(PoseView()->ReverseSort());
}
Shortcuts()->UpdateArrangeByItem(fArrangeByItem);
}
void
BContainerWindow::AddMimeTypesToMenu()
{
AddMimeTypesToMenu(fAttrMenu);
}
void
BContainerWindow::AddMimeTypesToMenu(BMenu* menu)
{
if (menu == NULL)
return;
int32 start = menu->CountItems();
while (start > 0 && menu->ItemAt(start - 1)->Submenu() != NULL) {
delete menu->RemoveItem(start - 1);
start--;
}
if (start > 0
&& dynamic_cast<BSeparatorItem*>(menu->ItemAt(start - 1)) == NULL)
menu->AddSeparatorItem();
BPath path;
if (TargetModel() != NULL) {
TargetModel()->GetPath(&path);
if (path.InitCheck() == B_OK
&& strstr(path.Path(), "/" kQueryTemplates "/") != NULL) {
BString name(TargetModel()->Name());
name.ReplaceFirst('_', '/');
PoseView()->AddMimeType(name.String());
}
}
int32 typeCount = PoseView()->CountMimeTypes();
for (int32 index = 0; index < typeCount; index++) {
BMimeType mimeType(PoseView()->MimeTypeAt(index));
if (mimeType.InitCheck() == B_OK) {
BMimeType superType;
mimeType.GetSupertype(&superType);
if (superType.InitCheck() == B_OK) {
BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
if (superMenu != NULL) {
AddMimeMenu(mimeType, false, superMenu, 0);
}
}
}
}
for (int32 index = 0; index < typeCount; index++) {
BMimeType mimeType(PoseView()->MimeTypeAt(index));
BMimeType superType;
mimeType.GetSupertype(&superType);
BMenu* superMenu = AddMimeMenu(superType, true, menu, start);
if (superMenu == NULL)
continue;
int32 itemsFound = 0;
int32 menusFound = 0;
for (int32 i = 0; BMenuItem* item = superMenu->ItemAt(i); i++) {
if (item->Submenu() != NULL)
menusFound++;
else
itemsFound++;
}
if (itemsFound == 0) {
if (menusFound != 0) {
while (BMenuItem* item = superMenu->RemoveItem((int32)0)) {
menu->AddItem(item);
}
}
menu->RemoveItem(superMenu->Superitem());
delete superMenu->Superitem();
}
}
BMenuItem* separator = menu->ItemAt(menu->CountItems() - 1);
if (dynamic_cast<BSeparatorItem*>(separator) != NULL) {
menu->RemoveItem(separator);
delete separator;
}
MarkAttributesMenu(menu);
}
BHandler*
BContainerWindow::ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 form, const char* property)
{
if (strcmp(property, "Poses") == 0) {
message->PopSpecifier();
return PoseView();
}
return _inherited::ResolveSpecifier(message, index, specifier,
form, property);
}
PiggybackTaskLoop*
BContainerWindow::DelayedTaskLoop()
{
if (!fTaskLoop)
fTaskLoop = new PiggybackTaskLoop;
return fTaskLoop;
}
bool
BContainerWindow::NeedsDefaultStateSetup()
{
if (TargetModel() == NULL)
return false;
if (TargetModel()->IsRoot()) {
return false;
}
WindowStateNodeOpener opener(this, false);
if (opener.StreamNode() == NULL) {
return false;
}
return !NodeHasSavedState(opener.Node());
}
bool
BContainerWindow::DefaultStateSourceNode(const char* name, BNode* result,
bool createNew, bool createFolder)
{
BPath settingsPath;
if (FSFindTrackerSettingsDir(&settingsPath) != B_OK)
return false;
BDirectory dir(settingsPath.Path());
BPath path(settingsPath);
path.Append(name);
if (!BEntry(path.Path()).Exists()) {
if (!createNew)
return false;
BPath tmpPath(settingsPath);
for (;;) {
const char* nextSlash = strchr(name, '/');
if (!nextSlash)
break;
BString tmp;
tmp.SetTo(name, nextSlash - name);
tmpPath.Append(tmp.String());
mkdir(tmpPath.Path(), 0777);
name = nextSlash + 1;
if (!name[0]) {
return false;
}
}
if (createFolder) {
if (mkdir(path.Path(), 0777) < 0)
return false;
} else {
BFile file;
if (dir.CreateFile(name, &file) != B_OK)
return false;
}
}
result->SetTo(path.Path());
return result->InitCheck() == B_OK;
}
void
BContainerWindow::SetupDefaultState()
{
BNode defaultingNode;
bool gotDefaultingNode = 0;
bool shouldStagger = false;
ASSERT(TargetModel() != NULL);
PRINT(("folder %s does not have any saved state\n", TargetModel()->Name()));
WindowStateNodeOpener opener(this, true);
if (opener.StreamNode() == NULL)
return;
if (!TargetModel()->IsRoot()) {
BDirectory deskDir;
FSGetDeskDir(&deskDir);
BEntry entry(TargetModel()->EntryRef());
BNode parent;
if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
&& parent != deskDir) {
PRINT(("looking at parent for state\n"));
if (NodeHasSavedState(&parent)) {
PRINT(("got state from parent\n"));
defaultingNode = parent;
gotDefaultingNode = true;
shouldStagger = true;
}
}
}
if (!gotDefaultingNode
&& !DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode,
true)) {
return;
}
if (PoseView()->IsDesktopView()) {
return;
}
const char* allowAttrs[] = {
kAttrWindowFrame,
kAttrWindowWorkspace,
kAttrViewState,
kAttrViewStateForeign,
kAttrColumns,
kAttrColumnsForeign,
0
};
StaggerOneParams params;
params.rectFromParent = shouldStagger;
SelectiveAttributeTransformer frameOffsetter(kAttrWindowFrame,
OffsetFrameOne, ¶ms);
SelectiveAttributeTransformer scrollOriginCleaner(kAttrViewState,
ClearViewOriginOne, ¶ms);
AttributeStreamMemoryNode memoryNode;
NamesToAcceptAttrFilter filter(allowAttrs);
AttributeStreamFileNode fileNode(&defaultingNode);
*opener.StreamNode() << scrollOriginCleaner << frameOffsetter
<< memoryNode << filter << fileNode;
}
void
BContainerWindow::RestoreWindowState(AttributeStreamNode* node)
{
if (node == NULL || PoseView()->IsDesktopView()) {
return;
}
const char* rectAttributeName;
const char* workspaceAttributeName;
if (TargetModel()->IsRoot()) {
rectAttributeName = kAttrDisksFrame;
workspaceAttributeName = kAttrDisksWorkspace;
} else {
rectAttributeName = kAttrWindowFrame;
workspaceAttributeName = kAttrWindowWorkspace;
}
BRect frame(Frame());
if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
== sizeof(BRect)) {
const float scalingFactor = be_plain_font->Size() / 12.0f;
frame.left *= scalingFactor;
frame.top *= scalingFactor;
frame.right *= scalingFactor;
frame.bottom *= scalingFactor;
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
} else
sNewWindRect.OffsetBy(sWindowStaggerBy, sWindowStaggerBy);
fPreviousBounds = Bounds();
uint32 workspace;
if (((fOpenFlags & kRestoreWorkspace) != 0)
&& node->Read(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
&workspace) == sizeof(uint32))
SetWorkspaces(workspace);
if ((fOpenFlags & kIsHidden) != 0)
Minimize(true);
int32 size = node->Contains(kAttrWindowDecor, B_RAW_TYPE);
if (size > 0) {
char buffer[size];
if (((fOpenFlags & kRestoreDecor) != 0)
&& node->Read(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer)
== size) {
BMessage decorSettings;
if (decorSettings.Unflatten(buffer) == B_OK)
SetDecoratorSettings(decorSettings);
}
}
}
void
BContainerWindow::RestoreWindowState(const BMessage& message)
{
if (PoseView()->IsDesktopView()) {
return;
}
const char* rectAttributeName;
const char* workspaceAttributeName;
if (TargetModel()->IsRoot()) {
rectAttributeName = kAttrDisksFrame;
workspaceAttributeName = kAttrDisksWorkspace;
} else {
rectAttributeName = kAttrWindowFrame;
workspaceAttributeName = kAttrWindowWorkspace;
}
BRect frame(Frame());
if (message.FindRect(rectAttributeName, &frame) == B_OK) {
const float scalingFactor = be_plain_font->Size() / 12.0f;
frame.left *= scalingFactor;
frame.top *= scalingFactor;
frame.right *= scalingFactor;
frame.bottom *= scalingFactor;
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
} else
sNewWindRect.OffsetBy(sWindowStaggerBy, sWindowStaggerBy);
uint32 workspace;
if (((fOpenFlags & kRestoreWorkspace) != 0)
&& message.FindInt32(workspaceAttributeName,
(int32*)&workspace) == B_OK) {
SetWorkspaces(workspace);
}
if ((fOpenFlags & kIsHidden) != 0)
Minimize(true);
BMessage decorSettings;
if (((fOpenFlags & kRestoreDecor) != 0)
&& message.FindMessage(kAttrWindowDecor, &decorSettings) == B_OK) {
SetDecoratorSettings(decorSettings);
}
fStateNeedsSaving = false;
}
void
BContainerWindow::SaveWindowState(AttributeStreamNode* node)
{
if (TargetModel() != NULL && PoseView()->IsDesktopView()) {
return;
}
ASSERT(node != NULL);
const char* rectAttributeName;
const char* workspaceAttributeName;
if (TargetModel()->IsRoot()) {
rectAttributeName = kAttrDisksFrame;
workspaceAttributeName = kAttrDisksWorkspace;
} else {
rectAttributeName = kAttrWindowFrame;
workspaceAttributeName = kAttrWindowWorkspace;
}
BRect frame(Frame());
const float scalingFactor = be_plain_font->Size() / 12.0f;
frame.left /= scalingFactor;
frame.top /= scalingFactor;
frame.right /= scalingFactor;
frame.bottom /= scalingFactor;
node->Write(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame);
uint32 workspaces = Workspaces();
node->Write(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32),
&workspaces);
BMessage decorSettings;
if (GetDecoratorSettings(&decorSettings) == B_OK) {
int32 size = decorSettings.FlattenedSize();
char buffer[size];
if (decorSettings.Flatten(buffer, size) == B_OK) {
node->Write(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer);
}
}
}
void
BContainerWindow::SaveWindowState(BMessage& message) const
{
const char* rectAttributeName;
const char* workspaceAttributeName;
if (TargetModel() != NULL && TargetModel()->IsRoot()) {
rectAttributeName = kAttrDisksFrame;
workspaceAttributeName = kAttrDisksWorkspace;
} else {
rectAttributeName = kAttrWindowFrame;
workspaceAttributeName = kAttrWindowWorkspace;
}
BRect frame(Frame());
const float scalingFactor = be_plain_font->Size() / 12.0f;
frame.left /= scalingFactor;
frame.top /= scalingFactor;
frame.right /= scalingFactor;
frame.bottom /= scalingFactor;
message.AddRect(rectAttributeName, frame);
message.AddInt32(workspaceAttributeName, (int32)Workspaces());
BMessage decorSettings;
if (GetDecoratorSettings(&decorSettings) == B_OK) {
message.AddMessage(kAttrWindowDecor, &decorSettings);
}
}
void
BContainerWindow::ShowSelectionWindow()
{
if (fSelectionWindow == NULL) {
fSelectionWindow = new SelectionWindow(this);
fSelectionWindow->Show();
} else if (fSelectionWindow->Lock()) {
fSelectionWindow->MoveCloseToMouse();
if (fSelectionWindow->IsHidden())
fSelectionWindow->Show();
fSelectionWindow->Unlock();
}
}
void
BContainerWindow::ShowNavigator(bool show)
{
if (PoseView()->IsDesktopView() || !TargetModel()->IsDirectory() || PoseView()->IsFilePanel())
return;
if (show) {
if (Navigator() && !Navigator()->IsHidden())
return;
if (Navigator() == NULL) {
fNavigator = new BNavigator(TargetModel());
fPoseContainer->GridLayout()->AddView(fNavigator, 0, 0, 2);
}
if (Navigator()->IsHidden())
Navigator()->Show();
if (PoseView()->VScrollBar())
PoseView()->UpdateScrollRange();
} else {
if (!Navigator() || Navigator()->IsHidden())
return;
if (PoseView()->VScrollBar())
PoseView()->UpdateScrollRange();
fNavigator->Hide();
}
}
void
BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled)
{
if (PoseView()->IsDesktopView())
return;
if (enabled) {
if (!Navigator())
return;
RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY,
new BMessage(kNavigatorCommandBackward), Navigator());
AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY,
new BMessage(kNavigatorCommandForward), Navigator());
AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
new BMessage(kNavigatorCommandUp), Navigator());
AddShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
new BMessage(kNavigatorCommandBackward), Navigator());
AddShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
new BMessage(kNavigatorCommandForward), Navigator());
AddShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
new BMessage(kNavigatorCommandUp), Navigator());
AddShortcut(B_DOWN_ARROW, B_OPTION_KEY | B_COMMAND_KEY,
new BMessage(kOpenSelection), PoseView());
AddShortcut('L', B_COMMAND_KEY,
new BMessage(kNavigatorCommandSetFocus), Navigator());
} else {
RemoveShortcut(B_LEFT_ARROW, B_COMMAND_KEY);
RemoveShortcut(B_RIGHT_ARROW, B_COMMAND_KEY);
RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY);
RemoveShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
RemoveShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
RemoveShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY);
RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY);
AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
new BMessage(kOpenSelection), PoseView());
AddShortcut(B_UP_ARROW, B_COMMAND_KEY,
new BMessage(kOpenParentDir), PoseView());
AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY,
new BMessage(kOpenParentDir), PoseView());
RemoveShortcut('L', B_COMMAND_KEY);
}
}
void
BContainerWindow::SetPathWatchingEnabled(bool enable)
{
if (IsPathWatchingEnabled()) {
stop_watching(this);
fIsWatchingPath = false;
}
if (enable) {
if (TargetModel() != NULL) {
BEntry entry;
TargetModel()->GetEntry(&entry);
status_t err;
do {
err = entry.GetParent(&entry);
if (err != B_OK)
break;
char name[B_FILE_NAME_LENGTH];
entry.GetName(name);
if (strcmp(name, "/") == 0)
break;
node_ref ref;
entry.GetNodeRef(&ref);
watch_node(&ref, B_WATCH_NAME, this);
} while (err == B_OK);
fIsWatchingPath = err == B_OK;
} else
fIsWatchingPath = false;
}
}
void
BContainerWindow::PulseTaskLoop()
{
if (fTaskLoop)
fTaskLoop->PulseMe();
}
WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow* window,
bool forWriting)
:
fModelOpener(NULL),
fNode(NULL),
fStreamNode(NULL)
{
if (window->TargetModel() != NULL && window->TargetModel()->IsRoot()) {
BDirectory dir;
if (FSGetDeskDir(&dir) == B_OK) {
fNode = new BDirectory(dir);
fStreamNode = new AttributeStreamFileNode(fNode);
}
} else if (window->TargetModel() != NULL) {
fModelOpener = new ModelNodeLazyOpener(window->TargetModel(),
forWriting, false);
if (fModelOpener->IsOpen(forWriting)) {
fStreamNode = new AttributeStreamFileNode(
fModelOpener->TargetModel()->Node());
}
}
}
WindowStateNodeOpener::~WindowStateNodeOpener()
{
delete fModelOpener;
delete fNode;
delete fStreamNode;
}
void
WindowStateNodeOpener::SetTo(const BDirectory* node)
{
delete fModelOpener;
delete fNode;
delete fStreamNode;
fModelOpener = NULL;
fNode = new BDirectory(*node);
fStreamNode = new AttributeStreamFileNode(fNode);
}
void
WindowStateNodeOpener::SetTo(const BEntry* entry, bool forWriting)
{
delete fModelOpener;
delete fNode;
delete fStreamNode;
fModelOpener = NULL;
fNode = new BFile(entry, (uint32)(forWriting ? O_RDWR : O_RDONLY));
fStreamNode = new AttributeStreamFileNode(fNode);
}
void
WindowStateNodeOpener::SetTo(Model* model, bool forWriting)
{
delete fModelOpener;
delete fNode;
delete fStreamNode;
fNode = NULL;
fStreamNode = NULL;
fModelOpener = new ModelNodeLazyOpener(model, forWriting, false);
if (fModelOpener->IsOpen(forWriting)) {
fStreamNode = new AttributeStreamFileNode(
fModelOpener->TargetModel()->Node());
}
}
AttributeStreamNode*
WindowStateNodeOpener::StreamNode() const
{
return fStreamNode;
}
BNode*
WindowStateNodeOpener::Node() const
{
if (!fStreamNode)
return NULL;
if (fNode)
return fNode;
return fModelOpener->TargetModel()->Node();
}
BorderedView::BorderedView()
:
BGroupView(B_VERTICAL, 0),
fEnableBorderHighlight(true)
{
GroupLayout()->SetInsets(1);
}
void
BorderedView::WindowActivated(bool active)
{
BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
if (window == NULL)
return;
if (window->PoseView()->IsFocus())
PoseViewFocused(active);
}
void BorderedView::EnableBorderHighlight(bool enable)
{
fEnableBorderHighlight = enable;
PoseViewFocused(false);
}
void
BorderedView::PoseViewFocused(bool focused)
{
BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
if (window == NULL)
return;
color_which base = B_DOCUMENT_BACKGROUND_COLOR;
float tint = B_DARKEN_2_TINT;
if (focused && window->IsActive() && fEnableBorderHighlight) {
base = B_KEYBOARD_NAVIGATION_COLOR;
tint = B_NO_TINT;
}
BScrollBar* hScrollBar = window->PoseView()->HScrollBar();
if (hScrollBar != NULL)
hScrollBar->SetBorderHighlighted(focused);
BScrollBar* vScrollBar = window->PoseView()->VScrollBar();
if (vScrollBar != NULL)
vScrollBar->SetBorderHighlighted(focused);
SetViewUIColor(base, tint);
Invalidate();
}
void
BorderedView::Pulse()
{
BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
if (window != NULL)
window->PulseTaskLoop();
}