Tracker: Redraw Trash icon when pose attribute updates.
* Don't set Trash icon in FSGetTrashDir anymore, do it in TrashWatcher. * Write vector icon to attr or large/mini icon attrs, but not both. * Only update Trash icon attribute on boot volume, not the hidden trash folder on other volumes. * Monitor Trash icon attribute changes by TrashWatcher in BPoseView. * Get rid of ShouldUseWellKnownIcon(), update ResetIconFrom(). * Remove model param from BPose::UpdateWidgetAndModel() and always use the ResolvedModel() of the pose instead. * Pass poseLoc into BPose::UpdateWidgetAndModel() in icon-mode. * Workaround for GetAttrInfo() failing to update removed icon attrs. * Check fNode not NULL in Model::StatChanged() to prevent crash when unmounting volumes with trashed items. * Filter out 0 capacity volumes checking for Trash directory. Fixes #8696 Change-Id: Ief2efc07c85866cc0e49468c668e47ec90b855af Reviewed-on: https://review.haiku-os.org/c/haiku/+/9458 Reviewed-by: waddlesplash <[email protected]> Reviewed-by: John Scipione <[email protected]> Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
@@ -2679,32 +2679,10 @@ FSGetTrashDir(BDirectory* trashDir, dev_t dev)
|
|||||||
PoseInfo poseInfo;
|
PoseInfo poseInfo;
|
||||||
poseInfo.fInvisible = true;
|
poseInfo.fInvisible = true;
|
||||||
poseInfo.fInitedDirectory = sbuf.st_ino;
|
poseInfo.fInitedDirectory = sbuf.st_ino;
|
||||||
trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo,
|
trashDir->WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &poseInfo, sizeof(PoseInfo));
|
||||||
sizeof(PoseInfo));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set Trash icons (if they haven't already been set)
|
// icon attributes set by TrashWatcher
|
||||||
attr_info attrInfo;
|
|
||||||
size_t size;
|
|
||||||
const void* data;
|
|
||||||
if (trashDir->GetAttrInfo(kAttrLargeIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
|
|
||||||
data = GetTrackerResources()->LoadResource('ICON', R_TrashIcon, &size);
|
|
||||||
if (data != NULL)
|
|
||||||
trashDir->WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trashDir->GetAttrInfo(kAttrMiniIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
|
|
||||||
data = GetTrackerResources()->LoadResource('MICN', R_TrashIcon, &size);
|
|
||||||
if (data != NULL)
|
|
||||||
trashDir->WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trashDir->GetAttrInfo(kAttrIcon, &attrInfo) == B_ENTRY_NOT_FOUND) {
|
|
||||||
data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE,
|
|
||||||
R_TrashIcon, &size);
|
|
||||||
if (data != NULL)
|
|
||||||
trashDir->WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -3233,7 +3211,7 @@ FSCreateTrashDirs()
|
|||||||
|
|
||||||
roster.Rewind();
|
roster.Rewind();
|
||||||
while (roster.GetNextVolume(&volume) == B_OK) {
|
while (roster.GetNextVolume(&volume) == B_OK) {
|
||||||
if (volume.IsReadOnly() || !volume.IsPersistent())
|
if (volume.IsReadOnly() || !volume.IsPersistent() || volume.Capacity() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
BDirectory trashDir;
|
BDirectory trashDir;
|
||||||
|
|||||||
+12
-11
@@ -727,16 +727,6 @@ Model::FinishSettingUpType()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
Model::ShouldUseWellKnownIcon() const
|
|
||||||
{
|
|
||||||
if (fBaseType == kDirectoryNode || fBaseType == kVolumeNode
|
|
||||||
|| fBaseType == kTrashNode || fBaseType == kDesktopNode)
|
|
||||||
return !CheckAppIconHint();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Model::CheckAppIconHint() const
|
Model::CheckAppIconHint() const
|
||||||
{
|
{
|
||||||
@@ -770,7 +760,14 @@ Model::ResetIconFrom()
|
|||||||
if (InitCheck() != B_OK)
|
if (InitCheck() != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ShouldUseWellKnownIcon()) {
|
bool hasAttrIcon = CheckAppIconHint();
|
||||||
|
|
||||||
|
if (hasAttrIcon && (fBaseType == kDesktopNode || fBaseType == kTrashNode)) {
|
||||||
|
// Desktop or Trash with an icon attribute
|
||||||
|
fIconFrom = kNode;
|
||||||
|
return;
|
||||||
|
} else if (!hasAttrIcon && (fBaseType == kDirectoryNode || fBaseType == kVolumeNode)) {
|
||||||
|
// No icon attribute override, check if well-known or root directory
|
||||||
BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
|
BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
|
||||||
if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
|
if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
|
||||||
fIconFrom = kTrackerSupplied;
|
fIconFrom = kTrackerSupplied;
|
||||||
@@ -780,6 +777,7 @@ Model::ResetIconFrom()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fIconFrom = kUnknownSource;
|
fIconFrom = kUnknownSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -966,6 +964,9 @@ Model::AttrChanged(const char* attrName)
|
|||||||
bool
|
bool
|
||||||
Model::StatChanged()
|
Model::StatChanged()
|
||||||
{
|
{
|
||||||
|
if (fNode == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
ASSERT(IsNodeOpen());
|
ASSERT(IsNodeOpen());
|
||||||
mode_t oldMode = fStatBuf.st_mode;
|
mode_t oldMode = fStatBuf.st_mode;
|
||||||
fStatus = fNode->GetStat(&fStatBuf);
|
fStatus = fNode->GetStat(&fStatBuf);
|
||||||
|
|||||||
@@ -215,7 +215,6 @@ private:
|
|||||||
status_t OpenNodeCommon(bool writable);
|
status_t OpenNodeCommon(bool writable);
|
||||||
void SetupBaseType();
|
void SetupBaseType();
|
||||||
void FinishSettingUpType();
|
void FinishSettingUpType();
|
||||||
bool ShouldUseWellKnownIcon() const;
|
|
||||||
bool CheckAppIconHint() const;
|
bool CheckAppIconHint() const;
|
||||||
void DeletePreferredAppVolumeNameLinkTo();
|
void DeletePreferredAppVolumeNameLinkTo();
|
||||||
void CacheLocalizedName();
|
void CacheLocalizedName();
|
||||||
|
|||||||
@@ -241,18 +241,18 @@ BPose::UpdateAllWidgets(int32, BPoint poseLoc, BPoseView* poseView)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BPose::UpdateWidgetAndModel(Model* resolvedModel, const char* attrName,
|
BPose::UpdateWidgetAndModel(const char* attrName, uint32 attrType, int32, BPoint poseLoc,
|
||||||
uint32 attrType, int32, BPoint poseLoc, BPoseView* poseView, bool visible)
|
BPoseView* poseView, bool visible)
|
||||||
{
|
{
|
||||||
if (poseView->ViewMode() != kListMode)
|
Model* resolvedModel = ResolvedModel();
|
||||||
poseLoc = Location(poseView);
|
|
||||||
|
|
||||||
ASSERT(resolvedModel == NULL || resolvedModel->IsNodeOpen());
|
ASSERT(resolvedModel == NULL || resolvedModel->IsNodeOpen());
|
||||||
|
|
||||||
if (attrName != NULL) {
|
if (attrName != NULL) {
|
||||||
// pick up new attributes and find out if icon needs updating
|
// pick up new attributes and find out if icon needs updating
|
||||||
if (resolvedModel->AttrChanged(attrName) && visible)
|
if (visible && resolvedModel != NULL && resolvedModel->InitCheck() == B_OK
|
||||||
|
&& resolvedModel->AttrChanged(attrName)) {
|
||||||
UpdateIcon(poseLoc, poseView);
|
UpdateIcon(poseLoc, poseView);
|
||||||
|
}
|
||||||
|
|
||||||
// ToDo: the following code is wrong, because this sort of hashing
|
// ToDo: the following code is wrong, because this sort of hashing
|
||||||
// may overlap and we get aliasing
|
// may overlap and we get aliasing
|
||||||
@@ -280,12 +280,9 @@ BPose::UpdateWidgetAndModel(Model* resolvedModel, const char* attrName,
|
|||||||
// no attr name means check all widgets for stat info changes
|
// no attr name means check all widgets for stat info changes
|
||||||
|
|
||||||
// pick up stat changes
|
// pick up stat changes
|
||||||
if (resolvedModel && resolvedModel->StatChanged()) {
|
if (visible && resolvedModel != NULL && resolvedModel->InitCheck() == B_OK
|
||||||
if (resolvedModel->InitCheck() != B_OK)
|
&& resolvedModel->StatChanged()) {
|
||||||
return;
|
UpdateIcon(poseLoc, poseView);
|
||||||
|
|
||||||
if (visible)
|
|
||||||
UpdateIcon(poseLoc, poseView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// distribute stat changes
|
// distribute stat changes
|
||||||
|
|||||||
@@ -95,9 +95,8 @@ public:
|
|||||||
BRect CalcRect(BPoint loc, const BPoseView*, bool minimal_rect = false) const;
|
BRect CalcRect(BPoint loc, const BPoseView*, bool minimal_rect = false) const;
|
||||||
BRect CalcRect(const BPoseView*) const;
|
BRect CalcRect(const BPoseView*) const;
|
||||||
void UpdateAllWidgets(int32 poseIndex, BPoint poseLoc, BPoseView*);
|
void UpdateAllWidgets(int32 poseIndex, BPoint poseLoc, BPoseView*);
|
||||||
void UpdateWidgetAndModel(Model* resolvedModel, const char* attrName,
|
void UpdateWidgetAndModel(const char* attrName, uint32 attrType, int32 poseIndex,
|
||||||
uint32 attrType, int32 poseIndex, BPoint poseLoc,
|
BPoint poseLoc, BPoseView* view, bool visible);
|
||||||
BPoseView* view, bool visible);
|
|
||||||
bool UpdateVolumeSpaceBar(BVolume* volume);
|
bool UpdateVolumeSpaceBar(BVolume* volume);
|
||||||
void UpdateIcon(BPoint poseLoc, BPoseView*);
|
void UpdateIcon(BPoint poseLoc, BPoseView*);
|
||||||
|
|
||||||
|
|||||||
@@ -1797,15 +1797,16 @@ BPoseView::RemoveRootPose()
|
|||||||
void
|
void
|
||||||
BPoseView::CreateTrashPose()
|
BPoseView::CreateTrashPose()
|
||||||
{
|
{
|
||||||
BVolume volume;
|
BVolume boot;
|
||||||
if (BVolumeRoster().GetBootVolume(&volume) == B_OK) {
|
if (BVolumeRoster().GetBootVolume(&boot) == B_OK) {
|
||||||
BDirectory trash;
|
BDirectory trash;
|
||||||
BEntry entry;
|
BEntry entry;
|
||||||
node_ref ref;
|
node_ref nref;
|
||||||
if (FSGetTrashDir(&trash, volume.Device()) == B_OK
|
if (FSGetTrashDir(&trash, boot.Device()) == B_OK
|
||||||
&& trash.GetEntry(&entry) == B_OK
|
&& trash.GetEntry(&entry) == B_OK
|
||||||
&& entry.GetNodeRef(&ref) == B_OK) {
|
&& entry.GetNodeRef(&nref) == B_OK) {
|
||||||
WatchNewNode(&ref);
|
WatchNewNode(&nref, B_WATCH_ATTR, BMessenger(this));
|
||||||
|
// redraw Trash icon when attribute changes
|
||||||
Model* model = new Model(&entry);
|
Model* model = new Model(&entry);
|
||||||
PoseInfo info;
|
PoseInfo info;
|
||||||
ReadPoseInfo(model, &info);
|
ReadPoseInfo(model, &info);
|
||||||
@@ -5873,8 +5874,7 @@ BPoseView::AttributeChanged(const BMessage* message)
|
|||||||
if (result == B_OK || result != B_BUSY)
|
if (result == B_OK || result != B_BUSY)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
PRINT(("poseModel %s busy, retrying in a bit\n",
|
PRINT(("poseModel %s busy, retrying in a bit\n", poseModel->Name()));
|
||||||
poseModel->Name()));
|
|
||||||
snooze(10000);
|
snooze(10000);
|
||||||
}
|
}
|
||||||
if (result != B_OK) {
|
if (result != B_OK) {
|
||||||
@@ -5888,18 +5888,50 @@ BPoseView::AttributeChanged(const BMessage* message)
|
|||||||
if (IsFiltering())
|
if (IsFiltering())
|
||||||
visible = fFilteredPoseList->FindPose(poseModel->NodeRef(), &index) != NULL;
|
visible = fFilteredPoseList->FindPose(poseModel->NodeRef(), &index) != NULL;
|
||||||
|
|
||||||
BPoint loc(0, index * fListElemHeight);
|
status_t infoStatus = B_ERROR;
|
||||||
if (attrName != NULL && poseModel->Node() != NULL) {
|
if (attrName != NULL) {
|
||||||
memset(&info, 0, sizeof(attr_info));
|
memset(&info, 0, sizeof(attr_info));
|
||||||
// the call below might fail if the attribute has been removed
|
if (strcmp(attrName, kAttrIcon) == 0
|
||||||
poseModel->Node()->GetAttrInfo(attrName, &info);
|
|| strcmp(attrName, kAttrLargeIcon) == 0
|
||||||
pose->UpdateWidgetAndModel(poseModel, attrName, info.type, index, loc, this, visible);
|
|| strcmp(attrName, kAttrMiniIcon) == 0
|
||||||
|
|| strcmp(attrName, kAttrThumbnail) == 0) {
|
||||||
|
// set icon type manually in case attribute was removed
|
||||||
|
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;
|
||||||
|
// old size not needed, writing new attr
|
||||||
|
infoStatus = B_OK;
|
||||||
|
} else if (poseModel->Node() != NULL) {
|
||||||
|
// the call below might fail if the attribute has been removed
|
||||||
|
infoStatus = poseModel->Node()->GetAttrInfo(attrName, &info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BPoint poseLoc;
|
||||||
|
if (ViewMode() == kListMode)
|
||||||
|
poseLoc.Set(0, index * fListElemHeight);
|
||||||
|
else
|
||||||
|
poseLoc = pose->Location(this);
|
||||||
|
|
||||||
|
if (attrName != NULL) {
|
||||||
|
// update attr
|
||||||
|
pose->UpdateWidgetAndModel(attrName, infoStatus == B_OK ? info.type : 0,
|
||||||
|
index, poseLoc, this, visible);
|
||||||
if (strcmp(attrName, kAttrMIMEType) == 0)
|
if (strcmp(attrName, kAttrMIMEType) == 0)
|
||||||
RefreshMimeTypeList();
|
RefreshMimeTypeList();
|
||||||
} else {
|
} else {
|
||||||
pose->UpdateWidgetAndModel(poseModel, 0, 0, index, loc, this, visible);
|
// update stat
|
||||||
|
pose->UpdateWidgetAndModel(0, 0, index, poseLoc, this, visible);
|
||||||
}
|
}
|
||||||
poseModel->CloseNode();
|
poseModel->CloseNode();
|
||||||
|
|
||||||
if (IsFiltering()) {
|
if (IsFiltering()) {
|
||||||
if (!visible && FilterPose(pose)) {
|
if (!visible && FilterPose(pose)) {
|
||||||
visible = true;
|
visible = true;
|
||||||
|
|||||||
@@ -296,6 +296,9 @@ TTracker::TTracker()
|
|||||||
|
|
||||||
// init Desktop now that pose view is created and window is locked
|
// init Desktop now that pose view is created and window is locked
|
||||||
deskWindow->Init();
|
deskWindow->Init();
|
||||||
|
|
||||||
|
// create this before ReadyToRun() so that the Trash icon gets set
|
||||||
|
fTrashWatcher = new BTrashWatcher();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1550,7 +1553,6 @@ TTracker::ReadyToRun()
|
|||||||
InstallIndices();
|
InstallIndices();
|
||||||
InstallTemporaryBackgroundImages();
|
InstallTemporaryBackgroundImages();
|
||||||
|
|
||||||
fTrashWatcher = new BTrashWatcher();
|
|
||||||
fTrashWatcher->Run();
|
fTrashWatcher->Run();
|
||||||
|
|
||||||
fClipboardRefsWatcher = new BClipboardRefsWatcher();
|
fClipboardRefsWatcher = new BClipboardRefsWatcher();
|
||||||
|
|||||||
@@ -154,25 +154,28 @@ BTrashWatcher::MessageReceived(BMessage* message)
|
|||||||
void
|
void
|
||||||
BTrashWatcher::UpdateTrashIcons()
|
BTrashWatcher::UpdateTrashIcons()
|
||||||
{
|
{
|
||||||
BVolumeRoster roster;
|
// only update Trash icon attributes on boot volume
|
||||||
BVolume volume;
|
BVolume boot;
|
||||||
roster.Rewind();
|
|
||||||
|
|
||||||
BDirectory trashDir;
|
BDirectory trashDir;
|
||||||
while (roster.GetNextVolume(&volume) == B_OK) {
|
if (BVolumeRoster().GetBootVolume(&boot) == B_OK
|
||||||
if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK) {
|
&& FSGetTrashDir(&trashDir, boot.Device()) == B_OK) {
|
||||||
// pull out the icons for the current trash state from resources
|
// pull out the icons for the current trash state from resources
|
||||||
// and apply them onto the trash directory node
|
// and apply them onto the trash directory node
|
||||||
size_t vectorSize = 0;
|
int32 id = fTrashFull ? R_TrashFullIcon : R_TrashIcon;
|
||||||
const void* vectorData = GetTrackerResources()->LoadResource(
|
size_t size = 0;
|
||||||
B_VECTOR_ICON_TYPE,
|
const void* data = GetTrackerResources()->LoadResource(B_VECTOR_ICON_TYPE, id, &size);
|
||||||
fTrashFull ? R_TrashFullIcon : R_TrashIcon, &vectorSize);
|
if (data != NULL && size > 0) {
|
||||||
|
// write vector icon attribute
|
||||||
|
trashDir.WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0, data, size);
|
||||||
|
} else {
|
||||||
|
// write large and mini icon attributes
|
||||||
|
data = GetTrackerResources()->LoadResource('ICON', id, &size);
|
||||||
|
if (data != NULL && size > 0)
|
||||||
|
trashDir.WriteAttr(kAttrLargeIcon, 'ICON', 0, data, size);
|
||||||
|
|
||||||
if (vectorData) {
|
data = GetTrackerResources()->LoadResource('MICN', id, &size);
|
||||||
trashDir.WriteAttr(kAttrIcon, B_VECTOR_ICON_TYPE, 0,
|
if (data != NULL && size > 0)
|
||||||
vectorData, vectorSize);
|
trashDir.WriteAttr(kAttrMiniIcon, 'MICN', 0, data, size);
|
||||||
} else
|
|
||||||
TRESPASS();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +209,7 @@ BTrashWatcher::CheckTrashDirs()
|
|||||||
volRoster.Rewind();
|
volRoster.Rewind();
|
||||||
BVolume volume;
|
BVolume volume;
|
||||||
while (volRoster.GetNextVolume(&volume) == B_OK) {
|
while (volRoster.GetNextVolume(&volume) == B_OK) {
|
||||||
if (volume.IsReadOnly() || !volume.IsPersistent())
|
if (volume.IsReadOnly() || !volume.IsPersistent() || volume.Capacity() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
BDirectory trashDir;
|
BDirectory trashDir;
|
||||||
|
|||||||
Reference in New Issue
Block a user