btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / tracker / QueryPoseView.cpp
blobaa328ef7768f05b8f8410622a5b20230c70d7cd1
1 /*
2 Open Tracker License
4 Terms and Conditions
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.
32 All rights reserved.
36 #include "QueryPoseView.h"
38 #include <new>
40 #include <Catalog.h>
41 #include <Debug.h>
42 #include <Locale.h>
43 #include <NodeMonitor.h>
44 #include <Query.h>
45 #include <Volume.h>
46 #include <VolumeRoster.h>
47 #include <Window.h>
49 #include "Attributes.h"
50 #include "AttributeStream.h"
51 #include "AutoLock.h"
52 #include "Commands.h"
53 #include "FindPanel.h"
54 #include "FSUtils.h"
55 #include "MimeTypeList.h"
56 #include "MimeTypes.h"
57 #include "Tracker.h"
59 #include <fs_attr.h>
62 using std::nothrow;
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
74 // Trash is supported
77 // #pragma mark - BQueryPoseView
80 BQueryPoseView::BQueryPoseView(Model* model)
82 BPoseView(model, kListMode),
83 fRefFilter(NULL),
84 fQueryList(NULL),
85 fQueryListContainer(NULL),
86 fCreateOldPoseList(false)
91 BQueryPoseView::~BQueryPoseView()
93 delete fQueryListContainer;
97 void
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);
105 break;
108 default:
109 _inherited::MessageReceived(message);
110 break;
115 void
116 BQueryPoseView::EditQueries()
118 BMessage message(kEditQuery);
119 message.AddRef("refs", TargetModel()->EntryRef());
120 BMessenger(kTrackerSignature, -1, 0).SendMessage(&message);
124 void
125 BQueryPoseView::SetUpDefaultColumnsIfNeeded()
127 // in case there were errors getting some columns
128 if (fColumnList->CountItems() != 0)
129 return;
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));
142 void
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);
151 void
152 BQueryPoseView::RestoreState(AttributeStreamNode* node)
154 _inherited::RestoreState(node);
155 fViewState->SetViewMode(kListMode);
159 void
160 BQueryPoseView::RestoreState(const BMessage &message)
162 _inherited::RestoreState(message);
163 fViewState->SetViewMode(kListMode);
167 void
168 BQueryPoseView::SavePoseLocations(BRect*)
173 void
174 BQueryPoseView::SetViewMode(uint32)
179 void
180 BQueryPoseView::OpenParent()
185 void
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();
199 ResetOrigin();
200 ResetPosePlacementHint();
204 void
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
226 EntryListBase*
227 BQueryPoseView::InitDirentIterator(const entry_ref* ref)
229 BEntry entry(ref);
230 if (entry.InitCheck() != B_OK)
231 return NULL;
233 Model sourceModel(&entry, true);
234 if (sourceModel.InitCheck() != B_OK)
235 return NULL;
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,
248 oldPoseList);
249 fCreateOldPoseList = false;
251 if (fQueryListContainer->InitCheck() != B_OK) {
252 delete fQueryListContainer;
253 fQueryListContainer = NULL;
254 return 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
269 tm timeData;
270 localtime_r(&nextMidnight, &timeData);
271 timeData.tm_sec = 0;
272 timeData.tm_min = 0;
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);
279 timeData.tm_sec = 0;
280 timeData.tm_min = 0;
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);
289 timeData.tm_sec = 0;
290 nextMinute = mktime(&timeData);
292 PRINT(("%" B_PRIdTIME " seconds till next minute\n", nextMinute - now));
294 bigtime_t delta;
295 if (fQueryListContainer->DynamicDateRefreshEveryMinute())
296 delta = nextMinute - now;
297 else if (fQueryListContainer->DynamicDateRefreshEveryHour())
298 delta = nextHour - now;
299 else
300 delta = nextMidnight - now;
302 #if DEBUG
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,
320 refreshInSeconds));
321 #endif
323 // bump up to microseconds
324 delta *= 1000000;
326 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
327 ThrowOnAssert(tracker != NULL);
329 tracker->MainTaskLoop()->RunLater(
330 NewLockingMemberFunctionObject(&BQueryPoseView::Refresh, this),
331 delta);
334 SetRefFilter(new QueryRefFilter(fQueryListContainer->ShowResultsFromTrash()));
336 return fQueryListContainer->Clone();
340 uint32
341 BQueryPoseView::WatchNewNodeMask()
343 return B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR;
347 const char*
348 BQueryPoseView::SearchForType() const
350 if (!fSearchForMimeType.Length()) {
351 BModelOpener opener(TargetModel());
352 BString buffer;
353 attr_info attrInfo;
355 // read the type of files we are looking for
356 status_t status
357 = TargetModel()->Node()->GetAttrInfo(kAttrQueryInitialMime,
358 &attrInfo);
359 if (status == B_OK) {
360 TargetModel()->Node()->ReadAttrString(kAttrQueryInitialMime,
361 &buffer);
364 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
365 if (tracker != NULL && buffer.Length() > 0) {
366 const ShortMimeInfo* info = tracker->MimeTypes()->FindMimeType(
367 buffer.String());
368 if (info != NULL)
369 fSearchForMimeType = info->InternalName();
372 if (!fSearchForMimeType.Length())
373 fSearchForMimeType = B_FILE_MIMETYPE;
376 return fSearchForMimeType.String();
380 bool
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)
386 return true;
389 return false;
393 // #pragma mark - QueryRefFilter
396 QueryRefFilter::QueryRefFilter(bool showResultsFromTrash)
398 fShowResultsFromTrash(showResultsFromTrash)
403 bool
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)))
421 Rewind();
422 attr_info info;
423 BQuery query;
425 BNode* modelNode = model->Node();
426 if (modelNode == NULL) {
427 fStatus = B_ERROR;
428 return;
431 // read the actual query string
432 fStatus = modelNode->GetAttrInfo(kAttrQueryString, &info);
433 if (fStatus != B_OK)
434 return;
436 BString buffer;
437 if (modelNode->ReadAttr(kAttrQueryString, B_STRING_TYPE, 0,
438 buffer.LockBuffer((int32)info.size),
439 (size_t)info.size) != info.size) {
440 fStatus = B_ERROR;
441 return;
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;
476 #if !DEBUG
477 // don't refresh every minute unless we are running debug build
478 fQueryListRep->fRefreshEveryMinute = false;
479 #endif
482 if (fStatus != B_OK)
483 return;
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) {
490 char* buffer = NULL;
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) {
495 BMessage message;
496 if (message.Unflatten(buffer) == B_OK) {
497 for (int32 index = 0; ;index++) {
498 ASSERT(index < 100);
499 BVolume volume;
500 // match a volume with the info embedded in
501 // the message
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);
507 if (result != B_OK)
508 continue;
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
515 break;
521 free(buffer);
524 if (searchAllVolumes) {
525 // no specific volumes embedded in query, search everything
526 BVolumeRoster roster;
527 BVolume volume;
529 roster.Rewind();
530 while (roster.GetNextVolume(&volume) == B_OK)
531 if (volume.IsPersistent() && volume.KnowsQuery()) {
532 result = FetchOneQuery(&query, target,
533 fQueryListRep->fQueryList, &volume);
534 if (result != B_OK)
535 continue;
539 fStatus = B_OK;
541 return;
545 status_t
546 QueryEntryListCollection::FetchOneQuery(const BQuery* copyThis,
547 BHandler* target, BObjectList<BQuery>* list, BVolume* volume)
549 BQuery* query = new (nothrow) BQuery;
550 if (query == NULL)
551 return B_NO_MEMORY;
553 // have to fake a copy constructor here because BQuery doesn't have
554 // a copy constructor
555 BString buffer;
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)));
565 delete query;
566 return result;
569 list->AddItem(query);
571 return B_OK;
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)
596 EntryListBase(),
597 fQueryListRep(cloneThis.fQueryListRep)
599 // only to be used by the Clone routine
603 void
604 QueryEntryListCollection::ClearOldPoseList()
606 delete fQueryListRep->fOldPoseList;
607 fQueryListRep->fOldPoseList = NULL;
611 status_t
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);
622 if (result == B_OK)
623 break;
626 return result;
630 int32
631 QueryEntryListCollection::GetNextDirents(struct dirent* buffer, size_t length,
632 int32 count)
634 int32 result = 0;
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);
642 if (result > 0)
643 break;
646 return result;
650 status_t
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);
661 if (result == B_OK)
662 break;
665 return result;
669 status_t
670 QueryEntryListCollection::Rewind()
672 fQueryListRep->fQueryListIndex = 0;
674 return B_OK;
678 int32
679 QueryEntryListCollection::CountEntries()
681 return 0;
685 bool
686 QueryEntryListCollection::ShowResultsFromTrash() const
688 return fQueryListRep->fShowResultsFromTrash;
692 bool
693 QueryEntryListCollection::DynamicDateQuery() const
695 return fQueryListRep->fDynamicDateQuery;
699 bool
700 QueryEntryListCollection::DynamicDateRefreshEveryHour() const
702 return fQueryListRep->fRefreshEveryHour;
706 bool
707 QueryEntryListCollection::DynamicDateRefreshEveryMinute() const
709 return fQueryListRep->fRefreshEveryMinute;