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.
8 * Fernando Francisco de Oliveira
14 * Stephan Aßmus <superstippi@gmx.de>
15 * Axel Dörfler, axeld@pinc-software.de
19 #include "ImageFileNavigator.h"
25 #include <BitmapStream.h>
26 #include <Directory.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"
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;
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
60 class TrackerNavigator
: public Navigator
{
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
);
73 BMessenger fTrackerMessenger
;
74 // of the window that this was launched from
78 class FolderNavigator
: public Navigator
{
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
);
88 void _BuildEntryList();
89 static int _CompareRefs(const entry_ref
* refA
,
90 const entry_ref
* refB
);
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
{
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
);
111 bool _CheckForTracker(const entry_ref
& ref
);
113 TrackerNavigator
* fTrackerNavigator
;
114 FolderNavigator
* fFolderNavigator
;
119 entry_ref_is_file(const entry_ref
& ref
)
121 BEntry
entry(&ref
, true);
122 if (entry
.InitCheck() != B_OK
)
125 return entry
.IsFile();
132 Navigator::Navigator()
137 Navigator::~Navigator()
143 Navigator::IsImage(const entry_ref
& ref
)
145 if (!entry_ref_is_file(ref
))
148 BFile
file(&ref
, B_READ_ONLY
);
149 if (file
.InitCheck() != B_OK
)
152 BTranslatorRoster
* roster
= BTranslatorRoster::Default();
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
;
166 TrackerNavigator::TrackerNavigator(const BMessenger
& trackerMessenger
)
168 fTrackerMessenger(trackerMessenger
)
173 TrackerNavigator::~TrackerNavigator()
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())
186 // Ask the Tracker what the next/prev file in the window is.
187 // Continue asking for the next reference until a valid
189 entry_ref nextRef
= currentRef
;
190 bool foundRef
= false;
192 BMessage
request(B_GET_PROPERTY
);
195 specifier
.what
= B_DIRECT_SPECIFIER
;
197 specifier
.what
= 'snxt';
199 specifier
.what
= 'sprv';
200 specifier
.AddString("property", "Entry");
202 // if rewinding, ask for the ref to the
203 // first item in the directory
204 specifier
.AddInt32("data", 0);
206 specifier
.AddRef("data", &nextRef
);
207 request
.AddSpecifier(&specifier
);
210 if (fTrackerMessenger
.SendMessage(&request
, &reply
) != B_OK
)
212 if (reply
.FindRef("result", &nextRef
) != B_OK
)
215 if (IsImage(nextRef
))
219 // stop asking for the first ref in the directory
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
);
238 TrackerNavigator::IsValid()
240 return fTrackerMessenger
.IsValid();
247 FolderNavigator::FolderNavigator(entry_ref
& ref
)
252 if (entry
.IsDirectory())
256 nodeRef
.device
= ref
.device
;
257 nodeRef
.node
= ref
.directory
;
259 fFolder
.SetTo(&nodeRef
);
264 // TODO: monitor the directory for changes, sort it naturally
266 if (entry
.IsDirectory())
267 FindNextImage(ref
, ref
, false, true);
271 FolderNavigator::~FolderNavigator()
277 FolderNavigator::FindNextImage(const entry_ref
& currentRef
, entry_ref
& nextRef
,
278 bool next
, bool rewind
)
282 index
= next
? fEntries
.CountItems() : 0;
285 index
= fEntries
.BinarySearchIndex(currentRef
,
286 &FolderNavigator::_CompareRefs
);
293 while (index
< fEntries
.CountItems() && index
>= 0) {
294 const entry_ref
& ref
= *fEntries
.ItemAt(index
);
299 // remove non-image entries
300 delete fEntries
.RemoveItemAt(index
);
311 FolderNavigator::UpdateSelection(const entry_ref
& ref
)
313 // nothing to do for us here
318 FolderNavigator::_BuildEntryList()
320 fEntries
.MakeEmpty();
324 entry_ref
* ref
= new entry_ref();
325 status_t status
= fFolder
.GetNextRef(ref
);
326 if (status
!= B_OK
) {
331 fEntries
.AddItem(ref
);
334 fEntries
.SortItems(&FolderNavigator::_CompareRefs
);
339 FolderNavigator::_CompareRefs(const entry_ref
* refA
, const entry_ref
* refB
)
341 return BPrivate::NaturalCompare(refA
->name
, refB
->name
);
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
);
358 fFolderNavigator
= new FolderNavigator(ref
);
362 AutoAdjustingNavigator::~AutoAdjustingNavigator()
364 delete fTrackerNavigator
;
365 delete fFolderNavigator
;
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
,
377 if (fFolderNavigator
!= NULL
)
378 return fFolderNavigator
->FindNextImage(currentRef
, nextRef
, next
,
386 AutoAdjustingNavigator::UpdateSelection(const entry_ref
& ref
)
388 if (_CheckForTracker(ref
)) {
389 fTrackerNavigator
->UpdateSelection(ref
);
393 if (fFolderNavigator
!= NULL
)
394 fFolderNavigator
->UpdateSelection(ref
);
399 AutoAdjustingNavigator::_CheckForTracker(const entry_ref
& ref
)
401 if (fTrackerNavigator
!= NULL
) {
402 if (fTrackerNavigator
->IsValid())
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
);
422 ImageFileNavigator::ImageFileNavigator(const entry_ref
& ref
,
423 const BMessenger
& trackerMessenger
)
429 fNavigator
= new AutoAdjustingNavigator(fCurrentRef
, trackerMessenger
);
433 ImageFileNavigator::~ImageFileNavigator()
440 ImageFileNavigator::SetTo(const entry_ref
& ref
, int32 page
, int32 pageCount
)
443 fDocumentIndex
= page
;
444 fDocumentCount
= pageCount
;
449 ImageFileNavigator::CurrentPage()
451 return fDocumentIndex
;
456 ImageFileNavigator::PageCount()
458 return fDocumentCount
;
463 ImageFileNavigator::FirstPage()
465 if (fDocumentIndex
!= 1) {
474 ImageFileNavigator::LastPage()
476 if (fDocumentIndex
!= fDocumentCount
) {
477 fDocumentIndex
= fDocumentCount
;
485 ImageFileNavigator::NextPage()
487 if (fDocumentIndex
< fDocumentCount
) {
496 ImageFileNavigator::PreviousPage()
498 if (fDocumentIndex
> 1) {
507 ImageFileNavigator::HasNextPage()
509 return fDocumentIndex
< fDocumentCount
;
514 ImageFileNavigator::HasPreviousPage()
516 return fDocumentIndex
> 1;
521 ImageFileNavigator::GoToPage(int32 page
)
523 if (page
> 0 && page
<= fDocumentCount
&& page
!= fDocumentIndex
) {
524 fDocumentIndex
= page
;
532 ImageFileNavigator::FirstFile()
535 if (fNavigator
->FindNextImage(fCurrentRef
, ref
, false, true)) {
537 fNavigator
->UpdateSelection(fCurrentRef
);
546 ImageFileNavigator::NextFile()
549 if (fNavigator
->FindNextImage(fCurrentRef
, ref
, true, false)) {
551 fNavigator
->UpdateSelection(fCurrentRef
);
560 ImageFileNavigator::PreviousFile()
563 if (fNavigator
->FindNextImage(fCurrentRef
, ref
, false, false)) {
565 fNavigator
->UpdateSelection(fCurrentRef
);
574 ImageFileNavigator::HasNextFile()
577 return fNavigator
->FindNextImage(fCurrentRef
, ref
, true, false);
582 ImageFileNavigator::HasPreviousFile()
585 return fNavigator
->FindNextImage(fCurrentRef
, ref
, false, false);
590 ImageFileNavigator::GetNextFile(const entry_ref
& ref
, entry_ref
& nextRef
)
592 return fNavigator
->FindNextImage(ref
, nextRef
, true, false);
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.
608 ImageFileNavigator::MoveFileToTrash()
611 if (!fNavigator
->FindNextImage(fCurrentRef
, nextRef
, true, false)
612 && !fNavigator
->FindNextImage(fCurrentRef
, nextRef
, false, false))
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
621 BMessenger
tracker(kTrackerSignature
);
622 if (tracker
.SendMessage(&trash
) != B_OK
)
625 if (nextRef
.device
!= -1) {
626 SetTo(nextRef
, 1, 1);