From b9767a83ed31ef0a0c8f7db0a6ec032f2ce82e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 8 Nov 2010 23:06:36 +0000 Subject: [PATCH] * The ImageFileNavigator is now only for navigation, it doesn't load any images anymore. * Moved the new ImageCache into its own source file. * The cache is now used within ShowImage. There is no read-ahead caching being done yet, though, but you can quickly return to previous images, and you can also skip images faster than before. * Improved separation between the ShowImageStatusView and the rest; ShowImageWindow no longer has a getter for the image view. * The status view is now using the private BDirMenu which implements enhancement ticket #6778. * Made a few more methods private/protected in ShowImageWindow. * Fixed bug #6797. * The bitmap is currently only owned by the ImageCache, but we need to have a separate referenceable object owning it. Added a TODO comment for this. * The ProgressWindow is currently not being used anymore, added a TODO comment for this as well. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39364 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/showimage/ImageCache.cpp | 300 +++++++++++++++ src/apps/showimage/ImageCache.h | 85 ++++ src/apps/showimage/ImageFileNavigator.cpp | 427 ++------------------- src/apps/showimage/ImageFileNavigator.h | 29 +- src/apps/showimage/Jamfile | 4 +- src/apps/showimage/ShowImageStatusView.cpp | 58 ++- src/apps/showimage/ShowImageStatusView.h | 23 +- src/apps/showimage/ShowImageView.cpp | 5 +- src/apps/showimage/ShowImageWindow.cpp | 85 ++-- src/apps/showimage/ShowImageWindow.h | 14 +- 10 files changed, 535 insertions(+), 495 deletions(-) create mode 100644 src/apps/showimage/ImageCache.cpp create mode 100644 src/apps/showimage/ImageCache.h diff --git a/src/apps/showimage/ImageCache.cpp b/src/apps/showimage/ImageCache.cpp new file mode 100644 index 0000000000..1161d8c253 --- /dev/null +++ b/src/apps/showimage/ImageCache.cpp @@ -0,0 +1,300 @@ +/* + * Copyright 2010, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "ImageCache.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ShowImageConstants.h" + + +struct QueueEntry { + entry_ref ref; + int32 page; + status_t status; + std::set listeners; +}; + + +/*static*/ ImageCache ImageCache::sCache; + + +// #pragma mark - + + +ImageCache::ImageCache() + : + fLocker("image cache"), + fThreadCount(0), + fBytes(0) +{ + system_info info; + get_system_info(&info); + + fMaxThreadCount = (info.cpu_count + 1) / 2; + fMaxBytes = info.max_pages * B_PAGE_SIZE / 8; + fMaxEntries = 10; +} + + +ImageCache::~ImageCache() +{ + // TODO: delete CacheEntries, and QueueEntries +} + + +status_t +ImageCache::RetrieveImage(const entry_ref& ref, int32 page, + const BMessenger* target) +{ + BAutolock locker(fLocker); + + CacheMap::iterator find = fCacheMap.find(std::make_pair(ref, page)); + if (find != fCacheMap.end()) { + CacheEntry* entry = find->second; + + // Requeue cache entry to the end of the by-age list + fCacheEntriesByAge.Remove(entry); + fCacheEntriesByAge.Add(entry); + + // Notify target, if any + _NotifyTarget(entry, target); + return B_OK; + } + + QueueMap::iterator findQueue = fQueueMap.find(std::make_pair(ref, page)); + QueueEntry* entry; + + if (findQueue == fQueueMap.end()) { + // Push new entry to the queue + entry = new(std::nothrow) QueueEntry(); + if (entry == NULL) + return B_NO_MEMORY; + + entry->ref = ref; + entry->page = page; + + if (fThreadCount < fMaxThreadCount) { + // start a new worker thread to load the image + thread_id thread = spawn_thread(&ImageCache::_QueueWorkerThread, + "image loader", B_LOW_PRIORITY, this); + if (thread >= B_OK) { + atomic_add(&fThreadCount, 1); + resume_thread(thread); + } else if (fThreadCount == 0) { + delete entry; + return thread; + } + } + + fQueueMap.insert(std::make_pair( + std::make_pair(entry->ref, entry->page), entry)); + fQueue.push_front(entry); + } else + entry = findQueue->second; + + if (target != NULL) { + // Attach target as listener + entry->listeners.insert(*target); + } + + return B_OK; +} + + +/*static*/ status_t +ImageCache::_QueueWorkerThread(void* _self) +{ + ImageCache* self = (ImageCache*)_self; + + // get next queue entry + while (true) { + self->fLocker.Lock(); + if (self->fQueue.empty()) { + self->fLocker.Unlock(); + break; + } + + QueueEntry* entry = *self->fQueue.begin(); + self->fQueue.pop_front(); + self->fLocker.Unlock(); + + if (entry == NULL) + break; + + CacheEntry* cacheEntry = NULL; + entry->status = self->_RetrieveImage(entry, &cacheEntry); + + self->fLocker.Lock(); + self->fQueueMap.erase(std::make_pair(entry->ref, entry->page)); + self->fLocker.Unlock(); + + self->_NotifyListeners(cacheEntry, entry); + delete entry; + } + + atomic_add(&self->fThreadCount, -1); + return B_OK; +} + + +status_t +ImageCache::_RetrieveImage(QueueEntry* queueEntry, CacheEntry** _entry) +{ + CacheEntry* entry = new(std::nothrow) CacheEntry(); + if (entry == NULL) + return B_NO_MEMORY; + + ObjectDeleter deleter(entry); + + BTranslatorRoster* roster = BTranslatorRoster::Default(); + if (roster == NULL) + return B_ERROR; + + BFile file; + status_t status = file.SetTo(&queueEntry->ref, B_READ_ONLY); + if (status != B_OK) + return status; + + translator_info info; + memset(&info, 0, sizeof(translator_info)); + BMessage ioExtension; + + if (queueEntry->page != 0 + && ioExtension.AddInt32("/documentIndex", queueEntry->page) != B_OK) + return B_NO_MEMORY; + +// TODO: rethink this! +#if 0 + if (fProgressWindow != NULL) { + BMessage progress(kMsgProgressStatusUpdate); + if (ioExtension.AddMessenger("/progressMonitor", + fProgressWindow) == B_OK + && ioExtension.AddMessage("/progressMessage", &progress) == B_OK) + fProgressWindow->Start(); + } +#endif + + // Translate image data and create a new ShowImage window + + BBitmapStream outstream; + + status = roster->Identify(&file, &ioExtension, &info, 0, NULL, + B_TRANSLATOR_BITMAP); + if (status == B_OK) { + status = roster->Translate(&file, &info, &ioExtension, &outstream, + B_TRANSLATOR_BITMAP); + } + +#if 0 + if (fProgressWindow != NULL) + fProgressWindow->Stop(); +#endif + + if (status != B_OK) + return status; + + BBitmap* bitmap; + if (outstream.DetachBitmap(&bitmap) != B_OK) + return B_ERROR; + + entry->ref = queueEntry->ref; + entry->page = queueEntry->page; + entry->bitmap = bitmap; + entry->type = info.name; + entry->mimeType = info.MIME; + + // get the number of documents (pages) if it has been supplied + int32 documentCount = 0; + if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK + && documentCount > 0) + entry->pageCount = documentCount; + else + entry->pageCount = 1; + + deleter.Detach(); + *_entry = entry; + + BAutolock locker(fLocker); + + fCacheMap.insert(std::make_pair( + std::make_pair(entry->ref, entry->page), entry)); + fCacheEntriesByAge.Add(entry); + + fBytes += bitmap->BitsLength(); + + while (fBytes > fMaxBytes || fCacheMap.size() > fMaxEntries) { + if (fCacheMap.size() <= 2) + break; + + // Remove the oldest entry + entry = fCacheEntriesByAge.RemoveHead(); + fBytes -= entry->bitmap->BitsLength(); + fCacheMap.erase(std::make_pair(entry->ref, entry->page)); + delete entry; + } + + return B_OK; +} + + +void +ImageCache::_NotifyListeners(CacheEntry* entry, QueueEntry* queueEntry) +{ + ASSERT(fLocker.IsLocked()); + + if (queueEntry->listeners.empty()) + return; + + BMessage notification(kMsgImageLoaded); + _BuildNotification(entry, notification); + + if (queueEntry->status != B_OK) + notification.AddInt32("error", queueEntry->status); + + std::set::iterator iterator = queueEntry->listeners.begin(); + for (; iterator != queueEntry->listeners.end(); iterator++) { + iterator->SendMessage(¬ification); + } +} + + +void +ImageCache::_NotifyTarget(CacheEntry* entry, const BMessenger* target) +{ + if (target == NULL) + return; + + BMessage notification(kMsgImageLoaded); + _BuildNotification(entry, notification); + + target->SendMessage(¬ification); +} + + +void +ImageCache::_BuildNotification(CacheEntry* entry, BMessage& message) +{ + if (entry == NULL) + return; + + message.AddString("type", entry->type); + message.AddString("mime", entry->mimeType); + message.AddRef("ref", &entry->ref); + message.AddInt32("page", entry->page); + message.AddInt32("pageCount", entry->pageCount); + message.AddPointer("bitmap", (void*)entry->bitmap); +} diff --git a/src/apps/showimage/ImageCache.h b/src/apps/showimage/ImageCache.h new file mode 100644 index 0000000000..fad065d773 --- /dev/null +++ b/src/apps/showimage/ImageCache.h @@ -0,0 +1,85 @@ +/* + * Copyright 2010, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef IMAGE_CACHE_H +#define IMAGE_CACHE_H + + +#include +#include +#include + +#include +#include +#include + +#include + + +class BBitmap; +class BMessage; +class BMessenger; +struct QueueEntry; + + +enum { + kMsgImageLoaded = 'ifnL' +}; + + +struct CacheEntry : DoublyLinkedListLinkImpl { + entry_ref ref; + int32 page; + int32 pageCount; + BBitmap* bitmap; + BString type; + BString mimeType; +}; + + +class ImageCache { +public: + static ImageCache& Default() { return sCache; } + + status_t RetrieveImage(const entry_ref& ref, int32 page, + const BMessenger* target); + +private: + ImageCache(); + virtual ~ImageCache(); + + static status_t _QueueWorkerThread(void* self); + + status_t _RetrieveImage(QueueEntry* entry, + CacheEntry** _entry); + void _NotifyListeners(CacheEntry* entry, + QueueEntry* queueEntry); + void _NotifyTarget(CacheEntry* entry, + const BMessenger* target); + void _BuildNotification(CacheEntry* entry, + BMessage& message); + +private: + typedef std::pair ImageSelector; + typedef std::map CacheMap; + typedef std::map QueueMap; + typedef std::deque QueueDeque; + typedef DoublyLinkedList CacheList; + + BLocker fLocker; + CacheMap fCacheMap; + CacheList fCacheEntriesByAge; + QueueMap fQueueMap; + QueueDeque fQueue; + vint32 fThreadCount; + int32 fMaxThreadCount; + uint64 fBytes; + uint64 fMaxBytes; + size_t fMaxEntries; + + static ImageCache sCache; +}; + + +#endif // IMAGE_CACHE_H diff --git a/src/apps/showimage/ImageFileNavigator.cpp b/src/apps/showimage/ImageFileNavigator.cpp index 1fa097e957..e876eb0740 100644 --- a/src/apps/showimage/ImageFileNavigator.cpp +++ b/src/apps/showimage/ImageFileNavigator.cpp @@ -18,26 +18,20 @@ #include "ImageFileNavigator.h" -#include -#include #include -#include #include -#include #include #include #include #include -#include +//#include #include -#include +//#include #include -#include #include -#include #include "ProgressWindow.h" #include "ShowImageConstants.h" @@ -320,254 +314,10 @@ FolderNavigator::_CompareRefs(const entry_ref* refA, const entry_ref* refB) // #pragma mark - -struct CacheEntry : DoublyLinkedListLinkImpl { - entry_ref ref; - int32 page; - int32 pageCount; - BBitmap* bitmap; - BString type; - BString mimeType; -}; - - -struct QueueEntry { - entry_ref ref; - int32 page; - std::set listeners; -}; - - -class ImageCache { -public: - ImageCache(); - virtual ~ImageCache(); - - void RetrieveImage(const entry_ref& ref, - const BMessenger* target); - -private: - static status_t _QueueWorkerThread(void* self); - - status_t _RetrieveImage(QueueEntry* entry, - BMessage& message); - void _NotifyListeners(QueueEntry* entry, - BMessage& message); - -private: - typedef std::pair ImageSelector; - typedef std::map CacheMap; - typedef std::map QueueMap; - typedef std::deque QueueDeque; - typedef DoublyLinkedList CacheList; - - BLocker fCacheLocker; - CacheMap fCacheMap; - CacheList fCacheEntriesByAge; - BLocker fQueueLocker; - QueueMap fQueueMap; - QueueDeque fQueue; - vint32 fThreadCount; - int32 fMaxThreadCount; - uint64 fBytes; - uint64 fMaxBytes; - size_t fMaxEntries; -}; - - -ImageCache::ImageCache() +ImageFileNavigator::ImageFileNavigator(const entry_ref& ref, + const BMessenger& trackerMessenger) : - fCacheLocker("image cache"), - fQueueLocker("image queue"), - fThreadCount(0), - fBytes(0) -{ - system_info info; - get_system_info(&info); - - fMaxThreadCount = (info.cpu_count + 1) / 2; - fMaxBytes = info.max_pages * B_PAGE_SIZE / 8; - fMaxEntries = 10; -} - - -ImageCache::~ImageCache() -{ - // TODO: delete CacheEntries, and QueueEntries -} - - -void -ImageCache::RetrieveImage(const entry_ref& ref, const BMessenger* target) -{ - // TODO! -} - - -/*static*/ status_t -ImageCache::_QueueWorkerThread(void* _self) -{ - ImageCache* self = (ImageCache*)_self; - - // get next queue entry - while (true) { - self->fQueueLocker.Lock(); - if (self->fQueue.empty()) { - self->fQueueLocker.Unlock(); - break; - } - - QueueEntry* entry = *self->fQueue.begin(); - self->fQueue.pop_front(); - self->fQueueLocker.Unlock(); - - if (entry == NULL) - break; - - BMessage notification(kMsgImageLoaded); - status_t status = self->_RetrieveImage(entry, notification); - if (status != B_OK) - notification.AddInt32("error", status); - - self->fQueueLocker.Lock(); - self->fQueueMap.erase(std::make_pair(entry->ref, entry->page)); - self->fQueueLocker.Unlock(); - - self->_NotifyListeners(entry, notification); - delete entry; - } - - atomic_add(&self->fThreadCount, -1); - return B_OK; -} - - -status_t -ImageCache::_RetrieveImage(QueueEntry* queueEntry, BMessage& message) -{ - CacheEntry* entry = new(std::nothrow) CacheEntry(); - if (entry == NULL) - return B_NO_MEMORY; - - ObjectDeleter deleter(entry); - - BTranslatorRoster* roster = BTranslatorRoster::Default(); - if (roster == NULL) - return B_ERROR; - - if (!entry_ref_is_file(queueEntry->ref)) - return B_IS_A_DIRECTORY; - - BFile file; - status_t status = file.SetTo(&queueEntry->ref, B_READ_ONLY); - if (status != B_OK) - return status; - - translator_info info; - memset(&info, 0, sizeof(translator_info)); - BMessage ioExtension; - - if (queueEntry->page != 0 - && ioExtension.AddInt32("/documentIndex", queueEntry->page) != B_OK) - return B_NO_MEMORY; - -// TODO: rethink this! -#if 0 - if (fProgressWindow != NULL) { - BMessage progress(kMsgProgressStatusUpdate); - if (ioExtension.AddMessenger("/progressMonitor", - fProgressWindow) == B_OK - && ioExtension.AddMessage("/progressMessage", &progress) == B_OK) - fProgressWindow->Start(); - } -#endif - - // Translate image data and create a new ShowImage window - - BBitmapStream outstream; - - status = roster->Identify(&file, &ioExtension, &info, 0, NULL, - B_TRANSLATOR_BITMAP); - if (status == B_OK) { - status = roster->Translate(&file, &info, &ioExtension, &outstream, - B_TRANSLATOR_BITMAP); - } - -#if 0 - if (fProgressWindow != NULL) - fProgressWindow->Stop(); -#endif - - if (status != B_OK) - return status; - - BBitmap* bitmap; - if (outstream.DetachBitmap(&bitmap) != B_OK) - return B_ERROR; - - entry->ref = queueEntry->ref; - entry->page = queueEntry->page; - entry->bitmap = bitmap; - entry->type = info.name; - entry->mimeType = info.MIME; - - // get the number of documents (pages) if it has been supplied - int32 documentCount = 0; - if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK - && documentCount > 0) - entry->pageCount = documentCount; - else - entry->pageCount = 1; - - message.AddString("type", info.name); - message.AddString("mime", info.MIME); - message.AddRef("ref", &entry->ref); - message.AddInt32("page", entry->page); - message.AddPointer("bitmap", (void*)bitmap); - - deleter.Detach(); - - fCacheLocker.Lock(); - - fCacheMap.insert(std::make_pair( - std::make_pair(entry->ref, entry->page), entry)); - fCacheEntriesByAge.Add(entry); - - fBytes += bitmap->BitsLength(); - - while (fBytes > fMaxBytes || fCacheMap.size() > fMaxEntries) { - if (fCacheMap.size() <= 2) - break; - - // Remove the oldest entry - entry = fCacheEntriesByAge.RemoveHead(); - fBytes -= entry->bitmap->BitsLength(); - fCacheMap.erase(std::make_pair(entry->ref, entry->page)); - delete entry; - } - - fCacheLocker.Unlock(); - return B_OK; -} - - -void -ImageCache::_NotifyListeners(QueueEntry* entry, BMessage& message) -{ - std::set::iterator iterator = entry->listeners.begin(); - for (; iterator != entry->listeners.end(); iterator++) { - iterator->SendMessage(&message); - } -} - - -// #pragma mark - - - -ImageFileNavigator::ImageFileNavigator(const BMessenger& target, - const entry_ref& ref, const BMessenger& trackerMessenger) - : - fTarget(target), - fProgressWindow(NULL), + fCurrentRef(ref), fDocumentIndex(1), fDocumentCount(1) { @@ -585,95 +335,11 @@ ImageFileNavigator::~ImageFileNavigator() void -ImageFileNavigator::SetProgressWindow(ProgressWindow* progressWindow) +ImageFileNavigator::SetTo(const entry_ref& ref, int32 page, int32 pageCount) { - fProgressWindow = progressWindow; -} - - -status_t -ImageFileNavigator::LoadImage(const entry_ref& ref, int32 page) -{ - BTranslatorRoster* roster = BTranslatorRoster::Default(); - if (roster == NULL) - return B_ERROR; - - if (!entry_ref_is_file(ref)) - return B_ERROR; - - BFile file(&ref, B_READ_ONLY); - translator_info info; - memset(&info, 0, sizeof(translator_info)); - BMessage ioExtension; - - if (page != 0 && ioExtension.AddInt32("/documentIndex", page) != B_OK) - return B_ERROR; - - if (fProgressWindow != NULL) { - BMessage progress(kMsgProgressStatusUpdate); - if (ioExtension.AddMessenger("/progressMonitor", - fProgressWindow) == B_OK - && ioExtension.AddMessage("/progressMessage", &progress) == B_OK) - fProgressWindow->Start(); - } - - // Translate image data and create a new ShowImage window - - BBitmapStream outstream; - - status_t status = roster->Identify(&file, &ioExtension, &info, 0, NULL, - B_TRANSLATOR_BITMAP); - if (status == B_OK) { - status = roster->Translate(&file, &info, &ioExtension, &outstream, - B_TRANSLATOR_BITMAP); - } - - if (fProgressWindow != NULL) - fProgressWindow->Stop(); - - if (status != B_OK) - return status; - - BBitmap* bitmap; - if (outstream.DetachBitmap(&bitmap) != B_OK) - return B_ERROR; - fCurrentRef = ref; fDocumentIndex = page; - - // get the number of documents (pages) if it has been supplied - int32 documentCount = 0; - if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK - && documentCount > 0) - fDocumentCount = documentCount; - else - fDocumentCount = 1; - - BMessage message(kMsgImageLoaded); - message.AddString("type", info.name); - message.AddString("mime", info.MIME); - message.AddRef("ref", &ref); - message.AddInt32("page", page); - message.AddPointer("bitmap", (void*)bitmap); - status = fTarget.SendMessage(&message); - if (status != B_OK) { - delete bitmap; - return status; - } - - return B_OK; -} - - -void -ImageFileNavigator::GetPath(BString* outPath) -{ - BEntry entry(&fCurrentRef); - BPath path; - if (entry.InitCheck() < B_OK || entry.GetPath(&path) < B_OK) - outPath->SetTo(""); - else - outPath->SetTo(path.Path()); + fDocumentCount = pageCount; } @@ -695,7 +361,7 @@ bool ImageFileNavigator::FirstPage() { if (fDocumentIndex != 1) { - LoadImage(fCurrentRef, 1); + fDocumentIndex = 1; return true; } return false; @@ -706,7 +372,7 @@ bool ImageFileNavigator::LastPage() { if (fDocumentIndex != fDocumentCount) { - LoadImage(fCurrentRef, fDocumentCount); + fDocumentIndex = fDocumentCount; return true; } return false; @@ -717,7 +383,7 @@ bool ImageFileNavigator::NextPage() { if (fDocumentIndex < fDocumentCount) { - LoadImage(fCurrentRef, ++fDocumentIndex); + fDocumentIndex++; return true; } return false; @@ -728,7 +394,7 @@ bool ImageFileNavigator::PreviousPage() { if (fDocumentIndex > 1) { - LoadImage(fCurrentRef, --fDocumentIndex); + fDocumentIndex--; return true; } return false; @@ -740,31 +406,51 @@ ImageFileNavigator::GoToPage(int32 page) { if (page > 0 && page <= fDocumentCount && page != fDocumentIndex) { fDocumentIndex = page; - LoadImage(fCurrentRef, fDocumentIndex); return true; } return false; } -void +bool ImageFileNavigator::FirstFile() { - _LoadNextImage(true, true); + entry_ref ref; + if (fNavigator->FindNextImage(fCurrentRef, ref, false, true)) { + SetTo(ref, 1, 1); + fNavigator->UpdateSelection(fCurrentRef); + return true; + } + + return false; } -void +bool ImageFileNavigator::NextFile() { - _LoadNextImage(true, false); + entry_ref ref; + if (fNavigator->FindNextImage(fCurrentRef, ref, true, false)) { + SetTo(ref, 1, 1); + fNavigator->UpdateSelection(fCurrentRef); + return true; + } + + return false; } -void +bool ImageFileNavigator::PreviousFile() { - _LoadNextImage(false, false); + entry_ref ref; + if (fNavigator->FindNextImage(fCurrentRef, ref, false, false)) { + SetTo(ref, 1, 1); + fNavigator->UpdateSelection(fCurrentRef); + return true; + } + + return false; } @@ -785,7 +471,7 @@ ImageFileNavigator::HasPreviousFile() /*! Moves the current file into the trash. - Returns true if a new file is being loaded, false if not. + Returns true if a new file should be loaded, false if not. */ bool ImageFileNavigator::MoveFileToTrash() @@ -803,41 +489,12 @@ ImageFileNavigator::MoveFileToTrash() // could be invalid BMessenger tracker(kTrackerSignature); if (tracker.SendMessage(&trash) != B_OK) - return true; + return false; - if (nextRef.device != -1 && LoadImage(nextRef) == B_OK) { - fNavigator->UpdateSelection(nextRef); + if (nextRef.device != -1) { + SetTo(nextRef, 1, 1); return true; } return false; } - - -// #pragma mark - - - -status_t -ImageFileNavigator::_LoadNextImage(bool next, bool rewind) -{ - entry_ref currentRef = fCurrentRef; - entry_ref ref; - if (fNavigator->FindNextImage(currentRef, ref, next, rewind)) { - // Keep trying to load images until: - // 1. The image loads successfully - // 2. The last file in the directory is found (for find next or find - // first) - // 3. The first file in the directory is found (for find prev) - // 4. The call to _FindNextImage fails for any other reason - while (LoadImage(ref) != B_OK) { - currentRef = ref; - if (!fNavigator->FindNextImage(currentRef, ref, next, false)) - return B_ENTRY_NOT_FOUND; - } - fNavigator->UpdateSelection(fCurrentRef); - return B_OK; - } - return B_ENTRY_NOT_FOUND; -} - - diff --git a/src/apps/showimage/ImageFileNavigator.h b/src/apps/showimage/ImageFileNavigator.h index 346a447b0a..fce4dc2ee4 100644 --- a/src/apps/showimage/ImageFileNavigator.h +++ b/src/apps/showimage/ImageFileNavigator.h @@ -22,27 +22,17 @@ class Navigator; -class ProgressWindow; - -enum { - kMsgImageLoaded = 'ifnL' -}; class ImageFileNavigator { public: - ImageFileNavigator(const BMessenger& target, - const entry_ref& ref, + ImageFileNavigator(const entry_ref& ref, const BMessenger& trackerMessenger); virtual ~ImageFileNavigator(); - void SetProgressWindow( - ProgressWindow* progressWindow); - - status_t LoadImage(const entry_ref& ref, int32 page = 1); - const entry_ref& ImageRef() const { return fCurrentRef; } - - void GetPath(BString* name); + void SetTo(const entry_ref& ref, int32 page = 1, + int32 pageCount = 1); + const entry_ref& CurrentRef() const { return fCurrentRef; } // The same image file may have multiple pages, TIFF images for // example. The page count is determined at image loading time. @@ -55,20 +45,15 @@ public: bool PreviousPage(); bool GoToPage(int32 page); - void FirstFile(); - void NextFile(); - void PreviousFile(); + bool FirstFile(); + bool NextFile(); + bool PreviousFile(); bool HasNextFile(); bool HasPreviousFile(); bool MoveFileToTrash(); private: - status_t _LoadNextImage(bool next, bool rewind); - -private: - BMessenger fTarget; - ProgressWindow* fProgressWindow; Navigator* fNavigator; entry_ref fCurrentRef; diff --git a/src/apps/showimage/Jamfile b/src/apps/showimage/Jamfile index 1f64425b7b..311dbc1163 100644 --- a/src/apps/showimage/Jamfile +++ b/src/apps/showimage/Jamfile @@ -3,10 +3,12 @@ SubDir HAIKU_TOP src apps showimage ; UsePrivateSystemHeaders ; UsePrivateHeaders tracker shared ; UsePublicHeaders [ FDirName be_apps Tracker ] ; +SubDirHdrs $(HAIKU_TOP) src kits tracker ; Application ShowImage : EntryMenuItem.cpp Filter.cpp + ImageCache.cpp ImageFileNavigator.cpp PrintOptionsWindow.cpp ProgressWindow.cpp @@ -18,7 +20,7 @@ Application ShowImage : ShowImageView.cpp ShowImageWindow.cpp : libshared.a - be tracker translation $(HAIKU_LOCALE_LIBS) $(TARGET_LIBSTDC++) + be tracker translation $(HAIKU_LOCALE_LIBS) $(TARGET_LIBSTDC++) $(TARGET_LIBSUPC++) : ShowImage.rdef ; diff --git a/src/apps/showimage/ShowImageStatusView.cpp b/src/apps/showimage/ShowImageStatusView.cpp index b16f8621a9..4518028e47 100644 --- a/src/apps/showimage/ShowImageStatusView.cpp +++ b/src/apps/showimage/ShowImageStatusView.cpp @@ -1,12 +1,14 @@ /* - * Copyright 2003-2009 Haiku Inc. All rights reserved. + * Copyright 2003-2010, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Fernando Francisco de Oliveira * Michael Wilber + * Axel Dörfler, axeld@pinc-software.de */ + #include "ShowImageStatusView.h" #include @@ -14,6 +16,9 @@ #include #include +#include +#include "DirMenu.h" + #include "ShowImageView.h" #include "ShowImageWindow.h" @@ -77,47 +82,30 @@ ShowImageStatusView::Draw(BRect updateRect) void ShowImageStatusView::MouseDown(BPoint where) { - ShowImageWindow *window = dynamic_cast(Window()); - if (!window || window->GetShowImageView() == NULL) - return; + BPrivate::BDirMenu* menu = new BDirMenu(NULL, B_REFS_RECEIVED); + BEntry entry; + if (entry.SetTo(&fRef) == B_OK) + menu->Populate(&entry, Window(), false, false, true, false, true); + else + menu->Populate(NULL, Window(), false, false, true, false, true); - BPath path; - path.SetTo(window->GetShowImageView()->Image()); - - BPopUpMenu popup("no title"); - popup.SetFont(be_plain_font); - - while (path.GetParent(&path) == B_OK && path != "/") { - popup.AddItem(new BMenuItem(path.Leaf(), NULL)); - } - - BRect bounds(Bounds()); - ConvertToScreen(&bounds); - where = bounds.LeftBottom(); - - BMenuItem *item; - item = popup.Go(where, true, false, ConvertToScreen(Bounds())); - - if (item) { - path.SetTo(window->GetShowImageView()->Image()); - path.GetParent(&path); - int index = popup.IndexOf(item); - while (index--) - path.GetParent(&path); - BMessenger tracker("application/x-vnd.Be-TRAK"); - BMessage msg(B_REFS_RECEIVED); - entry_ref ref; - get_ref_for_path(path.Path(), &ref); - msg.AddRef("refs", &ref); - tracker.SendMessage(&msg); - } + menu->SetTargetForItems(BMessenger(kTrackerSignature)); + BPoint point = Bounds().LeftBottom(); + point.y += 3; + ConvertToScreen(&point); + BRect clickToOpenRect(Bounds()); + ConvertToScreen(&clickToOpenRect); + menu->Go(point, true, true, clickToOpenRect); + delete menu; } void -ShowImageStatusView::SetText(BString &text) +ShowImageStatusView::Update(const entry_ref& ref, const BString& text) { fText = text; + fRef = ref; + Invalidate(); } diff --git a/src/apps/showimage/ShowImageStatusView.h b/src/apps/showimage/ShowImageStatusView.h index e1ca3b630c..05807b4df8 100644 --- a/src/apps/showimage/ShowImageStatusView.h +++ b/src/apps/showimage/ShowImageStatusView.h @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Haiku Inc. All rights reserved. + * Copyright 2003-2010 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -10,21 +10,26 @@ #define SHOW_IMAGE_STATUS_VIEW_H +#include #include #include class ShowImageStatusView : public BView { public: - ShowImageStatusView(BRect rect, const char *name, - uint32 resizingMode, uint32 flags); - - virtual void Draw(BRect updateRect); - virtual void MouseDown(BPoint where); - void SetText(BString &text); - + ShowImageStatusView(BRect rect, + const char* name, uint32 resizingMode, + uint32 flags); + + virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint where); + + void Update(const entry_ref& ref, + const BString& text); + private: - BString fText; + BString fText; + entry_ref fRef; }; diff --git a/src/apps/showimage/ShowImageView.cpp b/src/apps/showimage/ShowImageView.cpp index 08982635ea..79528e962a 100644 --- a/src/apps/showimage/ShowImageView.cpp +++ b/src/apps/showimage/ShowImageView.cpp @@ -316,7 +316,7 @@ ShowImageView::_DeleteBitmap() delete fDisplayBitmap; fDisplayBitmap = NULL; - delete fBitmap; + // TODO: the bitmap is currently only owned by the cache!!! fBitmap = NULL; } @@ -751,6 +751,9 @@ ShowImageView::Draw(BRect updateRect) void ShowImageView::FrameResized(float /*width*/, float /*height*/) { + if (fBitmap == NULL) + return; + fFitToBoundsZoom = _FitToBoundsZoom(); SetZoom(_ShouldStretch() ? fFitToBoundsZoom : fZoom); } diff --git a/src/apps/showimage/ShowImageWindow.cpp b/src/apps/showimage/ShowImageWindow.cpp index 1a1214d473..ea0cda36d3 100644 --- a/src/apps/showimage/ShowImageWindow.cpp +++ b/src/apps/showimage/ShowImageWindow.cpp @@ -44,7 +44,7 @@ #include #include -#include "EntryMenuItem.h" +#include "ImageCache.h" #include "ShowImageApp.h" #include "ShowImageConstants.h" #include "ShowImageStatusView.h" @@ -79,7 +79,7 @@ ShowImageWindow::ShowImageWindow(const entry_ref& ref, const BMessenger& trackerMessenger) : BWindow(BRect(5, 24, 250, 100), "", B_DOCUMENT_WINDOW, 0), - fNavigator(this, ref, trackerMessenger), + fNavigator(ref, trackerMessenger), fSavePanel(NULL), fBar(NULL), fBrowseMenu(NULL), @@ -96,7 +96,7 @@ ShowImageWindow::ShowImageWindow(const entry_ref& ref, // create menu bar fBar = new BMenuBar(BRect(0, 0, Bounds().right, 1), "menu_bar"); - AddMenus(fBar); + _AddMenus(fBar); AddChild(fBar); BRect viewFrame = Bounds(); @@ -142,7 +142,7 @@ ShowImageWindow::ShowImageWindow(const entry_ref& ref, SetSizeLimits(250, 100000, 100, 100000); // finish creating the window - if (fNavigator.LoadImage(ref) != B_OK) { + if (_LoadImage() != B_OK) { _LoadError(ref); Quit(); return; @@ -173,14 +173,6 @@ ShowImageWindow::~ShowImageWindow() } -void -ShowImageWindow::UpdateTitle() -{ - BPath path(fImageView->Image()); - SetTitle(path.Path()); -} - - void ShowImageWindow::BuildContextMenu(BMenu* menu) { @@ -261,7 +253,7 @@ ShowImageWindow::_BuildViewMenu(BMenu* menu, bool popupMenu) void -ShowImageWindow::AddMenus(BMenuBar* bar) +ShowImageWindow::_AddMenus(BMenuBar* bar) { BMenu* menu = new BMenu(B_TRANSLATE("File")); @@ -377,7 +369,7 @@ ShowImageWindow::_AddDelayItem(BMenu* menu, const char* label, float value) void -ShowImageWindow::WindowRedimension(BBitmap* bitmap) +ShowImageWindow::_WindowRedimension(BBitmap* bitmap) { BScreen screen; if (!screen.IsValid()) @@ -492,12 +484,15 @@ ShowImageWindow::MessageReceived(BMessage* message) case kMsgImageLoaded: { bool first = fImageView->Bitmap() == NULL; + entry_ref ref; + message->FindRef("ref", &ref); + if (!first && ref != fNavigator.CurrentRef()) { + // ignore older images + break; + } status_t status = fImageView->SetImage(message); if (status != B_OK) { - entry_ref ref; - message->FindRef("ref", &ref); - _LoadError(ref); // quit if file could not be opened @@ -507,9 +502,11 @@ ShowImageWindow::MessageReceived(BMessage* message) } fImageType = message->FindString("type"); + fNavigator.SetTo(ref, message->FindInt32("page"), + message->FindInt32("pageCount")); if (first) { - WindowRedimension(fImageView->Bitmap()); + _WindowRedimension(fImageView->Bitmap()); fImageView->ResetZoom(); fImageView->MakeFocus(true); // to receive key messages @@ -519,7 +516,7 @@ ShowImageWindow::MessageReceived(BMessage* message) if (!fImageView->StretchesToBounds() && !fImageView->ShrinksToBounds() && !fFullScreen) - WindowRedimension(fImageView->Bitmap()); + _WindowRedimension(fImageView->Bitmap()); } break; } @@ -596,7 +593,9 @@ ShowImageWindow::MessageReceived(BMessage* message) } _UpdateStatusText(message); - UpdateTitle(); + + BPath path(fImageView->Image()); + SetTitle(path.Path()); break; } @@ -648,23 +647,23 @@ ShowImageWindow::MessageReceived(BMessage* message) break; case MSG_PAGE_FIRST: - if (_ClosePrompt()) - fNavigator.FirstPage(); + if (_ClosePrompt() && fNavigator.FirstPage()) + _LoadImage(); break; case MSG_PAGE_LAST: - if (_ClosePrompt()) - fNavigator.LastPage(); + if (_ClosePrompt() && fNavigator.LastPage()) + _LoadImage(); break; case MSG_PAGE_NEXT: - if (_ClosePrompt()) - fNavigator.NextPage(); + if (_ClosePrompt() && fNavigator.NextPage()) + _LoadImage(); break; case MSG_PAGE_PREV: - if (_ClosePrompt()) - fNavigator.PreviousPage(); + if (_ClosePrompt() && fNavigator.PreviousPage()) + _LoadImage(); break; case MSG_GOTO_PAGE: @@ -679,13 +678,15 @@ ShowImageWindow::MessageReceived(BMessage* message) int32 currentPage = fNavigator.CurrentPage(); int32 pages = fNavigator.PageCount(); + // TODO: use radio mode instead! if (newPage > 0 && newPage <= pages) { BMenuItem* currentItem = fGoToPageMenu->ItemAt(currentPage - 1); BMenuItem* newItem = fGoToPageMenu->ItemAt(newPage - 1); if (currentItem != NULL && newItem != NULL) { currentItem->SetMarked(false); newItem->SetMarked(true); - fNavigator.GoToPage(newPage); + if (fNavigator.GoToPage(newPage)) + _LoadImage(); } } break; @@ -700,19 +701,23 @@ ShowImageWindow::MessageReceived(BMessage* message) break; case MSG_FILE_PREV: - if (_ClosePrompt()) - fNavigator.PreviousFile(); + if (_ClosePrompt() && fNavigator.PreviousFile()) + _LoadImage(); break; case MSG_FILE_NEXT: - if (_ClosePrompt()) - fNavigator.NextFile(); + if (_ClosePrompt() && fNavigator.NextFile()) + _LoadImage(); break; case kMsgDeleteCurrentFile: - if (!fNavigator.MoveFileToTrash()) + { + if (fNavigator.MoveFileToTrash()) + _LoadImage(); + else PostMessage(B_QUIT_REQUESTED); break; + } case MSG_ROTATE_90: fImageView->Rotate(90); @@ -838,7 +843,7 @@ ShowImageWindow::_UpdateStatusText(const BMessage* message) status << ", " << text; } - fStatusView->SetText(status); + fStatusView->Update(fNavigator.CurrentRef(), status); } @@ -934,6 +939,7 @@ ShowImageWindow::_SaveToFile(BMessage* message) #undef B_TRANSLATE_CONTEXT #define B_TRANSLATE_CONTEXT "ClosePrompt" + bool ShowImageWindow::_ClosePrompt() { @@ -969,6 +975,15 @@ ShowImageWindow::_ClosePrompt() } +status_t +ShowImageWindow::_LoadImage() +{ + BMessenger us(this); + return ImageCache::Default().RetrieveImage(fNavigator.CurrentRef(), + fNavigator.CurrentPage(), &us); +} + + void ShowImageWindow::_ToggleFullScreen() { diff --git a/src/apps/showimage/ShowImageWindow.h b/src/apps/showimage/ShowImageWindow.h index d957a1b6aa..922574aabb 100644 --- a/src/apps/showimage/ShowImageWindow.h +++ b/src/apps/showimage/ShowImageWindow.h @@ -31,18 +31,16 @@ public: const BMessenger& trackerMessenger); virtual ~ShowImageWindow(); + void BuildContextMenu(BMenu* menu); + +protected: virtual void FrameResized(float width, float height); virtual void MessageReceived(BMessage* message); virtual bool QuitRequested(); - ShowImageView* GetShowImageView() const { return fImageView; } - - void UpdateTitle(); - void AddMenus(BMenuBar* bar); - void BuildContextMenu(BMenu* menu); - void WindowRedimension(BBitmap* bitmap); - private: + void _AddMenus(BMenuBar* bar); + void _WindowRedimension(BBitmap* bitmap); void _BuildViewMenu(BMenu* menu, bool popupMenu); BMenuItem* _AddItemMenu(BMenu* menu, const char* label, uint32 what, char shortcut, uint32 modifier, @@ -66,6 +64,7 @@ private: void _SaveToFile(BMessage* message); // Handle save file panel message bool _ClosePrompt(); + status_t _LoadImage(); void _ToggleFullScreen(); void _ApplySettings(); void _SavePrintOptions(); @@ -88,6 +87,7 @@ private: BRect fWindowFrame; BMessage* fPrintSettings; PrintOptions fPrintOptions; + BString fImageType; };