Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#include "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())
poseInfo.fInitedDirectory = targetModel->NodeRef()->node;
else
poseInfo.fInitedDirectory = model->EntryRef()->directory;
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);
bool isTrash = model->IsTrash() && IsDesktopView();
if (model->IsRoot() || isTrash) {
BDirectory deskDir;
if (FSGetDeskDir(&deskDir) == B_OK) {
const char* poseInfoAttr = isTrash ? kAttrTrashPoseInfo : kAttrDisksPoseInfo;
const char* poseInfoAttrForeign = isTrash
? kAttrTrashPoseInfoForeign
: kAttrDisksPoseInfoForeign;
if (deskDir.WriteAttr(poseInfoAttr, B_RAW_TYPE, 0,
&poseInfo, sizeof(poseInfo)) == sizeof(poseInfo)) {
deskDir.RemoveAttr(poseInfoAttrForeign);
}
if (!isTrash && isDesktop && deskDir.WriteAttr(kAttrExtendedDisksPoseInfo,
B_RAW_TYPE, 0, extendedPoseInfo, extendedPoseInfoSize)
== (ssize_t)extendedPoseInfoSize) {
deskDir.RemoveAttr(kAttrExtendedDisksPoseInfoForegin);
}
}
} else {
model->WriteAttrKillForeign(kAttrPoseInfo,
kAttrPoseInfoForeign, B_RAW_TYPE, 0, &poseInfo,
sizeof(poseInfo));
if (isDesktop) {
model->WriteAttrKillForeign(kAttrExtendedPoseInfo,
kAttrExtendedPoseInfoForegin,
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(LeftTop());
else
fViewState->SetIconOrigin(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);
BView::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)
{
BPose* activePose = ActivePose();
if (activePose != NULL) {
int32 index = fPoseList->IndexOf(ActivePose());
if (IsFiltering())
index = fFilteredPoseList->IndexOf(ActivePose());
BPoint loc(0, index * fListElemHeight);
if (ViewMode() != kListMode)
loc = ActivePose()->Location(this);
activePose->Commit(saveChanges, loc, this, index);
BPose* pose = fActivePose;
fActivePose = NULL;
if (IsFiltering() && !FilterPose(pose))
RemoveFilteredPose(pose, 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;
}
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())
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)
BView::ScrollTo(bounds.left, std::max(lastItemTop, 0.0f));
}
}
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();
}
void
BPoseView::CreateTrashPose()
{
BVolume boot;
if (BVolumeRoster().GetBootVolume(&boot) == B_OK) {
BDirectory trash;
BEntry entry;
node_ref nref;
if (FSGetTrashDir(&trash, boot.Device()) == B_OK
&& trash.GetEntry(&entry) == B_OK
&& entry.GetNodeRef(&nref) == B_OK) {
WatchNewNode(&nref, B_WATCH_ATTR, BMessenger(this));
Model* model = new Model(&entry);
PoseInfo info;
ReadPoseInfo(model, &info);
CreatePose(model, &info, false, NULL, NULL, true);
}
}
}
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:
ContainerWindow()->UpdateBackgroundImage();
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())
break;
BPose* pose = fSelectionList->FirstItem();
if (pose != NULL) {
BPoint where(0, CurrentPoseList()->IndexOf(pose) * fListElemHeight);
pose->EditFirstWidget(where, 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);
bool isTrash = model->IsTrash() && IsDesktopView();
if (model->IsRoot() || isTrash) {
BDirectory dir;
if (FSGetDeskDir(&dir) == B_OK) {
const char* poseInfoAttr = isTrash
? kAttrTrashPoseInfo
: kAttrDisksPoseInfo;
const char* poseInfoAttrForeign = isTrash
? kAttrTrashPoseInfoForeign
: kAttrDisksPoseInfoForeign;
result = ReadAttr(&dir, 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 = kAttrExtendedDisksPoseInfoForegin;
} else
return NULL;
} else {
extendedPoseInfoAttrName = kAttrExtendedPoseInfo;
extendedPoseInfoAttrForeignName = kAttrExtendedPoseInfoForegin;
}
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);
pose->EditFirstWidget(BPoint(0, index * fListElemHeight), 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);
pose->EditFirstWidget(BPoint(0, index * fListElemHeight), 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();
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;
TrackerSettings settings;
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;
if (targetModel != NULL && targetModel->IsRoot()) {
BVolume volume(device);
if (volume.InitCheck() == B_OK)
CreateVolumePose(&volume);
} else if (TargetModel()->IsTrash()) {
BDirectory trashDir;
BEntry entry;
BVolume volume(device);
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) {
if (targetModel != NULL
&& targetModel->NodeRef()->device == device) {
DisableSaveLocation();
Window()->Close();
} else if (targetModel != NULL) {
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;
status_t infoStatus = B_ERROR;
if (attrName != NULL) {
memset(&info, 0, sizeof(attr_info));
if (strcmp(attrName, kAttrIcon) == 0
|| strcmp(attrName, kAttrLargeIcon) == 0
|| strcmp(attrName, kAttrMiniIcon) == 0
|| strcmp(attrName, kAttrThumbnail) == 0) {
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);
}
}
BPoint poseLoc;
if (ViewMode() == kListMode)
poseLoc.Set(0, index * fListElemHeight);
else
poseLoc = pose->Location(this);
if (attrName != NULL) {
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--;) {
if (sAttrColumnMap[i].attrHash == PrimarySort()
|| sAttrColumnMap[i].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();
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)
BView::DragMessage(&message, dragBitmap, B_OP_ALPHA, offset);
else
BView::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;
inner = inner & dragRect;
float fadeWidth = be_control_look->ComposeIconSize(64).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, false);
}
}
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,
bitmap->Bounds().IntegerWidth());
}
if (fadeRight) {
FadeRGBA32Horizontal(bits, width, int32(rect.bottom), int32(rect.right),
int32(rect.right) - bitmap->Bounds().IntegerWidth());
}
if (fadeTop) {
FadeRGBA32Vertical(bits, width, int32(rect.bottom), 0,
bitmap->Bounds().IntegerHeight());
}
if (fadeBottom) {
FadeRGBA32Vertical(bits, width, int32(rect.bottom), int32(rect.bottom),
int32(rect.bottom) - bitmap->Bounds().IntegerHeight());
}
}
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) {
BView::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();
if (!IsDesktopView()) {
if (ContainerWindow() != NULL)
ContainerWindow()->UpdateBackgroundImage();
}
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();
int32 poseCount = fPoseList->CountItems();
for (int32 index = 0; index < poseCount; index++)
AddToExtent(fPoseList->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);
BView::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)
BView::ScrollTo(0, 0);
else {
BRect bounds = Bounds();
float height = fFilteredPoseList->CountItems() * fListElemHeight;
if (bounds.top > 0 && bounds.bottom > height)
BView::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 = "";