BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / showimage / ImageFileNavigator.cpp
blobbe45ea8b7692719771a4939e9579eb197bacf23f
1 /*
2 * Copyright 2003-2010, Haiku, Inc. All Rights Reserved.
3 * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd.
4 * Copyright 2006 Bernd Korz. All Rights Reserved
5 * Distributed under the terms of the MIT License.
7 * Authors:
8 * Fernando Francisco de Oliveira
9 * Michael Wilber
10 * Michael Pfeiffer
11 * Ryan Leavengood
12 * yellowTAB GmbH
13 * Bernd Korz
14 * Stephan Aßmus <superstippi@gmx.de>
15 * Axel Dörfler, axeld@pinc-software.de
19 #include "ImageFileNavigator.h"
21 #include <new>
23 #include <stdio.h>
25 #include <BitmapStream.h>
26 #include <Directory.h>
27 #include <Entry.h>
28 #include <File.h>
29 #include <NaturalCompare.h>
30 #include <ObjectList.h>
31 #include <TranslatorRoster.h>
33 #include <tracker_private.h>
35 #include "ProgressWindow.h"
36 #include "ShowImageConstants.h"
39 class Navigator {
40 public:
41 Navigator();
42 virtual ~Navigator();
44 virtual bool FindNextImage(const entry_ref& currentRef,
45 entry_ref& ref, bool next, bool rewind) = 0;
46 virtual void UpdateSelection(const entry_ref& ref) = 0;
48 protected:
49 bool IsImage(const entry_ref& ref);
53 // Navigation to the next/previous image file is based on
54 // communication with Tracker, the folder containing the current
55 // image needs to be open for this to work. The routine first tries
56 // to find the next candidate file, then tries to load it as image.
57 // As long as loading fails, the operation is repeated for the next
58 // candidate file.
60 class TrackerNavigator : public Navigator {
61 public:
62 TrackerNavigator(
63 const BMessenger& trackerMessenger);
64 virtual ~TrackerNavigator();
66 virtual bool FindNextImage(const entry_ref& currentRef,
67 entry_ref& ref, bool next, bool rewind);
68 virtual void UpdateSelection(const entry_ref& ref);
70 bool IsValid();
72 private:
73 BMessenger fTrackerMessenger;
74 // of the window that this was launched from
78 class FolderNavigator : public Navigator {
79 public:
80 FolderNavigator(entry_ref& ref);
81 virtual ~FolderNavigator();
83 virtual bool FindNextImage(const entry_ref& currentRef,
84 entry_ref& ref, bool next, bool rewind);
85 virtual void UpdateSelection(const entry_ref& ref);
87 private:
88 void _BuildEntryList();
89 static int _CompareRefs(const entry_ref* refA,
90 const entry_ref* refB);
92 private:
93 BDirectory fFolder;
94 BObjectList<entry_ref> fEntries;
98 // This class handles the case of the user closing the Tracker window after
99 // opening ShowImage from that window.
100 class AutoAdjustingNavigator : public Navigator {
101 public:
102 AutoAdjustingNavigator(entry_ref& ref,
103 const BMessenger& trackerMessenger);
104 virtual ~AutoAdjustingNavigator();
106 virtual bool FindNextImage(const entry_ref& currentRef,
107 entry_ref& ref, bool next, bool rewind);
108 virtual void UpdateSelection(const entry_ref& ref);
110 private:
111 bool _CheckForTracker(const entry_ref& ref);
113 TrackerNavigator* fTrackerNavigator;
114 FolderNavigator* fFolderNavigator;
118 static bool
119 entry_ref_is_file(const entry_ref& ref)
121 BEntry entry(&ref, true);
122 if (entry.InitCheck() != B_OK)
123 return false;
125 return entry.IsFile();
129 // #pragma mark -
132 Navigator::Navigator()
137 Navigator::~Navigator()
142 bool
143 Navigator::IsImage(const entry_ref& ref)
145 if (!entry_ref_is_file(ref))
146 return false;
148 BFile file(&ref, B_READ_ONLY);
149 if (file.InitCheck() != B_OK)
150 return false;
152 BTranslatorRoster* roster = BTranslatorRoster::Default();
153 if (roster == NULL)
154 return false;
156 translator_info info;
157 memset(&info, 0, sizeof(translator_info));
158 return roster->Identify(&file, NULL, &info, 0, NULL,
159 B_TRANSLATOR_BITMAP) == B_OK;
163 // #pragma mark -
166 TrackerNavigator::TrackerNavigator(const BMessenger& trackerMessenger)
168 fTrackerMessenger(trackerMessenger)
173 TrackerNavigator::~TrackerNavigator()
178 bool
179 TrackerNavigator::FindNextImage(const entry_ref& currentRef, entry_ref& ref,
180 bool next, bool rewind)
182 // Based on GetTrackerWindowFile function from BeMail
183 if (!fTrackerMessenger.IsValid())
184 return false;
186 // Ask the Tracker what the next/prev file in the window is.
187 // Continue asking for the next reference until a valid
188 // image is found.
189 entry_ref nextRef = currentRef;
190 bool foundRef = false;
191 while (!foundRef) {
192 BMessage request(B_GET_PROPERTY);
193 BMessage specifier;
194 if (rewind)
195 specifier.what = B_DIRECT_SPECIFIER;
196 else if (next)
197 specifier.what = 'snxt';
198 else
199 specifier.what = 'sprv';
200 specifier.AddString("property", "Entry");
201 if (rewind) {
202 // if rewinding, ask for the ref to the
203 // first item in the directory
204 specifier.AddInt32("data", 0);
205 } else
206 specifier.AddRef("data", &nextRef);
207 request.AddSpecifier(&specifier);
209 BMessage reply;
210 if (fTrackerMessenger.SendMessage(&request, &reply) != B_OK)
211 return false;
212 if (reply.FindRef("result", &nextRef) != B_OK)
213 return false;
215 if (IsImage(nextRef))
216 foundRef = true;
218 rewind = false;
219 // stop asking for the first ref in the directory
222 ref = nextRef;
223 return foundRef;
227 void
228 TrackerNavigator::UpdateSelection(const entry_ref& ref)
230 BMessage setSelection(B_SET_PROPERTY);
231 setSelection.AddSpecifier("Selection");
232 setSelection.AddRef("data", &ref);
233 fTrackerMessenger.SendMessage(&setSelection);
237 bool
238 TrackerNavigator::IsValid()
240 return fTrackerMessenger.IsValid();
244 // #pragma mark -
247 FolderNavigator::FolderNavigator(entry_ref& ref)
249 fEntries(true)
251 BEntry entry(&ref);
252 if (entry.IsDirectory())
253 fFolder.SetTo(&ref);
254 else {
255 node_ref nodeRef;
256 nodeRef.device = ref.device;
257 nodeRef.node = ref.directory;
259 fFolder.SetTo(&nodeRef);
262 _BuildEntryList();
264 // TODO: monitor the directory for changes, sort it naturally
266 if (entry.IsDirectory())
267 FindNextImage(ref, ref, false, true);
271 FolderNavigator::~FolderNavigator()
276 bool
277 FolderNavigator::FindNextImage(const entry_ref& currentRef, entry_ref& nextRef,
278 bool next, bool rewind)
280 int32 index;
281 if (rewind) {
282 index = next ? fEntries.CountItems() : 0;
283 next = !next;
284 } else {
285 index = fEntries.BinarySearchIndex(currentRef,
286 &FolderNavigator::_CompareRefs);
287 if (next)
288 index++;
289 else
290 index--;
293 while (index < fEntries.CountItems() && index >= 0) {
294 const entry_ref& ref = *fEntries.ItemAt(index);
295 if (IsImage(ref)) {
296 nextRef = ref;
297 return true;
298 } else {
299 // remove non-image entries
300 delete fEntries.RemoveItemAt(index);
301 if (!next)
302 index--;
306 return false;
310 void
311 FolderNavigator::UpdateSelection(const entry_ref& ref)
313 // nothing to do for us here
317 void
318 FolderNavigator::_BuildEntryList()
320 fEntries.MakeEmpty();
321 fFolder.Rewind();
323 while (true) {
324 entry_ref* ref = new entry_ref();
325 status_t status = fFolder.GetNextRef(ref);
326 if (status != B_OK) {
327 delete ref;
328 break;
331 fEntries.AddItem(ref);
334 fEntries.SortItems(&FolderNavigator::_CompareRefs);
338 /*static*/ int
339 FolderNavigator::_CompareRefs(const entry_ref* refA, const entry_ref* refB)
341 return BPrivate::NaturalCompare(refA->name, refB->name);
345 // #pragma mark -
348 AutoAdjustingNavigator::AutoAdjustingNavigator(entry_ref& ref,
349 const BMessenger& trackerMessenger)
351 fTrackerNavigator(NULL),
352 fFolderNavigator(NULL)
354 // TODO: allow selecting a folder from Tracker as well!
355 if (trackerMessenger.IsValid())
356 fTrackerNavigator = new TrackerNavigator(trackerMessenger);
357 else
358 fFolderNavigator = new FolderNavigator(ref);
362 AutoAdjustingNavigator::~AutoAdjustingNavigator()
364 delete fTrackerNavigator;
365 delete fFolderNavigator;
369 bool
370 AutoAdjustingNavigator::FindNextImage(const entry_ref& currentRef,
371 entry_ref& nextRef, bool next, bool rewind)
373 if (_CheckForTracker(currentRef))
374 return fTrackerNavigator->FindNextImage(currentRef, nextRef, next,
375 rewind);
377 if (fFolderNavigator != NULL)
378 return fFolderNavigator->FindNextImage(currentRef, nextRef, next,
379 rewind);
381 return false;
385 void
386 AutoAdjustingNavigator::UpdateSelection(const entry_ref& ref)
388 if (_CheckForTracker(ref)) {
389 fTrackerNavigator->UpdateSelection(ref);
390 return;
393 if (fFolderNavigator != NULL)
394 fFolderNavigator->UpdateSelection(ref);
398 bool
399 AutoAdjustingNavigator::_CheckForTracker(const entry_ref& ref)
401 if (fTrackerNavigator != NULL) {
402 if (fTrackerNavigator->IsValid())
403 return true;
404 else {
405 delete fTrackerNavigator;
406 fTrackerNavigator = NULL;
408 // If for some reason we already have one
409 delete fFolderNavigator;
410 entry_ref currentRef = ref;
411 fFolderNavigator = new FolderNavigator(currentRef);
415 return false;
419 // #pragma mark -
422 ImageFileNavigator::ImageFileNavigator(const entry_ref& ref,
423 const BMessenger& trackerMessenger)
425 fCurrentRef(ref),
426 fDocumentIndex(1),
427 fDocumentCount(1)
429 fNavigator = new AutoAdjustingNavigator(fCurrentRef, trackerMessenger);
433 ImageFileNavigator::~ImageFileNavigator()
435 delete fNavigator;
439 void
440 ImageFileNavigator::SetTo(const entry_ref& ref, int32 page, int32 pageCount)
442 fCurrentRef = ref;
443 fDocumentIndex = page;
444 fDocumentCount = pageCount;
448 int32
449 ImageFileNavigator::CurrentPage()
451 return fDocumentIndex;
455 int32
456 ImageFileNavigator::PageCount()
458 return fDocumentCount;
462 bool
463 ImageFileNavigator::FirstPage()
465 if (fDocumentIndex != 1) {
466 fDocumentIndex = 1;
467 return true;
469 return false;
473 bool
474 ImageFileNavigator::LastPage()
476 if (fDocumentIndex != fDocumentCount) {
477 fDocumentIndex = fDocumentCount;
478 return true;
480 return false;
484 bool
485 ImageFileNavigator::NextPage()
487 if (fDocumentIndex < fDocumentCount) {
488 fDocumentIndex++;
489 return true;
491 return false;
495 bool
496 ImageFileNavigator::PreviousPage()
498 if (fDocumentIndex > 1) {
499 fDocumentIndex--;
500 return true;
502 return false;
506 bool
507 ImageFileNavigator::HasNextPage()
509 return fDocumentIndex < fDocumentCount;
513 bool
514 ImageFileNavigator::HasPreviousPage()
516 return fDocumentIndex > 1;
520 bool
521 ImageFileNavigator::GoToPage(int32 page)
523 if (page > 0 && page <= fDocumentCount && page != fDocumentIndex) {
524 fDocumentIndex = page;
525 return true;
527 return false;
531 bool
532 ImageFileNavigator::FirstFile()
534 entry_ref ref;
535 if (fNavigator->FindNextImage(fCurrentRef, ref, false, true)) {
536 SetTo(ref, 1, 1);
537 fNavigator->UpdateSelection(fCurrentRef);
538 return true;
541 return false;
545 bool
546 ImageFileNavigator::NextFile()
548 entry_ref ref;
549 if (fNavigator->FindNextImage(fCurrentRef, ref, true, false)) {
550 SetTo(ref, 1, 1);
551 fNavigator->UpdateSelection(fCurrentRef);
552 return true;
555 return false;
559 bool
560 ImageFileNavigator::PreviousFile()
562 entry_ref ref;
563 if (fNavigator->FindNextImage(fCurrentRef, ref, false, false)) {
564 SetTo(ref, 1, 1);
565 fNavigator->UpdateSelection(fCurrentRef);
566 return true;
569 return false;
573 bool
574 ImageFileNavigator::HasNextFile()
576 entry_ref ref;
577 return fNavigator->FindNextImage(fCurrentRef, ref, true, false);
581 bool
582 ImageFileNavigator::HasPreviousFile()
584 entry_ref ref;
585 return fNavigator->FindNextImage(fCurrentRef, ref, false, false);
589 bool
590 ImageFileNavigator::GetNextFile(const entry_ref& ref, entry_ref& nextRef)
592 return fNavigator->FindNextImage(ref, nextRef, true, false);
596 bool
597 ImageFileNavigator::GetPreviousFile(const entry_ref& ref,
598 entry_ref& previousRef)
600 return fNavigator->FindNextImage(ref, previousRef, false, false);
604 /*! Moves the current file into the trash.
605 Returns true if a new file should be loaded, false if not.
607 bool
608 ImageFileNavigator::MoveFileToTrash()
610 entry_ref nextRef;
611 if (!fNavigator->FindNextImage(fCurrentRef, nextRef, true, false)
612 && !fNavigator->FindNextImage(fCurrentRef, nextRef, false, false))
613 nextRef.device = -1;
615 // Move image to Trash
616 BMessage trash(BPrivate::kMoveToTrash);
617 trash.AddRef("refs", &fCurrentRef);
619 // We create our own messenger because the member fTrackerMessenger
620 // could be invalid
621 BMessenger tracker(kTrackerSignature);
622 if (tracker.SendMessage(&trash) != B_OK)
623 return false;
625 if (nextRef.device != -1) {
626 SetTo(nextRef, 1, 1);
627 return true;
630 return false;