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
),
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"), StartOffset(), 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 SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR
, B_DARKEN_1_TINT
);
147 SetLowUIColor(B_DOCUMENT_BACKGROUND_COLOR
, B_DARKEN_1_TINT
);
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::AddPosesCompleted()
207 ASSERT(Window()->IsLocked());
209 PoseList
* oldPoseList
= fQueryListContainer
->OldPoseList();
210 if (oldPoseList
!= NULL
) {
211 int32 count
= oldPoseList
->CountItems();
212 for (int32 index
= count
- 1; index
>= 0; index
--) {
213 BPose
* pose
= oldPoseList
->ItemAt(index
);
214 DeletePose(pose
->TargetModel()->NodeRef());
216 fQueryListContainer
->ClearOldPoseList();
219 _inherited::AddPosesCompleted();
223 // When using dynamic dates, such as "today", need to refresh the query
224 // window every now and then
227 BQueryPoseView::InitDirentIterator(const entry_ref
* ref
)
230 if (entry
.InitCheck() != B_OK
)
233 Model
sourceModel(&entry
, true);
234 if (sourceModel
.InitCheck() != B_OK
)
237 ASSERT(sourceModel
.IsQuery());
239 // old pose list is used for finding poses that no longer match a
240 // dynamic date query during a Refresh call
241 PoseList
* oldPoseList
= NULL
;
242 if (fCreateOldPoseList
) {
243 oldPoseList
= new PoseList(10, false);
244 oldPoseList
->AddList(fPoseList
);
247 fQueryListContainer
= new QueryEntryListCollection(&sourceModel
, this,
249 fCreateOldPoseList
= false;
251 if (fQueryListContainer
->InitCheck() != B_OK
) {
252 delete fQueryListContainer
;
253 fQueryListContainer
= NULL
;
257 TTracker::WatchNode(sourceModel
.NodeRef(), B_WATCH_NAME
| B_WATCH_STAT
258 | B_WATCH_ATTR
, this);
260 fQueryList
= fQueryListContainer
->QueryList();
262 if (fQueryListContainer
->DynamicDateQuery()) {
263 // calculate the time to trigger the query refresh - next midnight
265 time_t now
= time(0);
267 time_t nextMidnight
= now
+ 60 * 60 * 24;
268 // move ahead by a day
270 localtime_r(&nextMidnight
, &timeData
);
273 timeData
.tm_hour
= 0;
274 nextMidnight
= mktime(&timeData
);
276 time_t nextHour
= now
+ 60 * 60;
277 // move ahead by a hour
278 localtime_r(&nextHour
, &timeData
);
281 nextHour
= mktime(&timeData
);
283 PRINT(("%" B_PRIdTIME
" minutes, %" B_PRIdTIME
" seconds till next hour\n",
284 (nextHour
- now
) / 60, (nextHour
- now
) % 60));
286 time_t nextMinute
= now
+ 60;
287 // move ahead by a minute
288 localtime_r(&nextMinute
, &timeData
);
290 nextMinute
= mktime(&timeData
);
292 PRINT(("%" B_PRIdTIME
" seconds till next minute\n", nextMinute
- now
));
295 if (fQueryListContainer
->DynamicDateRefreshEveryMinute())
296 delta
= nextMinute
- now
;
297 else if (fQueryListContainer
->DynamicDateRefreshEveryHour())
298 delta
= nextHour
- now
;
300 delta
= nextMidnight
- now
;
303 int32 secondsTillMidnight
= (nextMidnight
- now
);
304 int32 minutesTillMidnight
= secondsTillMidnight
/60;
305 secondsTillMidnight
%= 60;
306 int32 hoursTillMidnight
= minutesTillMidnight
/60;
307 minutesTillMidnight
%= 60;
309 PRINT(("%" B_PRId32
" hours, %" B_PRId32
" minutes, %" B_PRId32
310 " seconds till midnight\n", hoursTillMidnight
, minutesTillMidnight
,
311 secondsTillMidnight
));
313 int32 refreshInSeconds
= delta
% 60;
314 int32 refreshInMinutes
= delta
/ 60;
315 int32 refreshInHours
= refreshInMinutes
/ 60;
316 refreshInMinutes
%= 60;
318 PRINT(("next refresh in %" B_PRId32
" hours, %" B_PRId32
"minutes, %"
319 B_PRId32
" seconds\n", refreshInHours
, refreshInMinutes
,
323 // bump up to microseconds
326 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
327 ThrowOnAssert(tracker
!= NULL
);
329 tracker
->MainTaskLoop()->RunLater(
330 NewLockingMemberFunctionObject(&BQueryPoseView::Refresh
, this),
334 SetRefFilter(new QueryRefFilter(fQueryListContainer
->ShowResultsFromTrash()));
336 return fQueryListContainer
->Clone();
341 BQueryPoseView::WatchNewNodeMask()
343 return B_WATCH_NAME
| B_WATCH_STAT
| B_WATCH_ATTR
;
348 BQueryPoseView::SearchForType() const
350 if (!fSearchForMimeType
.Length()) {
351 BModelOpener
opener(TargetModel());
355 // read the type of files we are looking for
357 = TargetModel()->Node()->GetAttrInfo(kAttrQueryInitialMime
,
359 if (status
== B_OK
) {
360 TargetModel()->Node()->ReadAttrString(kAttrQueryInitialMime
,
364 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
365 if (tracker
!= NULL
&& buffer
.Length() > 0) {
366 const ShortMimeInfo
* info
= tracker
->MimeTypes()->FindMimeType(
369 fSearchForMimeType
= info
->InternalName();
372 if (!fSearchForMimeType
.Length())
373 fSearchForMimeType
= B_FILE_MIMETYPE
;
376 return fSearchForMimeType
.String();
381 BQueryPoseView::ActiveOnDevice(dev_t device
) const
383 int32 count
= fQueryList
->CountItems();
384 for (int32 index
= 0; index
< count
; index
++) {
385 if (fQueryList
->ItemAt(index
)->TargetDevice() == device
)
393 // #pragma mark - QueryRefFilter
396 QueryRefFilter::QueryRefFilter(bool showResultsFromTrash
)
398 fShowResultsFromTrash(showResultsFromTrash
)
404 QueryRefFilter::Filter(const entry_ref
* ref
, BNode
* node
, stat_beos
* st
,
405 const char* filetype
)
407 TTracker
* tracker
= dynamic_cast<TTracker
*>(be_app
);
408 return !(!fShowResultsFromTrash
&& tracker
!= NULL
409 && tracker
->InTrashNode(ref
));
413 // #pragma mark - QueryEntryListCollection
416 QueryEntryListCollection::QueryEntryListCollection(Model
* model
,
417 BHandler
* target
, PoseList
* oldPoseList
)
419 fQueryListRep(new QueryListRep(new BObjectList
<BQuery
>(5, true)))
425 BNode
* modelNode
= model
->Node();
426 if (modelNode
== NULL
) {
431 // read the actual query string
432 fStatus
= modelNode
->GetAttrInfo(kAttrQueryString
, &info
);
437 if (modelNode
->ReadAttr(kAttrQueryString
, B_STRING_TYPE
, 0,
438 buffer
.LockBuffer((int32
)info
.size
),
439 (size_t)info
.size
) != info
.size
) {
444 buffer
.UnlockBuffer();
446 // read the extra options
447 MoreOptionsStruct saveMoreOptions
;
448 if (ReadAttr(modelNode
, kAttrQueryMoreOptions
,
449 kAttrQueryMoreOptionsForeign
, B_RAW_TYPE
, 0, &saveMoreOptions
,
450 sizeof(MoreOptionsStruct
),
451 &MoreOptionsStruct::EndianSwap
) != kReadAttrFailed
) {
452 fQueryListRep
->fShowResultsFromTrash
= saveMoreOptions
.searchTrash
;
455 fStatus
= query
.SetPredicate(buffer
.String());
457 fQueryListRep
->fOldPoseList
= oldPoseList
;
458 fQueryListRep
->fDynamicDateQuery
= false;
460 fQueryListRep
->fRefreshEveryHour
= false;
461 fQueryListRep
->fRefreshEveryMinute
= false;
463 if (modelNode
->ReadAttr(kAttrDynamicDateQuery
, B_BOOL_TYPE
, 0,
464 &fQueryListRep
->fDynamicDateQuery
,
465 sizeof(bool)) != sizeof(bool)) {
466 fQueryListRep
->fDynamicDateQuery
= false;
469 if (fQueryListRep
->fDynamicDateQuery
) {
470 // only refresh every minute on debug builds
471 fQueryListRep
->fRefreshEveryMinute
= buffer
.IFindFirst("second") != -1
472 || buffer
.IFindFirst("minute") != -1;
473 fQueryListRep
->fRefreshEveryHour
= fQueryListRep
->fRefreshEveryMinute
474 || buffer
.IFindFirst("hour") != -1;
477 // don't refresh every minute unless we are running debug build
478 fQueryListRep
->fRefreshEveryMinute
= false;
485 bool searchAllVolumes
= true;
486 status_t result
= B_OK
;
488 // get volumes to perform query on
489 if (modelNode
->GetAttrInfo(kAttrQueryVolume
, &info
) == B_OK
) {
492 if ((buffer
= (char*)malloc((size_t)info
.size
)) != NULL
493 && modelNode
->ReadAttr(kAttrQueryVolume
, B_MESSAGE_TYPE
, 0,
494 buffer
, (size_t)info
.size
) == info
.size
) {
496 if (message
.Unflatten(buffer
) == B_OK
) {
497 for (int32 index
= 0; ;index
++) {
500 // match a volume with the info embedded in
502 result
= MatchArchivedVolume(&volume
, &message
, index
);
503 if (result
== B_OK
) {
504 // start the query on this volume
505 result
= FetchOneQuery(&query
, target
,
506 fQueryListRep
->fQueryList
, &volume
);
510 searchAllVolumes
= false;
511 } else if (result
!= B_DEV_BAD_DRIVE_NUM
) {
512 // if B_DEV_BAD_DRIVE_NUM, the volume just isn't
513 // mounted this time around, keep looking for more
514 // if other error, bail
524 if (searchAllVolumes
) {
525 // no specific volumes embedded in query, search everything
526 BVolumeRoster roster
;
530 while (roster
.GetNextVolume(&volume
) == B_OK
)
531 if (volume
.IsPersistent() && volume
.KnowsQuery()) {
532 result
= FetchOneQuery(&query
, target
,
533 fQueryListRep
->fQueryList
, &volume
);
546 QueryEntryListCollection::FetchOneQuery(const BQuery
* copyThis
,
547 BHandler
* target
, BObjectList
<BQuery
>* list
, BVolume
* volume
)
549 BQuery
* query
= new (nothrow
) BQuery
;
553 // have to fake a copy constructor here because BQuery doesn't have
554 // a copy constructor
556 const_cast<BQuery
*>(copyThis
)->GetPredicate(&buffer
);
557 query
->SetPredicate(buffer
.String());
559 query
->SetTarget(BMessenger(target
));
560 query
->SetVolume(volume
);
562 status_t result
= query
->Fetch();
563 if (result
!= B_OK
) {
564 PRINT(("fetch error %s\n", strerror(result
)));
569 list
->AddItem(query
);
575 QueryEntryListCollection::~QueryEntryListCollection()
577 if (fQueryListRep
->CloseQueryList())
578 delete fQueryListRep
;
582 QueryEntryListCollection
*
583 QueryEntryListCollection::Clone()
585 fQueryListRep
->OpenQueryList();
586 return new QueryEntryListCollection(*this);
590 // #pragma mark - QueryEntryListCollection
593 QueryEntryListCollection::QueryEntryListCollection(
594 const QueryEntryListCollection
&cloneThis
)
597 fQueryListRep(cloneThis
.fQueryListRep
)
599 // only to be used by the Clone routine
604 QueryEntryListCollection::ClearOldPoseList()
606 delete fQueryListRep
->fOldPoseList
;
607 fQueryListRep
->fOldPoseList
= NULL
;
612 QueryEntryListCollection::GetNextEntry(BEntry
* entry
, bool traverse
)
614 status_t result
= B_ERROR
;
616 for (int32 count
= fQueryListRep
->fQueryList
->CountItems();
617 fQueryListRep
->fQueryListIndex
< count
;
618 fQueryListRep
->fQueryListIndex
++) {
619 result
= fQueryListRep
->fQueryList
->
620 ItemAt(fQueryListRep
->fQueryListIndex
)->
621 GetNextEntry(entry
, traverse
);
631 QueryEntryListCollection::GetNextDirents(struct dirent
* buffer
, size_t length
,
636 for (int32 queryCount
= fQueryListRep
->fQueryList
->CountItems();
637 fQueryListRep
->fQueryListIndex
< queryCount
;
638 fQueryListRep
->fQueryListIndex
++) {
639 result
= fQueryListRep
->fQueryList
->
640 ItemAt(fQueryListRep
->fQueryListIndex
)->
641 GetNextDirents(buffer
, length
, count
);
651 QueryEntryListCollection::GetNextRef(entry_ref
* ref
)
653 status_t result
= B_ERROR
;
655 for (int32 count
= fQueryListRep
->fQueryList
->CountItems();
656 fQueryListRep
->fQueryListIndex
< count
;
657 fQueryListRep
->fQueryListIndex
++) {
659 result
= fQueryListRep
->fQueryList
->
660 ItemAt(fQueryListRep
->fQueryListIndex
)->GetNextRef(ref
);
670 QueryEntryListCollection::Rewind()
672 fQueryListRep
->fQueryListIndex
= 0;
679 QueryEntryListCollection::CountEntries()
686 QueryEntryListCollection::ShowResultsFromTrash() const
688 return fQueryListRep
->fShowResultsFromTrash
;
693 QueryEntryListCollection::DynamicDateQuery() const
695 return fQueryListRep
->fDynamicDateQuery
;
700 QueryEntryListCollection::DynamicDateRefreshEveryHour() const
702 return fQueryListRep
->fRefreshEveryHour
;
707 QueryEntryListCollection::DynamicDateRefreshEveryMinute() const
709 return fQueryListRep
->fRefreshEveryMinute
;