6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
36 #include "QueryPoseView.h"
43 #include <NodeMonitor.h>
46 #include <VolumeRoster.h>
49 #include "Attributes.h"
50 #include "AttributeStream.h"
53 #include "FindPanel.h"
55 #include "MimeTypeList.h"
56 #include "MimeTypes.h"
65 #undef B_TRANSLATION_CONTEXT
66 #define B_TRANSLATION_CONTEXT "QueryPoseView"
69 // Currently filtering out Trash doesn't node monitor too well - if you
70 // remove an item from the Trash, it doesn't show up in the query result
71 // To do this properly, we would have to node monitor everything BQuery
72 // returns and after a node monitor re-chech if it should be part of
73 // query results and add/remove appropriately. Right now only moving to
77 // #pragma mark - BQueryPoseView
80 BQueryPoseView::BQueryPoseView(Model
* model
)
82 BPoseView(model
, kListMode
),
83 fShowResultsFromTrash(false),
85 fQueryListContainer(NULL
),
86 fCreateOldPoseList(false)
91 BQueryPoseView::~BQueryPoseView()
93 delete fQueryListContainer
;
98 BQueryPoseView::MessageReceived(BMessage
* message
)
100 switch (message
->what
) {
101 case kFSClipboardChanges
:
103 // poses have always to be updated for the query view
104 UpdatePosesClipboardModeFromClipboard(message
);
109 _inherited::MessageReceived(message
);
116 BQueryPoseView::EditQueries()
118 BMessage
message(kEditQuery
);
119 message
.AddRef("refs", TargetModel()->EntryRef());
120 BMessenger(kTrackerSignature
, -1, 0).SendMessage(&message
);
125 BQueryPoseView::SetUpDefaultColumnsIfNeeded()
127 // in case there were errors getting some columns
128 if (fColumnList
->CountItems() != 0)
131 fColumnList
->AddItem(new BColumn(B_TRANSLATE("Name"), kColumnStart
, 145,
132 B_ALIGN_LEFT
, kAttrStatName
, B_STRING_TYPE
, true, true));
133 fColumnList
->AddItem(new BColumn(B_TRANSLATE("Location"), 200, 225,
134 B_ALIGN_LEFT
, kAttrPath
, B_STRING_TYPE
, true, false));
135 fColumnList
->AddItem(new BColumn(B_TRANSLATE("Size"), 440, 80,
136 B_ALIGN_RIGHT
, kAttrStatSize
, B_OFF_T_TYPE
, true, false));
137 fColumnList
->AddItem(new BColumn(B_TRANSLATE("Modified"), 535, 150,
138 B_ALIGN_LEFT
, kAttrStatModified
, B_TIME_TYPE
, true, false));
143 BQueryPoseView::AttachedToWindow()
145 _inherited::AttachedToWindow();
146 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
147 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
152 BQueryPoseView::RestoreState(AttributeStreamNode
* node
)
154 _inherited::RestoreState(node
);
155 fViewState
->SetViewMode(kListMode
);
160 BQueryPoseView::RestoreState(const BMessage
&message
)
162 _inherited::RestoreState(message
);
163 fViewState
->SetViewMode(kListMode
);
168 BQueryPoseView::SavePoseLocations(BRect
*)
174 BQueryPoseView::SetViewMode(uint32
)
180 BQueryPoseView::OpenParent()
186 BQueryPoseView::Refresh()
188 PRINT(("refreshing dynamic date query\n"));
190 // cause the old AddPosesTask to die
191 fAddPosesThreads
.clear();
192 delete fQueryListContainer
;
193 fQueryListContainer
= NULL
;
195 fCreateOldPoseList
= true;
196 AddPoses(TargetModel());
197 TargetModel()->CloseNode();
200 ResetPosePlacementHint();
205 BQueryPoseView::ShouldShowPose(const Model
* model
, const PoseInfo
* poseInfo
)
207 // add_poses, etc. filter
208 ASSERT(TargetModel());
210 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
211 if (!fShowResultsFromTrash
&& tracker
!= NULL
212 && tracker
->InTrashNode(model
->EntryRef())) {
216 bool result
= _inherited::ShouldShowPose(model
, poseInfo
);
218 PoseList
* oldPoseList
= fQueryListContainer
->OldPoseList();
219 if (result
&& oldPoseList
!= NULL
) {
220 // pose will get added - remove it from the old pose list
221 // because it is supposed to be showing
222 BPose
* pose
= oldPoseList
->FindPose(model
);
224 oldPoseList
->RemoveItem(pose
);
232 BQueryPoseView::AddPosesCompleted()
234 ASSERT(Window()->IsLocked());
236 PoseList
* oldPoseList
= fQueryListContainer
->OldPoseList();
237 if (oldPoseList
!= NULL
) {
238 int32 count
= oldPoseList
->CountItems();
239 for (int32 index
= count
- 1; index
>= 0; index
--) {
240 BPose
* pose
= oldPoseList
->ItemAt(index
);
241 DeletePose(pose
->TargetModel()->NodeRef());
243 fQueryListContainer
->ClearOldPoseList();
246 _inherited::AddPosesCompleted();
250 // When using dynamic dates, such as "today", need to refresh the query
251 // window every now and then
254 BQueryPoseView::InitDirentIterator(const entry_ref
* ref
)
257 if (entry
.InitCheck() != B_OK
)
260 Model
sourceModel(&entry
, true);
261 if (sourceModel
.InitCheck() != B_OK
)
264 ASSERT(sourceModel
.IsQuery());
266 // old pose list is used for finding poses that no longer match a
267 // dynamic date query during a Refresh call
268 PoseList
* oldPoseList
= NULL
;
269 if (fCreateOldPoseList
) {
270 oldPoseList
= new PoseList(10, false);
271 oldPoseList
->AddList(fPoseList
);
274 fQueryListContainer
= new QueryEntryListCollection(&sourceModel
, this,
276 fCreateOldPoseList
= false;
278 if (fQueryListContainer
->InitCheck() != B_OK
) {
279 delete fQueryListContainer
;
280 fQueryListContainer
= NULL
;
284 fShowResultsFromTrash
= fQueryListContainer
->ShowResultsFromTrash();
286 TTracker::WatchNode(sourceModel
.NodeRef(), B_WATCH_NAME
| B_WATCH_STAT
287 | B_WATCH_ATTR
, this);
289 fQueryList
= fQueryListContainer
->QueryList();
291 if (fQueryListContainer
->DynamicDateQuery()) {
292 // calculate the time to trigger the query refresh - next midnight
294 time_t now
= time(0);
296 time_t nextMidnight
= now
+ 60 * 60 * 24;
297 // move ahead by a day
299 localtime_r(&nextMidnight
, &timeData
);
302 timeData
.tm_hour
= 0;
303 nextMidnight
= mktime(&timeData
);
305 time_t nextHour
= now
+ 60 * 60;
306 // move ahead by a hour
307 localtime_r(&nextHour
, &timeData
);
310 nextHour
= mktime(&timeData
);
312 PRINT(("%" B_PRId32
" minutes, %" B_PRId32
" seconds till next hour\n",
313 (nextHour
- now
) / 60, (nextHour
- now
) % 60));
315 time_t nextMinute
= now
+ 60;
316 // move ahead by a minute
317 localtime_r(&nextMinute
, &timeData
);
319 nextMinute
= mktime(&timeData
);
321 PRINT(("%" B_PRId32
" seconds till next minute\n", nextMinute
- now
));
324 if (fQueryListContainer
->DynamicDateRefreshEveryMinute())
325 delta
= nextMinute
- now
;
326 else if (fQueryListContainer
->DynamicDateRefreshEveryHour())
327 delta
= nextHour
- now
;
329 delta
= nextMidnight
- now
;
332 int32 secondsTillMidnight
= (nextMidnight
- now
);
333 int32 minutesTillMidnight
= secondsTillMidnight
/60;
334 secondsTillMidnight
%= 60;
335 int32 hoursTillMidnight
= minutesTillMidnight
/60;
336 minutesTillMidnight
%= 60;
338 PRINT(("%" B_PRId32
" hours, %" B_PRId32
" minutes, %" B_PRId32
339 " seconds till midnight\n", hoursTillMidnight
, minutesTillMidnight
,
340 secondsTillMidnight
));
342 int32 refreshInSeconds
= delta
% 60;
343 int32 refreshInMinutes
= delta
/ 60;
344 int32 refreshInHours
= refreshInMinutes
/ 60;
345 refreshInMinutes
%= 60;
347 PRINT(("next refresh in %" B_PRId32
" hours, %" B_PRId32
"minutes, %"
348 B_PRId32
" seconds\n", refreshInHours
, refreshInMinutes
,
352 // bump up to microseconds
355 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
356 ThrowOnAssert(tracker
!= NULL
);
358 tracker
->MainTaskLoop()->RunLater(
359 NewLockingMemberFunctionObject(&BQueryPoseView::Refresh
, this),
363 return fQueryListContainer
->Clone();
368 BQueryPoseView::WatchNewNodeMask()
370 return B_WATCH_NAME
| B_WATCH_STAT
| B_WATCH_ATTR
;
375 BQueryPoseView::SearchForType() const
377 if (!fSearchForMimeType
.Length()) {
378 BModelOpener
opener(TargetModel());
382 // read the type of files we are looking for
384 = TargetModel()->Node()->GetAttrInfo(kAttrQueryInitialMime
,
386 if (status
== B_OK
) {
387 TargetModel()->Node()->ReadAttrString(kAttrQueryInitialMime
,
391 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
392 if (tracker
!= NULL
&& buffer
.Length() > 0) {
393 const ShortMimeInfo
* info
= tracker
->MimeTypes()->FindMimeType(
396 fSearchForMimeType
= info
->InternalName();
399 if (!fSearchForMimeType
.Length())
400 fSearchForMimeType
= B_FILE_MIMETYPE
;
403 return fSearchForMimeType
.String();
408 BQueryPoseView::ActiveOnDevice(dev_t device
) const
410 int32 count
= fQueryList
->CountItems();
411 for (int32 index
= 0; index
< count
; index
++) {
412 if (fQueryList
->ItemAt(index
)->TargetDevice() == device
)
420 // #pragma mark - QueryEntryListCollection
423 QueryEntryListCollection::QueryEntryListCollection(Model
* model
,
424 BHandler
* target
, PoseList
* oldPoseList
)
426 fQueryListRep(new QueryListRep(new BObjectList
<BQuery
>(5, true)))
432 BNode
* modelNode
= model
->Node();
433 if (modelNode
== NULL
) {
438 // read the actual query string
439 fStatus
= modelNode
->GetAttrInfo(kAttrQueryString
, &info
);
444 if (modelNode
->ReadAttr(kAttrQueryString
, B_STRING_TYPE
, 0,
445 buffer
.LockBuffer((int32
)info
.size
),
446 (size_t)info
.size
) != info
.size
) {
451 buffer
.UnlockBuffer();
453 // read the extra options
454 MoreOptionsStruct saveMoreOptions
;
455 if (ReadAttr(modelNode
, kAttrQueryMoreOptions
,
456 kAttrQueryMoreOptionsForeign
, B_RAW_TYPE
, 0, &saveMoreOptions
,
457 sizeof(MoreOptionsStruct
),
458 &MoreOptionsStruct::EndianSwap
) != kReadAttrFailed
) {
459 fQueryListRep
->fShowResultsFromTrash
= saveMoreOptions
.searchTrash
;
462 fStatus
= query
.SetPredicate(buffer
.String());
464 fQueryListRep
->fOldPoseList
= oldPoseList
;
465 fQueryListRep
->fDynamicDateQuery
= false;
467 fQueryListRep
->fRefreshEveryHour
= false;
468 fQueryListRep
->fRefreshEveryMinute
= false;
470 if (modelNode
->ReadAttr(kAttrDynamicDateQuery
, B_BOOL_TYPE
, 0,
471 &fQueryListRep
->fDynamicDateQuery
,
472 sizeof(bool)) != sizeof(bool)) {
473 fQueryListRep
->fDynamicDateQuery
= false;
476 if (fQueryListRep
->fDynamicDateQuery
) {
477 // only refresh every minute on debug builds
478 fQueryListRep
->fRefreshEveryMinute
= buffer
.IFindFirst("second") != -1
479 || buffer
.IFindFirst("minute") != -1;
480 fQueryListRep
->fRefreshEveryHour
= fQueryListRep
->fRefreshEveryMinute
481 || buffer
.IFindFirst("hour") != -1;
484 // don't refresh every minute unless we are running debug build
485 fQueryListRep
->fRefreshEveryMinute
= false;
492 bool searchAllVolumes
= true;
493 status_t result
= B_OK
;
495 // get volumes to perform query on
496 if (modelNode
->GetAttrInfo(kAttrQueryVolume
, &info
) == B_OK
) {
499 if ((buffer
= (char*)malloc((size_t)info
.size
)) != NULL
500 && modelNode
->ReadAttr(kAttrQueryVolume
, B_MESSAGE_TYPE
, 0,
501 buffer
, (size_t)info
.size
) == info
.size
) {
503 if (message
.Unflatten(buffer
) == B_OK
) {
504 for (int32 index
= 0; ;index
++) {
507 // match a volume with the info embedded in
509 result
= MatchArchivedVolume(&volume
, &message
, index
);
510 if (result
== B_OK
) {
511 // start the query on this volume
512 result
= FetchOneQuery(&query
, target
,
513 fQueryListRep
->fQueryList
, &volume
);
517 searchAllVolumes
= false;
518 } else if (result
!= B_DEV_BAD_DRIVE_NUM
) {
519 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't
520 // mounted this time around, keep looking for more
521 // if other error, bail
531 if (searchAllVolumes
) {
532 // no specific volumes embedded in query, search everything
533 BVolumeRoster roster
;
537 while (roster
.GetNextVolume(&volume
) == B_OK
)
538 if (volume
.IsPersistent() && volume
.KnowsQuery()) {
539 result
= FetchOneQuery(&query
, target
,
540 fQueryListRep
->fQueryList
, &volume
);
553 QueryEntryListCollection::FetchOneQuery(const BQuery
* copyThis
,
554 BHandler
* target
, BObjectList
<BQuery
>* list
, BVolume
* volume
)
556 BQuery
* query
= new (nothrow
) BQuery
;
560 // have to fake a copy constructor here because BQuery doesn't have
561 // a copy constructor
563 const_cast<BQuery
*>(copyThis
)->GetPredicate(&buffer
);
564 query
->SetPredicate(buffer
.String());
566 query
->SetTarget(BMessenger(target
));
567 query
->SetVolume(volume
);
569 status_t result
= query
->Fetch();
570 if (result
!= B_OK
) {
571 PRINT(("fetch error %s\n", strerror(result
)));
576 list
->AddItem(query
);
582 QueryEntryListCollection::~QueryEntryListCollection()
584 if (fQueryListRep
->CloseQueryList())
585 delete fQueryListRep
;
589 QueryEntryListCollection
*
590 QueryEntryListCollection::Clone()
592 fQueryListRep
->OpenQueryList();
593 return new QueryEntryListCollection(*this);
597 // #pragma mark - QueryEntryListCollection
600 QueryEntryListCollection::QueryEntryListCollection(
601 const QueryEntryListCollection
&cloneThis
)
604 fQueryListRep(cloneThis
.fQueryListRep
)
606 // only to be used by the Clone routine
611 QueryEntryListCollection::ClearOldPoseList()
613 delete fQueryListRep
->fOldPoseList
;
614 fQueryListRep
->fOldPoseList
= NULL
;
619 QueryEntryListCollection::GetNextEntry(BEntry
* entry
, bool traverse
)
621 status_t result
= B_ERROR
;
623 for (int32 count
= fQueryListRep
->fQueryList
->CountItems();
624 fQueryListRep
->fQueryListIndex
< count
;
625 fQueryListRep
->fQueryListIndex
++) {
626 result
= fQueryListRep
->fQueryList
->
627 ItemAt(fQueryListRep
->fQueryListIndex
)->
628 GetNextEntry(entry
, traverse
);
638 QueryEntryListCollection::GetNextDirents(struct dirent
* buffer
, size_t length
,
643 for (int32 queryCount
= fQueryListRep
->fQueryList
->CountItems();
644 fQueryListRep
->fQueryListIndex
< queryCount
;
645 fQueryListRep
->fQueryListIndex
++) {
646 result
= fQueryListRep
->fQueryList
->
647 ItemAt(fQueryListRep
->fQueryListIndex
)->
648 GetNextDirents(buffer
, length
, count
);
658 QueryEntryListCollection::GetNextRef(entry_ref
* ref
)
660 status_t result
= B_ERROR
;
662 for (int32 count
= fQueryListRep
->fQueryList
->CountItems();
663 fQueryListRep
->fQueryListIndex
< count
;
664 fQueryListRep
->fQueryListIndex
++) {
666 result
= fQueryListRep
->fQueryList
->
667 ItemAt(fQueryListRep
->fQueryListIndex
)->GetNextRef(ref
);
677 QueryEntryListCollection::Rewind()
679 fQueryListRep
->fQueryListIndex
= 0;
686 QueryEntryListCollection::CountEntries()
693 QueryEntryListCollection::ShowResultsFromTrash() const
695 return fQueryListRep
->fShowResultsFromTrash
;
700 QueryEntryListCollection::DynamicDateQuery() const
702 return fQueryListRep
->fDynamicDateQuery
;
707 QueryEntryListCollection::DynamicDateRefreshEveryHour() const
709 return fQueryListRep
->fRefreshEveryHour
;
714 QueryEntryListCollection::DynamicDateRefreshEveryMinute() const
716 return fQueryListRep
->fRefreshEveryMinute
;