#include "PoseView.h"
#include <algorithm>
#include <functional>
#include <map>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <stdlib.h>
#include <strings.h>
#include <compat/sys/stat.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <Clipboard.h>
#include <ControlLook.h>
#include <Debug.h>
#include <Dragger.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <List.h>
#include <Locale.h>
#include <LongAndDragTrackingFilter.h>
#include <MenuItem.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Query.h>
#include <Screen.h>
#include <StopWatch.h>
#include <String.h>
#include <SymLink.h>
#include <TextView.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <Window.h>
#include <PathMonitor.h>
#include "Attributes.h"
#include "AutoLock.h"
#include "BackgroundImage.h"
#include "Bitmaps.h"
#include "Commands.h"
#include "CountView.h"
#include "DeskWindow.h"
#include "DesktopPoseView.h"
#include "FSClipboard.h"
#include "FSUtils.h"
#include "FilePanelPriv.h"
#include "FunctionObject.h"
#include "InfoWindow.h"
#include "MimeTypes.h"
#include "Navigator.h"
#include "Pose.h"
#include "Shortcuts.h"
#include "Tests.h"
#include "Thread.h"
#include "Tracker.h"
#include "TrackerString.h"
#include "WidgetAttributeText.h"
#include "WidthBuffer.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PoseView"
const float kDoubleClickTresh = 6;
const uint32 kAddNewPoses = 'Tanp';
const uint32 kAddPosesCompleted = 'Tapc';
const int32 kMaxAddPosesChunk = 50;
const int32 kRoomForLine = 2;
const int32 kMenuTrackMargin = 20;
const float kSlowScrollBucket = 30;
const float kBorderHeight = 20;
enum {
kAutoScrollOff,
kWaitForTransition,
kDelayAutoScroll,
kAutoScrollOn
};
enum {
kWasDragged,
kContextMenuShown,
kNotDragged
};
enum {
kInsertAtFront,
kInsertAfter
};
const BPoint kTransparentDragThreshold(be_control_look->ComposeIconSize(256).Width(),
be_control_look->ComposeIconSize(192).Width());
struct attr_column_relation {
uint32 attrHash;
int32 fieldMask;
};
static struct attr_column_relation sAttrColumnMap[] = {
{ AttrHashString(kAttrStatModified, B_TIME_TYPE),
B_STAT_MODIFICATION_TIME },
{ AttrHashString(kAttrStatSize, B_OFF_T_TYPE),
B_STAT_SIZE },
{ AttrHashString(kAttrStatCreated, B_TIME_TYPE),
B_STAT_CREATION_TIME },
{ AttrHashString(kAttrStatMode, B_STRING_TYPE),
B_STAT_MODE }
};
struct AddPosesResult {
~AddPosesResult();
void ReleaseModels();
Model* fModels[kMaxAddPosesChunk];
PoseInfo fPoseInfos[kMaxAddPosesChunk];
int32 fCount;
};
AddPosesResult::~AddPosesResult(void)
{
for (int32 i = 0; i < fCount; i++)
delete fModels[i];
}
void
AddPosesResult::ReleaseModels(void)
{
for (int32 i = 0; i < kMaxAddPosesChunk; i++)
fModels[i] = NULL;
}
static BPose*
BSearch(PoseList* table, const BPose* key, BPoseView* view,
int (*cmp)(const BPose*, const BPose*, BPoseView*),
bool returnClosest = true);
static int
PoseCompareAddWidget(const BPose* p1, const BPose* p2, BPoseView* view);
static bool
OneMatches(BPose* pose, BPoseView*, void* castToPose)
{
return pose == (const BPose*)castToPose;
}
static void
CopySelectionListToEntryRefList(const PoseList* original,
BObjectList<entry_ref, true>* copy)
{
int32 count = original->CountItems();
for (int32 index = 0; index < count; index++) {
copy->AddItem(new entry_ref(*(original->ItemAt(
index)->TargetModel()->EntryRef())));
}
}
BPoseView::BPoseView(Model* model, uint32 viewMode)
:
BView("PoseView", B_WILL_DRAW | B_PULSE_NEEDED),
fViewState(new BViewState),
fSelectionHandler(be_app),
fPoseList(new PoseList(40, true)),
fHScrollBar(NULL),
fVScrollBar(NULL),
fModel(model),
fActivePose(NULL),
fExtent(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
fFilteredPoseList(new PoseList()),
fVSPoseList(new PoseList()),
fSelectionList(new PoseList()),
fMimeTypesInSelectionCache(20),
fZombieList(new BObjectList<Model, true>(10)),
fColumnList(new BObjectList<BColumn, true>(4)),
fBrokenLinks(new BObjectList<Model>(10)),
fCountView(NULL),
fListElemHeight(ceilf(be_plain_font->Size() * 1.65f)),
fListOffset(ceilf(be_control_look->DefaultLabelSpacing() * 3.3f)),
fIconPoseHeight(0.0f),
fDropTarget(NULL),
fAlreadySelectedDropTarget(NULL),
fLastClickPoint(INT32_MAX, INT32_MAX),
fLastClickButtons(0),
fLastClickedPose(NULL),
fLastExtent(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
fTitleView(NULL),
fRefFilter(NULL),
fAutoScrollInc(20),
fAutoScrollState(kAutoScrollOff),
fSelectionPivotPose(NULL),
fRealPivotPose(NULL),
fKeyRunner(NULL),
fDragMessage(NULL),
fCachedTypesList(NULL),
fFilterStrings(4),
fLastFilterStringCount(1),
fLastFilterStringLength(0),
fStartFrame(0, 0, 0, 0),
fLastKeyTime(0),
fLastDeskbarFrameCheckTime(LONGLONG_MIN),
fDeskbarFrame(0, 0, -1, -1),
fTextWidgetToCheck(NULL),
fActiveTextWidget(NULL),
fCachedIconSizeFrom(0),
fStateNeedsSaving(false),
fSavePoseLocations(true),
fMultipleSelection(true),
fDragEnabled(true),
fDropEnabled(true),
fMimeTypeListIsDirty(false),
fWidgetTextOutline(false),
fTrackRightMouseUp(false),
fTrackMouseUp(false),
fSelectionVisible(true),
fSelectionRectEnabled(true),
fAlwaysAutoPlace(false),
fAllowPoseEditing(true),
fSelectionChangedHook(false),
fOkToMapIcons(false),
fEnsurePosesVisible(false),
fShouldAutoScroll(true),
fIsWatchingDateFormatChange(false),
fHasPosesInClipboard(false),
fCursorCheck(false),
fTypeAheadFiltering(false),
fShowSelectionWhenInactive(TrackerSettings().ShowSelectionWhenInactive()),
fIsDrawingSelectionRect(false),
fTransparentSelection(TrackerSettings().TransparentSelection()),
fWaitingForRefs(false)
{
fViewState->SetViewMode(viewMode);
fFilterStrings.AddItem(new BString());
}
BPoseView::~BPoseView()
{
delete fPoseList;
delete fFilteredPoseList;
delete fVSPoseList;
delete fColumnList;
delete fSelectionList;
delete fZombieList;
delete fViewState;
delete fModel;
delete fKeyRunner;
delete fBrokenLinks;
delete fDragMessage;
delete fCachedTypesList;
IconCache::sIconCache->Deleting(this);
}
void
BPoseView::Init(AttributeStreamNode* node)
{
RestoreState(node);
InitCommon();
}
void
BPoseView::Init(const BMessage &message)
{
RestoreState(message);
InitCommon();
}
void
BPoseView::InitCommon()
{
fTitleView = new BTitleView(this);
if (ViewMode() != kListMode)
fTitleView->Hide();
if (fHScrollBar != NULL)
fHScrollBar->SetTitleView(fTitleView);
fCountView = new BCountView(this);
BPoint origin;
if (ViewMode() == kListMode)
origin = fViewState->ListOrigin();
else
origin = fViewState->IconOrigin();
PinPointToValidRange(origin);
if (sFontHeight == -1) {
be_plain_font->GetHeight(&sFontInfo);
sFontHeight = sFontInfo.ascent + sFontInfo.descent + 1;
}
SetIconPoseHeight();
GetLayoutInfo(ViewMode(), &fGrid, &fOffset);
ResetPosePlacementHint();
DisableScrollBars();
ScrollTo(origin);
UpdateScrollRange();
SetScrollBarsTo(origin);
EnableScrollBars();
StartWatching();
if (TargetModel() != NULL && TargetModel()->IsTrash())
AddTrashPoses();
else
AddPoses(TargetModel());
}
void
BPoseView::AdoptSystemColors()
{
if (!TargetVolumeIsReadOnly())
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
else
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR, ReadOnlyTint(B_DOCUMENT_BACKGROUND_COLOR));
SetLowUIColor(ViewUIColor());
SetHighUIColor(B_DOCUMENT_TEXT_COLOR);
}
bool
BPoseView::HasSystemColors() const
{
float tint = B_NO_TINT;
float readOnlyTint = ReadOnlyTint(B_DOCUMENT_BACKGROUND_COLOR);
return ViewUIColor(&tint) == B_DOCUMENT_BACKGROUND_COLOR
&& (tint == B_NO_TINT || tint == readOnlyTint)
&& LowUIColor(&tint) == B_DOCUMENT_BACKGROUND_COLOR
&& (tint == B_NO_TINT || tint == readOnlyTint)
&& HighUIColor(&tint) == B_DOCUMENT_TEXT_COLOR && tint == B_NO_TINT;
}
static int
CompareColumns(const BColumn* c1, const BColumn* c2)
{
if (c1->Offset() > c2->Offset())
return 1;
else if (c1->Offset() < c2->Offset())
return -1;
return 0;
}
void
BPoseView::RestoreColumnState(AttributeStreamNode* node)
{
fColumnList->MakeEmpty();
if (fTitleView != NULL)
fTitleView->Reset();
if (node != NULL) {
const char* columnsAttr;
const char* columnsAttrForeign;
if (TargetModel() != NULL && TargetModel()->IsRoot()) {
columnsAttr = kAttrDisksColumns;
columnsAttrForeign = kAttrDisksColumnsForeign;
} else {
columnsAttr = kAttrColumns;
columnsAttrForeign = kAttrColumnsForeign;
}
bool wrongEndianness = false;
const char* name = columnsAttr;
size_t size = (size_t)node->Contains(name, B_RAW_TYPE);
if (size == 0) {
name = columnsAttrForeign;
wrongEndianness = true;
size = (size_t)node->Contains(name, B_RAW_TYPE);
}
if (size > 0 && size < 10000) {
char* buffer = new char[size];
off_t result = node->Read(name, 0, B_RAW_TYPE, size, buffer);
if (result) {
BMallocIO stream;
stream.WriteAt(0, buffer, size);
stream.Seek(0, SEEK_SET);
BObjectList<BColumn> tempSortedList;
for (;;) {
BColumn* column = BColumn::InstantiateFromStream(&stream,
wrongEndianness);
if (column == NULL)
break;
tempSortedList.AddItem(column);
}
AddColumnList(&tempSortedList);
}
delete[] buffer;
}
}
SetupDefaultColumnsIfNeeded();
if (!ColumnFor(PrimarySort())) {
fViewState->SetPrimarySort(FirstColumn()->AttrHash());
fViewState->SetPrimarySortType(FirstColumn()->AttrType());
}
if (PrimarySort() == SecondarySort())
fViewState->SetSecondarySort(0);
}
void
BPoseView::RestoreColumnState(const BMessage &message)
{
fColumnList->MakeEmpty();
if (fTitleView != NULL)
fTitleView->Reset();
BObjectList<BColumn> tempSortedList;
for (int32 index = 0; ; index++) {
BColumn* column = BColumn::InstantiateFromMessage(message, index);
if (column == NULL)
break;
tempSortedList.AddItem(column);
}
AddColumnList(&tempSortedList);
SetupDefaultColumnsIfNeeded();
if (!ColumnFor(PrimarySort())) {
fViewState->SetPrimarySort(FirstColumn()->AttrHash());
fViewState->SetPrimarySortType(FirstColumn()->AttrType());
}
if (PrimarySort() == SecondarySort())
fViewState->SetSecondarySort(0);
}
void
BPoseView::AddColumnList(BObjectList<BColumn>* list)
{
list->SortItems(&CompareColumns);
float nextLeftEdge = StartOffset();
for (int32 columIndex = 0; columIndex < list->CountItems(); columIndex++) {
BColumn* column = list->ItemAt(columIndex);
column->SetOffset(nextLeftEdge);
nextLeftEdge = column->Offset() + column->Width()
- kRoomForLine / 2.0f + kTitleColumnExtraMargin;
fColumnList->AddItem(column);
if (!IsWatchingDateFormatChange()
&& column->AttrType() == B_TIME_TYPE) {
StartWatchDateFormatChange();
}
}
if (fTitleView != NULL)
fTitleView->Reset();
}
void
BPoseView::RestoreState(AttributeStreamNode* node)
{
RestoreColumnState(node);
if (node != NULL) {
const char* viewStateAttr;
const char* viewStateAttrForeign;
if (TargetModel() != NULL && TargetModel()->IsRoot()) {
viewStateAttr = kAttrDisksViewState;
viewStateAttrForeign = kAttrDisksViewStateForeign;
} else {
viewStateAttr = ViewStateAttributeName();
viewStateAttrForeign = ForeignViewStateAttributeName();
}
bool wrongEndianness = false;
const char* name = viewStateAttr;
size_t size = (size_t)node->Contains(name, B_RAW_TYPE);
if (!size) {
name = viewStateAttrForeign;
wrongEndianness = true;
size = (size_t)node->Contains(name, B_RAW_TYPE);
}
if (size > 0 && size < 10000) {
char* buffer = new char[size];
off_t result = node->Read(name, 0, B_RAW_TYPE, size, buffer);
if (result) {
BMallocIO stream;
stream.WriteAt(0, buffer, size);
stream.Seek(0, SEEK_SET);
BViewState* viewstate
= BViewState::InstantiateFromStream(&stream,
wrongEndianness);
if (viewstate) {
delete fViewState;
fViewState = viewstate;
}
}
delete[] buffer;
}
}
if (IsDesktopView() && ViewMode() == kListMode) {
fViewState->SetViewMode(kIconMode);
}
}
void
BPoseView::RestoreState(const BMessage &message)
{
RestoreColumnState(message);
BViewState* viewstate = BViewState::InstantiateFromMessage(message);
if (viewstate != NULL) {
delete fViewState;
fViewState = viewstate;
}
if (IsDesktopView() && ViewMode() == kListMode) {
fViewState->SetViewMode(kIconMode);
}
}
namespace BPrivate {
bool
ClearViewOriginOne(const char* DEBUG_ONLY(name), uint32 type, off_t size,
void* viewStateArchive, void*)
{
ASSERT(strcmp(name, kAttrViewState) == 0);
if (viewStateArchive == NULL)
return false;
if (type != B_RAW_TYPE)
return false;
BMallocIO stream;
stream.WriteAt(0, viewStateArchive, (size_t)size);
stream.Seek(0, SEEK_SET);
BViewState* viewstate = BViewState::InstantiateFromStream(&stream, false);
if (!viewstate)
return false;
viewstate->SetListOrigin(B_ORIGIN);
viewstate->SetIconOrigin(B_ORIGIN);
stream.Seek(0, SEEK_SET);
viewstate->ArchiveToStream(&stream);
stream.ReadAt(0, viewStateArchive, (size_t)size);
return true;
}
}
void
BPoseView::SetupDefaultColumnsIfNeeded()
{
if (CountColumns() != 0)
return;
AddColumn(new BColumn(B_TRANSLATE("Name"), 145,
B_ALIGN_LEFT, kAttrStatName, B_STRING_TYPE, true, true));
AddColumn(new BColumn(B_TRANSLATE("Size"), 80,
B_ALIGN_RIGHT, kAttrStatSize, B_OFF_T_TYPE, true, false));
AddColumn(new BColumn(B_TRANSLATE("Modified"), 150,
B_ALIGN_LEFT, kAttrStatModified, B_TIME_TYPE, true, false));
if (!IsWatchingDateFormatChange())
StartWatchDateFormatChange();
}
const char*
BPoseView::ViewStateAttributeName() const
{
return IsDesktopView() ? kAttrDesktopViewState : kAttrViewState;
}
const char*
BPoseView::ForeignViewStateAttributeName() const
{
return IsDesktopView() ? kAttrDesktopViewStateForeign
: kAttrViewStateForeign;
}
void
BPoseView::SaveColumnState(AttributeStreamNode* node)
{
BMallocIO stream;
for (int32 index = 0; ; index++) {
const BColumn* column = ColumnAt(index);
if (column == NULL)
break;
column->ArchiveToStream(&stream);
}
const char* columnsAttr;
const char* columnsAttrForeign;
if (TargetModel() != NULL && TargetModel()->IsRoot()) {
columnsAttr = kAttrDisksColumns;
columnsAttrForeign = kAttrDisksColumnsForeign;
} else {
columnsAttr = kAttrColumns;
columnsAttrForeign = kAttrColumnsForeign;
}
node->Write(columnsAttr, columnsAttrForeign, B_RAW_TYPE,
stream.Position(), stream.Buffer());
}
void
BPoseView::SaveColumnState(BMessage& message) const
{
for (int32 index = 0; ; index++) {
const BColumn* column = ColumnAt(index);
if (column == NULL)
break;
column->ArchiveToMessage(message);
}
}
void
BPoseView::SaveState(AttributeStreamNode* node)
{
SaveColumnState(node);
BMallocIO stream;
stream.Seek(0, SEEK_SET);
fViewState->ArchiveToStream(&stream);
const char* viewStateAttr;
const char* viewStateAttrForeign;
if (TargetModel() != NULL && TargetModel()->IsRoot()) {
viewStateAttr = kAttrDisksViewState;
viewStateAttrForeign = kAttrDisksViewStateForeign;
} else {
viewStateAttr = ViewStateAttributeName();
viewStateAttrForeign = ForeignViewStateAttributeName();
}
node->Write(viewStateAttr, viewStateAttrForeign, B_RAW_TYPE,
stream.Position(), stream.Buffer());
fStateNeedsSaving = false;
}
void
BPoseView::SaveState(BMessage& message) const
{
SaveColumnState(message);
fViewState->ArchiveToMessage(message);
}
float
BPoseView::StringWidth(const char* str) const
{
return BPrivate::gWidthBuffer->StringWidth(str, 0, (int32)strlen(str),
be_plain_font);
}
float
BPoseView::StringWidth(const char* str, int32 len) const
{
ASSERT(strlen(str) == (uint32)len);
return BPrivate::gWidthBuffer->StringWidth(str, 0, len, be_plain_font);
}
void
BPoseView::SavePoseLocations(BRect* frameIfDesktop)
{
PoseInfo poseInfo;
if (!fSavePoseLocations)
return;
ASSERT(Window()->IsLocked());
Model* targetModel = TargetModel();
ThrowOnAssert(targetModel != NULL);
BVolume volume(TargetModel()->NodeRef()->device);
if (volume.InitCheck() != B_OK)
return;
if (!targetModel->IsRoot()
&& (volume.IsReadOnly() || !volume.KnowsAttr())) {
return;
}
bool isDesktop = !IsFilePanel() && IsDesktopView() && (frameIfDesktop != NULL);
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
if (pose->NeedsSaveLocation() && pose->HasLocation()) {
Model* model = pose->TargetModel();
poseInfo.fInvisible = false;
if (model->IsRoot() || (model->IsTrash() && isDesktop))
poseInfo.fInitedDirectory = targetModel->NodeRef()->node;
else
poseInfo.fInitedDirectory = model->EntryRef()->directory;
if (model->IsTrash() && !isDesktop)
poseInfo.fInvisible = true;
poseInfo.fLocation = pose->Location(this);
ExtendedPoseInfo* extendedPoseInfo = NULL;
size_t extendedPoseInfoSize = 0;
ModelNodeLazyOpener opener(model, true);
if (isDesktop) {
opener.OpenNode(true);
extendedPoseInfo = ReadExtendedPoseInfo(model);
if (!extendedPoseInfo) {
size_t size = ExtendedPoseInfo::Size(1);
extendedPoseInfo = (ExtendedPoseInfo*) new char[size];
memset((void*)extendedPoseInfo, 0, size);
extendedPoseInfo->fWorkspaces = 0xffffffff;
extendedPoseInfo->fInvisible = false;
extendedPoseInfo->fShowFromBootOnly = false;
extendedPoseInfo->fNumFrames = 0;
}
ASSERT(extendedPoseInfo != NULL);
extendedPoseInfo->SetLocationForFrame(pose->Location(this),
*frameIfDesktop);
extendedPoseInfoSize = extendedPoseInfo->Size();
}
if (model->InitCheck() != B_OK) {
delete[] (char*)extendedPoseInfo;
continue;
}
ASSERT(model != NULL);
ASSERT(model->InitCheck() == B_OK);
if (model->IsRoot() || (model->IsTrash() && isDesktop)) {
BDirectory deskDir;
if (FSGetDeskDir(&deskDir) == B_OK) {
const char* poseInfoAttr = model->IsTrash()
? kAttrTrashPoseInfo : kAttrDisksPoseInfo;
const char* poseInfoAttrForeign = model->IsTrash()
? kAttrTrashPoseInfoForeign : kAttrDisksPoseInfoForeign;
if (deskDir.WriteAttr(poseInfoAttr, B_RAW_TYPE, 0,
&poseInfo, sizeof(poseInfo)) == sizeof(poseInfo)) {
deskDir.RemoveAttr(poseInfoAttrForeign);
}
if (isDesktop && !model->IsTrash()
&& deskDir.WriteAttr(kAttrExtendedDisksPoseInfo, B_RAW_TYPE, 0,
extendedPoseInfo, extendedPoseInfoSize)
== (ssize_t)extendedPoseInfoSize) {
deskDir.RemoveAttr(kAttrExtendedDisksPoseInfoForeign);
}
}
} else {
model->WriteAttrKillForeign(kAttrPoseInfo,
kAttrPoseInfoForeign, B_RAW_TYPE, 0, &poseInfo,
sizeof(poseInfo));
if (isDesktop) {
model->WriteAttrKillForeign(kAttrExtendedPoseInfo,
kAttrExtendedPoseInfoForeign,
B_RAW_TYPE, 0, extendedPoseInfo,
extendedPoseInfoSize);
}
}
delete[] (char*)extendedPoseInfo;
}
}
}
void
BPoseView::StartWatching()
{
TTracker::WatchNode(NULL, B_WATCH_MOUNT, this);
Model* targetModel = TargetModel();
if (targetModel != NULL)
TTracker::WatchNode(targetModel->NodeRef(), B_WATCH_ATTR, this);
BMimeType::StartWatching(BMessenger(this));
}
void
BPoseView::StopWatching()
{
stop_watching(this);
BMimeType::StopWatching(BMessenger(this));
}
void
BPoseView::DetachedFromWindow()
{
if (fTitleView && !fTitleView->Window())
delete fTitleView;
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL && tracker->Lock()) {
tracker->StopWatching(this, kShowSelectionWhenInactiveChanged);
tracker->StopWatching(this, kTransparentSelectionChanged);
tracker->StopWatching(this, kSortFolderNamesFirstChanged);
tracker->StopWatching(this, kHideDotFilesChanged);
tracker->StopWatching(this, kTypeAheadFilteringChanged);
tracker->Unlock();
}
std::set<thread_id> addPosesThreads(fAddPosesThreads);
fAddPosesThreads.clear();
std::set<thread_id>::iterator it;
for (it = addPosesThreads.begin(); it != addPosesThreads.end(); it++) {
UnlockLooper();
wait_for_thread(*it, NULL);
LockLooper();
}
StopWatching();
CommitActivePose();
SavePoseLocations();
FSClipboardStopWatch(this);
}
void
BPoseView::Pulse()
{
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return;
window->PulseTaskLoop();
UpdateCount();
if (fAutoScrollState != kAutoScrollOff)
HandleAutoScroll();
BRect extent = Extent();
if ((fLastExtent != extent) || (fLastLeftTop != LeftTop())) {
uint32 buttons;
BPoint mouse;
GetMouse(&mouse, &buttons);
if (buttons == 0) {
UpdateScrollRange();
fLastExtent = extent;
fLastLeftTop = LeftTop();
}
}
if (fTextWidgetToCheck != NULL)
fTextWidgetToCheck->CheckExpiration();
}
void
BPoseView::ScrollTo(BPoint where)
{
_inherited::ScrollTo(where);
if (ViewMode() == kListMode)
fViewState->SetListOrigin(Extent().LeftTop());
else
fViewState->SetIconOrigin(Extent().LeftTop());
}
void
BPoseView::AttachedToWindow()
{
AdoptSystemColors();
AddFilter(new ShortcutFilter(B_RETURN, B_OPTION_KEY, kOpenSelection, this));
AddFilter(new ShortcutFilter(B_ESCAPE, 0, B_CANCEL, this));
AddFilter(new ShortcutFilter(B_ESCAPE, B_SHIFT_KEY, kCancelSelectionToClipboard, this));
AddFilter(new LongAndDragTrackingFilter(kMsgMouseLongDown, kMsgMouseDragged));
fLastLeftTop = LeftTop();
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL && tracker->Lock()) {
tracker->StartWatching(this, kShowSelectionWhenInactiveChanged);
tracker->StartWatching(this, kTransparentSelectionChanged);
tracker->StartWatching(this, kSortFolderNamesFirstChanged);
tracker->StartWatching(this, kHideDotFilesChanged);
tracker->StartWatching(this, kTypeAheadFilteringChanged);
tracker->Unlock();
}
FSClipboardStartWatch(this);
_inherited::AttachedToWindow();
}
BSize
BPoseView::IconSize() const
{
if (fCachedIconSizeFrom != fViewState->IconSize()) {
fCachedIconSizeFrom = fViewState->IconSize();
fCachedIconSize = be_control_look->ComposeIconSize(fCachedIconSizeFrom);
}
return fCachedIconSize;
}
void
BPoseView::SetIconPoseHeight()
{
switch (ViewMode()) {
case kIconMode:
fIconPoseHeight = IconSizeInt() + sFontHeight + 1;
break;
case kMiniIconMode:
fViewState->SetIconSize(B_MINI_ICON);
fIconPoseHeight = std::max((float)IconSizeInt(), sFontHeight + 1);
break;
case kListMode:
default:
fViewState->SetIconSize(B_MINI_ICON);
fIconPoseHeight = fListElemHeight;
break;
}
}
void
BPoseView::GetLayoutInfo(uint32 mode, BPoint* grid, BPoint* offset) const
{
switch (mode) {
case kMiniIconMode:
grid->Set(IconSizeInt() * 6, ceilf(IconSizeInt() * 1.25f));
offset->Set(ceilf(IconSizeInt() * 0.6f), ceilf(IconSizeInt() * 0.3f));
break;
case kIconMode:
{
const float gridOffset = ceilf(IconSizeInt() * 0.875f),
offsetValue = ceilf(IconSizeInt() * 0.625f);
grid->Set(IconSizeInt() + gridOffset, IconSizeInt() + gridOffset);
offset->Set(offsetValue, offsetValue);
break;
}
default:
{
const float labelSpacing = be_control_look->DefaultLabelSpacing();
grid->Set(0, 0);
offset->Set(labelSpacing - 1, labelSpacing - 1);
break;
}
}
}
void
BPoseView::ScrollView(int32 type)
{
if (fVScrollBar == NULL)
return;
float max, min;
fVScrollBar->GetSteps(&min, &max);
switch (type) {
case B_HOME:
fVScrollBar->SetValue(0);
break;
case B_END:
fVScrollBar->SetValue(max);
break;
case B_PAGE_UP:
fVScrollBar->SetValue(fVScrollBar->Value() - max);
break;
case B_PAGE_DOWN:
fVScrollBar->SetValue(fVScrollBar->Value() + max);
break;
}
}
void
BPoseView::MakeFocus(bool focused)
{
bool invalidate = false;
if (focused != IsFocus())
invalidate = true;
_inherited::MakeFocus(focused);
if (invalidate) {
BorderedView* view = dynamic_cast<BorderedView*>(Parent());
if (view != NULL)
view->PoseViewFocused(focused);
}
}
BSize
BPoseView::MinSize()
{
return BSize(0, 0);
}
void
BPoseView::WindowActivated(bool active)
{
if (!active)
CommitActivePose();
ShowSelection(active);
if (active && ActivePose() == NULL && !IsFilePanel())
MakeFocus();
}
void
BPoseView::SetActivePose(BPose* pose)
{
if (pose != ActivePose()) {
CommitActivePose();
fActivePose = pose;
}
}
void
BPoseView::CommitActivePose(bool saveChanges)
{
if (ActivePose() == NULL)
return;
int32 index = CurrentPoseList()->IndexOf(fActivePose);
BPoint poseLoc;
if (ViewMode() == kListMode)
poseLoc = BPoint(0, index * fListElemHeight);
else
poseLoc = fActivePose->Location(this);
fActivePose->Commit(saveChanges, poseLoc, this, index);
BPose* activePose = fActivePose;
fActivePose = NULL;
if (IsFiltering() && !FilterPose(activePose))
RemoveFilteredPose(activePose, index);
}
EntryListBase*
BPoseView::InitDirentIterator(const entry_ref* ref)
{
Model sourceModel(ref, false, true);
if (sourceModel.InitCheck() != B_OK)
return NULL;
ASSERT(!sourceModel.IsQuery());
ASSERT(!sourceModel.IsVirtualDirectory());
ASSERT(sourceModel.Node() != NULL);
BDirectory* directory = dynamic_cast<BDirectory*>(sourceModel.Node());
ASSERT(directory != NULL);
if (directory == NULL) {
HideBarberPole();
return NULL;
}
EntryListBase* entryList = new CachedDirectoryEntryList(*directory);
if (entryList->Rewind() != B_OK) {
delete entryList;
HideBarberPole();
return NULL;
}
TTracker::WatchNode(sourceModel.NodeRef(), B_WATCH_DIRECTORY | B_WATCH_CHILDREN
| B_WATCH_NAME | B_WATCH_STAT | B_WATCH_INTERIM_STAT | B_WATCH_ATTR, this);
return entryList;
}
void
BPoseView::ReturnDirentIterator(EntryListBase* iterator)
{
delete iterator;
}
uint32
BPoseView::WatchNewNodeMask()
{
return 0;
}
status_t
BPoseView::WatchNewNode(const node_ref* item)
{
return WatchNewNode(item, WatchNewNodeMask(), BMessenger(this));
}
status_t
BPoseView::WatchNewNode(const node_ref* item, uint32 mask, BMessenger messenger)
{
if (mask == 0)
return B_OK;
status_t result = TTracker::WatchNode(item, mask, messenger);
#if DEBUG
if (result != B_OK)
PRINT(("failed to watch node %s\n", strerror(result)));
#endif
return result;
}
struct AddPosesParams {
BMessenger target;
entry_ref ref;
};
bool
BPoseView::IsValidAddPosesThread(thread_id currentThread) const
{
return fAddPosesThreads.find(currentThread) != fAddPosesThreads.end();
}
void
BPoseView::AddPoses(Model* model)
{
if (TargetModel()->IsRoot()) {
AddVolumePoses();
return;
} else if (IsVolumesRoot())
AddVolumePoses();
ShowBarberPole();
AddPosesParams* params = new AddPosesParams();
BMessenger tmp(this);
params->target = tmp;
if (model != NULL)
params->ref = *model->EntryRef();
thread_id addPosesThread = spawn_thread(&BPoseView::AddPosesTask,
"add poses", B_DISPLAY_PRIORITY, params);
if (addPosesThread >= B_OK) {
fAddPosesThreads.insert(addPosesThread);
resume_thread(addPosesThread);
} else
delete params;
}
class AutoLockingMessenger {
public:
AutoLockingMessenger(const BMessenger &target, bool lockLater = false)
:
messenger(target),
hasLock(false)
{
if (!lockLater)
hasLock = messenger.LockTarget();
}
~AutoLockingMessenger()
{
if (hasLock) {
BLooper* looper;
messenger.Target(&looper);
ASSERT(looper->IsLocked());
looper->Unlock();
}
}
bool Lock()
{
if (!hasLock)
hasLock = messenger.LockTarget();
return hasLock;
}
bool IsLocked() const
{
return hasLock;
}
void Unlock()
{
if (hasLock) {
BLooper* looper;
messenger.Target(&looper);
ASSERT(looper != NULL);
looper->Unlock();
hasLock = false;
}
}
BLooper* Looper() const
{
BLooper* looper;
messenger.Target(&looper);
return looper;
}
BHandler* Handler() const
{
ASSERT(hasLock);
return messenger.Target(0);
}
BMessenger Target() const
{
return messenger;
}
private:
BMessenger messenger;
bool hasLock;
};
class failToLock { };
status_t
BPoseView::AddPosesTask(void* castToParams)
{
AddPosesParams* params = (AddPosesParams*)castToParams;
BMessenger target(params->target);
entry_ref ref(params->ref);
delete params;
AutoLockingMessenger lock(target);
if (!lock.IsLocked())
return B_ERROR;
thread_id threadID = find_thread(NULL);
BPoseView* view = dynamic_cast<BPoseView*>(lock.Handler());
ThrowOnAssert(view != NULL);
BWindow* window = dynamic_cast<BWindow*>(lock.Looper());
ThrowOnAssert(window != NULL);
EntryListBase* container = view->InitDirentIterator(&ref);
if (container == NULL) {
view->HideBarberPole();
return B_ERROR;
}
AddPosesResult* posesResult = new AddPosesResult;
posesResult->fCount = 0;
int32 modelChunkIndex = -1;
bigtime_t nextChunkTime = 0;
uint32 watchMask = view->WatchNewNodeMask();
bool hideDotFiles = TrackerSettings().HideDotFiles();
#if DEBUG
for (int32 index = 0; index < kMaxAddPosesChunk; index++)
posesResult->fModels[index] = (Model*)0xdeadbeef;
#endif
try {
for (;;) {
lock.Unlock();
status_t result = B_OK;
char entBuf[1024];
dirent* eptr = (dirent*)entBuf;
Model* model = 0;
node_ref dirNode;
node_ref itemNode;
int32 count = container->GetNextDirents(eptr, 1024, 1);
if (count <= 0 && modelChunkIndex == -1)
break;
if (count > 0) {
ASSERT(count == 1);
if ((!hideDotFiles && (!strcmp(eptr->d_name, ".")
|| !strcmp(eptr->d_name, "..")))
|| (hideDotFiles && eptr->d_name[0] == '.')) {
continue;
}
dirNode.device = eptr->d_pdev;
dirNode.node = eptr->d_pino;
itemNode.device = eptr->d_dev;
itemNode.node = eptr->d_ino;
BPoseView::WatchNewNode(&itemNode, watchMask, lock.Target());
model = new Model(&dirNode, &itemNode, eptr->d_name, false);
result = model->InitCheck();
modelChunkIndex++;
posesResult->fModels[modelChunkIndex] = model;
}
if (!lock.Lock()) {
PRINT(("failed to lock\n"));
posesResult->fCount = modelChunkIndex + 1;
throw failToLock();
}
if (!view->IsValidAddPosesThread(threadID)) {
view->HideBarberPole();
view->ReturnDirentIterator(container);
container = NULL;
posesResult->fCount = modelChunkIndex + 1;
throw failToLock();
}
if (count > 0) {
if (result != B_OK) {
PRINT(("1 adding model %s to zombie list, error %s\n",
model->Name(), strerror(model->InitCheck())));
view->fZombieList->AddItem(model);
modelChunkIndex--;
continue;
}
view->ReadPoseInfo(model,
&posesResult->fPoseInfos[modelChunkIndex]);
if (!PoseVisible(model,
&posesResult->fPoseInfos[modelChunkIndex])) {
modelChunkIndex--;
continue;
}
if (model->IsSymLink())
view->CreateSymlinkPoseTarget(model);
}
bigtime_t now = system_time();
if (count <= 0 || modelChunkIndex >= kMaxAddPosesChunk - 1
|| now > nextChunkTime) {
ASSERT(modelChunkIndex >= 0);
posesResult->fCount = modelChunkIndex + 1;
BMessage creationData(kAddNewPoses);
creationData.AddPointer("currentPoses", posesResult);
creationData.AddRef("ref", &ref);
lock.Target().SendMessage(&creationData);
modelChunkIndex = -1;
nextChunkTime = now + 300000;
posesResult = new AddPosesResult;
posesResult->fCount = 0;
snooze(500);
}
if (count <= 0)
break;
}
BMessage finishedSending(kAddPosesCompleted);
lock.Target().SendMessage(&finishedSending);
} catch (failToLock) {
PRINT(("add_poses cleanup \n"));
delete posesResult;
delete container;
return B_ERROR;
}
ASSERT(modelChunkIndex == -1);
delete posesResult;
if (lock.Lock()) {
view->ReturnDirentIterator(container);
view->fAddPosesThreads.erase(threadID);
} else
delete container;
return B_OK;
}
void
BPoseView::AddVolumePoses()
{
if (Window() == NULL)
return;
BVolumeRoster roster;
roster.Rewind();
BVolume volume;
if (TrackerSettings().ShowDisksIcon() && !TargetModel()->IsRoot()) {
BEntry entry("/");
Model model(&entry);
if (model.InitCheck() == B_OK) {
BMessage monitorMsg;
monitorMsg.what = B_NODE_MONITOR;
monitorMsg.AddInt32("opcode", B_ENTRY_CREATED);
monitorMsg.AddInt32("device", model.NodeRef()->device);
monitorMsg.AddInt64("node", model.NodeRef()->node);
monitorMsg.AddInt64("directory", model.EntryRef()->directory);
monitorMsg.AddString("name", model.EntryRef()->name);
Window()->PostMessage(&monitorMsg, this);
}
} else {
while (roster.GetNextVolume(&volume) == B_OK) {
if (!volume.IsPersistent())
continue;
if (volume.IsShared() && !TrackerSettings().MountSharedVolumesOntoDesktop())
continue;
CreateVolumePose(&volume);
}
}
SortPoses();
UpdateCount();
Invalidate();
}
void
BPoseView::RemoveVolumePoses()
{
int32 index;
int32 poseCount = fPoseList->CountItems();
for (index = 0; index < poseCount;) {
BPose* pose = fPoseList->ItemAt(index);
if (pose != NULL) {
Model* model = pose->TargetModel();
if (model != NULL) {
if (model->IsVolume()) {
DeletePose(model->NodeRef());
poseCount--;
} else
index++;
}
}
}
SortPoses();
UpdateCount();
Invalidate();
}
void
BPoseView::ToggleDisksVolumes()
{
if (IsVolumesRoot() && LockLooper()) {
SavePoseLocations();
if (TrackerSettings().MountVolumesOntoDesktop()) {
RemoveRootPose();
AddVolumePoses();
} else {
RemoveVolumePoses();
CreateRootPose();
}
UnlockLooper();
}
}
void
BPoseView::AddTrashPoses()
{
BVolumeRoster volRoster;
volRoster.Rewind();
BVolume volume;
while (volRoster.GetNextVolume(&volume) == B_OK) {
if (!volume.IsPersistent() || volume.Capacity() == 0)
continue;
BDirectory trashDir;
BEntry entry;
if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK
&& trashDir.GetEntry(&entry) == B_OK) {
Model model(&entry);
if (model.InitCheck() == B_OK)
AddPoses(&model);
}
}
}
void
BPoseView::AddPosesCompleted()
{
BContainerWindow* window = ContainerWindow();
if (window != NULL && window->ShouldAddMenus())
window->AddMimeTypesToMenu();
if (ViewMode() != kListMode)
CheckAutoPlacedPoses();
UpdateScrollRange();
HideBarberPole();
if (ViewMode() == kListMode) {
BRect bounds(Bounds());
float lastItemTop = (CurrentPoseList()->CountItems() - 1) * fListElemHeight;
if (bounds.top > lastItemTop)
_inherited::ScrollTo(bounds.left, std::max(lastItemTop, 0.0f));
SortPoses();
Invalidate();
}
}
void
BPoseView::CreateVolumePose(BVolume* volume)
{
if (volume->InitCheck() != B_OK || !volume->IsPersistent()) {
return;
}
BDirectory root;
if (volume->GetRootDirectory(&root) != B_OK)
return;
BEntry entry;
root.GetEntry(&entry);
entry_ref ref;
entry.GetRef(&ref);
BVolume parentVolume(ref.device);
if (parentVolume.InitCheck() == B_OK && parentVolume.IsPersistent())
return;
node_ref itemNode;
root.GetNodeRef(&itemNode);
node_ref dirNode;
dirNode.device = ref.device;
dirNode.node = ref.directory;
BPose* pose = EntryCreated(&dirNode, &itemNode, ref.name, 0);
if (pose != NULL && !TargetModel()->IsRoot()) {
pose->TargetModel()->WatchVolumeAndMountPoint(B_WATCH_NAME
| B_WATCH_STAT | B_WATCH_ATTR, this);
}
}
void
BPoseView::CreateRootPose()
{
BEntry entry("/");
Model* model = new Model(&entry);
if (model == NULL || model->InitCheck() != B_OK) {
delete model;
return;
}
PoseInfo info;
ReadPoseInfo(model, &info);
CreatePose(model, &info, true, NULL, NULL, true);
}
void
BPoseView::RemoveRootPose()
{
BEntry entry("/");
node_ref nref;
if (entry.GetNodeRef(&nref) != B_OK)
return;
DeletePose(&nref);
Invalidate();
}
BPose*
BPoseView::CreatePose(Model* model, PoseInfo* poseInfo, bool insertionSort,
int32* indexPtr, BRect* boundsPointer, bool forceDraw)
{
BPose* result;
CreatePoses(&model, poseInfo, 1, &result, insertionSort, indexPtr,
boundsPointer, forceDraw);
return result;
}
void
BPoseView::FinishPendingScroll(float &listViewScrollBy, BRect srcRect)
{
if (listViewScrollBy == 0.0)
return;
if (srcRect.Width() > listViewScrollBy) {
BRect dstRect = srcRect;
srcRect.bottom -= listViewScrollBy;
dstRect.top += listViewScrollBy;
CopyBits(srcRect, dstRect);
listViewScrollBy = 0;
srcRect.bottom = dstRect.top;
}
SynchronousUpdate(srcRect);
}
bool
BPoseView::AddPosesThreadValid(const entry_ref* ref) const
{
return *(TargetModel()->EntryRef()) == *ref || TargetModel()->IsTrash();
}
void
BPoseView::AddPoseToList(PoseList* list, bool visibleList, bool insertionSort,
BPose* pose, BRect &viewBounds, float &listViewScrollBy, bool forceDraw,
int32* indexPtr)
{
int32 poseIndex = list->CountItems();
BRect poseBounds;
bool havePoseBounds = false;
bool addedItem = false;
bool needToDraw = true;
if (insertionSort && poseIndex > 0) {
int32 orientation = BSearchList(list, pose, &poseIndex, poseIndex);
if (orientation == kInsertAfter)
poseIndex++;
if (visibleList) {
poseBounds = CalcPoseRectList(pose, poseIndex);
havePoseBounds = true;
if (poseBounds.top > viewBounds.bottom) {
needToDraw = false;
} else {
BRect srcRect(Extent());
srcRect.top = poseBounds.top;
srcRect = srcRect & viewBounds;
BRect destRect(srcRect);
destRect.OffsetBy(0, fListElemHeight);
if (destRect.bottom > viewBounds.top
&& destRect.top > destRect.bottom) {
destRect.top = viewBounds.top;
}
if (srcRect.Intersects(viewBounds)
|| destRect.Intersects(viewBounds)) {
if (srcRect.top == viewBounds.top
&& srcRect.bottom >= viewBounds.top
&& poseIndex != 0) {
listViewScrollBy += fListElemHeight;
needToDraw = false;
} else {
FinishPendingScroll(listViewScrollBy, viewBounds);
list->AddItem(pose, poseIndex);
fMimeTypeListIsDirty = true;
addedItem = true;
if (srcRect.IsValid()) {
CopyBits(srcRect, destRect);
srcRect.bottom = destRect.top;
SynchronousUpdate(srcRect);
} else {
SynchronousUpdate(destRect);
}
needToDraw = false;
}
}
}
}
}
if (!addedItem) {
list->AddItem(pose, poseIndex);
fMimeTypeListIsDirty = true;
}
if (visibleList && needToDraw && forceDraw) {
if (!havePoseBounds)
poseBounds = CalcPoseRectList(pose, poseIndex);
if (viewBounds.Intersects(poseBounds))
SynchronousUpdate(poseBounds);
}
if (indexPtr)
*indexPtr = poseIndex;
}
void
BPoseView::CreatePoses(Model** models, PoseInfo* poseInfoArray, int32 count,
BPose** resultingPoses, bool insertionSort, int32* lastPoseIndexPointer,
BRect* boundsPointer, bool forceDraw)
{
BRect viewBounds;
if (boundsPointer != NULL)
viewBounds = *boundsPointer;
else
viewBounds = Bounds();
bool clipboardLocked = be_clipboard->Lock();
int32 poseIndex = 0;
uint32 clipboardMode = 0;
float listViewScrollBy = 0;
for (int32 modelIndex = 0; modelIndex < count; modelIndex++) {
Model* model = models[modelIndex];
if (fInsertedNodes.Contains(*(model->NodeRef())) || FindZombie(model->NodeRef())) {
watch_node(model->NodeRef(), B_STOP_WATCHING, this);
delete model;
if (resultingPoses)
resultingPoses[modelIndex] = NULL;
continue;
} else
fInsertedNodes.Add(*(model->NodeRef()));
if ((clipboardMode = FSClipboardFindNodeMode(model, !clipboardLocked, true)) != 0
&& !HasPosesInClipboard()) {
SetHasPosesInClipboard(true);
}
model->OpenNode();
ASSERT(model->IsNodeOpen());
PoseInfo* poseInfo = &poseInfoArray[modelIndex];
BPose* pose = new BPose(model, this, clipboardMode);
if (resultingPoses)
resultingPoses[modelIndex] = pose;
if (poseInfo->fInitedDirectory != -1LL) {
PinPointToValidRange(poseInfo->fLocation);
pose->SetLocation(poseInfo->fLocation, this);
AddToVSList(pose);
}
BRect poseBounds;
switch (ViewMode()) {
case kListMode:
{
AddPoseToList(fPoseList, !IsFiltering(), insertionSort, pose, viewBounds,
listViewScrollBy, forceDraw, &poseIndex);
if (IsFiltering() && FilterPose(pose)) {
AddPoseToList(fFilteredPoseList, true, insertionSort, pose,
viewBounds, listViewScrollBy, forceDraw, &poseIndex);
}
break;
}
case kIconMode:
case kMiniIconMode:
if (poseInfo->fInitedDirectory == -1LL || fAlwaysAutoPlace) {
if (pose->HasLocation())
RemoveFromVSList(pose);
PlacePose(pose, viewBounds);
if (!fAlwaysAutoPlace)
pose->SetAutoPlaced(true);
AddToVSList(pose);
}
fPoseList->AddItem(pose);
fMimeTypeListIsDirty = true;
poseBounds = pose->CalcRect(this);
if (fEnsurePosesVisible && !viewBounds.Intersects(poseBounds)) {
viewBounds.InsetBy(20, 20);
RemoveFromVSList(pose);
BPoint loc(pose->Location(this));
loc.ConstrainTo(viewBounds);
pose->SetLocation(loc, this);
pose->SetSaveLocation();
AddToVSList(pose);
poseBounds = pose->CalcRect(this);
viewBounds.InsetBy(-20, -20);
}
if (forceDraw && viewBounds.Intersects(poseBounds))
Invalidate(poseBounds);
if (!fExtent.IsValid())
fExtent = poseBounds;
else
AddToExtent(poseBounds);
break;
}
if (model->IsSymLink())
model->ResolveIfLink()->CloseNode();
model->CloseNode();
}
if (clipboardLocked)
be_clipboard->Unlock();
FinishPendingScroll(listViewScrollBy, viewBounds);
if (lastPoseIndexPointer != NULL)
*lastPoseIndexPointer = poseIndex;
}
bool
BPoseView::PoseVisible(const Model* model, const PoseInfo* poseInfo)
{
return !poseInfo->fInvisible;
}
const char*
BPoseView::MimeTypeAt(int32 index)
{
if (fMimeTypeListIsDirty)
RefreshMimeTypeList();
return fMimeTypeList.StringAt(index).String();
}
int32
BPoseView::CountMimeTypes()
{
if (fMimeTypeListIsDirty)
RefreshMimeTypeList();
return fMimeTypeList.CountStrings();
}
void
BPoseView::AddMimeType(const char* mimeType)
{
int32 count = fMimeTypeList.CountStrings();
for (int32 index = 0; index < count; index++) {
if (fMimeTypeList.StringAt(index) == mimeType)
return;
}
fMimeTypeList.Add(mimeType);
}
void
BPoseView::RefreshMimeTypeList()
{
fMimeTypeList.MakeEmpty();
fMimeTypeListIsDirty = false;
for (int32 index = 0;; index++) {
BPose* pose = PoseAtIndex(index);
if (pose == NULL)
break;
Model* targetModel = pose->TargetModel();
if (targetModel != NULL)
AddMimeType(targetModel->MimeType());
}
}
void
BPoseView::InsertPoseAfter(BPose* pose, int32* index, int32 orientation, BRect* invalidRect)
{
if (orientation == kInsertAfter) {
(*index)++;
}
BRect bounds(Bounds());
BRect srcRect(Extent());
srcRect.top = CalcPoseRectList(pose, *index).top;
srcRect = srcRect & bounds;
BRect destRect(srcRect);
destRect.OffsetBy(0, fListElemHeight);
if (srcRect.Intersects(bounds) || destRect.Intersects(bounds))
CopyBits(srcRect, destRect);
srcRect.bottom = destRect.top;
*invalidRect = srcRect;
}
void
BPoseView::DisableScrollBars()
{
if (fHScrollBar != NULL)
fHScrollBar->SetTarget((BView*)NULL);
if (fVScrollBar != NULL)
fVScrollBar->SetTarget((BView*)NULL);
}
void
BPoseView::EnableScrollBars()
{
if (fHScrollBar != NULL)
fHScrollBar->SetTarget(this);
if (fVScrollBar != NULL)
fVScrollBar->SetTarget(this);
}
void
BPoseView::AddScrollBars()
{
fHScrollBar = new TScrollBar("HScrollBar", this, 0, 100);
fVScrollBar = new BScrollBar("VScrollBar", this, 0, 100, B_VERTICAL);
}
void
BPoseView::UpdateCount()
{
if (fCountView != NULL)
fCountView->CheckCount();
}
void
BPoseView::MessageReceived(BMessage* message)
{
if (message->WasDropped() && HandleMessageDropped(message))
return;
if (HandleScriptingMessage(message))
return;
switch (message->what) {
case kAddNewPoses:
{
AddPosesResult* currentPoses;
entry_ref ref;
if (message->FindPointer("currentPoses",
reinterpret_cast<void**>(¤tPoses)) == B_OK
&& message->FindRef("ref", &ref) == B_OK) {
if (AddPosesThreadValid(&ref)) {
CreatePoses(currentPoses->fModels,
currentPoses->fPoseInfos,
currentPoses->fCount, NULL, true, 0, 0, true);
currentPoses->ReleaseModels();
}
delete currentPoses;
}
break;
}
case kAddPosesCompleted:
AddPosesCompleted();
break;
case kRestoreBackgroundImage:
if (ContainerWindow() != NULL)
ContainerWindow()->MessageReceived(message);
break;
case B_META_MIME_CHANGED:
NoticeMetaMimeChanged(message);
break;
case B_NODE_MONITOR:
case B_PATH_MONITOR:
case B_QUERY_UPDATE:
if (!FSNotification(message))
pendingNodeMonitorCache.Add(message);
break;
case kIconMode: {
int32 size = -1;
int32 scale;
if (message->FindInt32("size", &size) == B_OK) {
} else if (message->FindInt32("scale", &scale) == B_OK
&& fViewState->ViewMode() == kIconMode) {
if (scale == 0 && (int32)UnscaledIconSizeInt() != 32) {
switch ((int32)UnscaledIconSizeInt()) {
case 40: size = 32; break;
case 48: size = 40; break;
case 64: size = 48; break;
case 96: size = 64; break;
case 128: size = 96; break;
}
} else if (scale == 1 && (int32)UnscaledIconSizeInt() != 128) {
switch ((int32)UnscaledIconSizeInt()) {
case 32: size = 40; break;
case 40: size = 48; break;
case 48: size = 64; break;
case 64: size = 96; break;
case 96: size = 128; break;
}
}
} else {
int32 iconSize = fViewState->LastIconSize();
if (iconSize < 32 || iconSize > 128) {
iconSize = 32;
}
size = iconSize;
}
if (size <= 0)
break;
if (size != (int32)UnscaledIconSizeInt())
fViewState->SetIconSize(size);
SetViewMode(message->what);
break;
}
case kListMode:
case kMiniIconMode:
SetViewMode(message->what);
break;
case kMsgMouseDragged:
MouseDragged(message);
break;
case kMsgMouseLongDown:
MouseLongDown(message);
break;
case B_MOUSE_IDLE:
MouseIdle(message);
break;
case B_SELECT_ALL:
{
BTextWidget* widget;
if (ActivePose() && ((widget = ActivePose()->ActiveWidget())) != 0)
widget->SelectAll(this);
else
SelectAll();
break;
}
case B_CUT:
{
Model* targetModel = TargetModel();
if (targetModel != NULL) {
FSClipboardAddPoses(targetModel->NodeRef(), fSelectionList,
kMoveSelectionTo, true);
}
break;
}
case kCutMoreSelectionToClipboard:
{
Model* targetModel = TargetModel();
if (targetModel != NULL) {
FSClipboardAddPoses(targetModel->NodeRef(), fSelectionList,
kMoveSelectionTo, false);
}
break;
}
case B_COPY:
{
Model* targetModel = TargetModel();
if (targetModel != NULL) {
FSClipboardAddPoses(targetModel->NodeRef(), fSelectionList,
kCopySelectionTo, true);
}
break;
}
case kCopyMoreSelectionToClipboard:
{
Model* targetModel = TargetModel();
if (targetModel != NULL) {
FSClipboardAddPoses(targetModel->NodeRef(), fSelectionList,
kCopySelectionTo, false);
}
break;
}
case B_PASTE:
FSClipboardPaste(TargetModel());
break;
case kPasteLinksFromClipboard:
FSClipboardPaste(TargetModel(), kCreateLink);
break;
case B_CANCEL:
if (FSClipboardHasRefs())
FSClipboardClear();
if (IsTypeAheadFiltering())
StopTypeAheadFiltering();
break;
case kCancelSelectionToClipboard:
{
Model* targetModel = TargetModel();
if (targetModel != NULL) {
FSClipboardRemovePoses(targetModel->NodeRef(),
fSelectionList != NULL && CountSelected() > 0
? fSelectionList : fPoseList);
}
break;
}
case kFSClipboardChanges:
{
node_ref node;
message->FindInt32("device", &node.device);
message->FindInt64("directory", &node.node);
Model* targetModel = TargetModel();
if (targetModel != NULL && *targetModel->NodeRef() == node)
UpdatePosesClipboardModeFromClipboard(message);
else if (message->FindBool("clearClipboard")
&& HasPosesInClipboard()) {
SetHasPosesInClipboard(false);
SetPosesClipboardMode(0);
}
break;
}
case kInvertSelection:
InvertSelection();
break;
case kShowSelectionWindow:
ShowSelectionWindow();
break;
case kDuplicateSelection:
DuplicateSelection();
break;
case kOpenSelection:
OpenSelection();
break;
case kOpenSelectionWith:
OpenSelectionUsing();
break;
case kRestoreSelectionFromTrash:
RestoreSelectionFromTrash();
break;
case kDeleteSelection:
DoDelete();
break;
case kMoveSelectionToTrash:
{
BView* view = Window()->CurrentFocus();
if (dynamic_cast<BTextView*>(view) != NULL) {
char bytes[1];
bytes[0] = B_DELETE;
view->KeyDown(bytes, 1);
} else {
DoMoveToTrash();
}
break;
}
case kCleanupAll:
Cleanup(true);
break;
case kCleanup:
Cleanup();
break;
case kEditQuery:
EditQueries();
break;
case kRunAutomounterSettings:
be_app->PostMessage(message);
break;
case kNewEntryFromTemplate:
if (message->HasRef("refs_template"))
NewFileFromTemplate(message);
break;
case kNewFolder:
NewFolder(message);
break;
case kUnmountVolume:
UnmountSelectedVolumes();
break;
case kEmptyTrash:
FSEmptyTrash();
break;
case kGetInfo:
OpenInfoWindows();
break;
case kIdentifyEntry:
IdentifySelection(message->GetBool("force", false));
break;
case kEditName:
{
if (ActivePose() != NULL)
break;
BPose* pose = fSelectionList->FirstItem();
if (pose == NULL)
break;
BPoint poseLoc;
if (ViewMode() == kListMode)
poseLoc = BPoint(0, CurrentPoseList()->IndexOf(pose) * fListElemHeight);
else
poseLoc = pose->Location(this);
pose->EditFirstWidget(poseLoc, this);
break;
}
case kOpenParentDir:
OpenParent();
break;
case kCopyAttributes:
if (be_clipboard->Lock()) {
be_clipboard->Clear();
BMessage* data = be_clipboard->Data();
if (data != NULL) {
BMessage state;
SaveState(state);
BMallocIO stream;
ssize_t size;
if (state.Flatten(&stream, &size) == B_OK) {
data->AddData("application/tracker-columns",
B_MIME_TYPE, stream.Buffer(), size);
be_clipboard->Commit();
}
}
be_clipboard->Unlock();
}
break;
case kPasteAttributes:
if (be_clipboard->Lock()) {
BMessage* data = be_clipboard->Data();
if (data != NULL) {
const void* buffer;
ssize_t size;
if (data->FindData("application/tracker-columns",
B_MIME_TYPE, &buffer, &size) == B_OK) {
BMessage state;
if (state.Unflatten((const char*)buffer) == B_OK) {
BColumn* old;
while ((old = ColumnAt(0)) != NULL) {
if (!RemoveColumn(old, false))
break;
}
for (int32 index = 0; ; index++) {
BColumn* column
= BColumn::InstantiateFromMessage(state,
index);
if (column == NULL)
break;
AddColumn(column);
}
RemoveColumn(old, false);
BViewState* viewState
= BViewState::InstantiateFromMessage(state);
if (viewState != NULL) {
SetPrimarySort(viewState->PrimarySort());
SetSecondarySort(viewState->SecondarySort());
SetReverseSort(viewState->ReverseSort());
SortPoses();
Invalidate();
}
}
}
}
be_clipboard->Unlock();
}
break;
case kArrangeBy:
{
uint32 attrHash;
if (message->FindInt32("attr_hash", (int32*)&attrHash) == B_OK) {
if (ColumnFor(attrHash) == NULL)
HandleAttrMenuItemSelected(message);
if (PrimarySort() == attrHash)
attrHash = 0;
SetPrimarySort(attrHash);
SetSecondarySort(0);
Cleanup(true);
}
break;
}
case kArrangeReverseOrder:
SetReverseSort(!fViewState->ReverseSort());
Cleanup(true);
break;
case kAttributeItem:
HandleAttrMenuItemSelected(message);
break;
case kAddPrinter:
be_app->PostMessage(message);
break;
case kMakeActivePrinter:
SetDefaultPrinter();
break;
#if DEBUG
case kTestIconCache:
RunIconCacheTests();
break;
case 'dbug':
{
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++)
fSelectionList->ItemAt(index)->PrintToStream();
break;
}
#ifdef CHECK_OPEN_MODEL_LEAKS
case 'dpfl':
DumpOpenModels(false);
break;
case 'dpfL':
DumpOpenModels(true);
break;
#endif
#endif
case kCheckTypeahead:
{
bigtime_t doubleClickSpeed;
get_click_speed(&doubleClickSpeed);
if (system_time() - fLastKeyTime > (doubleClickSpeed * 2)) {
fCountView->SetTypeAhead("");
delete fKeyRunner;
fKeyRunner = NULL;
}
break;
}
case B_OBSERVER_NOTICE_CHANGE:
{
int32 observerWhat;
if (message->FindInt32("be:observe_change_what", &observerWhat)
== B_OK) {
switch (observerWhat) {
case kDateFormatChanged:
UpdateDateColumns(message);
break;
case kVolumesOnDesktopChanged:
AdaptToVolumeChange(message);
break;
case kDesktopIntegrationChanged:
AdaptToDesktopIntegrationChange(message);
break;
case kShowSelectionWhenInactiveChanged:
{
bool showSelection;
if (message->FindBool("ShowSelectionWhenInactive",
&showSelection) == B_OK) {
fShowSelectionWhenInactive = showSelection;
TrackerSettings().SetShowSelectionWhenInactive(
fShowSelectionWhenInactive);
}
Invalidate();
break;
}
case kTransparentSelectionChanged:
{
bool transparentSelection;
if (message->FindBool("TransparentSelection",
&transparentSelection) == B_OK) {
fTransparentSelection = transparentSelection;
TrackerSettings().SetTransparentSelection(fTransparentSelection);
}
break;
}
case kSortFolderNamesFirstChanged:
if (ViewMode() == kListMode) {
TrackerSettings settings;
bool sortFolderNamesFirst;
if (message->FindBool("SortFolderNamesFirst",
&sortFolderNamesFirst) == B_OK) {
settings.SetSortFolderNamesFirst(sortFolderNamesFirst);
}
NameAttributeText::SetSortFolderNamesFirst(
settings.SortFolderNamesFirst());
RealNameAttributeText::SetSortFolderNamesFirst(
settings.SortFolderNamesFirst());
SortPoses();
Invalidate();
}
break;
case kHideDotFilesChanged:
{
TrackerSettings settings;
bool hideDotFiles;
if (message->FindBool("HideDotFiles", &hideDotFiles) == B_OK)
settings.SetHideDotFiles(hideDotFiles);
Refresh();
break;
}
case kTypeAheadFilteringChanged:
{
TrackerSettings settings;
bool typeAheadFilter;
if (message->FindBool("TypeAheadFiltering", &typeAheadFilter) == B_OK) {
settings.SetTypeAheadFiltering(typeAheadFilter);
if (IsTypeAheadFiltering() && !typeAheadFilter)
StopTypeAheadFiltering();
}
break;
}
}
}
break;
}
default:
_inherited::MessageReceived(message);
break;
}
}
bool
BPoseView::RemoveColumn(BColumn* columnToRemove, bool runAlert)
{
if (CountColumns() == 1) {
if (runAlert) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You must have at least one attribute showing."),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
}
return false;
}
int32 columnIndex = IndexOfColumn(columnToRemove);
float offset = columnToRemove->Offset();
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++)
fPoseList->ItemAt(index)->RemoveWidget(this, columnToRemove);
fColumnList->RemoveItem(columnToRemove, false);
fTitleView->RemoveTitle(columnToRemove);
float attrWidth = columnToRemove->Width();
delete columnToRemove;
int32 count = CountColumns();
for (int32 index = columnIndex; index < count; index++) {
BColumn* column = ColumnAt(index);
column->SetOffset(column->Offset()
- (attrWidth + kTitleColumnExtraMargin));
}
BRect rect(Bounds());
rect.left = offset;
Invalidate(rect);
ContainerWindow()->MarkAttributesMenu();
if (IsWatchingDateFormatChange()) {
int32 columnCount = CountColumns();
bool anyDateAttributesLeft = false;
for (int32 i = 0; i < columnCount; i++) {
BColumn* column = ColumnAt(i);
if (column->AttrType() == B_TIME_TYPE)
anyDateAttributesLeft = true;
if (anyDateAttributesLeft)
break;
}
if (!anyDateAttributesLeft)
StopWatchDateFormatChange();
}
fStateNeedsSaving = true;
if (IsFiltering()) {
int32 poseCount = fFilteredPoseList->CountItems();
for (int32 index = poseCount - 1; index >= 0; index--) {
BPose* pose = fFilteredPoseList->ItemAt(index);
if (!FilterPose(pose))
RemoveFilteredPose(pose, index);
}
}
return true;
}
bool
BPoseView::AddColumn(BColumn* newColumn, const BColumn* after)
{
if (after == NULL && CountColumns() > 0)
after = LastColumn();
float offset;
int32 afterColumnIndex;
if (after != NULL) {
offset = after->Offset() + after->Width() + kTitleColumnExtraMargin;
afterColumnIndex = IndexOfColumn(after);
} else {
offset = StartOffset();
afterColumnIndex = CountColumns() - 1;
}
fColumnList->AddItem(newColumn, afterColumnIndex + 1);
if (fTitleView != NULL)
fTitleView->AddTitle(newColumn);
BRect rect(Bounds());
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
int32 startIndex = (int32)(rect.top / fListElemHeight);
BPoint loc(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose->WidgetFor(newColumn->AttrHash()) == NULL)
pose->AddWidget(this, newColumn);
loc.y += fListElemHeight;
if (loc.y > rect.bottom)
break;
}
newColumn->SetOffset(offset);
float attrWidth = newColumn->Width();
int32 count = CountColumns();
for (int32 index = afterColumnIndex + 2; index < count; index++) {
BColumn* column = ColumnAt(index);
ASSERT(newColumn != column);
column->SetOffset(column->Offset() + attrWidth + kTitleColumnExtraMargin);
}
rect.left = offset;
Invalidate(rect);
ContainerWindow()->MarkAttributesMenu();
if (!IsWatchingDateFormatChange() && newColumn->AttrType() == B_TIME_TYPE)
StartWatchDateFormatChange();
fStateNeedsSaving = true;
if (IsFiltering()) {
RebuildFilteringPoseList();
}
return true;
}
void
BPoseView::HandleAttrMenuItemSelected(BMessage* message)
{
BMenuItem* item;
if (message->FindPointer("source", (void**)&item) != B_OK)
item = NULL;
uint32 attrHash;
if (message->FindInt32("attr_hash", (int32*)&attrHash) != B_OK)
return;
BColumn* column = ColumnFor(attrHash);
if (column != NULL) {
RemoveColumn(column, true);
return;
} else {
const char* attrName;
if (message->FindString("attr_name", &attrName) != B_OK)
return;
uint32 attrType;
if (message->FindInt32("attr_type", (int32*)&attrType) != B_OK)
return;
float attrWidth;
if (message->FindFloat("attr_width", &attrWidth) != B_OK)
return;
alignment attrAlign;
if (message->FindInt32("attr_align", (int32*)&attrAlign) != B_OK)
return;
bool isEditable;
if (message->FindBool("attr_editable", &isEditable) != B_OK)
return;
bool isStatfield;
if (message->FindBool("attr_statfield", &isStatfield) != B_OK)
return;
const char* displayAs;
message->FindString("attr_display_as", &displayAs);
column = new BColumn(item->Label(), attrWidth, attrAlign,
attrName, attrType, displayAs, isStatfield, isEditable);
AddColumn(column);
if (item->Menu()->Supermenu() == NULL)
delete item->Menu();
}
}
const int32 kSanePoseLocation = 50000;
void
BPoseView::ReadPoseInfo(Model* model, PoseInfo* poseInfo)
{
BModelOpener opener(model);
if (model->Node() == NULL)
return;
ReadAttrResult result = kReadAttrFailed;
BEntry entry;
model->GetEntry(&entry);
if (model->IsRoot() || (model->IsTrash() && IsDesktopView())) {
BDirectory desktopDir;
if (FSGetDeskDir(&desktopDir) == B_OK) {
const char* poseInfoAttr = model->IsTrash()
? kAttrTrashPoseInfo : kAttrDisksPoseInfo;
const char* poseInfoAttrForeign = model->IsTrash()
? kAttrTrashPoseInfoForeign : kAttrDisksPoseInfoForeign;
result = ReadAttr(&desktopDir, poseInfoAttr, poseInfoAttrForeign,
B_RAW_TYPE, 0, poseInfo, sizeof(*poseInfo), &PoseInfo::EndianSwap);
}
} else {
ASSERT(model->IsNodeOpen());
time_t now = time(NULL);
for (int32 count = 10; count >= 0; count--) {
if (model->Node() == NULL)
break;
result = ReadAttr(model->Node(), kAttrPoseInfo, kAttrPoseInfoForeign,
B_RAW_TYPE, 0, poseInfo, sizeof(*poseInfo), &PoseInfo::EndianSwap);
if (result != kReadAttrFailed) {
break;
}
if (ViewMode() == kListMode)
break;
const StatStruct* stat = model->StatBuf();
if (stat->st_crtime < now - 5 || stat->st_crtime > now)
break;
snooze(10000);
}
}
if (result == kReadAttrFailed) {
poseInfo->fInitedDirectory = -1LL;
poseInfo->fInvisible = false;
} else if (TargetModel() == NULL
|| (poseInfo->fInitedDirectory != model->EntryRef()->directory
&& (poseInfo->fInitedDirectory != TargetModel()->NodeRef()->node))) {
poseInfo->fInitedDirectory = -1LL;
} else if (poseInfo->fLocation.x < -kSanePoseLocation
|| poseInfo->fLocation.x > kSanePoseLocation
|| poseInfo->fLocation.y < -kSanePoseLocation
|| poseInfo->fLocation.y > kSanePoseLocation) {
poseInfo->fInitedDirectory = -1LL;
}
}
ExtendedPoseInfo*
BPoseView::ReadExtendedPoseInfo(Model* model)
{
BModelOpener opener(model);
if (model->Node() == NULL)
return NULL;
ReadAttrResult result = kReadAttrFailed;
const char* extendedPoseInfoAttrName;
const char* extendedPoseInfoAttrForeignName;
if (model->IsRoot()) {
BDirectory dir;
if (FSGetDeskDir(&dir) == B_OK) {
extendedPoseInfoAttrName = kAttrExtendedDisksPoseInfo;
extendedPoseInfoAttrForeignName = kAttrExtendedDisksPoseInfoForeign;
} else
return NULL;
} else {
extendedPoseInfoAttrName = kAttrExtendedPoseInfo;
extendedPoseInfoAttrForeignName = kAttrExtendedPoseInfoForeign;
}
type_code type;
size_t size;
result = GetAttrInfo(model->Node(), extendedPoseInfoAttrName,
extendedPoseInfoAttrForeignName, &type, &size);
if (result == kReadAttrFailed)
return NULL;
char* buffer = new char[ExtendedPoseInfo::SizeWithHeadroom(size)];
ExtendedPoseInfo* poseInfo = reinterpret_cast<ExtendedPoseInfo*>(buffer);
result = ReadAttr(model->Node(), extendedPoseInfoAttrName,
extendedPoseInfoAttrForeignName,
B_RAW_TYPE, 0, buffer, size, &ExtendedPoseInfo::EndianSwap);
if (result == kReadAttrFailed
|| size > poseInfo->SizeWithHeadroom()
|| size < poseInfo->Size()) {
delete[] buffer;
return NULL;
}
return poseInfo;
}
void
BPoseView::SetViewMode(uint32 newMode)
{
uint32 oldMode = ViewMode();
uint32 lastIconSize = fViewState->LastIconSize();
if (newMode == oldMode) {
if (newMode != kIconMode || lastIconSize == fViewState->IconSize())
return;
}
ASSERT(!IsFilePanel());
uint32 lastIconMode = fViewState->LastIconMode();
if (newMode != kListMode) {
fViewState->SetLastIconMode(newMode);
if (oldMode == kIconMode)
fViewState->SetLastIconSize(fViewState->IconSize());
}
fViewState->SetViewMode(newMode);
BPoint scaleOffset(0, 0);
bool iconSizeChanged = newMode == kIconMode && oldMode == kIconMode;
if (!IsDesktopView() && iconSizeChanged) {
BRect bounds(Bounds());
BPoint center(bounds.LeftTop());
center.x += bounds.Width() / 2.0;
center.y += bounds.Height() / 2.0;
float oldScale = lastIconSize / 32.0;
BPoint unscaledCenter(center.x / oldScale, center.y / oldScale);
float newScale = fViewState->IconSize() / 32.0f;
BPoint newCenter(unscaledCenter.x * newScale,
unscaledCenter.y * newScale);
scaleOffset = newCenter - center;
}
BContainerWindow* window = ContainerWindow();
if (oldMode == kListMode) {
if (IsTypeAheadFiltering())
ClearTypeAheadFiltering();
if (window != NULL)
window->HideAttributesMenu();
fTitleView->Hide();
} else if (newMode == kListMode) {
if (window != NULL)
window->ShowAttributesMenu();
fTitleView->Show();
}
CommitActivePose();
SetIconPoseHeight();
GetLayoutInfo(newMode, &fGrid, &fOffset);
bool mapIcons = false;
if (fOkToMapIcons) {
mapIcons = (newMode != kListMode) && (newMode != lastIconMode
|| fViewState->IconSize() != lastIconSize);
}
bool checkLocations = IsDesktopView() && iconSizeChanged;
BPoint oldOffset;
BPoint oldGrid;
if (mapIcons)
GetLayoutInfo(lastIconMode, &oldGrid, &oldOffset);
BRect bounds(Bounds());
PoseList newPoseList(30);
if (newMode != kListMode) {
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
if (pose->HasLocation() == false) {
newPoseList.AddItem(pose);
} else if (checkLocations && !IsValidLocation(pose)) {
RemoveFromVSList(pose);
newPoseList.AddItem(pose);
} else if (iconSizeChanged) {
pose->SetSaveLocation();
} else if (mapIcons) {
MapToNewIconMode(pose, oldGrid, oldOffset);
}
}
}
Invalidate();
BPoint newOrigin;
if (newMode == kListMode)
newOrigin = fViewState->ListOrigin();
else
newOrigin = fViewState->IconOrigin() + scaleOffset;
PinPointToValidRange(newOrigin);
DisableScrollBars();
ScrollTo(newOrigin);
ResetPosePlacementHint();
int32 poseCount = newPoseList.CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = newPoseList.ItemAt(index);
PlacePose(pose, bounds);
AddToVSList(pose);
}
SortPoses();
if (newMode != kListMode)
RecalcExtent();
UpdateScrollRange();
SetScrollBarsTo(newOrigin);
EnableScrollBars();
ContainerWindow()->ViewModeChanged(oldMode, newMode);
}
void
BPoseView::MapToNewIconMode(BPose* pose, BPoint oldGrid, BPoint oldOffset)
{
BPoint delta;
BPoint poseLoc;
poseLoc = PinToGrid(pose->Location(this), oldGrid, oldOffset);
delta = pose->Location(this) - poseLoc;
poseLoc -= oldOffset;
if (poseLoc.x >= 0)
poseLoc.x = floorf(poseLoc.x / oldGrid.x) * fGrid.x;
else
poseLoc.x = ceilf(poseLoc.x / oldGrid.x) * fGrid.x;
if (poseLoc.y >= 0)
poseLoc.y = floorf(poseLoc.y / oldGrid.y) * fGrid.y;
else
poseLoc.y = ceilf(poseLoc.y / oldGrid.y) * fGrid.y;
if ((delta.x != 0) || (delta.y != 0)) {
if (delta.x >= 0)
delta.x = fGrid.x * floorf(delta.x / oldGrid.x);
else
delta.x = fGrid.x * ceilf(delta.x / oldGrid.x);
if (delta.y >= 0)
delta.y = fGrid.y * floorf(delta.y / oldGrid.y);
else
delta.y = fGrid.y * ceilf(delta.y / oldGrid.y);
poseLoc += delta;
}
poseLoc += fOffset;
pose->SetLocation(poseLoc, this);
pose->SetSaveLocation();
}
void
BPoseView::SetPosesClipboardMode(uint32 clipboardMode)
{
if (ViewMode() == kListMode) {
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
BPoint loc(0,0);
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose->ClipboardMode() != clipboardMode) {
pose->SetClipboardMode(clipboardMode);
Invalidate(pose->CalcRect(loc, this, false));
}
loc.y += fListElemHeight;
}
} else {
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
if (pose->ClipboardMode() != clipboardMode) {
pose->SetClipboardMode(clipboardMode);
BRect poseRect(pose->CalcRect(this));
Invalidate(poseRect);
}
}
}
}
void
BPoseView::UpdatePosesClipboardModeFromClipboard(BMessage* clipboardReport)
{
CommitActivePose();
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
bool fullInvalidateNeeded = false;
node_ref node;
clipboardReport->FindInt32("device", &node.device);
clipboardReport->FindInt64("directory", &node.node);
bool clearClipboard = clipboardReport->GetBool("clearClipboard", false);
if (clearClipboard && fHasPosesInClipboard) {
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++)
fPoseList->ItemAt(index)->SetClipboardMode(0);
SetHasPosesInClipboard(false);
fullInvalidateNeeded = true;
fHasPosesInClipboard = false;
}
BRect bounds(Bounds());
BPoint loc(0, 0);
bool hasPosesInClipboard = false;
int32 foundNodeIndex = 0;
TClipboardNodeRef* clipNode = NULL;
ssize_t size;
for (int32 index = 0; clipboardReport->FindData("tcnode", T_CLIPBOARD_NODE,
index, (const void**)&clipNode, &size) == B_OK; index++) {
BPose* pose = fPoseList->FindPose(&clipNode->node, &foundNodeIndex);
if (pose == NULL)
break;
if (clipNode->moveMode != pose->ClipboardMode() || pose->IsSelected()) {
pose->SetClipboardMode(clipNode->moveMode);
if (!fullInvalidateNeeded) {
if (ViewMode() == kListMode) {
if (IsFiltering())
pose = fFilteredPoseList->FindPose(&clipNode->node, &foundNodeIndex);
if (pose != NULL) {
loc.y = foundNodeIndex * fListElemHeight;
if (loc.y <= bounds.bottom && loc.y >= bounds.top)
Invalidate(pose->CalcRect(loc, this, false));
}
} else {
BRect poseRect(pose->CalcRect(this));
if (bounds.Contains(poseRect.LeftTop())
|| bounds.Contains(poseRect.LeftBottom())
|| bounds.Contains(poseRect.RightBottom())
|| bounds.Contains(poseRect.RightTop())) {
Invalidate(poseRect);
}
}
}
if (clipNode->moveMode)
hasPosesInClipboard = true;
}
}
SetHasPosesInClipboard(hasPosesInClipboard || fHasPosesInClipboard);
if (fullInvalidateNeeded)
Invalidate();
}
void
BPoseView::PlaceFolder(const entry_ref* ref, const BMessage* message)
{
BNode node(ref);
BPoint location;
bool setPosition = false;
if (message->FindPoint("be:invoke_origin", &location) == B_OK) {
setPosition = true;
location = ConvertFromScreen(location);
} else if (ViewMode() != kListMode) {
uint32 buttons;
GetMouse(&location, &buttons);
BPoint globalLocation(location);
ConvertToScreen(&globalLocation);
if (Window()->Frame().Contains(globalLocation))
setPosition = true;
}
if (setPosition) {
Model* targetModel = TargetModel();
if (targetModel != NULL && targetModel->NodeRef() != NULL) {
FSSetPoseLocation(targetModel->NodeRef()->node, &node,
location);
}
}
}
void
BPoseView::NewFileFromTemplate(const BMessage* message)
{
Model* targetModel = TargetModel();
ThrowOnAssert(targetModel != NULL);
entry_ref destEntryRef;
node_ref destNodeRef;
BDirectory destDir(targetModel->NodeRef());
if (destDir.InitCheck() != B_OK)
return;
char fileName[B_FILE_NAME_LENGTH] = "New ";
strlcat(fileName, message->FindString("name"), sizeof(fileName));
FSMakeOriginalName(fileName, &destDir, " copy");
entry_ref srcRef;
message->FindRef("refs_template", &srcRef);
BDirectory dir(&srcRef);
if (dir.InitCheck() == B_OK) {
if (FSCreateNewFolderIn(targetModel->NodeRef(), &destEntryRef,
&destNodeRef) == B_OK) {
BEntry destEntry(&destEntryRef);
destEntry.Rename(fileName);
}
} else {
BFile srcFile(&srcRef, B_READ_ONLY);
BFile destFile(&destDir, fileName, B_READ_WRITE | B_CREATE_FILE);
char* buffer = new char[1024];
ssize_t result;
do {
result = srcFile.Read(buffer, 1024);
if (result > 0) {
ssize_t written = destFile.Write(buffer, (size_t)result);
if (written != result)
result = written < B_OK ? written : B_ERROR;
}
} while (result > 0);
delete[] buffer;
}
BNode srcNode(&srcRef);
BNode destNode(&destDir, fileName);
FSCopyAttributesAndStats(&srcNode, &destNode, false);
BEntry entry(&destDir, fileName);
entry.GetRef(&destEntryRef);
PlaceFolder(&destEntryRef, message);
int32 index;
BPose* pose = EntryCreated(targetModel->NodeRef(), &destNodeRef, destEntryRef.name, &index);
if (pose != NULL) {
WatchNewNode(pose->TargetModel()->NodeRef());
UpdateScrollRange();
CommitActivePose();
SelectPose(pose, index);
BPoint poseLoc;
if (ViewMode() == kListMode)
poseLoc = BPoint(0, index * fListElemHeight);
else
poseLoc = pose->Location(this);
pose->EditFirstWidget(poseLoc, this);
}
}
void
BPoseView::NewFolder(const BMessage* message)
{
Model* targetModel = TargetModel();
ThrowOnAssert(targetModel != NULL);
entry_ref ref;
node_ref nodeRef;
if (FSCreateNewFolderIn(targetModel->NodeRef(), &ref, &nodeRef) == B_OK) {
PlaceFolder(&ref, message);
int32 index;
BPose* pose = EntryCreated(targetModel->NodeRef(), &nodeRef, ref.name, &index);
if (IsFiltering()) {
if (fFilteredPoseList->FindPose(&nodeRef, &index) == NULL) {
float scrollBy = 0;
BRect bounds = Bounds();
AddPoseToList(fFilteredPoseList, true, true, pose, bounds, scrollBy, true, &index);
}
}
if (pose != NULL) {
UpdateScrollRange();
CommitActivePose();
SelectPose(pose, index);
BPoint poseLoc;
if (ViewMode() == kListMode)
poseLoc = BPoint(0, index * fListElemHeight);
else
poseLoc = pose->Location(this);
pose->EditFirstWidget(poseLoc, this);
}
}
}
void
BPoseView::Cleanup(bool doAll)
{
if (ViewMode() == kListMode)
return;
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return;
if (doAll) {
SortPoses();
DisableScrollBars();
ClearExtent();
ClearSelection();
ScrollTo(B_ORIGIN);
UpdateScrollRange();
SetScrollBarsTo(B_ORIGIN);
ResetPosePlacementHint();
BRect viewBounds(Bounds());
fVSPoseList->MakeEmpty();
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
PlacePose(pose, viewBounds);
AddToVSList(pose);
}
RecalcExtent();
UpdateScrollRange();
EnableScrollBars();
if (HScrollBar()) {
float min;
float max;
HScrollBar()->GetRange(&min, &max);
HScrollBar()->SetValue(min);
}
UpdateScrollRange();
Invalidate(viewBounds);
} else {
BRect viewBounds(Bounds());
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
BPoint location(pose->Location(this));
BPoint newLocation(PinToGrid(location, fGrid, fOffset));
bool intersectsDesktopElements = !IsValidLocation(pose);
if (newLocation != location || intersectsDesktopElements) {
RemoveFromVSList(pose);
BRect oldBounds(pose->CalcRect(this));
BRect poseBounds(oldBounds);
pose->MoveTo(newLocation, this);
if (SlotOccupied(oldBounds, viewBounds) || intersectsDesktopElements) {
ResetPosePlacementHint();
PlacePose(pose, viewBounds);
poseBounds = pose->CalcRect(this);
}
AddToVSList(pose);
AddToExtent(poseBounds);
if (viewBounds.Intersects(poseBounds))
Invalidate(poseBounds);
if (viewBounds.Intersects(oldBounds))
Invalidate(oldBounds);
}
}
}
}
void
BPoseView::PlacePose(BPose* pose, BRect &viewBounds)
{
pose->SetLocation(fHintLocation, this);
BRect rect(pose->CalcRect(this));
BPoint deltaFromBounds(fHintLocation - rect.LeftTop());
rect.InsetBy(-3, 0);
bool checkValidLocation = IsDesktopView();
while (SlotOccupied(rect, viewBounds)
|| (checkValidLocation && !IsValidLocation(rect))) {
NextSlot(pose, rect, viewBounds);
if (checkValidLocation && !rect.Intersects(viewBounds)) {
fHintLocation = PinToGrid(BPoint(0.0, 0.0), fGrid, fOffset);
pose->SetLocation(fHintLocation, this);
rect = pose->CalcRect(this);
break;
}
}
rect.InsetBy(3, 0);
fHintLocation = pose->Location(this) + BPoint(fGrid.x, 0);
pose->SetLocation(rect.LeftTop() + deltaFromBounds, this);
pose->SetSaveLocation();
}
bool
BPoseView::IsValidLocation(const BPose* pose)
{
if (!IsDesktopView())
return true;
BRect rect(pose->CalcRect(this));
rect.InsetBy(-3, 0);
return IsValidLocation(rect);
}
bool
BPoseView::IsValidLocation(const BRect& rect)
{
if (!IsDesktopView())
return true;
if (!Bounds().Contains(rect))
return false;
BRect deskbarFrame;
if (GetDeskbarFrame(&deskbarFrame) == B_OK) {
deskbarFrame.InsetBy(-10, -10);
if (deskbarFrame.Intersects(rect))
return false;
}
for (int32 i = 0; BView* child = ChildAt(i); i++) {
BRect childFrame = child->Frame();
childFrame.InsetBy(-5, -5);
if (childFrame.Intersects(rect))
return false;
}
return true;
}
status_t
BPoseView::GetDeskbarFrame(BRect* frame)
{
status_t result = B_OK;
bigtime_t now = system_time();
if (fLastDeskbarFrameCheckTime + 500000 < now) {
result = get_deskbar_frame(&fDeskbarFrame);
fLastDeskbarFrameCheckTime = now;
}
*frame = fDeskbarFrame;
return result;
}
void
BPoseView::CheckAutoPlacedPoses()
{
if (ViewMode() == kListMode)
return;
BRect viewBounds(Bounds());
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
if (pose->WasAutoPlaced()) {
RemoveFromVSList(pose);
fHintLocation = pose->Location(this);
BRect oldBounds(pose->CalcRect(this));
PlacePose(pose, viewBounds);
BRect newBounds(pose->CalcRect(this));
AddToVSList(pose);
pose->SetAutoPlaced(false);
AddToExtent(newBounds);
Invalidate(oldBounds);
Invalidate(newBounds);
}
}
}
void
BPoseView::CheckPoseVisibility(BRect* newFrame)
{
bool desktop = IsDesktopView() && newFrame != 0;
BRect deskFrame;
if (desktop) {
ASSERT(newFrame != NULL);
deskFrame = *newFrame;
}
ASSERT(ViewMode() != kListMode);
BRect bounds(Bounds());
bounds.InsetBy(20, 20);
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
BPoint newLocation(pose->Location(this));
bool locationNeedsUpdating = false;
if (desktop) {
Model* model = pose->TargetModel();
ExtendedPoseInfo* info = ReadExtendedPoseInfo(model);
if (info && info->HasLocationForFrame(deskFrame)) {
BPoint locationForFrame = info->LocationForFrame(deskFrame);
if (locationForFrame != newLocation) {
newLocation = locationForFrame;
locationNeedsUpdating = true;
Invalidate(pose->CalcRect(this));
RemoveFromVSList(pose);
pose->SetLocation(newLocation, this);
}
}
delete[] (char*)info;
}
BRect rect(pose->CalcRect(this));
if (!rect.Intersects(bounds)) {
if (!locationNeedsUpdating) {
Invalidate(rect);
RemoveFromVSList(pose);
}
BPoint loc(pose->Location(this));
loc.ConstrainTo(bounds);
pose->SetLocation(loc, this);
locationNeedsUpdating = true;
}
if (locationNeedsUpdating) {
pose->SetSaveLocation();
AddToVSList(pose);
Invalidate(pose->CalcRect(this));
}
}
}
bool
BPoseView::SlotOccupied(BRect poseRect, BRect viewBounds) const
{
if (fVSPoseList->IsEmpty())
return false;
if (poseRect.right >= viewBounds.right) {
BPoint point(viewBounds.left + fOffset.x, 0);
point = PinToGrid(point, fGrid, fOffset);
if (fHintLocation.x != point.x)
return true;
}
int32 index = FirstIndexAtOrBelow((int32)(poseRect.top - IconPoseHeight()));
int32 numPoses = fVSPoseList->CountItems();
while (index < numPoses && fVSPoseList->ItemAt(index)->Location(this).y
< poseRect.bottom) {
BRect rect(fVSPoseList->ItemAt(index)->CalcRect(this));
if (poseRect.Intersects(rect))
return true;
index++;
}
return false;
}
void
BPoseView::NextSlot(BPose* pose, BRect &poseRect, BRect viewBounds)
{
poseRect.OffsetBy(fGrid.x, 0);
if (poseRect.right > viewBounds.right) {
fHintLocation.y += fGrid.y;
fHintLocation.x = viewBounds.left + fOffset.x;
fHintLocation = PinToGrid(fHintLocation, fGrid, fOffset);
pose->SetLocation(fHintLocation, this);
poseRect = pose->CalcRect(this);
poseRect.InsetBy(-3, 0);
}
}
int32
BPoseView::FirstIndexAtOrBelow(int32 y, bool constrainIndex) const
{
int32 index = 0;
int32 l = 0;
int32 r = fVSPoseList->CountItems() - 1;
while (l <= r) {
index = (l + r) >> 1;
int32 result
= (int32)(y - fVSPoseList->ItemAt(index)->Location(this).y);
if (result < 0)
r = index - 1;
else if (result > 0)
l = index + 1;
else {
while (index > 0
&& y == fVSPoseList->ItemAt(index - 1)->Location(this).y)
index--;
return index;
}
}
while (index < fVSPoseList->CountItems()
&& fVSPoseList->ItemAt(index)->Location(this).y <= y) {
index++;
}
if (constrainIndex && index >= fVSPoseList->CountItems())
index = fVSPoseList->CountItems() - 1;
return index;
}
void
BPoseView::AddToVSList(BPose* pose)
{
int32 index = FirstIndexAtOrBelow((int32)pose->Location(this).y, false);
fVSPoseList->AddItem(pose, index);
}
int32
BPoseView::RemoveFromVSList(const BPose* pose)
{
int32 index = 0;
int32 poseCount = fVSPoseList->CountItems();
for (; index < poseCount; index++) {
BPose* matchingPose = fVSPoseList->ItemAt(index);
ASSERT(matchingPose);
if (!matchingPose)
return -1;
if (pose == matchingPose) {
fVSPoseList->RemoveItemAt(index);
return index;
}
}
return -1;
}
BPoint
BPoseView::PinToGrid(BPoint point, BPoint grid, BPoint offset) const
{
if (grid.x == 0 || grid.y == 0)
return point;
point -= offset;
BPoint gridLoc(point);
if (point.x >= 0)
gridLoc.x = floorf((point.x / grid.x) + 0.5f) * grid.x;
else
gridLoc.x = ceilf((point.x / grid.x) - 0.5f) * grid.x;
if (point.y >= 0)
gridLoc.y = floorf((point.y / grid.y) + 0.5f) * grid.y;
else
gridLoc.y = ceilf((point.y / grid.y) - 0.5f) * grid.y;
gridLoc += offset;
return gridLoc;
}
void
BPoseView::ResetPosePlacementHint()
{
fHintLocation = PinToGrid(BPoint(LeftTop().x + fOffset.x,
LeftTop().y + fOffset.y), fGrid, fOffset);
}
void
BPoseView::SelectPoses(int32 start, int32 end)
{
fSelectionList->MakeEmpty();
fMimeTypesInSelectionCache.MakeEmpty();
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
bool iconMode = ViewMode() != kListMode;
BPoint loc(0, start * fListElemHeight);
BRect bounds(Bounds());
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
for (int32 index = start; index < end && index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
fSelectionList->AddItem(pose);
if (index == start)
fSelectionPivotPose = pose;
if (!pose->IsSelected()) {
pose->Select(true);
BRect poseRect;
if (iconMode)
poseRect = pose->CalcRect(this);
else
poseRect = pose->CalcRect(loc, this, false);
if (bounds.Intersects(poseRect)) {
Invalidate(poseRect);
}
}
loc.y += fListElemHeight;
}
}
void
BPoseView::MoveOrChangePoseSelection(int32 to)
{
PoseList* poseList = CurrentPoseList();
BPose* first = fSelectionList->FirstItem();
if (first != NULL && fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0) {
BPose* target = poseList->ItemAt(to);
BPose* last = fSelectionList->LastItem();
int32 firstIndex = poseList->IndexOf(first);
int32 lastIndex = poseList->IndexOf(last);
int32 from = to < firstIndex ? firstIndex : lastIndex;
int32 step = from < to ? 1 : -1;
bool select = true;
for (int32 index = from; step > 0 ? index <= to : index >= to;
index += step) {
BPose* pose = poseList->ItemAt(index);
if (pose != NULL && pose->IsSelected() != select)
AddRemovePoseFromSelection(pose, index, select);
}
if (target != NULL)
ScrollIntoView(target, to);
} else {
SelectPose(poseList->ItemAt(to), to);
}
}
void
BPoseView::ScrollIntoView(BPose* pose, int32 index)
{
ScrollIntoView(CalcPoseRect(pose, index, true));
}
void
BPoseView::ScrollIntoView(BRect poseRect)
{
if (IsDesktopView())
return;
BPoint oldPos = Bounds().LeftTop(), newPos = oldPos;
if (ViewMode() != kListMode) {
if (poseRect.left < Bounds().left
|| poseRect.Width() > Bounds().Width())
newPos.x += poseRect.left - Bounds().left;
else if (poseRect.right > Bounds().right)
newPos.x += poseRect.right - Bounds().right;
}
if (poseRect.top < Bounds().top
|| poseRect.Height() > Bounds().Height())
newPos.y += poseRect.top - Bounds().top;
else if (poseRect.bottom > Bounds().bottom)
newPos.y += poseRect.bottom - Bounds().bottom;
if (newPos != oldPos)
SetScrollBarsTo(newPos);
}
void
BPoseView::SelectPose(BPose* pose, int32 index, bool scrollIntoView)
{
if (pose == NULL || CountSelected() > 1 || !pose->IsSelected())
ClearSelection();
AddPoseToSelection(pose, index, scrollIntoView);
if (pose != NULL)
fSelectionPivotPose = pose;
}
void
BPoseView::AddPoseToSelection(BPose* pose, int32 index, bool scrollIntoView)
{
if (pose != NULL && !pose->IsSelected()) {
pose->Select(true);
fSelectionList->AddItem(pose);
BRect poseRect = CalcPoseRect(pose, index);
Invalidate(poseRect);
if (scrollIntoView)
ScrollIntoView(poseRect);
if (fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
}
}
void
BPoseView::RemovePoseFromSelection(BPose* pose)
{
if (fSelectionPivotPose == pose)
fSelectionPivotPose = NULL;
if (fRealPivotPose == pose)
fRealPivotPose = NULL;
if (!fSelectionList->RemoveItem(pose)) {
return;
}
pose->Select(false);
if (ViewMode() == kListMode) {
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
BPoint loc(0, 0);
for (int32 index = 0; index < poseCount; index++) {
if (pose == poseList->ItemAt(index)) {
Invalidate(pose->CalcRect(loc, this));
break;
}
loc.y += fListElemHeight;
}
} else
Invalidate(pose->CalcRect(this));
if (fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
}
bool
BPoseView::EachItemInDraggedSelection(const BMessage* message,
bool (*func)(BPose*, BPoseView*, void*), BPoseView* poseView,
void* passThru)
{
BContainerWindow* srcWindow;
if (message->FindPointer("src_window", (void**)&srcWindow) != B_OK)
return false;
AutoLock<BWindow> lock(srcWindow);
if (!lock)
return false;
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
int32 selectCount = selectionList->CountItems();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = selectionList->ItemAt(index);
if (func(pose, poseView, passThru))
return true;
}
return false;
}
bool
BPoseView::FindDragNDropAction(const BMessage* dragMessage, bool &canCopy,
bool &canMove, bool &canLink, bool &canErase)
{
canCopy = false;
canMove = false;
canErase = false;
canLink = false;
if (!dragMessage->HasInt32("be:actions"))
return false;
int32 action;
for (int32 index = 0;
dragMessage->FindInt32("be:actions", index, &action) == B_OK;
index++) {
switch (action) {
case B_MOVE_TARGET:
canMove = true;
break;
case B_COPY_TARGET:
canCopy = true;
break;
case B_TRASH_TARGET:
canErase = true;
break;
case B_LINK_TARGET:
canLink = true;
break;
}
}
return canCopy || canMove || canErase || canLink;
}
bool
BPoseView::CanTrashForeignDrag(const Model* targetModel)
{
return targetModel->IsTrash();
}
bool
BPoseView::CanCopyOrMoveForeignDrag(const Model* targetModel,
const BMessage* dragMessage)
{
if (!targetModel->IsDirectory() && !targetModel->IsVirtualDirectory())
return false;
for (int32 index = 0; ; index++) {
const char* type;
if (dragMessage->FindString("be:types", index, &type) != B_OK)
break;
if (strcasecmp(type, B_FILE_MIME_TYPE) == 0)
return true;
}
return false;
}
bool
BPoseView::CanHandleDragSelection(const Model* target,
const BMessage* dragMessage, bool ignoreTypes)
{
if (ignoreTypes)
return target->IsDropTarget();
ASSERT(dragMessage != NULL);
BContainerWindow* srcWindow;
status_t result = dragMessage->FindPointer("src_window", (void**)&srcWindow);
if (result != B_OK || srcWindow == NULL) {
bool canCopy;
bool canMove;
bool canErase;
bool canLink;
FindDragNDropAction(dragMessage, canCopy, canMove, canLink, canErase);
if (canErase && CanTrashForeignDrag(target))
return true;
if (canCopy || canMove) {
if (CanCopyOrMoveForeignDrag(target, dragMessage))
return true;
}
if (dragMessage->HasRef("refs")
&& (target->IsDirectory() || target->IsVirtualDirectory())) {
return true;
}
if (dragMessage->HasData(kPlainTextMimeType, B_MIME_TYPE)
&& target->IsDirectory()) {
return true;
}
if (target->IsDirectory()
&& (dragMessage->HasData(kBitmapMimeType, B_MESSAGE_TYPE)
|| dragMessage->HasData(kLargeIconType, B_MESSAGE_TYPE)
|| dragMessage->HasData(kMiniIconType, B_MESSAGE_TYPE))) {
return true;
}
return false;
}
ASSERT(srcWindow != NULL);
AutoLock<BWindow> lock(srcWindow);
if (!lock)
return false;
BStringList* mimeTypeList = srcWindow->PoseView()->MimeTypesInSelection();
if (mimeTypeList->IsEmpty()) {
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
if (!selectionList->IsEmpty()) {
int32 selectCount = selectionList->CountItems();
for (int32 index = 0; index < selectCount; index++) {
BEntry entry(selectionList->ItemAt(
index)->TargetModel()->EntryRef(), true);
if (entry.InitCheck() != B_OK)
continue;
BFile file(&entry, O_RDONLY);
BNodeInfo mime(&file);
if (mime.InitCheck() != B_OK)
continue;
char mimeType[B_MIME_TYPE_LENGTH];
mime.GetType(mimeType);
if (!mimeTypeList->HasString(mimeType))
mimeTypeList->Add(mimeType);
}
}
}
return target->IsDropTargetForList(mimeTypeList);
}
void
BPoseView::TrySettingPoseLocation(BNode* node, BPoint point)
{
if (ViewMode() == kListMode)
return;
if ((modifiers() & B_COMMAND_KEY) != 0) {
point = PinToGrid(point, fGrid, fOffset);
}
Model* targetModel = TargetModel();
ASSERT(targetModel != NULL);
if (targetModel != NULL && targetModel->NodeRef() != NULL
&& FSSetPoseLocation(targetModel->NodeRef()->node, node, point) == B_OK) {
node->RemoveAttr(kAttrPoseInfoForeign);
}
}
status_t
BPoseView::CreateClippingFile(BPoseView* poseView, BFile &result,
char* resultingName, BDirectory* directory, BMessage* message,
const char* fallbackName, bool setLocation, BPoint dropPoint)
{
const char* suggestedName;
if (message && message->FindString("be:clip_name", &suggestedName) == B_OK)
strncpy(resultingName, suggestedName, B_FILE_NAME_LENGTH - 1);
else
strcpy(resultingName, fallbackName);
FSMakeOriginalName(resultingName, directory, "");
status_t error = directory->CreateFile(resultingName, &result, true);
if (error != B_OK)
return error;
if (setLocation && poseView != NULL)
poseView->TrySettingPoseLocation(&result, dropPoint);
return B_OK;
}
static int32
RunMimeTypeDestinationMenu(const char* actionText, const BStringList* types,
const BStringList* specificItems, BPoint where)
{
int32 count;
if (types != NULL)
count = types->CountStrings();
else
count = specificItems->CountStrings();
if (count == 0)
return 0;
BPopUpMenu* menu = new BPopUpMenu("create clipping");
for (int32 index = 0; index < count; index++) {
const char* embedTypeAs = NULL;
char buffer[256];
if (types != NULL) {
BMimeType mimeType(embedTypeAs);
if (mimeType.GetShortDescription(buffer) == B_OK)
embedTypeAs = buffer;
}
BString description;
if (specificItems->StringAt(index).Length()) {
description << specificItems->StringAt(index);
if (embedTypeAs)
description << " (" << embedTypeAs << ")";
} else if (types)
description = embedTypeAs;
BString labelText;
if (actionText) {
int32 length = 1024 - 1 - (int32)strlen(actionText);
if (length > 0) {
description.Truncate(length);
labelText.SetTo(actionText);
labelText.ReplaceFirst("%s", description.String());
} else
labelText.SetTo(B_TRANSLATE("label too long"));
} else
labelText = description;
menu->AddItem(new BMenuItem(labelText.String(), 0));
}
menu->AddSeparatorItem();
menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"), 0));
int32 result = -1;
BMenuItem* resultingItem = menu->Go(where, false, true);
if (resultingItem) {
int32 index = menu->IndexOf(resultingItem);
if (index < count)
result = index;
}
delete menu;
return result;
}
bool
BPoseView::HandleMessageDropped(BMessage* message)
{
ASSERT(message->WasDropped());
SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
fCursorCheck = false;
if (!fDropEnabled)
return false;
BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window());
if (window != NULL && message->HasData("RGBColor", 'RGBC')) {
BMessenger((BHandler*)window).SendMessage(message);
return true;
}
if (fDropTarget && !DragSelectionContains(fDropTarget, message))
HiliteDropTarget(false);
fDropTarget = NULL;
ASSERT(TargetModel() != NULL);
BPoint offset;
BPoint dropPoint(message->DropPoint(&offset));
ConvertFromScreen(&dropPoint);
int32 index;
BPose* targetPose = FindPose(dropPoint, &index);
Model tmpTarget;
Model* targetModel = NULL;
if (targetPose != NULL) {
targetModel = targetPose->TargetModel();
if (targetModel->IsSymLink()
&& tmpTarget.SetTo(targetPose->TargetModel()->EntryRef(),
true, true) == B_OK) {
targetModel = &tmpTarget;
}
}
return HandleDropCommon(message, targetModel, targetPose, this, dropPoint);
}
bool
BPoseView::HandleDropCommon(BMessage* message, Model* targetModel,
BPose* targetPose, BView* view, BPoint dropPoint)
{
uint32 buttons = (uint32)message->FindInt32("buttons");
BContainerWindow* window = NULL;
BPoseView* poseView = dynamic_cast<BPoseView*>(view);
if (poseView != NULL)
window = poseView->ContainerWindow();
BContainerWindow* srcWindow = NULL;
status_t result = message->FindPointer("src_window", (void**)&srcWindow);
if (result != B_OK || srcWindow == NULL) {
if (targetModel == NULL && poseView != NULL)
targetModel = poseView->TargetModel();
BDirectory targetDirectory;
if (targetModel != NULL && targetModel->IsDirectory())
targetDirectory.SetTo(targetModel->EntryRef());
if (targetModel != NULL && targetModel->IsRoot()) {
return false;
}
bool canCopy;
bool canMove;
bool canErase;
bool canLink;
if (FindDragNDropAction(message, canCopy, canMove, canLink, canErase)) {
if (canErase && CanTrashForeignDrag(targetModel)) {
BMessage reply(B_TRASH_TARGET);
message->SendReply(&reply);
return true;
}
if ((canCopy || canMove)
&& CanCopyOrMoveForeignDrag(targetModel, message)) {
BStringList actionSpecifiers(10);
for (int32 index = 0; ; index++) {
const char* string;
if (message->FindString("be:actionspecifier", index,
&string) != B_OK) {
break;
}
ASSERT(string != NULL);
actionSpecifiers.Add(string);
}
BStringList types(10);
BStringList typeNames(10);
for (int32 index = 0; ; index++) {
const char* string;
if (message->FindString("be:filetypes", index, &string)
!= B_OK) {
break;
}
ASSERT(string != NULL);
types.Add(string);
const char* typeName = "";
message->FindString("be:type_descriptions", index,
&typeName);
typeNames.Add(typeName);
}
int32 specificTypeIndex = -1;
int32 specificActionIndex = -1;
if (canCopy
&& SecondaryMouseButtonDown(modifiers(), buttons)) {
if (actionSpecifiers.CountStrings() > 0) {
specificActionIndex = RunMimeTypeDestinationMenu(NULL,
NULL, &actionSpecifiers,
view->ConvertToScreen(dropPoint));
if (specificActionIndex == -1)
return false;
} else if (types.CountStrings() > 0) {
specificTypeIndex = RunMimeTypeDestinationMenu(
B_TRANSLATE("Create %s clipping"),
&types, &typeNames,
view->ConvertToScreen(dropPoint));
if (specificTypeIndex == -1)
return false;
}
}
char name[B_FILE_NAME_LENGTH];
BFile file;
if (CreateClippingFile(poseView, file, name, &targetDirectory,
message, B_TRANSLATE("Untitled clipping"),
targetPose == NULL, dropPoint) != B_OK) {
return false;
}
BMessage reply(canCopy ? B_COPY_TARGET : B_MOVE_TARGET);
reply.AddString("be:types", B_FILE_MIME_TYPE);
if (specificTypeIndex != -1) {
reply.AddString("be:filetypes",
types.StringAt(specificTypeIndex).String());
if (typeNames.StringAt(specificTypeIndex).Length()) {
reply.AddString("be:type_descriptions",
typeNames.StringAt(specificTypeIndex).String());
}
}
if (specificActionIndex != -1) {
reply.AddString("be:actionspecifier",
actionSpecifiers.StringAt(specificActionIndex).String());
}
reply.AddRef("directory", targetModel->EntryRef());
reply.AddString("name", name);
BMessage data;
if (message->FindMessage("be:originator-data", &data) == B_OK)
reply.AddMessage("be:originator-data", &data);
for (int32 index = 0; ; index++) {
const char* type;
if (message->FindString("be:filetypes", index, &type)
!= B_OK) {
break;
}
reply.AddString("be:filetypes", type);
}
message->SendReply(&reply);
return true;
}
}
if (message->HasRef("refs")) {
if (!targetModel->IsDirectory()
&& !targetModel->IsVirtualDirectory()) {
return false;
}
bool canRelativeLink = false;
if (!canCopy && !canMove && !canLink && window) {
if (SecondaryMouseButtonDown(modifiers(), buttons)) {
switch (window->ShowDropContextMenu(dropPoint,
srcWindow != NULL ? srcWindow->PoseView() : NULL)) {
case kCreateRelativeLink:
canRelativeLink = true;
break;
case kCreateLink:
canLink = true;
break;
case kMoveSelectionTo:
canMove = true;
break;
case kCopySelectionTo:
canCopy = true;
break;
case kCancelButton:
default:
return true;
}
} else
canCopy = true;
}
uint32 moveMode;
if (canCopy)
moveMode = kCopySelectionTo;
else if (canMove)
moveMode = kMoveSelectionTo;
else if (canLink)
moveMode = kCreateLink;
else if (canRelativeLink)
moveMode = kCreateRelativeLink;
else {
TRESPASS();
return true;
}
BObjectList<entry_ref, true>* entryList
= new BObjectList<entry_ref, true>(10);
for (int32 index = 0; ; index++) {
entry_ref ref;
if (message->FindRef("refs", index, &ref) != B_OK)
break;
entryList->AddItem(new entry_ref(ref));
}
int32 count = entryList->CountItems();
if (count != 0) {
BList* pointList = 0;
if (poseView != NULL && targetPose != NULL) {
pointList = new BList(count);
for (int32 index = 0; count; index++) {
for (int32 j = 0; count && j < 4; j++, count--) {
BPoint point(
dropPoint + BPoint(j * poseView->fGrid.x,
index * poseView->fGrid.y));
pointList->AddItem(
new BPoint(poseView->PinToGrid(point,
poseView->fGrid, poseView->fOffset)));
}
}
}
FSMoveToFolder(entryList, new BEntry(targetModel->EntryRef()),
moveMode, pointList);
return true;
}
delete entryList;
return true;
}
if (message->HasData(kPlainTextMimeType, B_MIME_TYPE)) {
if (!targetModel->IsDirectory()) {
return false;
}
ssize_t textLength;
const char* text;
if (message->FindData(kPlainTextMimeType, B_MIME_TYPE,
(const void**)&text, &textLength) != B_OK) {
return false;
}
char name[B_FILE_NAME_LENGTH];
BFile file;
if (CreateClippingFile(poseView, file, name, &targetDirectory,
message, B_TRANSLATE("Untitled clipping"), !targetPose,
dropPoint) != B_OK) {
return false;
}
if (file.Seek(0, SEEK_SET) == B_ERROR
|| file.Write(text, (size_t)textLength) < 0
|| file.SetSize(textLength) != B_OK) {
file.Unset();
BEntry entry(&targetDirectory, name);
entry.Remove();
PRINT(("error writing text into file %s\n", name));
}
const text_run_array* textRuns = NULL;
ssize_t dataSize = 0;
if (message->FindData("application/x-vnd.Be-text_run_array",
B_MIME_TYPE, (const void**)&textRuns, &dataSize) == B_OK
&& textRuns && dataSize) {
int32 tmpSize = dataSize;
void* data = BTextView::FlattenRunArray(textRuns, &tmpSize);
file.WriteAttr("styles", B_RAW_TYPE, 0, data, (size_t)tmpSize);
free(data);
}
int32 tmp;
file.WriteAttr(kAttrClippingFile, B_RAW_TYPE, 0, &tmp,
sizeof(int32));
BNodeInfo info(&file);
info.SetType(kPlainTextMimeType);
return true;
}
if (message->HasData(kBitmapMimeType, B_MESSAGE_TYPE)
|| message->HasData(kLargeIconType, B_MESSAGE_TYPE)
|| message->HasData(kMiniIconType, B_MESSAGE_TYPE)) {
if (!targetModel->IsDirectory()) {
return false;
}
BMessage embeddedBitmap;
if (message->FindMessage(kBitmapMimeType, &embeddedBitmap)
!= B_OK
&& message->FindMessage(kLargeIconType, &embeddedBitmap)
!= B_OK
&& message->FindMessage(kMiniIconType, &embeddedBitmap)
!= B_OK) {
return false;
}
char name[B_FILE_NAME_LENGTH];
BFile file;
if (CreateClippingFile(poseView, file, name, &targetDirectory,
message, B_TRANSLATE("Untitled bitmap"), targetPose == NULL,
dropPoint) != B_OK) {
return false;
}
int32 size = embeddedBitmap.FlattenedSize();
if (size > 1024 * 1024) {
return false;
}
char* buffer = new char [size];
embeddedBitmap.Flatten(buffer, size);
if (file.Seek(0, SEEK_SET) == B_ERROR
|| file.Write(buffer, (size_t)size) < 0
|| file.SetSize(size) != B_OK) {
file.Unset();
BEntry entry(&targetDirectory, name);
entry.Remove();
PRINT(("error writing bitmap into file %s\n", name));
}
int32 tmp;
file.WriteAttr(kAttrClippingFile, B_RAW_TYPE, 0, &tmp,
sizeof(int32));
BNodeInfo info(&file);
info.SetType(kBitmapMimeType);
return true;
}
return false;
}
ASSERT(srcWindow != NULL);
if (srcWindow == window) {
window->Activate();
window->UpdateIfNeeded();
poseView->ResetPosePlacementHint();
if (DragSelectionContains(targetPose, message)) {
targetModel = NULL;
}
}
bool wasHandled = false;
bool ignoreTypes = (modifiers() & B_CONTROL_KEY) != 0;
if (targetModel != NULL && window != NULL) {
if (targetModel->IsDirectory() || targetModel->IsVirtualDirectory()) {
MoveSelectionInto(targetModel, srcWindow, window, buttons, dropPoint, false);
wasHandled = true;
} else if (CanHandleDragSelection(targetModel, message, ignoreTypes)) {
LaunchAppWithSelection(targetModel, message, !ignoreTypes);
wasHandled = true;
}
}
if (poseView != NULL && !wasHandled) {
BPoint where = message->FindPoint("click_pt");
poseView->MoveSelectionTo(dropPoint, where, srcWindow);
}
if (poseView != NULL && poseView->fEnsurePosesVisible)
poseView->CheckPoseVisibility();
return true;
}
struct LaunchParams {
Model* app;
bool checkTypes;
BMessage* refsMessage;
};
static bool
AddOneToLaunchMessage(BPose* pose, BPoseView*, void* castToParams)
{
LaunchParams* params = (LaunchParams*)castToParams;
ThrowOnAssert(params != NULL);
ThrowOnAssert(pose != NULL);
ThrowOnAssert(pose->TargetModel() != NULL);
if (params->app->IsDropTarget(params->checkTypes
? pose->TargetModel() : NULL, true)) {
params->refsMessage->AddRef("refs", pose->TargetModel()->EntryRef());
}
return false;
}
void
BPoseView::LaunchAppWithSelection(Model* appModel, const BMessage* dragMessage,
bool checkTypes)
{
BMessage refs(B_REFS_RECEIVED);
LaunchParams params;
params.app = appModel;
params.checkTypes = checkTypes;
params.refsMessage = &refs;
BContainerWindow* srcWindow;
if (dragMessage->FindPointer("src_window", (void**)&srcWindow) == B_OK
&& srcWindow != NULL) {
params.refsMessage->AddMessenger("TrackerViewToken",
BMessenger(srcWindow->PoseView()));
}
EachItemInDraggedSelection(dragMessage, AddOneToLaunchMessage, 0, ¶ms);
if (params.refsMessage->HasRef("refs"))
TrackerLaunch(appModel->EntryRef(), params.refsMessage, true);
}
bool
BPoseView::DragSelectionContains(const BPose* target,
const BMessage* dragMessage)
{
return EachItemInDraggedSelection(dragMessage, OneMatches, 0,
(void*)target);
}
void
BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
bool forceCopy, bool forceMove, bool createLink, bool relativeLink)
{
uint32 buttons;
BPoint loc;
GetMouse(&loc, &buttons);
MoveSelectionInto(destFolder, srcWindow,
dynamic_cast<BContainerWindow*>(Window()), buttons, loc, forceCopy,
forceMove, createLink, relativeLink);
}
void
BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
BContainerWindow* destWindow, uint32 buttons, BPoint loc, bool forceCopy,
bool forceMove, bool createLink, bool relativeLink, BPoint where, bool dropOnGrid)
{
AutoLock<BWindow> lock(srcWindow);
if (!lock)
return;
ASSERT(srcWindow->PoseView()->TargetModel() != NULL);
if (srcWindow->PoseView()->CountSelected() == 0)
return;
bool createRelativeLink = relativeLink;
if (SecondaryMouseButtonDown(modifiers(), buttons)
&& destWindow != NULL) {
switch (destWindow->ShowDropContextMenu(loc,
srcWindow != NULL ? srcWindow->PoseView() : NULL)) {
case kCreateRelativeLink:
createRelativeLink = true;
break;
case kCreateLink:
createLink = true;
break;
case kMoveSelectionTo:
forceMove = true;
break;
case kCopySelectionTo:
forceCopy = true;
break;
case kCancelButton:
default:
return;
}
}
if (*srcWindow->PoseView()->TargetModel()->NodeRef() == *destFolder->NodeRef()) {
BPoseView* targetView = srcWindow->PoseView();
if (forceCopy) {
targetView->DuplicateSelection(&where, &loc);
return;
}
if (createLink || createRelativeLink) {
return;
}
if (targetView->ViewMode() == kListMode) {
return;
}
BPoint delta = loc - where;
int32 selectCount = targetView->CountSelected();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = targetView->SelectionList()->ItemAt(index);
targetView->RemoveFromVSList(pose);
BPoint location(pose->Location(targetView) + delta);
BRect oldBounds(pose->CalcRect(targetView));
if (dropOnGrid) {
location = targetView->PinToGrid(location, targetView->fGrid,
targetView->fOffset);
}
pose->MoveTo(location, targetView);
targetView->RemoveFromExtent(oldBounds);
targetView->AddToExtent(pose->CalcRect(targetView));
targetView->AddToVSList(pose);
}
return;
}
BEntry* destEntry = new BEntry(destFolder->EntryRef());
bool destIsTrash = destFolder->IsTrash();
forceCopy = forceCopy || (modifiers() & B_OPTION_KEY) != 0;
bool okToMove = true;
if (destFolder->IsRoot()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You must drop items on one of the disk icons "
"in the \"Disks\" window."), B_TRANSLATE("Cancel"), NULL, NULL,
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
okToMove = false;
}
BVolume destVolume(destFolder->NodeRef()->device);
if (destVolume.InitCheck() == B_OK && destVolume.IsReadOnly()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("You can't move or copy items to read-only volumes."),
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
okToMove = false;
}
if (forceCopy && destIsTrash) {
BAlert* alert = new BAlert("",
B_TRANSLATE("Sorry, you can't copy items to the Trash."),
B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
okToMove = false;
}
if (createLink && destIsTrash) {
BAlert* alert = new BAlert("",
B_TRANSLATE("Sorry, you can't create links in the Trash."),
B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
okToMove = false;
}
if (okToMove) {
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
BList* pointList = destWindow->PoseView()->GetDropPointList(where, loc, selectionList,
srcWindow->PoseView()->ViewMode() == kListMode, dropOnGrid);
int32 selectionSize = selectionList->CountItems();
BObjectList<entry_ref, true>* srcList = new BObjectList<entry_ref, true>(selectionSize);
if (srcWindow->TargetModel()->IsVirtualDirectory()) {
for (int32 i = 0; i < selectionSize; i++) {
Model* model = selectionList->ItemAt(i)->ResolvedModel();
if (model != NULL)
srcList->AddItem(new entry_ref(*(model->EntryRef())));
}
if (!(forceCopy || forceMove || createRelativeLink))
createLink = true;
} else
CopySelectionListToEntryRefList(selectionList, srcList);
uint32 moveMode;
if (forceCopy)
moveMode = kCopySelectionTo;
else if (forceMove)
moveMode = kMoveSelectionTo;
else if (createRelativeLink)
moveMode = kCreateRelativeLink;
else if (createLink)
moveMode = kCreateLink;
else if (!CheckDevicesEqual(srcList->ItemAt(0), destFolder))
moveMode = kCopySelectionTo;
else
moveMode = kMoveSelectionTo;
FSMoveToFolder(srcList, destEntry, moveMode, pointList);
return;
}
delete destEntry;
}
void
BPoseView::MoveSelectionTo(BPoint dropPoint, BPoint where, BContainerWindow* srcWindow)
{
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return;
ASSERT(window->PoseView() != NULL);
ASSERT(TargetModel() != NULL);
if (srcWindow != window && !TargetModel()->IsDropTarget())
return;
uint32 buttons = (uint32)window->CurrentMessage()->FindInt32("buttons");
bool pinToGrid = (modifiers() & B_COMMAND_KEY) != 0;
MoveSelectionInto(TargetModel(), srcWindow, window, buttons, dropPoint,
false, false, false, false, where, pinToGrid);
}
inline void
UpdateWasBrokenSymlinkBinder(BPose* pose, Model* model, int32 index,
BPoseView* poseView, BObjectList<Model>* fBrokenLinks)
{
if (!model->IsSymLink())
return;
BPoint loc(0, index * poseView->ListElemHeight());
pose->UpdateWasBrokenSymlink(loc, poseView);
if (model->LinkTo() != NULL)
fBrokenLinks->RemoveItem(model);
}
void
BPoseView::TryUpdatingBrokenLinks()
{
AutoLock<BWindow> lock(Window());
if (!lock)
return;
BObjectList<Model>* brokenLinksCopy = new BObjectList<Model>(*fBrokenLinks);
EachPoseAndModel(fPoseList, &UpdateWasBrokenSymlinkBinder, this,
fBrokenLinks);
for (int i = brokenLinksCopy->CountItems() - 1; i >= 0; i--) {
if (!fBrokenLinks->HasItem(brokenLinksCopy->ItemAt(i)))
StopWatchingParentsOf(brokenLinksCopy->ItemAt(i)->EntryRef());
}
delete brokenLinksCopy;
}
void
BPoseView::PoseHandleDeviceUnmounted(BPose* pose, Model* model, int32 index,
BPoseView* poseView, dev_t device)
{
if (model->NodeRef()->device == device)
poseView->DeletePose(model->NodeRef());
else if (model->IsSymLink() && model->LinkTo() != NULL
&& model->LinkTo()->NodeRef()->device == device) {
poseView->DeleteSymLinkPoseTarget(model->LinkTo()->NodeRef(),
pose, index);
}
}
static void
OneMetaMimeChanged(BPose* pose, Model* model, int32 index, BPoseView* poseView, const char* type)
{
ASSERT(model != NULL);
if (model->IconFrom() != kNode
&& model->IconFrom() != kUnknownSource
&& model->IconFrom() != kUnknownNotFromNode
&& strcasecmp(model->MimeType(), type) == 0) {
BPoint poseLoc(0, index * poseView->ListElemHeight());
pose->UpdateIcon(poseLoc, poseView);
}
}
void
BPoseView::MetaMimeChanged(const char* type, const char* preferredApp)
{
IconCache::sIconCache->IconChanged(type, preferredApp);
snooze(10000);
Window()->UpdateIfNeeded();
EachPoseAndResolvedModel(fPoseList, &OneMetaMimeChanged, this, type);
}
class MetaMimeChangedAccumulator : public AccumulatingFunctionObject {
public:
MetaMimeChangedAccumulator(void (BPoseView::*func)(const char* type,
const char* preferredApp),
BContainerWindow* window, const char* type, const char* preferredApp)
:
fCallOnThis(window),
fFunc(func),
fType(type),
fPreferredApp(preferredApp)
{
}
virtual bool CanAccumulate(const AccumulatingFunctionObject* functor) const
{
const MetaMimeChangedAccumulator* accumulator
= dynamic_cast<const MetaMimeChangedAccumulator*>(functor);
if (accumulator == NULL)
return false;
return accumulator && accumulator->fType == fType
&& accumulator->fPreferredApp == fPreferredApp;
}
virtual void Accumulate(AccumulatingFunctionObject* DEBUG_ONLY(functor))
{
ASSERT(CanAccumulate(functor));
}
protected:
virtual void operator()()
{
AutoLock<BWindow> lock(fCallOnThis);
if (!lock)
return;
(fCallOnThis->PoseView()->*fFunc)(fType.String(),
fPreferredApp.String());
}
virtual ulong Size() const
{
return sizeof (*this);
}
private:
BContainerWindow* fCallOnThis;
void (BPoseView::*fFunc)(const char* type, const char* preferredApp);
BString fType;
BString fPreferredApp;
};
bool
BPoseView::NoticeMetaMimeChanged(const BMessage* message)
{
int32 change;
if (message->FindInt32("be:which", &change) != B_OK)
return true;
bool iconChanged = (change & B_ICON_CHANGED) != 0;
bool iconForTypeChanged = (change & B_ICON_FOR_TYPE_CHANGED) != 0;
bool preferredAppChanged = (change & B_APP_HINT_CHANGED)
|| (change & B_PREFERRED_APP_CHANGED);
const char* type = NULL;
const char* preferredApp = NULL;
if (iconChanged || preferredAppChanged)
message->FindString("be:type", &type);
if (iconForTypeChanged) {
message->FindString("be:extra_type", &type);
message->FindString("be:type", &preferredApp);
}
if (iconChanged || preferredAppChanged || iconForTypeChanged) {
TaskLoop* taskLoop = ContainerWindow()->DelayedTaskLoop();
ASSERT(taskLoop != NULL);
taskLoop->AccumulatedRunLater(new MetaMimeChangedAccumulator(
&BPoseView::MetaMimeChanged, ContainerWindow(), type, preferredApp),
200000, 5000000);
}
return true;
}
bool
BPoseView::FSNotification(const BMessage* message)
{
node_ref itemNode;
dev_t device;
Model* targetModel = TargetModel();
TrackerSettings settings;
switch (message->GetInt32("opcode", 0)) {
case B_ENTRY_CREATED:
{
ASSERT(targetModel != NULL);
message->FindInt32("device", &itemNode.device);
node_ref dirNode;
dirNode.device = itemNode.device;
message->FindInt64("directory", (int64*)&dirNode.node);
message->FindInt64("node", (int64*)&itemNode.node);
int32 count = fBrokenLinks->CountItems();
bool createPose = true;
if (targetModel != NULL && dirNode != *targetModel->NodeRef()
&& !targetModel->IsQuery()
&& !targetModel->IsVirtualDirectory()
&& !targetModel->IsRoot()
&& (!settings.ShowDisksIcon() || !IsVolumesRoot())) {
if (count == 0)
break;
createPose = false;
}
const char* name;
if (message->FindString("name", &name) != B_OK) {
#if DEBUG
SERIAL_PRINT(("no name in entry creation message\n"));
#endif
break;
}
if (count != 0) {
Model* model = new Model(&dirNode, &itemNode, name);
if (model->IsDirectory()) {
BString createdPath(BPath(model->EntryRef()).Path());
BDirectory currentDir(targetModel->EntryRef());
BPath createdDir(model->EntryRef());
for (int32 i = 0; i < count; i++) {
BSymLink link(fBrokenLinks->ItemAt(i)->EntryRef());
BPath path;
link.MakeLinkedPath(¤tDir, &path);
BString pathStr(path.Path());
pathStr.Append("/");
if (pathStr.Compare(createdPath,
createdPath.Length()) == 0) {
if (pathStr[createdPath.Length()] != '/')
break;
StopWatchingParentsOf(fBrokenLinks->ItemAt(i)->EntryRef());
watch_node(&itemNode, B_WATCH_DIRECTORY, this);
break;
}
}
}
delete model;
}
if (createPose)
EntryCreated(&dirNode, &itemNode, name);
TryUpdatingBrokenLinks();
break;
}
case B_ENTRY_MOVED:
return EntryMoved(message);
break;
case B_ENTRY_REMOVED:
message->FindInt32("device", &itemNode.device);
message->FindInt64("node", (int64*)&itemNode.node);
if (message->what == B_NODE_MONITOR && targetModel != NULL
&& *(targetModel->NodeRef()) == itemNode) {
if (!targetModel->IsRoot()) {
DisableSaveLocation();
Window()->Close();
}
} else {
int32 index;
BPose* pose = fPoseList->FindPose(&itemNode, &index);
if (pose == NULL) {
pose = fPoseList->DeepFindPose(&itemNode, &index);
if (pose != NULL) {
DeleteSymLinkPoseTarget(&itemNode, pose, index);
break;
}
}
DeletePose(&itemNode);
TryUpdatingBrokenLinks();
}
break;
case B_DEVICE_MOUNTED:
{
if (message->FindInt32("new device", &device) != B_OK)
break;
BVolume volume(device);
if (volume.InitCheck() != B_OK)
break;
ASSERT(targetModel != NULL);
if (targetModel->IsRoot()
|| ((IsVolumesRoot() && settings.MountVolumesOntoDesktop())
&& ((!volume.IsShared() || settings.MountSharedVolumesOntoDesktop())))) {
CreateVolumePose(&volume);
} else if (targetModel->IsTrash()) {
BDirectory trashDir;
BEntry entry;
if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK
&& trashDir.GetEntry(&entry) == B_OK) {
Model model(&entry);
if (model.InitCheck() == B_OK)
AddPoses(&model);
}
}
TaskLoop* taskLoop = ContainerWindow()->DelayedTaskLoop();
ASSERT(taskLoop != NULL);
taskLoop->RunLater(NewMemberFunctionObject(
&BPoseView::TryUpdatingBrokenLinks, this), 500000);
break;
}
case B_DEVICE_UNMOUNTED:
if (message->FindInt32("device", &device) == B_OK) {
ASSERT(targetModel != NULL);
if (targetModel->NodeRef()->device == device) {
if (IsFilePanel()) {
BMessage message(kSwitchToHome);
Window()->PostMessage(&message, this);
} else {
DisableSaveLocation();
Window()->Close();
}
} else {
EachPoseAndModel(fPoseList, &PoseHandleDeviceUnmounted, this, device);
}
}
break;
case B_STAT_CHANGED:
case B_ATTR_CHANGED:
return AttributeChanged(message);
}
return true;
}
bool
BPoseView::CreateSymlinkPoseTarget(Model* symlink)
{
Model* newResolvedModel = NULL;
Model* result = symlink->LinkTo();
if (result == NULL) {
BEntry entry(symlink->EntryRef(), true);
if (entry.InitCheck() == B_OK) {
node_ref nref;
entry_ref eref;
entry.GetNodeRef(&nref);
entry.GetRef(&eref);
if (eref.directory != TargetModel()->NodeRef()->node)
WatchNewNode(&nref, B_WATCH_STAT | B_WATCH_ATTR | B_WATCH_NAME
| B_WATCH_INTERIM_STAT, this);
newResolvedModel = new Model(&entry, true);
} else {
fBrokenLinks->AddItem(symlink);
WatchParentOf(symlink->EntryRef());
return true;
}
result = newResolvedModel;
}
symlink->SetLinkTo(result);
return true;
}
BPose*
BPoseView::EntryCreated(const node_ref* dirNode, const node_ref* itemNode,
const char* name, int32* indexPtr)
{
if (fPoseList->FindPose(itemNode) || FindZombie(itemNode))
return NULL;
BPoseView::WatchNewNode(itemNode);
Model* model = new Model(dirNode, itemNode, name, true);
if (model->InitCheck() != B_OK) {
PRINT(("2 adding model %s to zombie list, error %s\n", model->Name(),
strerror(model->InitCheck())));
fZombieList->AddItem(model);
return NULL;
}
PoseInfo poseInfo;
ReadPoseInfo(model, &poseInfo);
if (!PoseVisible(model, &poseInfo)) {
watch_node(model->NodeRef(), B_STOP_WATCHING, this);
delete model;
return NULL;
}
if (model->IsSymLink() && !CreateSymlinkPoseTarget(model)) {
watch_node(model->NodeRef(), B_STOP_WATCHING, this);
delete model;
return NULL;
}
return CreatePose(model, &poseInfo, true, indexPtr);
}
bool
BPoseView::EntryMoved(const BMessage* message)
{
ino_t oldDir;
node_ref dirNode;
node_ref itemNode;
message->FindInt32("device", &dirNode.device);
itemNode.device = dirNode.device;
message->FindInt64("to directory", (int64*)&dirNode.node);
message->FindInt64("node", (int64*)&itemNode.node);
message->FindInt64("from directory", (int64*)&oldDir);
const char* name;
if (message->FindString("name", &name) != B_OK)
return true;
StatStruct st;
if (stat("/", &st) >= 0
&& st.st_dev == dirNode.device
&& st.st_ino == dirNode.node) {
BString buffer;
buffer << "/" << name;
if (stat(buffer.String(), &st) >= 0) {
itemNode.node = st.st_ino;
itemNode.device = st.st_dev;
}
}
Model* targetModel = TargetModel();
ThrowOnAssert(targetModel != NULL);
node_ref thisDirNode;
if (TargetModel()->IsTrash()) {
BDirectory trashDir;
if (FSGetTrashDir(&trashDir, itemNode.device) != B_OK)
return true;
trashDir.GetNodeRef(&thisDirNode);
} else {
thisDirNode = *targetModel->NodeRef();
}
if (thisDirNode == itemNode) {
targetModel->UpdateEntryRef(&dirNode, name);
assert_cast<BContainerWindow*>(Window())->UpdateTitle();
}
if (oldDir == dirNode.node || targetModel->IsQuery()
|| targetModel->IsVirtualDirectory()) {
int32 index;
BPose* pose = fPoseList->FindPose(&itemNode, &index);
int32 poseListIndex = index;
bool visible = true;
if (IsFiltering())
visible = fFilteredPoseList->FindPose(&itemNode, &index) != NULL;
if (pose != NULL) {
Model* poseModel = pose->TargetModel();
ASSERT(poseModel != NULL);
poseModel->UpdateEntryRef(&dirNode, name);
BPoint loc(0, index * fListElemHeight);
if (poseModel->OpenNode() == B_OK) {
pose->UpdateAllWidgets(index, loc, this);
poseModel->CloseNode();
_CheckPoseSortOrder(fPoseList, pose, poseListIndex);
if (IsFiltering()) {
if (!visible && FilterPose(pose)) {
BRect bounds = Bounds();
float scrollBy = 0;
AddPoseToList(fFilteredPoseList, true, true, pose, bounds, scrollBy, true);
} else if (visible && !FilterPose(pose))
RemoveFilteredPose(pose, index);
else if (visible)
_CheckPoseSortOrder(fFilteredPoseList, pose, index);
}
}
} else {
Model* zombie = FindZombie(&itemNode, &index);
if (zombie) {
PRINT(("converting model %s from a zombie\n", zombie->Name()));
zombie->UpdateEntryRef(&dirNode, name);
pose = ConvertZombieToPose(zombie, index);
} else
return false;
}
if (pose != NULL)
pendingNodeMonitorCache.PoseCreatedOrMoved(this, pose);
} else if (oldDir == thisDirNode.node) {
DeletePose(&itemNode);
} else if (dirNode.node == thisDirNode.node) {
EntryCreated(&dirNode, &itemNode, name);
}
TryUpdatingBrokenLinks();
return true;
}
void
BPoseView::WatchParentOf(const entry_ref* ref)
{
BPath currentDir(ref);
currentDir.GetParent(¤tDir);
BSymLink symlink(ref);
BPath path;
symlink.MakeLinkedPath(currentDir.Path(), &path);
status_t status = path.GetParent(&path);
while (status == B_BAD_VALUE)
status = path.GetParent(&path);
if (status == B_ENTRY_NOT_FOUND)
return;
node_ref nref;
BNode(path.Path()).GetNodeRef(&nref);
if (nref != *TargetModel()->NodeRef())
watch_node(&nref, B_WATCH_DIRECTORY, this);
}
void
BPoseView::StopWatchingParentsOf(const entry_ref* ref)
{
BPath path;
BSymLink symlink(ref);
BPath currentDir(ref);
currentDir.GetParent(¤tDir);
symlink.MakeLinkedPath(currentDir.Path(), &path);
if (path.InitCheck() != B_OK)
return;
BObjectList<Model>* brokenLinksCopy = new BObjectList<Model>(*fBrokenLinks);
int32 count = brokenLinksCopy->CountItems();
while (path.GetParent(&path) == B_OK) {
if (strcmp(path.Path(), "/") == 0)
break;
BNode dir(path.Path());
node_ref dirNode;
dir.GetNodeRef(&dirNode);
if (dirNode == *TargetModel()->NodeRef())
continue;
bool keep = false;
for (int32 i = count - 1; i >= 0; i--) {
BSymLink link(brokenLinksCopy->ItemAt(i)->EntryRef());
BPath absolutePath;
link.MakeLinkedPath(currentDir.Path(), &absolutePath);
if (BString(absolutePath.Path()).Compare(path.Path(),
strlen(path.Path())) == 0) {
brokenLinksCopy->RemoveItemAt(i);
count--;
keep = true;
}
}
if (!keep)
watch_node(&dirNode, B_STOP_WATCHING, this);
}
delete brokenLinksCopy;
}
bool
BPoseView::AttributeChanged(const BMessage* message)
{
node_ref itemNode;
message->FindInt32("device", &itemNode.device);
message->FindInt64("node", (int64*)&itemNode.node);
const char* attrName;
if (message->FindString("attr", &attrName) != B_OK)
attrName = NULL;
Model* targetModel = TargetModel();
if (ContainerWindow()->ShouldHaveDraggableFolderIcon() && targetModel != NULL
&& *targetModel->NodeRef() == itemNode && targetModel->IsNodeOpen()
&& targetModel->AttrChanged(attrName)) {
BView* view = Window()->FindView("MenuBar");
if (view != NULL) {
view = view->FindView("DraggableContainerIcon");
if (view != NULL) {
IconCache::sIconCache->IconChanged(targetModel);
view->Invalidate();
}
}
}
int32 index;
attr_info info;
PoseList* posesFound = fPoseList->FindAllPoses(&itemNode);
int32 posesCount = posesFound->CountItems();
for (int i = 0; i < posesCount; i++) {
BPose* pose = posesFound->ItemAt(i);
Model* poseModel = pose->TargetModel();
if (poseModel->IsSymLink() && *(poseModel->NodeRef()) != itemNode) {
poseModel = poseModel->ResolveIfLink();
}
ASSERT(poseModel != NULL);
status_t result = B_OK;
for (int32 count = 0; count < 100; count++) {
result = poseModel->OpenNode();
if (result == B_OK || result != B_BUSY)
break;
PRINT(("poseModel %s busy, retrying in a bit\n", poseModel->Name()));
snooze(10000);
}
if (result != B_OK) {
PRINT(("Cache Error %s\n", strerror(result)));
continue;
}
bool visible = fPoseList->FindPose(poseModel->NodeRef(), &index) != NULL;
int32 poseListIndex = index;
if (IsFiltering())
visible = fFilteredPoseList->FindPose(poseModel->NodeRef(), &index) != NULL;
BPoint poseLoc;
if (ViewMode() == kListMode)
poseLoc.Set(0, index * fListElemHeight);
else
poseLoc = pose->Location(this);
if (attrName != NULL) {
status_t infoStatus = B_ERROR;
memset(&info, 0, sizeof(attr_info));
if (poseModel->IconAttrChanged(attrName)) {
if (strcmp(attrName, kAttrIcon) == 0)
info.type = B_VECTOR_ICON_TYPE;
else if (strcmp(attrName, kAttrLargeIcon) == 0)
info.type = 'ICON';
else if (strcmp(attrName, kAttrMiniIcon) == 0)
info.type = 'MICN';
else if (strcmp(attrName, kAttrThumbnail) == 0)
info.type = B_RAW_TYPE;
info.size = 0;
infoStatus = B_OK;
} else if (poseModel->Node() != NULL) {
infoStatus = poseModel->Node()->GetAttrInfo(attrName, &info);
}
pose->UpdateWidgetAndModel(attrName, infoStatus == B_OK ? info.type : 0,
index, poseLoc, this, visible);
if (strcmp(attrName, kAttrMIMEType) == 0)
RefreshMimeTypeList();
} else {
pose->UpdateWidgetAndModel(0, 0, index, poseLoc, this, visible);
}
poseModel->CloseNode();
if (IsFiltering()) {
if (!visible && FilterPose(pose)) {
visible = true;
float scrollBy = 0;
BRect bounds = Bounds();
AddPoseToList(fFilteredPoseList, true, true, pose, bounds, scrollBy, true);
continue;
} else if (visible && !FilterPose(pose)) {
RemoveFilteredPose(pose, index);
continue;
}
}
if (attrName != NULL) {
uint32 attrHash = AttrHashString(attrName, info.type);
if (attrHash == PrimarySort() || attrHash == SecondarySort()) {
_CheckPoseSortOrder(fPoseList, pose, poseListIndex);
if (IsFiltering() && visible)
_CheckPoseSortOrder(fFilteredPoseList, pose, index);
}
} else {
int32 fields;
if (message->FindInt32("fields", &fields) != B_OK)
continue;
for (int i = sizeof(sAttrColumnMap) / sizeof(attr_column_relation); i--;) {
uint32 attrHash = sAttrColumnMap[i].attrHash;
if (attrHash == PrimarySort() || attrHash == SecondarySort()) {
if ((fields & sAttrColumnMap[i].fieldMask) != 0) {
_CheckPoseSortOrder(fPoseList, pose, poseListIndex);
if (IsFiltering() && visible)
_CheckPoseSortOrder(fFilteredPoseList, pose, index);
break;
}
}
}
}
}
delete posesFound;
if (posesCount == 0) {
Model* zombie = FindZombie(&itemNode, &index);
if (zombie != NULL) {
PRINT(("converting model %s from a zombie\n", zombie->Name()));
return ConvertZombieToPose(zombie, index) != NULL;
} else {
PRINT(("model has no pose but is not a zombie either!\n"));
return false;
}
}
return true;
}
void
BPoseView::UpdateIcon(BPose* pose)
{
BPoint location;
if (ViewMode() == kListMode) {
bool found = false;
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
if (poseList->ItemAt(index) == pose) {
location.Set(0, index * fListElemHeight);
found = true;
break;
}
}
if (!found)
return;
} else {
location = pose->Location(this);
}
pose->UpdateIcon(location, this);
}
BPose*
BPoseView::ConvertZombieToPose(Model* zombie, int32 index)
{
if (zombie->UpdateStatAndOpenNode() != B_OK)
return NULL;
fZombieList->RemoveItemAt(index);
PoseInfo poseInfo;
ReadPoseInfo(zombie, &poseInfo);
if (PoseVisible(zombie, &poseInfo)) {
return CreatePose(zombie, &poseInfo);
}
delete zombie;
return NULL;
}
BList*
BPoseView::GetDropPointList(BPoint dropStart, BPoint dropEnd, const PoseList* poses,
bool sourceInListMode, bool dropOnGrid) const
{
if (ViewMode() == kListMode)
return NULL;
int32 poseCount = poses->CountItems();
BList* pointList = new BList(poseCount);
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = poses->ItemAt(index);
BPoint poseLoc;
if (sourceInListMode)
poseLoc = dropEnd + BPoint(0, index * (IconPoseHeight() + 3));
else
poseLoc = dropEnd + (pose->Location(this) - dropStart);
if (dropOnGrid)
poseLoc = PinToGrid(poseLoc, fGrid, fOffset);
pointList->AddItem(new BPoint(poseLoc));
}
return pointList;
}
void
BPoseView::DuplicateSelection(BPoint* dropStart, BPoint* dropEnd)
{
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = (BPose*)fSelectionList->ItemAt(index);
Model* poseModel = pose->TargetModel();
if (poseModel->IsTrash() || poseModel->IsVolume()) {
fSelectionList->RemoveItemAt(index);
index--;
selectCount--;
if (fSelectionPivotPose == pose)
fSelectionPivotPose = NULL;
if (fRealPivotPose == pose)
fRealPivotPose = NULL;
continue;
}
}
if (!fSelectionList->IsEmpty()) {
BObjectList<entry_ref, true>* srcList = new BObjectList<entry_ref, true>(CountSelected());
CopySelectionListToEntryRefList(fSelectionList, srcList);
BList* dropPoints;
if (dropStart) {
dropPoints = GetDropPointList(*dropStart, *dropEnd, fSelectionList,
ViewMode() == kListMode, (modifiers() & B_COMMAND_KEY) != 0);
} else
dropPoints = NULL;
FSDuplicate(srcList, dropPoints);
}
}
void
BPoseView::SelectPoseAtLocation(BPoint point)
{
int32 index;
BPose* pose = FindPose(point, &index);
if (pose != NULL)
SelectPose(pose, index);
}
void
BPoseView::MoveListToTrash(BObjectList<entry_ref, true>* list, bool selectNext,
bool deleteDirectly)
{
if (!list->CountItems())
return;
BObjectList<FunctionObject, true>* taskList =
new BObjectList<FunctionObject, true>(2);
if (deleteDirectly) {
taskList->AddItem(NewFunctionObject(FSDeleteRefList, list,
false, true));
} else {
taskList->AddItem(NewFunctionObject(FSMoveToTrash, list,
(BList*)NULL, false));
}
if (selectNext && ViewMode() == kListMode) {
BPose* pose = fSelectionList->ItemAt(0);
BPoint pointInPose(fListOffset + 5, 5);
int32 index = IndexOfPose(pose);
pointInPose.y += fListElemHeight * index;
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL) {
ThrowOnAssert(TargetModel() != NULL);
taskList->AddItem(NewMemberFunctionObject(
&TTracker::SelectPoseAtLocationSoon, tracker,
*TargetModel()->NodeRef(), pointInPose));
}
}
ThreadSequence::Launch(taskList, true);
}
inline void
CopyOneTrashedRefAsEntry(const entry_ref* ref, BObjectList<entry_ref, true>* trashList,
BObjectList<entry_ref, true>* noTrashList, std::map<int32, bool>* deviceHasTrash)
{
std::map<int32, bool> &deviceHasTrashTmp = *deviceHasTrash;
BDirectory entryDir(ref);
bool isVolume = entryDir.IsRootDirectory();
int32 device = ref->device;
BDirectory trashDir;
if (!isVolume
&& deviceHasTrashTmp.find(device) == deviceHasTrashTmp.end()) {
deviceHasTrashTmp[device] = FSGetTrashDir(&trashDir, device) == B_OK;
}
if (isVolume || deviceHasTrashTmp[device])
trashList->AddItem(new entry_ref(*ref));
else
noTrashList->AddItem(new entry_ref(*ref));
}
static void
CopyPoseOneAsEntry(BPose* pose, BObjectList<entry_ref, true>* trashList,
BObjectList<entry_ref, true>* noTrashList, std::map<int32, bool>* deviceHasTrash)
{
CopyOneTrashedRefAsEntry(pose->TargetModel()->EntryRef(), trashList,
noTrashList, deviceHasTrash);
}
static bool
CheckVolumeReadOnly(const entry_ref* ref)
{
BVolume volume (ref->device);
if (volume.IsReadOnly()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("Files cannot be moved or deleted from a read-only "
"volume."), B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return false;
}
return true;
}
void
BPoseView::MoveSelectionOrEntryToTrash(const entry_ref* ref, bool selectNext)
{
BObjectList<entry_ref, true>* entriesToTrash = new
BObjectList<entry_ref, true>(CountSelected());
BObjectList<entry_ref, true>* entriesToDeleteOnTheSpot = new
BObjectList<entry_ref, true>(20);
std::map<int32, bool> deviceHasTrash;
if (ref != NULL) {
if (!CheckVolumeReadOnly(ref)) {
delete entriesToTrash;
delete entriesToDeleteOnTheSpot;
return;
}
CopyOneTrashedRefAsEntry(ref, entriesToTrash, entriesToDeleteOnTheSpot,
&deviceHasTrash);
} else {
if (!CheckVolumeReadOnly(
fSelectionList->ItemAt(0)->TargetModel()->EntryRef())) {
delete entriesToTrash;
delete entriesToDeleteOnTheSpot;
return;
}
fSelectionList->EachListItem(CopyPoseOneAsEntry, entriesToTrash,
entriesToDeleteOnTheSpot, &deviceHasTrash);
}
if (entriesToDeleteOnTheSpot->CountItems()) {
BString alertText;
if (ref != NULL) {
alertText.SetTo(B_TRANSLATE("The selected item cannot be moved to "
"the Trash. Would you like to delete it instead? "
"(This operation cannot be reverted.)"));
} else {
alertText.SetTo(B_TRANSLATE("Some of the selected items cannot be "
"moved to the Trash. Would you like to delete them instead? "
"(This operation cannot be reverted.)"));
}
BAlert* alert = new BAlert("", alertText.String(),
B_TRANSLATE("Cancel"), B_TRANSLATE("Delete"));
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
return;
}
MoveListToTrash(entriesToTrash, selectNext, false);
MoveListToTrash(entriesToDeleteOnTheSpot, selectNext, true);
}
void
BPoseView::MoveSelectionToTrash(bool selectNext)
{
if (fSelectionList->IsEmpty())
return;
MoveSelectionOrEntryToTrash(0, selectNext);
}
void
BPoseView::MoveEntryToTrash(const entry_ref* ref, bool selectNext)
{
MoveSelectionOrEntryToTrash(ref, selectNext);
}
void
BPoseView::DeleteSelection(bool selectNext, bool confirm)
{
int32 selectCount = CountSelected();
if (selectCount <= 0)
return;
if (!CheckVolumeReadOnly(fSelectionList->ItemAt(0)->TargetModel()->EntryRef()))
return;
BObjectList<entry_ref, true>* entriesToDelete = new BObjectList<entry_ref, true>(selectCount);
for (int32 index = 0; index < selectCount; index++) {
entry_ref* ref = new entry_ref(*fSelectionList->ItemAt(index)->TargetModel()->EntryRef());
entriesToDelete->AddItem(ref);
}
Delete(entriesToDelete, selectNext, confirm);
}
void
BPoseView::RestoreSelectionFromTrash(bool selectNext)
{
int32 selectCount = CountSelected();
if (selectCount <= 0)
return;
BObjectList<entry_ref, true>* entriesToRestore
= new BObjectList<entry_ref, true>(selectCount);
for (int32 index = 0; index < selectCount; index++) {
entriesToRestore->AddItem(new entry_ref(
*fSelectionList->ItemAt(index)->TargetModel()->EntryRef()));
}
RestoreItemsFromTrash(entriesToRestore, selectNext);
}
void
BPoseView::Delete(const entry_ref &ref, bool selectNext, bool confirm)
{
BObjectList<entry_ref, true>* entriesToDelete = new BObjectList<entry_ref, true>(1);
entriesToDelete->AddItem(new entry_ref(ref));
Delete(entriesToDelete, selectNext, confirm);
}
void
BPoseView::Delete(BObjectList<entry_ref, true>* list, bool selectNext, bool confirm)
{
if (list->CountItems() == 0) {
delete list;
return;
}
BObjectList<FunctionObject, true>* taskList = new BObjectList<FunctionObject, true>(2);
taskList->AddItem(NewFunctionObject(FSDeleteRefList, list, false, confirm));
if (selectNext && ViewMode() == kListMode) {
BPose* pose = fSelectionList->ItemAt(0);
BPoint pointInPose(fListOffset + 5, 5);
int32 index = IndexOfPose(pose);
pointInPose.y += fListElemHeight * index;
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL) {
ThrowOnAssert(TargetModel() != NULL);
Model* targetModel = TargetModel();
ASSERT(targetModel != NULL);
taskList->AddItem(NewMemberFunctionObject(&TTracker::SelectPoseAtLocationSoon, tracker,
*targetModel->NodeRef(), pointInPose));
}
}
ThreadSequence::Launch(taskList, true);
}
void
BPoseView::RestoreItemsFromTrash(BObjectList<entry_ref, true>* list, bool selectNext)
{
if (list->CountItems() == 0) {
delete list;
return;
}
BObjectList<FunctionObject, true>* taskList = new BObjectList<FunctionObject, true>(2);
taskList->AddItem(NewFunctionObject(FSRestoreRefList, list, false));
if (selectNext && ViewMode() == kListMode) {
BPose* pose = fSelectionList->ItemAt(0);
BPoint pointInPose(fListOffset + 5, 5);
int32 index = IndexOfPose(pose);
pointInPose.y += fListElemHeight * index;
TTracker* tracker = dynamic_cast<TTracker*>(be_app);
if (tracker != NULL) {
ThrowOnAssert(TargetModel() != NULL);
Model* targetModel = TargetModel();
ASSERT(targetModel != NULL);
taskList->AddItem(NewMemberFunctionObject(&TTracker::SelectPoseAtLocationSoon, tracker,
*targetModel->NodeRef(), pointInPose));
}
}
ThreadSequence::Launch(taskList, true);
}
void
BPoseView::DoDelete()
{
ExcludeTrashFromSelection();
if (TargetModel()->IsTrash())
return DeleteSelection(true, false);
DeleteSelection();
}
void
BPoseView::DoMoveToTrash()
{
ExcludeTrashFromSelection();
if (TargetModel() == NULL)
return;
if (TargetModel()->IsTrash())
return DeleteSelection(true, false);
bool shiftDown = (Window()->CurrentMessage()->FindInt32("modifiers") & B_SHIFT_KEY) != 0;
if (shiftDown)
DeleteSelection();
else
MoveSelectionToTrash();
}
void
BPoseView::SelectAll()
{
BRect bounds(Bounds());
fSelectionList->MakeEmpty();
fMimeTypesInSelectionCache.MakeEmpty();
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
int32 startIndex = 0;
BPoint loc(0, fListElemHeight * startIndex);
bool iconMode = ViewMode() != kListMode;
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
fSelectionList->AddItem(pose);
if (index == startIndex)
fSelectionPivotPose = pose;
if (!pose->IsSelected()) {
pose->Select(true);
BRect poseRect;
if (iconMode)
poseRect = pose->CalcRect(this);
else
poseRect = pose->CalcRect(loc, this);
if (bounds.Intersects(poseRect)) {
pose->Draw(poseRect, bounds, this, false);
Flush();
}
}
loc.y += fListElemHeight;
}
if (fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
}
void
BPoseView::InvertSelection()
{
BRect bounds(Bounds());
int32 startIndex = 0;
BPoint loc(0, fListElemHeight * startIndex);
fMimeTypesInSelectionCache.MakeEmpty();
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
bool iconMode = ViewMode() != kListMode;
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose->IsSelected()) {
fSelectionList->RemoveItem(pose);
pose->Select(false);
} else {
if (index == startIndex)
fSelectionPivotPose = pose;
fSelectionList->AddItem(pose);
pose->Select(true);
}
BRect poseRect;
if (iconMode)
poseRect = pose->CalcRect(this);
else
poseRect = pose->CalcRect(loc, this);
if (bounds.Intersects(poseRect))
Invalidate();
loc.y += fListElemHeight;
}
if (fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
}
int32
BPoseView::SelectMatchingEntries(const BMessage* message)
{
int32 matchCount = 0;
SetMultipleSelection(true);
ClearSelection();
TrackerStringExpressionType expressionType;
BString expression;
const char* expressionPointer;
bool invertSelection;
bool ignoreCase;
message->FindInt32("ExpressionType", (int32*)&expressionType);
message->FindString("Expression", &expressionPointer);
message->FindBool("InvertSelection", &invertSelection);
message->FindBool("IgnoreCase", &ignoreCase);
expression = expressionPointer;
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
TrackerString name;
RegExp regExpression;
if (expressionType == kRegexpMatch) {
regExpression.SetTo(expression);
if (regExpression.InitCheck() != B_OK) {
BString message(B_TRANSLATE("Error in regular expression:\n\n'%errstring'"));
message.ReplaceFirst("%errstring", regExpression.ErrorString());
BAlert* alert = new BAlert("", message.String(), B_TRANSLATE("OK"),
NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return 0;
}
}
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
name = pose->TargetModel()->Name();
if (name.Matches(expression.String(), !ignoreCase, expressionType) ^ invertSelection) {
matchCount++;
AddPoseToSelection(pose, index);
}
}
Window()->Activate();
return matchCount;
}
void
BPoseView::ShowSelectionWindow()
{
Window()->PostMessage(kShowSelectionWindow);
}
void
BPoseView::KeyDown(const char* bytes, int32 count)
{
char key = bytes[0];
switch (key) {
case B_LEFT_ARROW:
case B_RIGHT_ARROW:
case B_UP_ARROW:
case B_DOWN_ARROW:
{
int32 index;
BPose* pose = FindNearbyPose(key, &index);
if (pose == NULL)
break;
if (fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0) {
if (pose->IsSelected()) {
RemovePoseFromSelection(fSelectionList->LastItem());
fSelectionPivotPose = pose;
ScrollIntoView(pose, index);
} else
AddPoseToSelection(pose, index, true);
} else
SelectPose(pose, index);
break;
}
case B_RETURN:
if (IsFiltering() && CountSelected() == 0)
SelectPose(fFilteredPoseList->FirstItem(), 0);
OpenSelection();
if (IsTypeAheadFiltering() && (modifiers() & B_SHIFT_KEY) != 0) {
StopTypeAheadFiltering();
}
break;
case B_HOME:
if (ViewMode() == kListMode)
MoveOrChangePoseSelection(0);
else
ScrollView(B_HOME);
break;
case B_END:
if (ViewMode() == kListMode)
MoveOrChangePoseSelection(CurrentPoseList()->CountItems() - 1);
else
ScrollView(B_END);
break;
case B_PAGE_UP:
if (ViewMode() == kListMode) {
int32 firstIndex = CurrentPoseList()->IndexOf(fSelectionList->FirstItem());
int32 index;
BPose* first = FirstVisiblePose(&index);
if (first != NULL) {
if (index == firstIndex) {
ScrollView(B_PAGE_UP);
first = FirstVisiblePose(&index);
}
MoveOrChangePoseSelection(index);
}
} else
ScrollView(B_PAGE_UP);
break;
case B_PAGE_DOWN:
if (ViewMode() == kListMode) {
int32 lastIndex = CurrentPoseList()->IndexOf(fSelectionList->LastItem());
int32 index;
BPose* last = LastVisiblePose(&index);
if (last != NULL) {
if (index == lastIndex) {
ScrollView(B_PAGE_DOWN);
last = LastVisiblePose(&index);
}
MoveOrChangePoseSelection(index);
}
} else
ScrollView(B_PAGE_DOWN);
break;
case B_TAB:
if (IsFilePanel())
_inherited::KeyDown(bytes, count);
else {
if (ViewMode() == kListMode && TrackerSettings().TypeAheadFiltering())
break;
if (fSelectionList->IsEmpty())
sMatchString.Truncate(0);
else {
BPose* pose = fSelectionList->FirstItem();
sMatchString.SetTo(pose->TargetModel()->Name());
}
bool reverse
= (Window()->CurrentMessage()->FindInt32("modifiers") & B_SHIFT_KEY) != 0;
int32 index;
BPose* pose = FindNextMatch(&index, reverse);
if (pose == NULL) {
if (reverse)
sMatchString.SetTo(0x7f, 1);
else
sMatchString.Truncate(0);
pose = FindNextMatch(&index, reverse);
}
SelectPose(pose, index);
}
break;
case B_BACKSPACE:
{
if (IsTypeAheadFiltering()) {
BString* lastString = fFilterStrings.LastItem();
if (lastString->Length() == 0) {
int32 stringCount = fFilterStrings.CountItems();
if (stringCount > 1)
delete fFilterStrings.RemoveItemAt(stringCount - 1);
else
break;
} else
lastString->TruncateChars(lastString->CountChars() - 1);
fCountView->RemoveFilterCharacter();
TypeAheadFilteringChanged();
break;
}
if (sMatchString.Length() == 0)
break;
sMatchString.TruncateChars(sMatchString.CountChars() - 1);
fLastKeyTime = system_time();
fCountView->SetTypeAhead(sMatchString.String());
int32 index;
BPose* pose = FindBestMatch(&index);
if (pose == NULL)
break;
SelectPose(pose, index);
break;
}
case B_FUNCTION_KEY:
{
BMessage* message = Window()->CurrentMessage();
if (message != NULL) {
int32 key;
if (message->FindInt32("key", &key) == B_OK && key == B_F2_KEY)
Window()->PostMessage(kEditName, this);
}
break;
}
case B_INSERT:
break;
default:
{
if (ViewMode() == kListMode && TrackerSettings().TypeAheadFiltering()) {
if (key == ' ' && (modifiers() & B_SHIFT_KEY) != 0) {
if (fFilterStrings.LastItem()->Length() == 0)
break;
fFilterStrings.AddItem(new BString());
fCountView->AddFilterCharacter("|");
break;
}
fFilterStrings.LastItem()->AppendChars(bytes, 1);
fCountView->AddFilterCharacter(bytes);
TypeAheadFilteringChanged();
break;
}
bigtime_t doubleClickSpeed;
get_click_speed(&doubleClickSpeed);
if (fKeyRunner == NULL) {
fKeyRunner = new BMessageRunner(this,
new BMessage(kCheckTypeahead), doubleClickSpeed);
if (fKeyRunner->InitCheck() != B_OK)
return;
}
bigtime_t eventTime;
BMessage* message = Window()->CurrentMessage();
if (message == NULL || message->FindInt64("when", &eventTime) < B_OK)
eventTime = system_time();
if (eventTime - fLastKeyTime < (doubleClickSpeed * 2))
sMatchString.AppendChars(bytes, 1);
else
sMatchString.SetToChars(bytes, 1);
fLastKeyTime = eventTime;
fCountView->SetTypeAhead(sMatchString.String());
int32 index;
BPose* pose = FindBestMatch(&index);
if (pose == NULL)
break;
SelectPose(pose, index);
break;
}
}
}
BPose*
BPoseView::FindNextMatch(int32* matchingIndex, bool reverse)
{
char bestSoFar[B_FILE_NAME_LENGTH] = { 0 };
BPose* poseToSelect = NULL;
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
if (reverse) {
if (sMatchString.ICompare(pose->TargetModel()->Name()) > 0) {
if (strcasecmp(pose->TargetModel()->Name(), bestSoFar) >= 0
|| !bestSoFar[0]) {
strlcpy(bestSoFar, pose->TargetModel()->Name(),
sizeof(bestSoFar));
poseToSelect = pose;
*matchingIndex = index;
}
}
} else if (sMatchString.ICompare(pose->TargetModel()->Name()) < 0) {
if (strcasecmp(pose->TargetModel()->Name(), bestSoFar) <= 0
|| !bestSoFar[0]) {
strlcpy(bestSoFar, pose->TargetModel()->Name(),
sizeof(bestSoFar));
poseToSelect = pose;
*matchingIndex = index;
}
}
}
return poseToSelect;
}
BPose*
BPoseView::FindBestMatch(int32* index)
{
BPose* poseToSelect = NULL;
float bestScore = -1;
int32 poseCount = fPoseList->CountItems();
for (int32 j = 0; j < CountColumns(); j++) {
BColumn* column = ColumnAt(j);
for (int32 i = 0; i < poseCount; i++) {
BPose* pose = fPoseList->ItemAt(i);
float score = -1;
if (ViewMode() == kListMode) {
ModelNodeLazyOpener modelOpener(pose->TargetModel());
BTextWidget* widget = pose->WidgetFor(column, this,
modelOpener);
const char* text = NULL;
if (widget != NULL)
text = widget->Text(this);
if (text != NULL)
score = ComputeTypeAheadScore(text, sMatchString.String());
} else {
score = ComputeTypeAheadScore(pose->TargetModel()->Name(),
sMatchString.String());
}
if (score > bestScore) {
poseToSelect = pose;
bestScore = score;
*index = i;
}
if (score == kExactMatchScore)
break;
}
if (bestScore > 0 || ViewMode() != kListMode)
break;
}
return poseToSelect;
}
static bool
LinesIntersect(float s1, float e1, float s2, float e2)
{
return std::max(s1, s2) < std::min(e1, e2);
}
BPose*
BPoseView::FindNearbyPose(char arrowKey, int32* poseIndex)
{
int32 resultIndex = -1;
BPose* poseToSelect = NULL;
BPose* selectedPose = fSelectionList->LastItem();
if (ViewMode() == kListMode) {
PoseList* poseList = CurrentPoseList();
switch (arrowKey) {
case B_UP_ARROW:
case B_LEFT_ARROW:
if (selectedPose) {
resultIndex = poseList->IndexOf(selectedPose) - 1;
poseToSelect = poseList->ItemAt(resultIndex);
if (poseToSelect == NULL && arrowKey == B_LEFT_ARROW) {
resultIndex = poseList->CountItems() - 1;
poseToSelect = poseList->LastItem();
}
} else {
resultIndex = poseList->CountItems() - 1;
poseToSelect = poseList->LastItem();
}
break;
case B_DOWN_ARROW:
case B_RIGHT_ARROW:
if (selectedPose) {
resultIndex = poseList->IndexOf(selectedPose) + 1;
poseToSelect = poseList->ItemAt(resultIndex);
if (poseToSelect == NULL && arrowKey == B_RIGHT_ARROW) {
resultIndex = 0;
poseToSelect = poseList->FirstItem();
}
} else {
resultIndex = 0;
poseToSelect = poseList->FirstItem();
}
break;
}
*poseIndex = resultIndex;
return poseToSelect;
}
if (fSelectionList->IsEmpty()) {
poseToSelect = fVSPoseList->FirstItem();
for (int32 index = 0; ;index++) {
BPose* pose = fVSPoseList->ItemAt(++index);
if (pose == NULL)
break;
if (poseToSelect != NULL) {
BRect selectedBounds;
selectedBounds = poseToSelect->CalcRect(this);
BRect poseRect(pose->CalcRect(this));
if (poseRect.top > selectedBounds.top)
break;
if (poseRect.left < selectedBounds.left)
poseToSelect = pose;
}
}
return poseToSelect;
}
BRect selectionRect;
if (selectedPose != NULL)
selectionRect = selectedPose->CalcRect(this);
BRect bestRect;
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
BRect poseRect(pose->CalcRect(this));
switch (arrowKey) {
case B_LEFT_ARROW:
if (LinesIntersect(poseRect.top, poseRect.bottom,
selectionRect.top, selectionRect.bottom)
&& poseRect.left < selectionRect.left
&& (poseRect.left > bestRect.left || !bestRect.IsValid())) {
bestRect = poseRect;
poseToSelect = pose;
}
break;
case B_RIGHT_ARROW:
if (LinesIntersect(poseRect.top, poseRect.bottom,
selectionRect.top, selectionRect.bottom)
&& poseRect.right > selectionRect.right
&& (poseRect.right < bestRect.right
|| !bestRect.IsValid())) {
bestRect = poseRect;
poseToSelect = pose;
}
break;
case B_UP_ARROW:
if (LinesIntersect(poseRect.left, poseRect.right,
selectionRect.left, selectionRect.right)
&& poseRect.top < selectionRect.top
&& (poseRect.top > bestRect.top
|| !bestRect.IsValid())) {
bestRect = poseRect;
poseToSelect = pose;
}
break;
case B_DOWN_ARROW:
if (LinesIntersect(poseRect.left, poseRect.right,
selectionRect.left, selectionRect.right)
&& poseRect.bottom > selectionRect.bottom
&& (poseRect.bottom < bestRect.bottom
|| !bestRect.IsValid())) {
bestRect = poseRect;
poseToSelect = pose;
}
break;
}
}
if (poseToSelect != NULL)
return poseToSelect;
return selectedPose;
}
void
BPoseView::ShowContextMenu(BPoint where)
{
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return;
int32 index;
BPose* pose = FindPose(where, &index);
if (pose != NULL) {
if (!pose->IsSelected()) {
ClearSelection();
pose->Select(true);
fSelectionList->AddItem(pose);
DrawPose(pose, index, false);
}
} else
ClearSelection();
window->Activate();
window->UpdateIfNeeded();
window->ShowContextMenu(where, pose == NULL ? NULL : pose->TargetModel()->EntryRef());
if (fSelectionChangedHook)
window->SelectionChanged();
}
void
BPoseView::_BeginSelectionRect(const BPoint& point, bool shouldExtend)
{
fSelectionRectInfo.rect = BRect(point, point - BPoint(1, 1));
if (!fTransparentSelection) {
SetDrawingMode(B_OP_INVERT);
StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
SetDrawingMode(B_OP_OVER);
}
fSelectionRectInfo.lastRect = fSelectionRectInfo.rect;
fSelectionRectInfo.selection = new BList(CurrentPoseList()->CountItems());
fSelectionRectInfo.startPoint = point;
fSelectionRectInfo.lastPoint = point;
fSelectionRectInfo.isDragging = true;
if (fAutoScrollState == kAutoScrollOff) {
fAutoScrollState = kAutoScrollOn;
Window()->SetPulseRate(20000);
}
}
static void
AddIfPoseSelected(BPose* pose, PoseList* list)
{
if (pose->IsSelected())
list->AddItem(pose);
}
void
BPoseView::_UpdateSelectionRect(const BPoint& point)
{
if (point == fSelectionRectInfo.lastPoint)
return;
fSelectionRectInfo.lastPoint = point;
if (!fTransparentSelection) {
SetDrawingMode(B_OP_INVERT);
StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
SetDrawingMode(B_OP_OVER);
}
fSelectionRectInfo.rect.top = std::min(point.y, fSelectionRectInfo.startPoint.y);
fSelectionRectInfo.rect.left = std::min(point.x, fSelectionRectInfo.startPoint.x);
fSelectionRectInfo.rect.bottom = std::max(point.y, fSelectionRectInfo.startPoint.y);
fSelectionRectInfo.rect.right = std::max(point.x, fSelectionRectInfo.startPoint.x);
fIsDrawingSelectionRect = true;
SelectPoses(fSelectionRectInfo.rect, &fSelectionRectInfo.selection);
Window()->UpdateIfNeeded();
if (!fTransparentSelection) {
SetDrawingMode(B_OP_INVERT);
StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
SetDrawingMode(B_OP_OVER);
} else {
BRegion updateRegion1;
BRegion updateRegion2;
bool sameWidth = fSelectionRectInfo.rect.Width()
== fSelectionRectInfo.lastRect.Width();
bool sameHeight = fSelectionRectInfo.rect.Height()
== fSelectionRectInfo.lastRect.Height();
updateRegion1.Include(fSelectionRectInfo.rect);
updateRegion1.Exclude(fSelectionRectInfo.lastRect.InsetByCopy(
sameWidth ? 0 : 1, sameHeight ? 0 : 1));
updateRegion2.Include(fSelectionRectInfo.lastRect);
updateRegion2.Exclude(fSelectionRectInfo.rect.InsetByCopy(
sameWidth ? 0 : 1, sameHeight ? 0 : 1));
updateRegion1.Include(&updateRegion2);
BRect unionRect = fSelectionRectInfo.rect & fSelectionRectInfo.lastRect;
updateRegion1.Exclude(unionRect
& BRect(-2000, fSelectionRectInfo.startPoint.y, 2000,
fSelectionRectInfo.startPoint.y));
updateRegion1.Exclude(unionRect
& BRect(fSelectionRectInfo.startPoint.x, -2000,
fSelectionRectInfo.startPoint.x, 2000));
fSelectionRectInfo.lastRect = fSelectionRectInfo.rect;
Invalidate(&updateRegion1);
Window()->UpdateIfNeeded();
}
Flush();
}
void
BPoseView::_EndSelectionRect()
{
delete fSelectionRectInfo.selection;
fSelectionRectInfo.selection = NULL;
fSelectionRectInfo.isDragging = false;
fIsDrawingSelectionRect = false;
if (!fTransparentSelection) {
SetDrawingMode(B_OP_INVERT);
StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS);
SetDrawingMode(B_OP_COPY);
fSelectionRectInfo.rect.Set(0, 0, -1, -1);
} else {
Invalidate(fSelectionRectInfo.rect);
fSelectionRectInfo.rect.Set(0, 0, -1, -1);
Window()->UpdateIfNeeded();
}
fSelectionList->MakeEmpty();
fMimeTypesInSelectionCache.MakeEmpty();
CurrentPoseList()->EachListItem(AddIfPoseSelected, fSelectionList);
if (fSelectionPivotPose && !fSelectionList->HasItem(fSelectionPivotPose))
fSelectionPivotPose = NULL;
if (fRealPivotPose && !fSelectionList->HasItem(fRealPivotPose))
fRealPivotPose = NULL;
}
void
BPoseView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
{
if (fSelectionRectInfo.isDragging)
_UpdateSelectionRect(where);
if (!fDropEnabled || dragMessage == NULL)
return;
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return;
if (!IsDragging())
DragStart(dragMessage);
switch (transit) {
case B_INSIDE_VIEW:
case B_ENTERED_VIEW:
UpdateDropTarget(where, dragMessage, window->ContextMenu());
if (fAutoScrollState == kAutoScrollOff) {
fAutoScrollState = kWaitForTransition;
window->SetPulseRate(100000);
}
break;
case B_EXITED_VIEW:
DragStop();
SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
fCursorCheck = false;
if (window->ContextMenu() != NULL) {
HiliteDropTarget(false);
fDropTarget = NULL;
}
break;
}
}
void
BPoseView::MouseDragged(const BMessage* message)
{
if (fTextWidgetToCheck != NULL)
fTextWidgetToCheck->CancelWait();
fTrackRightMouseUp = false;
fTrackMouseUp = false;
BPoint where;
uint32 buttons = 0;
if (message->FindPoint("be:view_where", &where) != B_OK
|| message->FindInt32("buttons", (int32*)&buttons) != B_OK) {
return;
}
bool extendSelection = (modifiers() & B_COMMAND_KEY) != 0 && fMultipleSelection;
int32 index;
BPose* pose = FindPose(where, &index);
if (pose != NULL)
DragSelectedPoses(pose, where, buttons);
else if (buttons == B_PRIMARY_MOUSE_BUTTON)
_BeginSelectionRect(where, extendSelection);
}
void
BPoseView::MouseLongDown(const BMessage* message)
{
fTrackRightMouseUp = false;
fTrackMouseUp = false;
BPoint where;
if (message->FindPoint("where", &where) != B_OK)
return;
ShowContextMenu(where);
}
void
BPoseView::MouseIdle(const BMessage* message)
{
BPoint where;
uint32 buttons = 0;
GetMouse(&where, &buttons);
if (buttons == 0 || !IsDragging())
return;
if (fDropTarget != NULL) {
FrameForPose(fDropTarget, true, &fStartFrame);
ShowContextMenu(where);
} else
Window()->Activate();
}
void
BPoseView::MouseDown(BPoint where)
{
DragStop();
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return;
if (IsDesktopView()) {
BScreen screen(Window());
rgb_color color = screen.DesktopColor();
SetLowColor(color);
SetViewColor(color);
}
MakeFocus();
uint32 buttons = (uint32)window->CurrentMessage()->GetInt32("buttons", 0);
uint32 mods = modifiers();
bool secondaryMouseButtonDown = SecondaryMouseButtonDown(mods, buttons);
fTrackRightMouseUp = secondaryMouseButtonDown;
fTrackMouseUp = !secondaryMouseButtonDown;
bool extendSelection = (mods & B_COMMAND_KEY) != 0 && fMultipleSelection;
CommitActivePose();
int32 index;
BPose* pose = FindPose(where, &index);
if (pose != NULL) {
if (!pose->IsSelected() || !secondaryMouseButtonDown)
AddRemoveSelectionRange(where, extendSelection, pose);
if (fTextWidgetToCheck != NULL && (pose != fLastClickedPose || secondaryMouseButtonDown))
fTextWidgetToCheck->CancelWait();
if (!extendSelection && WasDoubleClick(pose, where) && buttons == B_PRIMARY_MOUSE_BUTTON
&& fLastClickButtons == B_PRIMARY_MOUSE_BUTTON && (mods & B_CONTROL_KEY) == 0) {
fTrackRightMouseUp = false;
fTrackMouseUp = false;
if (!WasClickInPath(pose, index, where))
OpenSelection(pose, &index);
}
} else {
fLastClickedPose = NULL;
if (fTextWidgetToCheck != NULL)
fTextWidgetToCheck->CancelWait();
window->Activate();
window->UpdateIfNeeded();
if (!extendSelection || !fSelectionRectEnabled || !fMultipleSelection)
ClearSelection();
if (SecondaryMouseButtonDown(mods, buttons))
ShowContextMenu(where);
}
if (fSelectionChangedHook)
window->SelectionChanged();
}
void
BPoseView::SetTextWidgetToCheck(BTextWidget* widget, BTextWidget* old)
{
if (old == NULL || fTextWidgetToCheck == old)
fTextWidgetToCheck = widget;
}
void
BPoseView::MouseUp(BPoint where)
{
if (fSelectionRectInfo.isDragging)
_EndSelectionRect();
DragStop();
int32 index;
BPose* pose = FindPose(where, &index);
uint32 lastButtons = Window()->CurrentMessage()->FindInt32("last_buttons");
if (pose != NULL && fLastClickedPose != NULL && fAllowPoseEditing
&& !fTrackRightMouseUp) {
pose->MouseUp(BPoint(0, index * fListElemHeight), this, where, index);
}
if (pose != NULL && fTrackRightMouseUp
&& (SecondaryMouseButtonDown(modifiers(), lastButtons))) {
if (!pose->IsSelected()) {
ClearSelection();
pose->Select(true);
fSelectionList->AddItem(pose);
DrawPose(pose, index, false);
}
ShowContextMenu(where);
}
if (fTrackMouseUp)
Window()->Activate();
fTrackRightMouseUp = false;
fTrackMouseUp = false;
}
bool
BPoseView::WasClickInPath(const BPose* pose, int32 index, BPoint where) const
{
if (pose == NULL || (ViewMode() != kListMode))
return false;
BPoint loc(0, index * fListElemHeight);
BTextWidget* widget;
if (!pose->PointInPose(loc, this, where, &widget) || widget == NULL)
return false;
if (widget->AttrHash() != AttrHashString(kAttrPath, B_STRING_TYPE))
return false;
BEntry entry(widget->Text(this));
if (entry.InitCheck() != B_OK)
return false;
entry_ref ref;
if (entry.GetRef(&ref) == B_OK) {
BMessage message(B_REFS_RECEIVED);
message.AddRef("refs", &ref);
BMessenger(kTrackerSignature).SendMessage(&message);
return true;
}
return false;
}
bool
BPoseView::WasDoubleClick(const BPose* pose, BPoint where)
{
ASSERT(Window() != NULL);
ASSERT(Window()->CurrentMessage() != NULL);
BPoint delta = where - fLastClickPoint;
int32 buttons = Window()->CurrentMessage()->GetInt32("buttons", 0);
int32 clicks = Window()->CurrentMessage()->GetInt32("clicks", 0);
bool xGood = fabs(delta.x) < kDoubleClickTresh;
bool yGood = fabs(delta.y) < kDoubleClickTresh;
if (clicks == 2 && pose == fLastClickedPose && xGood && yGood) {
fLastClickPoint.Set(INT32_MAX, INT32_MAX);
fLastClickedPose = NULL;
if (fTextWidgetToCheck != NULL)
fTextWidgetToCheck->CancelWait();
return buttons == fLastClickButtons;
}
fLastClickPoint = where;
fLastClickedPose = pose;
fLastClickButtons = buttons;
return false;
}
static void
AddPoseRefToMessage(Model* model, BMessage* message)
{
BNode node(model->EntryRef());
if (node.InitCheck() == B_OK) {
BNodeInfo info(&node);
char type[B_MIME_TYPE_LENGTH];
type[0] = '\0';
if (info.GetType(type) != B_OK) {
BPath path(model->EntryRef());
if (path.InitCheck() == B_OK)
update_mime_info(path.Path(), false, false, false);
}
}
message->AddRef("refs", model->EntryRef());
}
void
BPoseView::DragSelectedPoses(const BPose* pose, BPoint where, uint32 buttons)
{
if (!fDragEnabled || buttons == 0)
return;
ASSERT(pose != NULL);
if (!pose->IsSelected())
return;
BMessage message(B_SIMPLE_DATA);
message.AddPointer("src_window", Window());
message.AddPoint("click_pt", where);
message.AddMessenger("TrackerViewToken", BMessenger(this));
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++)
AddPoseRefToMessage(fSelectionList->ItemAt(index)->TargetModel(), &message);
int32 index = CurrentPoseList()->IndexOf(pose);
message.AddInt32("buttons", (int32)buttons);
BRect dragRect(GetDragRect(index));
BBitmap* dragBitmap = NULL;
BPoint offset;
dragBitmap = MakeDragBitmap(dragRect, where, index, offset);
if (dragBitmap != NULL)
_inherited::DragMessage(&message, dragBitmap, B_OP_ALPHA, offset);
else
_inherited::DragMessage(&message, dragRect);
fAutoScrollState = kWaitForTransition;
Window()->SetPulseRate(100000);
}
BBitmap*
BPoseView::MakeDragBitmap(BRect dragRect, BPoint where, int32 poseIndex, BPoint& offset)
{
BRect bounds(Bounds());
int32 startIndex;
if (ViewMode() == kListMode)
startIndex = (int32)(bounds.top / fListElemHeight);
else
startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()));
const PoseList* poseList = CurrentPoseList();
if (poseList == NULL)
return NULL;
int32 poseCount = poseList->CountItems();
if (poseCount == 0)
return NULL;
BRect inner(where.x - roundf(kTransparentDragThreshold.x / 2),
where.y - roundf(kTransparentDragThreshold.y / 2),
where.x + roundf(kTransparentDragThreshold.x / 2),
where.y + roundf(kTransparentDragThreshold.y / 2));
if (!inner.Intersects(dragRect))
return NULL;
if (inner.right > dragRect.right)
inner.OffsetBy(dragRect.right - inner.right, 0);
if (inner.bottom > dragRect.bottom)
inner.OffsetBy(0, dragRect.bottom - inner.bottom);
if (inner.left < dragRect.left)
inner.OffsetBy(dragRect.left - inner.left, 0);
if (inner.top < dragRect.top)
inner.OffsetBy(0, dragRect.top - inner.top);
float fadeWidth = be_control_look->ComposeIconSize(32).Width();
bool fadeTop = false;
bool fadeBottom = false;
bool fadeLeft = false;
bool fadeRight = false;
bool fade = false;
if (inner.left > dragRect.left) {
inner.left = std::max(inner.left - fadeWidth, dragRect.left);
fade = fadeLeft = true;
}
if (inner.right < dragRect.right) {
inner.right = std::min(inner.right + fadeWidth, dragRect.right);
fade = fadeRight = true;
}
if (inner.top > dragRect.top) {
inner.top = std::max(inner.top - fadeWidth, dragRect.top);
fade = fadeTop = true;
}
if (inner.bottom < dragRect.bottom) {
inner.bottom = std::min(inner.bottom + fadeWidth, dragRect.bottom);
fade = fadeBottom = true;
}
offset = where - BPoint(2, 1) - inner.LeftTop();
BRect rect(inner);
rect.OffsetTo(B_ORIGIN);
BBitmap* bitmap = new BBitmap(rect, B_RGBA32, true);
bitmap->Lock();
BView* view = new BView(bitmap->Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW);
bitmap->AddChild(view);
view->SetOrigin(B_ORIGIN);
BRect clipRect(view->Bounds());
BRegion newClip;
newClip.Set(clipRect);
view->ConstrainClippingRegion(&newClip);
memset(bitmap->Bits(), 0, bitmap->BitsLength());
view->SetDrawingMode(B_OP_ALPHA);
view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
uint8 alpha = fade ? 164 : 192;
if (HighColor().IsDark())
view->SetHighColor(0, 0, 0, alpha);
else
view->SetHighColor(255, 255, 255, alpha);
BPoint offsetBy = B_ORIGIN;
BPoint rowLocation = B_ORIGIN;
if (ViewMode() == kListMode)
rowLocation = BPoint(0, startIndex * fListElemHeight);
BPose* pose;
BRect poseRect;
for (int32 index = startIndex; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose != NULL && pose->IsSelected()) {
if (ViewMode() == kListMode)
poseRect = BRect(pose->CalcRect(rowLocation, this, true));
else
poseRect = BRect(pose->CalcRect(this));
if (poseRect.Intersects(inner)) {
offsetBy = BPoint(-inner.LeftTop().x, -inner.LeftTop().y);
pose->Draw(poseRect, poseRect, this, view, true, offsetBy, true);
}
}
if (ViewMode() == kListMode) {
rowLocation.y += fListElemHeight;
if (rowLocation.y > bounds.bottom)
break;
}
}
view->Sync();
if (fade) {
uint32* bits = (uint32*)bitmap->Bits();
int32 width = bitmap->BytesPerRow() / 4;
if (fadeLeft) {
FadeRGBA32Horizontal(bits, width, int32(rect.bottom), 0,
int32(fadeWidth));
}
if (fadeRight) {
FadeRGBA32Horizontal(bits, width, int32(rect.bottom), int32(rect.right),
int32(rect.right - fadeWidth));
}
if (fadeTop) {
FadeRGBA32Vertical(bits, width, int32(rect.bottom), 0,
int32(fadeWidth));
}
if (fadeBottom) {
FadeRGBA32Vertical(bits, width, int32(rect.bottom), int32(rect.bottom),
int32(rect.bottom - fadeWidth));
}
}
bitmap->Unlock();
return bitmap;
}
BRect
BPoseView::GetDragRect(int32 poseIndex)
{
BRect dragRect;
BRect bounds(Bounds());
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
BPose* pose = poseList->ItemAt(poseIndex);
if (ViewMode() == kListMode) {
dragRect = CalcPoseRectList(pose, poseIndex, true);
int32 poseCount = poseList->CountItems();
int32 startIndex = (int32)(bounds.top / fListElemHeight);
BPoint loc(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose != NULL && pose->IsSelected())
dragRect = dragRect | pose->CalcRect(loc, this, true);
loc.y += fListElemHeight;
if (loc.y > bounds.bottom)
break;
}
} else {
dragRect = pose->CalcRect(this);
const int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()));
for (int32 index = startIndex; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose != NULL) {
if (pose->IsSelected())
dragRect = dragRect | pose->CalcRect(this);
if (pose->Location(this).y > bounds.bottom)
break;
}
}
}
return dragRect;
}
void
BPoseView::SelectPoses(BRect selectionRect, BList** oldList)
{
const bool inListMode = (ViewMode() == kListMode);
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
BList* newList = new BList(poseCount);
BRect bounds(Bounds());
int32 startIndex;
if (inListMode)
startIndex = (int32)(selectionRect.top / fListElemHeight);
else
startIndex = FirstIndexAtOrBelow((int32)(selectionRect.top - IconPoseHeight()), true);
if (startIndex < 0)
startIndex = 0;
BPoint listLoc;
if (inListMode)
listLoc.Set(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose == NULL)
break;
BRect poseRect;
if (inListMode)
poseRect = pose->CalcRect(listLoc, this);
else
poseRect = pose->CalcRect(this);
if (selectionRect.Intersects(poseRect)) {
bool selected = pose->IsSelected();
pose->Select(!fSelectionList->HasItem(pose));
newList->AddItem((void*)(addr_t)index);
if (selected != pose->IsSelected())
Invalidate(poseRect);
if (fSelectionPivotPose == NULL && selected == false)
fSelectionPivotPose = pose;
}
if (inListMode) {
listLoc.y += fListElemHeight;
if (listLoc.y > selectionRect.bottom)
break;
} else {
if (pose->Location(this).y > selectionRect.bottom)
break;
}
}
int32 count = (*oldList)->CountItems();
for (int32 index = 0; index < count; index++) {
int32 oldIndex = (addr_t)(*oldList)->ItemAt(index);
if (!newList->HasItem((void*)(addr_t)oldIndex)) {
BPose* pose = poseList->ItemAt(oldIndex);
pose->Select(!pose->IsSelected());
BRect poseRect;
if (inListMode) {
listLoc.Set(0, oldIndex * fListElemHeight);
poseRect = pose->CalcRect(listLoc, this);
} else {
poseRect = pose->CalcRect(this);
}
if (poseRect.Intersects(bounds))
Invalidate(poseRect);
}
}
delete *oldList;
*oldList = newList;
}
void
BPoseView::AddRemoveSelectionRange(BPoint where, bool extendSelection, BPose* pose)
{
ASSERT(pose != NULL);
if (pose == fSelectionPivotPose && !extendSelection)
return;
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
if (fMultipleSelection && (modifiers() & B_SHIFT_KEY) != 0 && fSelectionPivotPose) {
bool select = !pose->IsSelected() || !extendSelection;
if (!extendSelection) {
const BPose* savedPivotPose = fSelectionPivotPose;
ClearSelection();
fSelectionPivotPose = savedPivotPose;
}
if (ViewMode() == kListMode) {
int32 currentSelectedIndex = poseList->IndexOf(pose);
int32 lastSelectedIndex = poseList->IndexOf(fSelectionPivotPose);
int32 startRange;
int32 endRange;
if (lastSelectedIndex < currentSelectedIndex) {
startRange = lastSelectedIndex;
endRange = currentSelectedIndex;
} else {
startRange = currentSelectedIndex;
endRange = lastSelectedIndex;
}
for (int32 i = startRange; i <= endRange; i++)
AddRemovePoseFromSelection(poseList->ItemAt(i), i, select);
} else {
BRect selection(where, fSelectionPivotPose->Location(this));
if (selection.left > selection.right)
std::swap(selection.left, selection.right);
if (selection.top > selection.bottom)
std::swap(selection.top, selection.bottom);
if (selection.IntegerWidth() < 1)
selection.right = selection.left + 1.0f;
if (selection.IntegerHeight() < 1)
selection.bottom = selection.top + 1.0f;
ASSERT(selection.IsValid());
for (int32 index = poseCount - 1; index >= 0; index--) {
BPose* currPose = poseList->ItemAt(index);
if (selection.Intersects(currPose->CalcRect(this)))
AddRemovePoseFromSelection(currPose, index, select);
}
}
} else {
int32 index = poseList->IndexOf(pose);
if (!extendSelection) {
if (!pose->IsSelected()) {
ClearSelection();
AddRemovePoseFromSelection(pose, index, true);
fSelectionPivotPose = pose;
}
} else {
fMimeTypesInSelectionCache.MakeEmpty();
AddRemovePoseFromSelection(pose, index, !pose->IsSelected());
}
}
if (fSelectionList->IsEmpty()) {
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
} else if (fSelectionPivotPose == NULL) {
fSelectionPivotPose = pose;
fRealPivotPose = pose;
}
}
void
BPoseView::DeleteSymLinkPoseTarget(const node_ref* itemNode, BPose* pose, int32 index)
{
ASSERT(pose->TargetModel()->IsSymLink());
watch_node(itemNode, B_STOP_WATCHING, this);
WatchParentOf(pose->TargetModel()->EntryRef());
BPoint loc(0, index * fListElemHeight);
pose->TargetModel()->SetLinkTo(NULL);
pose->UpdateBrokenSymLink(loc, this);
}
bool
BPoseView::DeletePose(const node_ref* itemNode, BPose* pose, int32 index)
{
watch_node(itemNode, B_STOP_WATCHING, this);
if (pose == NULL)
pose = fPoseList->FindPose(itemNode, &index);
if (pose != NULL) {
fInsertedNodes.Remove(*itemNode);
if (pose->TargetModel()->IsSymLink()) {
fBrokenLinks->RemoveItem(pose->TargetModel());
StopWatchingParentsOf(pose->TargetModel()->EntryRef());
Model* target = pose->TargetModel()->LinkTo();
if (target != NULL)
watch_node(target->NodeRef(), B_STOP_WATCHING, this);
}
ASSERT(TargetModel() != NULL);
if (pose == fDropTarget)
fDropTarget = NULL;
if (pose == ActivePose())
CommitActivePose();
Window()->UpdateIfNeeded();
fSelectionList->RemoveItem(pose);
if (fSelectionPivotPose == pose)
fSelectionPivotPose = NULL;
if (fRealPivotPose == pose)
fRealPivotPose = NULL;
if (pose->IsSelected() && fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
fPoseList->RemoveItemAt(index);
bool visible = true;
if (IsFiltering()) {
visible = fFilteredPoseList->FindPose(itemNode, &index) != NULL;
if (visible)
fFilteredPoseList->RemoveItemAt(index);
}
fMimeTypeListIsDirty = true;
if (pose->HasLocation())
RemoveFromVSList(pose);
if (visible) {
BRect invalidRect;
if (ViewMode() == kListMode)
invalidRect = CalcPoseRectList(pose, index);
else
invalidRect = pose->CalcRect(this);
if (ViewMode() == kListMode)
CloseGapInList(&invalidRect);
else
RemoveFromExtent(invalidRect);
Invalidate(invalidRect);
UpdateCount();
UpdateScrollRange();
ResetPosePlacementHint();
if (ViewMode() == kListMode) {
BRect bounds(Bounds());
int32 index = (int32)(bounds.bottom / fListElemHeight);
BPose* pose = CurrentPoseList()->ItemAt(index);
if (pose == NULL && bounds.top > 0) {
_inherited::ScrollTo(bounds.left, std::max(bounds.top - fListElemHeight, 0.0f));
}
}
}
delete pose;
} else {
Model* zombie = FindZombie(itemNode, &index);
if (zombie) {
PRINT(("deleting zombie model %s\n", zombie->Name()));
fZombieList->RemoveItemAt(index);
delete zombie;
} else
return false;
}
return true;
}
Model*
BPoseView::FindZombie(const node_ref* itemNode, int32* resultIndex)
{
int32 count = fZombieList->CountItems();
for (int32 index = 0; index < count; index++) {
Model* zombie = fZombieList->ItemAt(index);
if (*zombie->NodeRef() == *itemNode) {
if (resultIndex)
*resultIndex = index;
return zombie;
}
}
return NULL;
}
BPose*
BPoseView::FindPose(BPoint where, int32* poseIndex) const
{
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
BPose* pose;
if (ViewMode() == kListMode) {
int32 index = (int32)(where.y / fListElemHeight);
if (poseIndex != NULL)
*poseIndex = index;
BPoint poseLoc(0, index * fListElemHeight);
pose = poseList->ItemAt(index);
if (pose != NULL && pose->PointInPose(poseLoc, this, where))
return pose;
} else {
for (int32 index = poseCount - 1; index >= 0; index--) {
pose = poseList->ItemAt(index);
if (pose->PointInPose(this, where)) {
if (poseIndex)
*poseIndex = index;
return pose;
}
}
}
return NULL;
}
BPose*
BPoseView::FirstVisiblePose(int32* _index) const
{
ASSERT(ViewMode() == kListMode);
return FindPose(BPoint(fListOffset, Bounds().top + fListElemHeight - 1), _index);
}
BPose*
BPoseView::LastVisiblePose(int32* _index) const
{
ASSERT(ViewMode() == kListMode);
float poseY = Bounds().top + Frame().Height() - fListElemHeight + 2;
BPose* pose = FindPose(BPoint(fListOffset, poseY), _index);
if (pose == NULL) {
pose = CurrentPoseList()->LastItem();
if (_index != NULL)
*_index = CurrentPoseList()->CountItems() - 1;
}
return pose;
}
void
BPoseView::OpenSelection(BPose* clickedPose, int32* index)
{
BPose* singleWindowBrowsePose = clickedPose;
TrackerSettings settings;
if (settings.SingleWindowBrowse()
&& !singleWindowBrowsePose
&& CountSelected() == 1
&& !IsFilePanel()) {
singleWindowBrowsePose = fSelectionList->ItemAt(0);
}
if (settings.SingleWindowBrowse() && !IsDesktopView() && !IsFilePanel()
&& (modifiers() & B_OPTION_KEY) == 0 && TargetModel()->IsDirectory()
&& singleWindowBrowsePose && singleWindowBrowsePose->ResolvedModel()
&& singleWindowBrowsePose->ResolvedModel()->IsDirectory()) {
BMessage msg(kSwitchDirectory);
msg.AddRef("refs", singleWindowBrowsePose->ResolvedModel()->EntryRef());
Window()->PostMessage(&msg);
} else {
OpenSelectionCommon(clickedPose, index, false);
}
}
void
BPoseView::OpenSelectionUsing(BPose* clickedPose, int32* index)
{
OpenSelectionCommon(clickedPose, index, true);
}
void
BPoseView::OpenSelectionCommon(BPose* clickedPose, int32* poseIndex, bool openWith)
{
int32 selectCount = CountSelected();
if (selectCount == 0)
return;
BMessage message(B_REFS_RECEIVED);
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
message.AddRef("refs", pose->TargetModel()->EntryRef());
if (dynamic_cast<TTracker*>(be_app) == NULL || (modifiers() & B_OPTION_KEY) == 0
|| IsFilePanel() || IsDesktopView() || TrackerSettings().SingleWindowBrowse()) {
continue;
}
ASSERT(TargetModel() != NULL);
message.AddData("nodeRefsToClose", B_RAW_TYPE, TargetModel()->NodeRef(),
sizeof (node_ref));
}
if (openWith)
message.AddInt32("launchUsingSelector", 0);
message.AddMessenger("TrackerViewToken", BMessenger(this));
if (fSelectionHandler)
fSelectionHandler->PostMessage(&message);
if (clickedPose) {
ASSERT(poseIndex != NULL);
if (ViewMode() == kListMode)
DrawOpenAnimation(CalcPoseRectList(clickedPose, *poseIndex, true));
else
DrawOpenAnimation(clickedPose->CalcRect(this));
}
}
void
BPoseView::DrawOpenAnimation(BRect rect)
{
SetDrawingMode(B_OP_INVERT);
BRect box1(rect);
box1.InsetBy(rect.Width() / 2 - 2, rect.Height() / 2 - 2);
BRect box2(box1);
for (int32 index = 0; index < 7; index++) {
box2 = box1;
box2.InsetBy(-2, -2);
StrokeRect(box1, B_MIXED_COLORS);
Sync();
StrokeRect(box2, B_MIXED_COLORS);
Sync();
snooze(10000);
StrokeRect(box1, B_MIXED_COLORS);
StrokeRect(box2, B_MIXED_COLORS);
Sync();
box1 = box2;
}
SetDrawingMode(B_OP_OVER);
}
bool
BPoseView::CanUnmountSelection()
{
int32 selectCount = CountSelected();
if (selectCount == 0)
return false;
BVolume boot;
BVolumeRoster().GetBootVolume(&boot);
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
if (pose == NULL)
continue;
Model* model = pose->TargetModel();
if (!model->IsVolume())
return false;
BVolume volume(model->NodeRef()->device);
if (volume.InitCheck() != B_OK)
continue;
if (volume == boot)
return false;
}
return true;
}
void
BPoseView::UnmountSelectedVolumes()
{
BVolume boot;
BVolumeRoster().GetBootVolume(&boot);
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
if (pose == NULL)
continue;
Model* model = pose->TargetModel();
if (!model->IsVolume())
continue;
BVolume volume(model->NodeRef()->device);
if (volume.InitCheck() != B_OK)
continue;
if (volume == boot)
continue;
BMessage message(kUnmountVolume);
message.AddInt32("device_id", volume.Device());
be_app->PostMessage(&message);
}
}
void
BPoseView::ClearPoses()
{
CommitActivePose();
SavePoseLocations();
ClearTypeAheadFiltering();
fPoseList->MakeEmpty();
fFilteredPoseList->MakeEmpty();
fMimeTypeListIsDirty = true;
fVSPoseList->MakeEmpty();
fZombieList->MakeEmpty();
fSelectionList->MakeEmpty();
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
fMimeTypesInSelectionCache.MakeEmpty();
fBrokenLinks->MakeEmpty();
DisableScrollBars();
ScrollTo(B_ORIGIN);
UpdateScrollRange();
SetScrollBarsTo(B_ORIGIN);
EnableScrollBars();
ResetPosePlacementHint();
ClearExtent();
if (fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
}
void
BPoseView::SwitchDir(const entry_ref* newDirRef, AttributeStreamNode* node)
{
ASSERT(TargetModel() != NULL);
if (*newDirRef == *TargetModel()->EntryRef())
return;
Model* model = new Model(newDirRef, true);
if (model->InitCheck() != B_OK || !model->IsDirectory()) {
delete model;
return;
}
CommitActivePose();
fAddPosesThreads.clear();
fInsertedNodes.Clear();
delete fModel;
fModel = model;
StopWatching();
ClearPoses();
uint32 oldMode = ViewMode();
bool viewStateRestored = false;
if (node != NULL) {
BViewState* previousState = fViewState;
RestoreState(node);
viewStateRestored = (fViewState != previousState);
}
if (viewStateRestored) {
if (ViewMode() == kListMode && oldMode != kListMode) {
if (ContainerWindow() != NULL)
ContainerWindow()->ShowAttributesMenu();
fTitleView->Show();
} else if (ViewMode() != kListMode && oldMode == kListMode) {
fTitleView->Hide();
if (ContainerWindow() != NULL)
ContainerWindow()->HideAttributesMenu();
} else if (ViewMode() == kListMode && oldMode == kListMode)
fTitleView->Invalidate();
BPoint origin;
if (ViewMode() == kListMode)
origin = fViewState->ListOrigin();
else
origin = fViewState->IconOrigin();
PinPointToValidRange(origin);
SetIconPoseHeight();
GetLayoutInfo(ViewMode(), &fGrid, &fOffset);
ResetPosePlacementHint();
DisableScrollBars();
ScrollTo(origin);
UpdateScrollRange();
SetScrollBarsTo(origin);
EnableScrollBars();
} else {
ResetOrigin();
ResetPosePlacementHint();
}
StartWatching();
if (TargetModel() != NULL && TargetModel()->IsTrash())
AddTrashPoses();
else
AddPoses(TargetModel());
TargetModel()->CloseNode();
AdoptSystemColors();
Invalidate();
fLastKeyTime = 0;
}
void
BPoseView::Refresh()
{
ASSERT(TargetModel() != NULL);
if (TargetModel()->OpenNode() != B_OK)
return;
StopWatching();
fInsertedNodes.Clear();
ClearPoses();
StartWatching();
AddPoses(TargetModel());
TargetModel()->CloseNode();
if (IsRefFiltering())
RebuildFilteringPoseList();
Invalidate();
ResetOrigin();
ResetPosePlacementHint();
}
void
BPoseView::ResetOrigin()
{
DisableScrollBars();
ScrollTo(B_ORIGIN);
UpdateScrollRange();
SetScrollBarsTo(B_ORIGIN);
EnableScrollBars();
}
void
BPoseView::EditQueries()
{
SendSelectionAsRefs(kEditQuery, true);
}
void
BPoseView::SendSelectionAsRefs(uint32 what, bool onlyQueries)
{
int32 selectCount = CountSelected();
if (selectCount <= 0)
return;
bool haveRef = false;
BMessage message;
message.what = what;
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
if (onlyQueries) {
BEntry resolvedEntry(pose->TargetModel()->EntryRef(), true);
if (resolvedEntry.InitCheck() != B_OK)
continue;
Model model(&resolvedEntry);
if (!model.IsQuery() && !model.IsQueryTemplate())
continue;
}
haveRef = true;
message.AddRef("refs", pose->TargetModel()->EntryRef());
}
if (!haveRef)
return;
if (onlyQueries)
message.AddBool("editQueryOnPose", onlyQueries);
BMessenger(kTrackerSignature).SendMessage(&message);
}
void
BPoseView::OpenInfoWindows()
{
BMessenger tracker(kTrackerSignature);
if (!tracker.IsValid()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("The Tracker must be running to see Info windows."),
B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return;
}
if (fSelectionList != NULL && fSelectionList->CountItems() > 0)
SendSelectionAsRefs(kGetInfo);
else if (TargetModel()->EntryRef() != NULL) {
BMessage message(kGetInfo);
message.AddRef("refs", TargetModel()->EntryRef());
BMessenger(kTrackerSignature).SendMessage(&message);
}
}
void
BPoseView::SetDefaultPrinter()
{
BMessenger trackerMessenger(kTrackerSignature);
if (!trackerMessenger.IsValid()) {
BAlert* alert = new BAlert("",
B_TRANSLATE("The Tracker must be running to set the default "
"printer."), B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return;
}
SendSelectionAsRefs(kMakeActivePrinter);
}
void
BPoseView::OpenParent()
{
if (!CanOpenParent())
return;
BMessage message(kOpenParentDir);
message.AddRef("refs", TargetModel()->EntryRef());
Window()->PostMessage(&message);
}
bool
BPoseView::CanOpenParent()
{
TrackerSettings settings;
BEntry entry(TargetModel()->EntryRef());
if (IsFilePanel()) {
if (settings.DesktopFilePanelRoot()) {
if (TargetModel()->IsDesktop())
return false;
} else if (TargetModel()->IsRoot()) {
return false;
}
} else {
if (IsDesktopView() || TargetModel()->IsRoot())
return false;
}
if ((modifiers() & B_CONTROL_KEY) != 0)
return true;
if (IsFilePanel() && settings.DesktopFilePanelRoot())
return true;
BEntry parentEntry;
if (FSGetParentVirtualDirectoryAware(entry, parentEntry) != B_OK)
return false;
bool parentIsRoot = FSIsRootDir(&parentEntry);
if (parentIsRoot && settings.ShowDisksIcon())
return true;
return !parentIsRoot;
}
void
BPoseView::IdentifySelection(bool force)
{
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
BEntry entry(pose->TargetModel()->ResolveIfLink()->EntryRef());
if (entry.InitCheck() == B_OK) {
BPath path;
if (entry.GetPath(&path) == B_OK)
update_mime_info(path.Path(), true, false, force ? 2 : 1);
}
}
}
void
BPoseView::ClearSelection()
{
CommitActivePose();
fSelectionPivotPose = NULL;
fRealPivotPose = NULL;
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
if (CountSelected() > 0) {
BRect bounds(Bounds());
if (ViewMode() == kListMode) {
int32 startIndex = (int32)(bounds.top / fListElemHeight);
BPoint loc(0, startIndex * fListElemHeight);
const PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose->IsSelected()) {
pose->Select(false);
Invalidate(pose->CalcRect(loc, this, false));
}
loc.y += fListElemHeight;
if (loc.y > bounds.bottom)
break;
}
} else {
const int32 startIndex
= FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()), true);
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose != NULL) {
if (pose->IsSelected()) {
pose->Select(false);
Invalidate(pose->CalcRect(this));
}
if (pose->Location(this).y > bounds.bottom)
break;
}
}
}
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++)
fSelectionList->ItemAt(index)->Select(false);
fSelectionList->MakeEmpty();
}
fMimeTypesInSelectionCache.MakeEmpty();
}
void
BPoseView::ShowSelection(bool show)
{
if (fSelectionVisible == show)
return;
fSelectionVisible = show;
if (CountSelected() <= 0)
return;
BRect bounds(Bounds());
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
if (ViewMode() == kListMode) {
const int32 startIndex = (int32)(bounds.top / fListElemHeight);
BPoint loc(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose == NULL)
break;
if (fSelectionList->HasItem(pose)) {
if (pose->IsSelected() != show || fShowSelectionWhenInactive) {
if (!fShowSelectionWhenInactive)
pose->Select(show);
Invalidate(pose->CalcRect(loc, this, false));
}
}
loc.y += fListElemHeight;
if (loc.y > bounds.bottom)
break;
}
} else {
const int32 startIndex
= FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()), true);
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose == NULL)
break;
if (fSelectionList->HasItem(pose)) {
if (pose->IsSelected() != show || fShowSelectionWhenInactive) {
if (!fShowSelectionWhenInactive)
pose->Select(show);
Invalidate(pose->CalcRect(this));
}
}
if (pose->Location(this).y > bounds.bottom)
break;
}
}
int32 selectCount = CountSelected();
for (int32 index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
if (pose->IsSelected() != show && !fShowSelectionWhenInactive)
pose->Select(show);
}
if (!show) {
fRealPivotPose = fSelectionPivotPose;
fSelectionPivotPose = NULL;
} else {
if (fRealPivotPose)
fSelectionPivotPose = fRealPivotPose;
fRealPivotPose = NULL;
}
}
void
BPoseView::AddRemovePoseFromSelection(BPose* pose, int32 index, bool select)
{
if (select == pose->IsSelected())
return;
pose->Select(select);
if (ViewMode() == kListMode)
Invalidate(pose->CalcRect(BPoint(0, index * fListElemHeight), this, false));
else
Invalidate(pose->CalcRect(this));
if (select)
fSelectionList->AddItem(pose);
else {
fSelectionList->RemoveItem(pose, false);
if (fSelectionPivotPose == pose)
fSelectionPivotPose = NULL;
if (fRealPivotPose == pose)
fRealPivotPose = NULL;
}
}
bool
BPoseView::SelectedVolumeIsReadOnly() const
{
BVolume volume;
BPose* pose;
BEntry entry;
BNode parent;
node_ref nref;
int32 selectCount = fSelectionList->CountItems();
if (selectCount > 1 && TargetModel()->IsQuery()) {
for (int32 i = 0; i < selectCount; i++) {
pose = fSelectionList->ItemAt(i);
if (pose == NULL || pose->TargetModel() == NULL)
continue;
entry.SetTo(pose->TargetModel()->EntryRef());
if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK) {
parent.GetNodeRef(&nref);
volume.SetTo(nref.device);
if (volume.InitCheck() == B_OK && volume.IsReadOnly())
return true;
}
}
} else if (selectCount > 0) {
pose = fSelectionList->FirstItem();
if (pose->TargetModel()->IsRoot()) {
return true;
} else if (pose->TargetModel()->IsVolume()) {
volume.SetTo(pose->TargetModel()->NodeRef()->device);
} else {
entry.SetTo(pose->TargetModel()->EntryRef());
if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK) {
parent.GetNodeRef(&nref);
volume.SetTo(nref.device);
}
}
} else {
volume.SetTo(TargetModel()->NodeRef()->device);
}
return volume.InitCheck() == B_OK && volume.IsReadOnly();
}
bool
BPoseView::TargetVolumeIsReadOnly() const
{
Model* target = TargetModel();
BVolume volume(target->NodeRef()->device);
return target->IsQuery() || target->IsQueryTemplate() || target->IsVirtualDirectory()
|| (volume.InitCheck() == B_OK && volume.IsReadOnly());
}
bool
BPoseView::CanEditName() const
{
if (CountSelected() != 1)
return false;
Model* selected = fSelectionList->FirstItem()->TargetModel();
return !ActivePose() && selected != NULL && !selected->IsDesktop()
&& !selected->IsRoot() && !selected->IsTrash();
}
bool
BPoseView::CanMoveToTrashOrDuplicate() const
{
const int32 selectCount = CountSelected();
if (selectCount < 1)
return false;
if (SelectedVolumeIsReadOnly())
return false;
BPose* pose;
Model* selected;
for (int32 i = 0; i < selectCount; i++) {
pose = fSelectionList->ItemAt(i);
selected = pose->TargetModel();
if (pose == NULL || selected == NULL)
continue;
if (selected->IsDesktop() || selected->IsRoot() || selected->IsTrash())
return false;
}
return true;
}
void
BPoseView::RemoveFromExtent(const BRect &rect)
{
ASSERT(ViewMode() != kListMode);
if (rect.left <= fExtent.left || rect.top <= fExtent.top
|| rect.right >= fExtent.right || rect.bottom >= fExtent.bottom) {
RecalcExtent();
}
}
void
BPoseView::RecalcExtent()
{
ASSERT(ViewMode() != kListMode);
ClearExtent();
if (IsFiltering()) {
int32 poseCount = fFilteredPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++)
AddToExtent(fFilteredPoseList->ItemAt(index)->CalcRect(this));
} else {
int32 poseCount = fVSPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++)
AddToExtent(fVSPoseList->ItemAt(index)->CalcRect(this));
}
}
BRect
BPoseView::Extent() const
{
if (ViewMode() == kListMode)
return ListModeExtent();
else
return IconModeExtent();
}
BRect
BPoseView::ListModeExtent() const
{
BColumn* column = fColumnList->LastItem();
if (column == NULL)
return BRect(LeftTop().x, LeftTop().y, LeftTop().x, LeftTop().y);
BRect rect;
rect.left = rect.top = 0;
rect.right = column->Offset() + column->Width() + kTitleColumnRightExtraMargin
- kRoomForLine / 2.f;
rect.bottom = fListElemHeight * CurrentPoseList()->CountItems();
return rect;
}
BRect
BPoseView::IconModeExtent() const
{
BRect rect(fExtent.InsetByCopy(-fOffset));
if (!rect.IsValid())
rect.Set(LeftTop().x, LeftTop().y, LeftTop().x, LeftTop().y);
return rect;
}
void
BPoseView::SetScrollBarsTo(BPoint point)
{
if (fHScrollBar && fVScrollBar) {
fHScrollBar->SetValue(point.x);
fVScrollBar->SetValue(point.y);
} else {
BPoint origin = LeftTop();
ScrollTo(BPoint(origin.x, point.y));
ScrollTo(point);
}
}
void
BPoseView::PinPointToValidRange(BPoint& origin)
{
if (!(origin.x >= 0) && !(origin.x <= 0))
origin.x = 0;
else if (origin.x < -40000.0 || origin.x > 40000.0)
origin.x = 0;
if (!(origin.y >= 0) && !(origin.y <= 0))
origin.y = 0;
else if (origin.y < -40000.0 || origin.y > 40000.0)
origin.y = 0;
}
void
BPoseView::UpdateScrollRange()
{
AutoLock<BWindow> lock(Window());
if (!lock)
return;
BRect bounds(Bounds());
BPoint origin(LeftTop());
BRect extent(Extent());
lock.Unlock();
BPoint minVal(std::min(extent.left, origin.x),
std::min(extent.top, origin.y));
BPoint maxVal((extent.right - bounds.right) + origin.x,
(extent.bottom - bounds.bottom) + origin.y);
maxVal.x = std::max(maxVal.x, origin.x);
maxVal.y = std::max(maxVal.y, origin.y);
if (fHScrollBar) {
float scrollMin;
float scrollMax;
fHScrollBar->GetRange(&scrollMin, &scrollMax);
if (minVal.x != scrollMin || maxVal.x != scrollMax) {
fHScrollBar->SetRange(minVal.x, maxVal.x);
fHScrollBar->SetSteps(fListElemHeight / 2.0f, bounds.Width());
}
}
if (fVScrollBar) {
float scrollMin;
float scrollMax;
fVScrollBar->GetRange(&scrollMin, &scrollMax);
if (minVal.y != scrollMin || maxVal.y != scrollMax) {
fVScrollBar->SetRange(minVal.y, maxVal.y);
fVScrollBar->SetSteps(fListElemHeight / 2.0f, bounds.Height());
}
}
BRect totalExtent(extent | bounds);
if (fHScrollBar && totalExtent.Width() != 0.0) {
float proportion = bounds.Width() / totalExtent.Width();
if (fHScrollBar->Proportion() != proportion)
fHScrollBar->SetProportion(proportion);
}
if (fVScrollBar && totalExtent.Height() != 0.0) {
float proportion = bounds.Height() / totalExtent.Height();
if (fVScrollBar->Proportion() != proportion)
fVScrollBar->SetProportion(proportion);
}
}
void
BPoseView::DrawPose(BPose* pose, int32 index, bool fullDraw)
{
BRect rect = CalcPoseRect(pose, index, fullDraw);
if (TrackerSettings().ShowVolumeSpaceBar() && pose->TargetModel()->IsVolume())
Invalidate(rect);
else
pose->Draw(rect, rect, this, fullDraw);
}
void
BPoseView::Draw(BRect updateRect)
{
DrawViewCommon(updateRect);
if ((Flags() & B_DRAW_ON_CHILDREN) == 0)
DrawAfterChildren(updateRect);
_inherited::Draw(updateRect);
}
void
BPoseView::DrawAfterChildren(BRect updateRect)
{
if (!fTransparentSelection || !fSelectionRectInfo.rect.IsValid())
return;
PushState();
SetDrawingMode(B_OP_ALPHA);
rgb_color color = ui_color(B_NAVIGATION_BASE_COLOR);
color.alpha = 128;
SetHighColor(color);
if (fSelectionRectInfo.rect.Width() == 0 || fSelectionRectInfo.rect.Height() == 0) {
StrokeLine(fSelectionRectInfo.rect.LeftTop(), fSelectionRectInfo.rect.RightBottom());
} else {
StrokeRect(fSelectionRectInfo.rect);
BRect interior = fSelectionRectInfo.rect;
interior.InsetBy(1, 1);
if (interior.IsValid()) {
color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
color.alpha = 90;
SetHighColor(color);
FillRect(interior);
}
}
PopState();
}
void
BPoseView::SynchronousUpdate(BRect updateRect, bool clip)
{
if (clip) {
BRegion updateRegion;
updateRegion.Set(updateRect);
ConstrainClippingRegion(&updateRegion);
}
Invalidate(updateRect);
Window()->UpdateIfNeeded();
if (clip)
ConstrainClippingRegion(NULL);
}
void
BPoseView::DrawViewCommon(const BRect& updateRect)
{
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
BPose* pose;
BRect poseRect;
if (ViewMode() == kListMode) {
int32 startIndex = (int32)((updateRect.top - fListElemHeight) / fListElemHeight);
if (startIndex < 0)
startIndex = 0;
BPoint location(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose == NULL)
break;
poseRect = pose->CalcRect(location, this, false);
if (updateRect.Intersects(poseRect))
pose->Draw(poseRect, updateRect, this, true);
location.y += fListElemHeight;
if (location.y >= updateRect.bottom)
break;
}
} else {
for (int32 index = 0; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose == NULL)
break;
poseRect = pose->CalcRect(this);
if (updateRect.Intersects(poseRect))
pose->Draw(poseRect, updateRect, this, true);
if (pose->Location(this).y > updateRect.bottom)
break;
}
}
}
void
BPoseView::ColumnRedraw(BRect updateRect)
{
ASSERT(ViewMode() == kListMode);
#if COLUMN_MODE_ON_DESKTOP
if (IsDesktopView()) {
BScreen screen(Window());
rgb_color d = screen.DesktopColor();
SetLowColor(d);
SetViewColor(d);
}
#endif
int32 startIndex = (int32)((updateRect.top - fListElemHeight) / fListElemHeight);
if (startIndex < 0)
startIndex = 0;
const PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
if (poseCount <= 0)
return;
PushState();
SetDrawingMode(B_OP_COPY);
BPoint location(0, startIndex * fListElemHeight);
BRect srcRect = poseList->ItemAt(0)->CalcRect(B_ORIGIN, this, false);
srcRect.right += 1024;
sOffscreen->BeginUsing(srcRect);
BView* offscreenView = sOffscreen->View();
BRegion updateRegion;
updateRegion.Set(updateRect);
ConstrainClippingRegion(&updateRegion);
offscreenView->SetDrawingMode(B_OP_COPY);
if (!TargetVolumeIsReadOnly())
offscreenView->SetLowUIColor(LowUIColor());
else
offscreenView->SetLowUIColor(LowUIColor(), ReadOnlyTint(LowUIColor()));
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose == NULL)
break;
offscreenView->FillRect(offscreenView->Bounds(), B_SOLID_LOW);
BRect dstRect = srcRect.OffsetByCopy(location);
BPoint offsetBy(0, -(index * ListElemHeight()));
pose->Draw(dstRect, updateRect, this, offscreenView, true, offsetBy, pose->IsSelected());
offscreenView->Sync();
DrawBitmap(sOffscreen->Bitmap(), srcRect, dstRect);
if (!TargetVolumeIsReadOnly())
offscreenView->SetLowUIColor(LowUIColor());
else
offscreenView->SetLowUIColor(LowUIColor(), ReadOnlyTint(LowUIColor()));
location.y += fListElemHeight;
if (location.y > updateRect.bottom)
break;
}
sOffscreen->DoneUsing();
ConstrainClippingRegion(0);
PopState();
}
void
BPoseView::CloseGapInList(BRect* invalidRect)
{
(*invalidRect).bottom = Extent().bottom + fListElemHeight;
BRect bounds(Bounds());
if (bounds.Intersects(*invalidRect)) {
BRect destRect(*invalidRect);
destRect = destRect & bounds;
destRect.bottom -= fListElemHeight;
BRect srcRect(destRect);
srcRect.OffsetBy(0, fListElemHeight);
if (srcRect.Intersects(bounds) || destRect.Intersects(bounds))
CopyBits(srcRect, destRect);
*invalidRect = srcRect;
(*invalidRect).top = destRect.bottom;
}
}
void
BPoseView::CheckPoseSortOrder(BPose* pose, int32 oldIndex)
{
_CheckPoseSortOrder(CurrentPoseList(), pose, oldIndex);
}
void
BPoseView::_CheckPoseSortOrder(PoseList* poseList, BPose* pose, int32 oldIndex)
{
if (ViewMode() != kListMode)
return;
Window()->UpdateIfNeeded();
poseList->RemoveItemAt(oldIndex);
int32 afterIndex;
int32 orientation = BSearchList(poseList, pose, &afterIndex, oldIndex);
int32 newIndex;
if (orientation == kInsertAtFront)
newIndex = 0;
else
newIndex = afterIndex + 1;
if (newIndex == oldIndex) {
poseList->AddItem(pose, oldIndex);
return;
}
if (IsFiltering() && poseList != fFilteredPoseList) {
poseList->AddItem(pose, newIndex);
return;
}
BRect invalidRect(CalcPoseRectList(pose, oldIndex));
CloseGapInList(&invalidRect);
Invalidate(invalidRect);
InsertPoseAfter(pose, &afterIndex, orientation, &invalidRect);
poseList->AddItem(pose, newIndex);
Invalidate(invalidRect);
}
static int
PoseCompareAddWidget(const BPose* p1, const BPose* p2, BPoseView* view)
{
uint32 sort = view->PrimarySort();
BColumn* column = view->ColumnFor(sort);
if (column == NULL)
return 0;
BPose* primary;
BPose* secondary;
if (!view->ReverseSort()) {
primary = const_cast<BPose*>(p1);
secondary = const_cast<BPose*>(p2);
} else {
primary = const_cast<BPose*>(p2);
secondary = const_cast<BPose*>(p1);
}
int32 result = 0;
for (int32 count = 0; ; count++) {
BTextWidget* widget1 = primary->WidgetFor(sort);
if (widget1 == NULL)
widget1 = primary->AddWidget(view, column);
BTextWidget* widget2 = secondary->WidgetFor(sort);
if (widget2 == NULL)
widget2 = secondary->AddWidget(view, column);
if (widget1 == NULL || widget2 == NULL)
return result;
result = widget1->Compare(*widget2, view);
if (result != 0 || count != 0)
return result;
sort = view->SecondarySort();
if (!sort)
return result;
column = view->ColumnFor(sort);
if (column == NULL)
return result;
}
return result;
}
static BPose*
BSearch(PoseList* table, const BPose* key, BPoseView* view,
int (*cmp)(const BPose*, const BPose*, BPoseView*), bool returnClosest)
{
int32 r = table->CountItems();
BPose* result = 0;
for (int32 l = 1; l <= r;) {
int32 m = (l + r) / 2;
result = table->ItemAt(m - 1);
int32 compareResult = (cmp)(result, key, view);
if (compareResult == 0)
return result;
else if (compareResult < 0)
l = m + 1;
else
r = m - 1;
}
if (returnClosest)
return result;
return NULL;
}
int32
BPoseView::BSearchList(PoseList* poseList, const BPose* pose, int32* resultIndex, int32 oldIndex)
{
const BPose* firstPose = poseList->FirstItem();
if (!firstPose)
return kInsertAtFront;
if (PoseCompareAddWidget(pose, firstPose, this) < 0) {
*resultIndex = 0;
return kInsertAtFront;
}
int32 poseCount = poseList->CountItems();
bool valid = oldIndex > 0 && oldIndex < poseCount - 1;
valid = valid && PoseCompareAddWidget(pose,
poseList->ItemAt(oldIndex - 1), this) >= 0;
valid = valid && PoseCompareAddWidget(pose,
poseList->ItemAt(oldIndex), this) <= 0;
if (valid) {
*resultIndex = oldIndex - 1;
return kInsertAfter;
}
*resultIndex = poseCount - 1;
const BPose* searchResult = BSearch(poseList, pose, this, PoseCompareAddWidget);
if (searchResult != NULL) {
int32 index = poseList->IndexOf(searchResult);
for (; index < poseCount; index++) {
int32 result = PoseCompareAddWidget(pose, poseList->ItemAt(index), this);
if (result <= 0) {
--index;
break;
}
}
if (index != poseCount)
*resultIndex = index;
}
return kInsertAfter;
}
void
BPoseView::SetPrimarySort(uint32 attrHash)
{
BColumn* column = ColumnFor(attrHash);
if (column != NULL) {
fViewState->SetPrimarySort(attrHash);
fViewState->SetPrimarySortType(column->AttrType());
}
}
void
BPoseView::SetSecondarySort(uint32 attrHash)
{
BColumn* column = ColumnFor(attrHash);
if (column != NULL) {
fViewState->SetSecondarySort(attrHash);
fViewState->SetSecondarySortType(column->AttrType());
} else {
fViewState->SetSecondarySort(0);
fViewState->SetSecondarySortType(0);
}
}
void
BPoseView::SetReverseSort(bool reverse)
{
fViewState->SetReverseSort(reverse);
}
inline int
PoseCompareAddWidgetBinder(const BPose* p1, const BPose* p2,
void* castToPoseView)
{
return PoseCompareAddWidget(p1, p2, (BPoseView*)castToPoseView);
}
struct PoseComparator
{
PoseComparator(BPoseView* poseView): fPoseView(poseView) { }
bool operator() (const BPose* p1, const BPose* p2)
{
return PoseCompareAddWidget(p1, p2, fPoseView) < 0;
}
BPoseView* fPoseView;
};
#if xDEBUG
static BPose*
DumpOne(BPose* pose, void*)
{
pose->TargetModel()->PrintToStream(0);
return 0;
}
#endif
void
BPoseView::SortPoses()
{
if (fTextWidgetToCheck != NULL)
fTextWidgetToCheck->CancelWait();
CommitActivePose();
#if xDEBUG
fPoseList->EachElement(DumpOne, 0);
PRINT(("===================\n"));
#endif
BPose** poses = reinterpret_cast<BPose**>(fPoseList->AsBList()->Items());
std::stable_sort(poses, &poses[fPoseList->CountItems()], PoseComparator(this));
if (IsFiltering()) {
poses = reinterpret_cast<BPose**>(fFilteredPoseList->AsBList()->Items());
std::stable_sort(poses, &poses[fFilteredPoseList->CountItems()], PoseComparator(this));
}
}
BColumn*
BPoseView::ColumnFor(uint32 attr) const
{
int32 count = fColumnList->CountItems();
for (int32 index = 0; index < count; index++) {
BColumn* column = ColumnAt(index);
if (column->AttrHash() == attr)
return column;
}
return NULL;
}
bool
BPoseView::ResizeColumnToWidest(BColumn* column)
{
ASSERT(ViewMode() == kListMode);
float maxWidth = kMinColumnWidth;
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
for (int32 i = 0; i < poseCount; ++i) {
BTextWidget* widget = poseList->ItemAt(i)->WidgetFor(column->AttrHash());
if (widget != NULL) {
float width = widget->PreferredWidth(this);
if (width > maxWidth)
maxWidth = width;
}
}
if (maxWidth > kMinColumnWidth || maxWidth < column->Width()) {
ResizeColumn(column, maxWidth);
return true;
}
return false;
}
BPoint
BPoseView::ResizeColumn(BColumn* column, float newSize, float* lastLineDrawPos,
void (*drawLineFunc)(BPoseView*, BPoint, BPoint),
void (*undrawLineFunc)(BPoseView*, BPoint, BPoint))
{
BRect sourceRect(Bounds());
BPoint result(sourceRect.RightBottom());
BRect destRect(sourceRect);
BRect invalidateRect(sourceRect);
BRect columnDrawRect(sourceRect);
bool shrinking = newSize < column->Width();
columnDrawRect.left = column->Offset();
columnDrawRect.right = column->Offset() + kTitleColumnRightExtraMargin
- kRoomForLine + newSize;
sourceRect.left = column->Offset() + kTitleColumnRightExtraMargin
- kRoomForLine + column->Width();
destRect.left = columnDrawRect.right;
destRect.right = destRect.left + sourceRect.Width();
invalidateRect.left = destRect.right;
invalidateRect.right = sourceRect.right;
column->SetWidth(newSize);
float offset = StartOffset();
int32 count = fColumnList->CountItems();
for (int32 index = 0; index < count; index++) {
column = fColumnList->ItemAt(index);
column->SetOffset(offset);
BColumn* last = column;
offset = last->Offset() + last->Width() + kTitleColumnExtraMargin;
}
if (shrinking) {
ColumnRedraw(columnDrawRect);
CopyBits(sourceRect, destRect);
if (drawLineFunc != NULL) {
ASSERT(lastLineDrawPos != NULL);
(drawLineFunc)(this, BPoint(destRect.left + kRoomForLine, destRect.top),
BPoint(destRect.left + kRoomForLine, destRect.bottom));
*lastLineDrawPos = destRect.left + kRoomForLine;
}
} else {
CopyBits(sourceRect, destRect);
if (undrawLineFunc != NULL) {
ASSERT(lastLineDrawPos != NULL);
(undrawLineFunc)(this, BPoint(*lastLineDrawPos, sourceRect.top),
BPoint(*lastLineDrawPos, sourceRect.bottom));
}
if (drawLineFunc != NULL) {
ASSERT(lastLineDrawPos != NULL);
(drawLineFunc)(this, BPoint(destRect.left + kRoomForLine, destRect.top),
BPoint(destRect.left + kRoomForLine, destRect.bottom));
*lastLineDrawPos = destRect.left + kRoomForLine;
}
ColumnRedraw(columnDrawRect);
}
if (invalidateRect.left < invalidateRect.right)
SynchronousUpdate(invalidateRect, true);
fStateNeedsSaving = true;
return result;
}
void
BPoseView::MoveColumnTo(BColumn* src, BColumn* dest)
{
float miny = src->Offset();
if (miny > dest->Offset())
miny = dest->Offset();
int32 index = fColumnList->IndexOf(dest);
fColumnList->RemoveItem(src, false);
fColumnList->AddItem(src, index);
float offset = StartOffset();
int32 count = fColumnList->CountItems();
for (int32 index = 0; index < count; index++) {
BColumn* column = fColumnList->ItemAt(index);
column->SetOffset(offset);
BColumn* last = column;
offset = last->Offset() + last->Width() + kTitleColumnExtraMargin
- kRoomForLine / 2;
}
BRect bounds(Bounds());
bounds.left = miny;
Invalidate(bounds);
fStateNeedsSaving = true;
}
bool
BPoseView::UpdateDropTarget(BPoint mouseLoc, const BMessage* dragMessage,
bool trackingContextMenu)
{
ASSERT(dragMessage != NULL);
int32 index;
BPose* targetPose = FindPose(mouseLoc, &index);
if (targetPose != NULL && DragSelectionContains(targetPose, dragMessage))
targetPose = NULL;
if ((fCursorCheck && targetPose == fDropTarget)
|| (trackingContextMenu && !targetPose)) {
return false;
}
fCursorCheck = true;
if (fDropTarget && !DragSelectionContains(fDropTarget, dragMessage))
HiliteDropTarget(false);
fDropTarget = targetPose;
Model* targetModel = NULL;
if (targetPose != NULL)
targetModel = targetPose->TargetModel();
Model tmpTarget;
if (targetModel != NULL && targetModel->IsSymLink()
&& tmpTarget.SetTo(targetPose->TargetModel()->EntryRef(), true, true) == B_OK) {
targetModel = &tmpTarget;
}
bool ignoreTypes = (modifiers() & B_CONTROL_KEY) != 0;
if (targetPose != NULL) {
if (targetModel != NULL
&& CanHandleDragSelection(targetModel, dragMessage, ignoreTypes)) {
HiliteDropTarget(true);
} else {
fDropTarget = NULL;
fCursorCheck = false;
}
}
if (targetModel == NULL)
targetModel = TargetModel();
if (targetModel == NULL)
return false;
entry_ref srcRef;
if (targetModel->IsDirectory() && dragMessage->HasRef("refs")
&& dragMessage->FindRef("refs", &srcRef) == B_OK) {
Model srcModel(&srcRef);
if (!CheckDevicesEqual(&srcRef, targetModel)
&& !srcModel.IsVolume()
&& !srcModel.IsRoot()) {
BCursor copyCursor(B_CURSOR_ID_COPY);
SetViewCursor(©Cursor);
return true;
}
}
SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
return true;
}
bool
BPoseView::FrameForPose(BPose* targetPose, bool convert, BRect* poseRect)
{
bool frameIsValid = false;
BRect bounds(Bounds());
PoseList* poseList = CurrentPoseList();
int32 poseCount = poseList->CountItems();
if (ViewMode() == kListMode) {
int32 startIndex = (int32)(bounds.top / fListElemHeight);
BPoint location(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
if (targetPose == poseList->ItemAt(index)) {
*poseRect = fDropTarget->CalcRect(location, this, false);
frameIsValid = true;
}
location.y += fListElemHeight;
if (location.y > bounds.bottom)
frameIsValid = false;
}
} else {
int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()), true);
for (int32 index = startIndex; index < poseCount; index++) {
BPose* pose = poseList->ItemAt(index);
if (pose == NULL)
break;
if (pose == fDropTarget) {
*poseRect = pose->CalcRect(this);
frameIsValid = true;
break;
}
if (pose->Location(this).y > bounds.bottom) {
frameIsValid = false;
break;
}
}
}
if (convert)
ConvertToScreen(poseRect);
return frameIsValid;
}
bool
BPoseView::MenuTrackingHook(BMenu* menu, void*)
{
if (!menu->LockLooper())
return false;
uint32 buttons;
BPoint location;
menu->GetMouse(&location, &buttons);
bool mouseInMenu = true;
BRect bounds(menu->Bounds());
bounds.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
if (bounds.Contains(location)) {
mouseInMenu = false;
}
if (mouseInMenu) {
menu->ConvertToScreen(&location);
int32 poseCount = menu->CountItems();
for (int32 index = 0 ; index < poseCount; index++) {
BMenuItem* item = menu->ItemAt(index);
if (item && item->Submenu()) {
BWindow* window = item->Submenu()->Window();
bool inSubmenu = false;
if (window && window->Lock()) {
if (!window->IsHidden()) {
BRect frame(window->Frame());
frame.InsetBy(-kMenuTrackMargin, -kMenuTrackMargin);
inSubmenu = frame.Contains(location);
}
window->Unlock();
if (inSubmenu) {
mouseInMenu = false;
break;
}
}
}
}
}
menu->UnlockLooper();
return mouseInMenu;
}
void
BPoseView::HiliteDropTarget(bool hiliteState)
{
if (fDropTarget == NULL)
return;
if (fAlreadySelectedDropTarget != fDropTarget)
fAlreadySelectedDropTarget = NULL;
if (fDropTarget->IsSelected() && hiliteState) {
fAlreadySelectedDropTarget = fDropTarget;
return;
}
if ((fAlreadySelectedDropTarget == fDropTarget) && !hiliteState) {
fAlreadySelectedDropTarget = NULL;
return;
}
fDropTarget->Select(hiliteState);
BRect bounds(Bounds());
const PoseList* poseList = CurrentPoseList();
const int32 poseCount = poseList->CountItems();
BPose* pose;
BRect poseRect;
if (ViewMode() == kListMode) {
int32 startIndex = (int32)(bounds.top / fListElemHeight);
BPoint location(0, startIndex * fListElemHeight);
for (int32 index = startIndex; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose == NULL)
break;
if (fDropTarget == poseList->ItemAt(index)) {
poseRect = fDropTarget->CalcRect(location, this, false);
fDropTarget->Draw(poseRect, poseRect, this, false);
break;
}
location.y += fListElemHeight;
if (location.y > bounds.bottom)
break;
}
} else {
int32 startIndex = FirstIndexAtOrBelow((int32)(bounds.top - IconPoseHeight()), true);
for (int32 index = startIndex; index < poseCount; index++) {
pose = poseList->ItemAt(index);
if (pose == NULL)
break;
if (pose == fDropTarget) {
poseRect = fDropTarget->CalcRect(this);
if (!hiliteState) {
fDropTarget->DeselectWithoutErasingBackground(poseRect, this);
} else {
fDropTarget->Draw(poseRect, poseRect, this, false);
}
break;
}
if (pose->Location(this).y > bounds.bottom)
break;
}
}
}
bool
BPoseView::CheckAutoScroll(BPoint mouseLoc, bool shouldScroll)
{
if (!fShouldAutoScroll)
return false;
BContainerWindow* window = ContainerWindow();
if (window == NULL)
return false;
BRect bounds(Bounds());
BRect extent(Extent());
bool wouldScroll = false;
bool keepGoing;
float scrollIncrement;
BRect border(bounds);
border.bottom = border.top;
border.top -= kBorderHeight;
if (ViewMode() == kListMode)
border.top -= TitleView()->Bounds().Height();
bool selectionScrolling = fSelectionRectInfo.isDragging;
if (bounds.top > extent.top) {
if (selectionScrolling) {
keepGoing = mouseLoc.y < bounds.top;
if (fabs(bounds.top - mouseLoc.y) > kSlowScrollBucket)
scrollIncrement = fAutoScrollInc / 1.5f;
else
scrollIncrement = fAutoScrollInc / 4;
} else {
keepGoing = border.Contains(mouseLoc);
scrollIncrement = fAutoScrollInc;
}
if (keepGoing) {
wouldScroll = true;
if (shouldScroll) {
if (fVScrollBar != NULL) {
fVScrollBar->SetValue(
fVScrollBar->Value() - scrollIncrement);
} else
ScrollBy(0, -scrollIncrement);
}
}
}
border = bounds;
border.top = border.bottom;
border.bottom += (float)B_H_SCROLL_BAR_HEIGHT;
if (bounds.bottom < extent.bottom) {
if (selectionScrolling) {
keepGoing = mouseLoc.y > bounds.bottom;
if (fabs(bounds.bottom - mouseLoc.y) > kSlowScrollBucket)
scrollIncrement = fAutoScrollInc / 1.5f;
else
scrollIncrement = fAutoScrollInc / 4;
} else {
keepGoing = border.Contains(mouseLoc);
scrollIncrement = fAutoScrollInc;
}
if (keepGoing) {
wouldScroll = true;
if (shouldScroll) {
if (fVScrollBar != NULL) {
fVScrollBar->SetValue(
fVScrollBar->Value() + scrollIncrement);
} else
ScrollBy(0, scrollIncrement);
}
}
}
border = bounds;
border.right = border.left;
border.left -= 6;
if (bounds.left > extent.left) {
if (selectionScrolling) {
keepGoing = mouseLoc.x < bounds.left;
if (fabs(bounds.left - mouseLoc.x) > kSlowScrollBucket)
scrollIncrement = fAutoScrollInc / 1.5f;
else
scrollIncrement = fAutoScrollInc / 4;
} else {
keepGoing = border.Contains(mouseLoc);
scrollIncrement = fAutoScrollInc;
}
if (keepGoing) {
wouldScroll = true;
if (shouldScroll) {
if (fHScrollBar != NULL)
fHScrollBar->SetValue(fHScrollBar->Value() - scrollIncrement);
else
ScrollBy(-scrollIncrement, 0);
}
}
}
border = bounds;
border.left = border.right;
border.right += (float)B_V_SCROLL_BAR_WIDTH;
if (bounds.right < extent.right) {
if (selectionScrolling) {
keepGoing = mouseLoc.x > bounds.right;
if (fabs(bounds.right - mouseLoc.x) > kSlowScrollBucket)
scrollIncrement = fAutoScrollInc / 1.5f;
else
scrollIncrement = fAutoScrollInc / 4;
} else {
keepGoing = border.Contains(mouseLoc);
scrollIncrement = fAutoScrollInc;
}
if (keepGoing) {
wouldScroll = true;
if (shouldScroll) {
if (fHScrollBar != NULL)
fHScrollBar->SetValue(fHScrollBar->Value() + scrollIncrement);
else
ScrollBy(scrollIncrement, 0);
}
}
}
if (selectionScrolling)
_UpdateSelectionRect(mouseLoc);
return wouldScroll;
}
void
BPoseView::HandleAutoScroll()
{
if (!fShouldAutoScroll)
return;
uint32 buttons;
BPoint mouseLoc;
GetMouse(&mouseLoc, &buttons);
if (buttons == 0) {
fAutoScrollState = kAutoScrollOff;
Window()->SetPulseRate(500000);
return;
}
switch (fAutoScrollState) {
case kWaitForTransition:
if (CheckAutoScroll(mouseLoc, false) == false)
fAutoScrollState = kDelayAutoScroll;
break;
case kDelayAutoScroll:
if (CheckAutoScroll(mouseLoc, false) == true) {
snooze(600000);
GetMouse(&mouseLoc, &buttons);
if (CheckAutoScroll(mouseLoc, false) == true)
fAutoScrollState = kAutoScrollOn;
}
break;
case kAutoScrollOn:
CheckAutoScroll(mouseLoc, true);
break;
}
}
BRect
BPoseView::CalcPoseRect(const BPose* pose, int32 index, bool firstColumnOnly) const
{
if (ViewMode() == kListMode)
return CalcPoseRectList(pose, index, firstColumnOnly);
else
return CalcPoseRectIcon(pose);
}
BRect
BPoseView::CalcPoseRectIcon(const BPose* pose) const
{
return pose->CalcRect(this);
}
BRect
BPoseView::CalcPoseRectList(const BPose* pose, int32 index, bool firstColumnOnly) const
{
return pose->CalcRect(BPoint(0, index * fListElemHeight), this, firstColumnOnly);
}
bool
BPoseView::Represents(const node_ref* node) const
{
return *(fModel->NodeRef()) == *node;
}
bool
BPoseView::Represents(const entry_ref* ref) const
{
return *fModel->EntryRef() == *ref;
}
void
BPoseView::ShowBarberPole()
{
if (fCountView) {
AutoLock<BWindow> lock(Window());
if (!lock)
return;
fCountView->StartBarberPole();
}
}
void
BPoseView::HideBarberPole()
{
if (fCountView != NULL) {
AutoLock<BWindow> lock(Window());
if (!lock)
return;
fCountView->EndBarberPole();
}
}
status_t
BPoseView::DragStart(const BMessage* dragMessage)
{
if (dragMessage == NULL)
return B_ERROR;
if (IsDragging() && SpringLoadedFolderCompareMessages(dragMessage, fDragMessage))
return B_OK;
SpringLoadedFolderCacheDragData(dragMessage, &fDragMessage, &fCachedTypesList);
fWaitingForRefs = true;
return B_OK;
}
void
BPoseView::DragStop()
{
delete fDragMessage;
fDragMessage = NULL;
delete fCachedTypesList;
fCachedTypesList = NULL;
fStartFrame.Set(0, 0, 0, 0);
fWaitingForRefs = false;
}
void
BPoseView::StartWatchDateFormatChange()
{
BMessenger trackerMessenger(kTrackerSignature);
BHandler::StartWatching(trackerMessenger, kDateFormatChanged);
fIsWatchingDateFormatChange = true;
}
void
BPoseView::StopWatchDateFormatChange()
{
if (IsFilePanel()) {
BMessenger trackerMessenger(kTrackerSignature);
BHandler::StopWatching(trackerMessenger, kDateFormatChanged);
} else if (be_app->LockLooper()) {
be_app->StopWatching(this, kDateFormatChanged);
be_app->UnlockLooper();
}
fIsWatchingDateFormatChange = false;
}
void
BPoseView::UpdateDateColumns(BMessage* message)
{
int32 columnCount = CountColumns();
BRect columnRect(Bounds());
for (int32 i = 0; i < columnCount; i++) {
BColumn* col = ColumnAt(i);
if (col && col->AttrType() == B_TIME_TYPE) {
columnRect.left = col->Offset();
columnRect.right = columnRect.left + col->Width();
Invalidate(columnRect);
}
}
}
void
BPoseView::AdaptToVolumeChange(BMessage*)
{
}
void
BPoseView::AdaptToDesktopIntegrationChange(BMessage*)
{
}
void
BPoseView::SetWidgetTextOutline(bool on)
{
fWidgetTextOutline = on;
}
void
BPoseView::EnsurePoseUnselected(BPose* pose)
{
if (pose == fDropTarget)
fDropTarget = NULL;
if (pose == ActivePose())
CommitActivePose();
fSelectionList->RemoveItem(pose);
if (fSelectionPivotPose == pose)
fSelectionPivotPose = NULL;
if (fRealPivotPose == pose)
fRealPivotPose = NULL;
if (pose->IsSelected()) {
pose->Select(false);
if (fSelectionChangedHook)
ContainerWindow()->SelectionChanged();
}
}
void
BPoseView::RemoveFilteredPose(BPose* pose, int32 index)
{
EnsurePoseUnselected(pose);
fFilteredPoseList->RemoveItemAt(index);
BRect invalidRect = CalcPoseRectList(pose, index);
CloseGapInList(&invalidRect);
Invalidate(invalidRect);
}
void
BPoseView::TypeAheadFilteringChanged()
{
if (ViewMode() != kListMode)
return;
int32 stringCount = fFilterStrings.CountItems();
int32 length = fFilterStrings.LastItem()->CountChars();
if (!IsTypeAheadFiltering() && length > 0) {
StartTypeAheadFiltering();
} else if (IsTypeAheadFiltering() && stringCount == 1 && length == 0) {
ClearTypeAheadFiltering();
} else if (fLastFilterStringCount > stringCount
|| (fLastFilterStringCount == stringCount && fLastFilterStringLength > length)) {
RebuildFilteringPoseList();
Invalidate();
} else {
int32 poseCount = fFilteredPoseList->CountItems();
for (int32 index = poseCount - 1; index >= 0; index--) {
BPose* pose = fFilteredPoseList->ItemAt(index);
if (!FilterPose(pose))
RemoveFilteredPose(pose, index);
}
}
fLastFilterStringCount = stringCount;
fLastFilterStringLength = length;
UpdateAfterFilterChange();
}
void
BPoseView::UpdateAfterFilterChange()
{
UpdateCount();
BPose* pose = fFilteredPoseList->LastItem();
if (pose == NULL)
_inherited::ScrollTo(0, 0);
else {
BRect bounds = Bounds();
float height = fFilteredPoseList->CountItems() * fListElemHeight;
if (bounds.top > 0 && bounds.bottom > height)
_inherited::ScrollTo(0, std::max(height - bounds.Height(), 0.0f));
}
UpdateScrollRange();
}
bool
BPoseView::FilterPose(BPose* pose)
{
if (pose == NULL || !IsFiltering())
return false;
if (IsRefFiltering()) {
Model* model = pose->TargetModel();
if (model->OpenNode() != B_OK)
return false;
struct stat_beos stat;
convert_to_stat_beos(model->StatBuf(), &stat);
if (!fRefFilter->Filter(model->EntryRef(), model->Node(), &stat, model->MimeType()))
return false;
}
int32 stringCount = fFilterStrings.CountItems();
int32 matchesLeft = stringCount;
bool found[stringCount];
memset(found, 0, sizeof(found));
ModelNodeLazyOpener modelOpener(pose->TargetModel());
for (int32 i = 0; i < CountColumns(); i++) {
BTextWidget* widget = pose->WidgetFor(ColumnAt(i), this, modelOpener);
const char* text = NULL;
if (widget == NULL)
continue;
text = widget->Text(this);
if (text == NULL)
continue;
for (int32 j = 0; j < stringCount; j++) {
if (found[j])
continue;
if (strcasestr(text, fFilterStrings.ItemAt(j)->String()) != NULL) {
if (--matchesLeft == 0)
return true;
found[j] = true;
}
}
}
return false;
}
void
BPoseView::StartTypeAheadFiltering()
{
if (fTypeAheadFiltering)
return;
fTypeAheadFiltering = true;
RebuildFilteringPoseList();
Invalidate();
}
void
BPoseView::StopTypeAheadFiltering()
{
ClearTypeAheadFiltering();
UpdateAfterFilterChange();
}
void
BPoseView::ClearTypeAheadFiltering()
{
if (!fTypeAheadFiltering)
return;
fTypeAheadFiltering = false;
fCountView->CancelFilter();
int32 stringCount = fFilterStrings.CountItems();
for (int32 i = stringCount - 1; i > 0; i--)
delete fFilterStrings.RemoveItemAt(i);
fFilterStrings.LastItem()->Truncate(0);
fLastFilterStringCount = 1;
fLastFilterStringLength = 0;
if (IsRefFiltering())
RebuildFilteringPoseList();
Invalidate();
}
void
BPoseView::RebuildFilteringPoseList()
{
fFilteredPoseList->MakeEmpty();
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++) {
BPose* pose = fPoseList->ItemAt(index);
if (pose == NULL)
break;
if (FilterPose(pose))
fFilteredPoseList->AddItem(pose);
else
EnsurePoseUnselected(pose);
}
}
void
BPoseView::ExcludeTrashFromSelection()
{
int32 selectCount = CountSelected();
for (int index = 0; index < selectCount; index++) {
BPose* pose = fSelectionList->ItemAt(index);
if (CanTrashForeignDrag(pose->TargetModel())) {
RemovePoseFromSelection(pose);
break;
}
}
}
TScrollBar::TScrollBar(const char* name, BView* target, float min, float max)
:
BScrollBar(name, target, min, max, B_HORIZONTAL),
fTitleView(NULL)
{
SetExplicitMinSize(PreferredSize());
}
void
TScrollBar::ValueChanged(float value)
{
if (fTitleView) {
BPoint origin = fTitleView->LeftTop();
fTitleView->ScrollTo(BPoint(value, origin.y));
}
_inherited::ValueChanged(value);
}
TPoseViewFilter::TPoseViewFilter(BPoseView* pose)
:
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
fPoseView(pose)
{
}
TPoseViewFilter::~TPoseViewFilter()
{
}
filter_result
TPoseViewFilter::Filter(BMessage* message, BHandler**)
{
filter_result result = B_DISPATCH_MESSAGE;
switch (message->what) {
case B_ARCHIVED_OBJECT:
bool handled = fPoseView->HandleMessageDropped(message);
if (handled)
result = B_SKIP_MESSAGE;
break;
}
return result;
}
float BPoseView::sFontHeight = -1;
font_height BPoseView::sFontInfo = { 0, 0, 0 };
OffscreenBitmap* BPoseView::sOffscreen = new OffscreenBitmap;
BString BPoseView::sMatchString = "";