Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / kits / tracker / QueryPoseView.cpp
blobf621bb002e1fbfd5099949ee4140d6e2ab7ad076
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 fShowResultsFromTrash(false),
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"), 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));
142 void
143 BQueryPoseView::AttachedToWindow()
145 _inherited::AttachedToWindow();
146 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
147 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
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 bool
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())) {
213 return false;
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);
223 if (pose != NULL)
224 oldPoseList->RemoveItem(pose);
227 return result;
231 void
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
253 EntryListBase*
254 BQueryPoseView::InitDirentIterator(const entry_ref* ref)
256 BEntry entry(ref);
257 if (entry.InitCheck() != B_OK)
258 return NULL;
260 Model sourceModel(&entry, true);
261 if (sourceModel.InitCheck() != B_OK)
262 return NULL;
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,
275 oldPoseList);
276 fCreateOldPoseList = false;
278 if (fQueryListContainer->InitCheck() != B_OK) {
279 delete fQueryListContainer;
280 fQueryListContainer = NULL;
281 return 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
298 tm timeData;
299 localtime_r(&nextMidnight, &timeData);
300 timeData.tm_sec = 0;
301 timeData.tm_min = 0;
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);
308 timeData.tm_sec = 0;
309 timeData.tm_min = 0;
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);
318 timeData.tm_sec = 0;
319 nextMinute = mktime(&timeData);
321 PRINT(("%" B_PRId32 " seconds till next minute\n", nextMinute - now));
323 bigtime_t delta;
324 if (fQueryListContainer->DynamicDateRefreshEveryMinute())
325 delta = nextMinute - now;
326 else if (fQueryListContainer->DynamicDateRefreshEveryHour())
327 delta = nextHour - now;
328 else
329 delta = nextMidnight - now;
331 #if DEBUG
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,
349 refreshInSeconds));
350 #endif
352 // bump up to microseconds
353 delta *= 1000000;
355 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
356 ThrowOnAssert(tracker != NULL);
358 tracker->MainTaskLoop()->RunLater(
359 NewLockingMemberFunctionObject(&BQueryPoseView::Refresh, this),
360 delta);
363 return fQueryListContainer->Clone();
367 uint32
368 BQueryPoseView::WatchNewNodeMask()
370 return B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR;
374 const char*
375 BQueryPoseView::SearchForType() const
377 if (!fSearchForMimeType.Length()) {
378 BModelOpener opener(TargetModel());
379 BString buffer;
380 attr_info attrInfo;
382 // read the type of files we are looking for
383 status_t status
384 = TargetModel()->Node()->GetAttrInfo(kAttrQueryInitialMime,
385 &attrInfo);
386 if (status == B_OK) {
387 TargetModel()->Node()->ReadAttrString(kAttrQueryInitialMime,
388 &buffer);
391 TTracker* tracker = dynamic_cast<TTracker*>(be_app);
392 if (tracker != NULL && buffer.Length() > 0) {
393 const ShortMimeInfo* info = tracker->MimeTypes()->FindMimeType(
394 buffer.String());
395 if (info != NULL)
396 fSearchForMimeType = info->InternalName();
399 if (!fSearchForMimeType.Length())
400 fSearchForMimeType = B_FILE_MIMETYPE;
403 return fSearchForMimeType.String();
407 bool
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)
413 return true;
416 return false;
420 // #pragma mark - QueryEntryListCollection
423 QueryEntryListCollection::QueryEntryListCollection(Model* model,
424 BHandler* target, PoseList* oldPoseList)
426 fQueryListRep(new QueryListRep(new BObjectList<BQuery>(5, true)))
428 Rewind();
429 attr_info info;
430 BQuery query;
432 BNode* modelNode = model->Node();
433 if (modelNode == NULL) {
434 fStatus = B_ERROR;
435 return;
438 // read the actual query string
439 fStatus = modelNode->GetAttrInfo(kAttrQueryString, &info);
440 if (fStatus != B_OK)
441 return;
443 BString buffer;
444 if (modelNode->ReadAttr(kAttrQueryString, B_STRING_TYPE, 0,
445 buffer.LockBuffer((int32)info.size),
446 (size_t)info.size) != info.size) {
447 fStatus = B_ERROR;
448 return;
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;
483 #if !DEBUG
484 // don't refresh every minute unless we are running debug build
485 fQueryListRep->fRefreshEveryMinute = false;
486 #endif
489 if (fStatus != B_OK)
490 return;
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) {
497 char* buffer = NULL;
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) {
502 BMessage message;
503 if (message.Unflatten(buffer) == B_OK) {
504 for (int32 index = 0; ;index++) {
505 ASSERT(index < 100);
506 BVolume volume;
507 // match a volume with the info embedded in
508 // the message
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);
514 if (result != B_OK)
515 continue;
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
522 break;
528 free(buffer);
531 if (searchAllVolumes) {
532 // no specific volumes embedded in query, search everything
533 BVolumeRoster roster;
534 BVolume volume;
536 roster.Rewind();
537 while (roster.GetNextVolume(&volume) == B_OK)
538 if (volume.IsPersistent() && volume.KnowsQuery()) {
539 result = FetchOneQuery(&query, target,
540 fQueryListRep->fQueryList, &volume);
541 if (result != B_OK)
542 continue;
546 fStatus = B_OK;
548 return;
552 status_t
553 QueryEntryListCollection::FetchOneQuery(const BQuery* copyThis,
554 BHandler* target, BObjectList<BQuery>* list, BVolume* volume)
556 BQuery* query = new (nothrow) BQuery;
557 if (query == NULL)
558 return B_NO_MEMORY;
560 // have to fake a copy constructor here because BQuery doesn't have
561 // a copy constructor
562 BString buffer;
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)));
572 delete query;
573 return result;
576 list->AddItem(query);
578 return B_OK;
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)
603 EntryListBase(),
604 fQueryListRep(cloneThis.fQueryListRep)
606 // only to be used by the Clone routine
610 void
611 QueryEntryListCollection::ClearOldPoseList()
613 delete fQueryListRep->fOldPoseList;
614 fQueryListRep->fOldPoseList = NULL;
618 status_t
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);
629 if (result == B_OK)
630 break;
633 return result;
637 int32
638 QueryEntryListCollection::GetNextDirents(struct dirent* buffer, size_t length,
639 int32 count)
641 int32 result = 0;
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);
649 if (result > 0)
650 break;
653 return result;
657 status_t
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);
668 if (result == B_OK)
669 break;
672 return result;
676 status_t
677 QueryEntryListCollection::Rewind()
679 fQueryListRep->fQueryListIndex = 0;
681 return B_OK;
685 int32
686 QueryEntryListCollection::CountEntries()
688 return 0;
692 bool
693 QueryEntryListCollection::ShowResultsFromTrash() const
695 return fQueryListRep->fShowResultsFromTrash;
699 bool
700 QueryEntryListCollection::DynamicDateQuery() const
702 return fQueryListRep->fDynamicDateQuery;
706 bool
707 QueryEntryListCollection::DynamicDateRefreshEveryHour() const
709 return fQueryListRep->fRefreshEveryHour;
713 bool
714 QueryEntryListCollection::DynamicDateRefreshEveryMinute() const
716 return fQueryListRep->fRefreshEveryMinute;