diff options
Diffstat (limited to 'src/gvcore/imageviewcontroller.cpp')
-rw-r--r-- | src/gvcore/imageviewcontroller.cpp | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/src/gvcore/imageviewcontroller.cpp b/src/gvcore/imageviewcontroller.cpp new file mode 100644 index 0000000..85a587a --- /dev/null +++ b/src/gvcore/imageviewcontroller.cpp @@ -0,0 +1,527 @@ +// vim: set tabstop=4 shiftwidth=4 noexpandtab +/* +Gwenview - A simple image viewer for KDE +Copyright 2006 Aurélien Gâteau + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#include <imageviewcontroller.moc> + +// Qt +#include <qcursor.h> +#include <qlayout.h> +#include <qpopupmenu.h> +#include <qvbox.h> +#include <qwidgetstack.h> + +// KDE +#include <kaction.h> +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmediaplayer/player.h> +#include <kmimetype.h> +#include <ktoolbar.h> +#include <kuserprofile.h> +#include <kparts/componentfactory.h> +#include <kxmlguibuilder.h> +#include <kxmlguifactory.h> + +// Local +#include <document.h> +#include <externaltoolcontext.h> +#include <externaltoolmanager.h> +#include <fullscreenbar.h> +#include <imageview.h> + +namespace Gwenview { + +#undef ENABLE_LOG +#undef LOG +//#define ENABLE_LOG +#ifdef ENABLE_LOG +#define LOG(x) kdDebug() << k_funcinfo << x << endl +#else +#define LOG(x) ; +#endif + + +/** + * A KXMLGUIBuilder which only creates containers for toolbars. + */ +class XMLGUIBuilder : public KXMLGUIBuilder { +public: + XMLGUIBuilder(QWidget* parent) : KXMLGUIBuilder(parent) {} + + virtual QWidget* createContainer(QWidget *parent, int index, const QDomElement &element, int &id) { + if (element.tagName().lower() == "toolbar") { + return KXMLGUIBuilder::createContainer(parent, index, element, id); + } else { + return 0; + } + } +}; + + +const int AUTO_HIDE_TIMEOUT=4000; + + +//------------------------------------------------------------------------ +// +// ImageViewController::Private +// +//------------------------------------------------------------------------ +struct ImageViewController::Private { + ImageViewController* mImageViewController; + + Document* mDocument; + KActionCollection* mActionCollection; + QWidget* mContainer; + KToolBar* mToolBar; + KXMLGUIFactory* mFactory; + XMLGUIBuilder* mBuilder; + QWidgetStack* mStack; + + ImageView* mImageView; + KActionPtrList mImageViewActions; + + // Hide cursor stuff + QTimer* mAutoHideTimer; + bool mCursorHidden; + + KParts::ReadOnlyPart* mPlayerPart; + + // Fullscreen stuff + bool mFullScreen; + FullScreenBar* mFullScreenBar; + KActionPtrList mFullScreenCommonActions; + + + void setXMLGUIClient(KXMLGUIClient* client) { + QPtrList<KXMLGUIClient> list=mFactory->clients(); + KXMLGUIClient* oldClient=list.getFirst(); + if (oldClient) { + mFactory->removeClient(oldClient); + // There should be at most one client, so the list should be empty + // now + Q_ASSERT(!mFactory->clients().getFirst()); + } + + // Unplug image view actions, if plugged + KActionPtrList::Iterator + it=mImageViewActions.begin(), + end=mImageViewActions.end(); + for (; it!=end; ++it) { + KAction* action=*it; + if (action->isPlugged(mToolBar)) { + action->unplug(mToolBar); + } + } + + if (client) { + mFactory->addClient(client); + } + } + + + void createPlayerPart(void) { + if (mPlayerPart) { + setXMLGUIClient(0); + delete mPlayerPart; + } + mPlayerPart=0; + + QString mimeType=KMimeType::findByURL(mDocument->url())->name(); + KService::Ptr service = KServiceTypeProfile::preferredService(mimeType, "KParts/ReadOnlyPart"); + if (!service) { + kdWarning() << "Couldn't find a KPart for " << mimeType << endl; + return; + } + + QString library=service->library(); + Q_ASSERT(!library.isNull()); + LOG("Library:" << library); + mPlayerPart = KParts::ComponentFactory::createPartInstanceFromService<KParts::ReadOnlyPart>(service, mStack, 0, mStack, 0); + if (!mPlayerPart) { + kdWarning() << "Failed to instantiate KPart from library " << library << endl; + return; + } + mStack->addWidget(mPlayerPart->widget()); + setXMLGUIClient(mPlayerPart); + } + + + void showPlayerPart(void) { + LOG(""); + createPlayerPart(); + if (!mPlayerPart) return; + mStack->raiseWidget(mPlayerPart->widget()); + mPlayerPart->openURL(mDocument->url()); + + // If the part implements the KMediaPlayer::Player interface, start + // playing (needed for Kaboodle) + KMediaPlayer::Player* player=dynamic_cast<KMediaPlayer::Player *>(mPlayerPart); + if (player) { + player->play(); + } + } + + + void showImageView(void) { + LOG(""); + if (mStack->visibleWidget()==mImageView) { + KAction* action=mImageViewActions.first(); + if (action && !action->isPlugged(mToolBar)) { + // In the ctor, we set the imageview as the visible widget but + // we did not fill the toolbar because mImageViewActions was + // empty. In this case, fill the toolbar now. + plugImageViewActions(); + } + return; + } + + if (mPlayerPart) { + setXMLGUIClient(0); + delete mPlayerPart; + mPlayerPart=0; + } + plugImageViewActions(); + mStack->raiseWidget(mImageView); + } + + void plugImageViewActions() { + KActionPtrList::Iterator + it=mImageViewActions.begin(), + end=mImageViewActions.end(); + for (; it!=end; ++it) { + KAction* action=*it; + action->plug(mToolBar); + } + } + + + void restartAutoHideTimer() { + mAutoHideTimer->start(AUTO_HIDE_TIMEOUT,true); + } + + + void updateFullScreenBarPosition() { + int mouseY=mStack->mapFromGlobal(QCursor::pos()).y(); + bool visible = mFullScreenBar->y()==0; + + if (visible && mouseY>mFullScreenBar->height()) { + mFullScreenBar->slideOut(); + } + + if (!visible && mouseY<2) { + mFullScreenBar->slideIn(); + } + } + + + /** + * This function creates the fullscreen toolbar. + * NOTE: It should not be called from/merged with setFullScreenActions, + * otherwise the toolbar will have a one pixel border which will prevent + * reaching easily buttons by pushing the mouse to the top edge of the + * screen. + * My guess is that instanciating the toolbar *before* the main + * window is shown causes the main window to tweak its bars. This happens + * with KDE 3.5.1. + */ + void initFullScreenBar() { + Q_ASSERT(!mFullScreenBar); + mFullScreenBar=new FullScreenBar(mContainer); + + KActionPtrList::ConstIterator + it=mFullScreenCommonActions.begin(), + end=mFullScreenCommonActions.end(); + + for (; it!=end; ++it) { + (*it)->plug(mFullScreenBar); + } + } +}; + + +//------------------------------------------------------------------------ +// +// ImageViewController +// +//------------------------------------------------------------------------ + + +ImageViewController::ImageViewController(QWidget* parent, Document* document, KActionCollection* actionCollection) +: QObject(parent) { + d=new Private; + d->mImageViewController=this; + d->mDocument=document; + d->mActionCollection=actionCollection; + d->mAutoHideTimer=new QTimer(this); + d->mCursorHidden=false; + + d->mContainer=new QWidget(parent); + d->mContainer->setMinimumWidth(1); // Make sure we can resize the toolbar smaller than its minimum size + QVBoxLayout* layout=new QVBoxLayout(d->mContainer); + d->mToolBar=new KToolBar(d->mContainer, "", true); + + layout->add(d->mToolBar); + d->mStack=new QWidgetStack(d->mContainer); + layout->add(d->mStack); + + d->mImageView=new ImageView(d->mStack, document, actionCollection); + d->mStack->addWidget(d->mImageView); + + KApplication::kApplication()->installEventFilter(this); + + d->mPlayerPart=0; + d->mBuilder=new XMLGUIBuilder(d->mToolBar); + d->mFactory=new KXMLGUIFactory(d->mBuilder, this); + + d->mFullScreen=false; + d->mFullScreenBar=0; + + connect(d->mDocument,SIGNAL(loaded(const KURL&)), + this,SLOT(slotLoaded()) ); + + connect(d->mImageView, SIGNAL(requestContextMenu(const QPoint&)), + this, SLOT(openImageViewContextMenu(const QPoint&)) ); + + connect(d->mImageView, SIGNAL(requestHintDisplay(const QString&)), + this, SIGNAL(requestHintDisplay(const QString&)) ); + + connect(d->mAutoHideTimer,SIGNAL(timeout()), + this,SLOT(slotAutoHide()) ); + + // Forward Image view signals + connect(d->mImageView, SIGNAL(selectPrevious()), SIGNAL(selectPrevious()) ); + connect(d->mImageView, SIGNAL(selectNext()), SIGNAL(selectNext()) ); + connect(d->mImageView, SIGNAL(doubleClicked()), SIGNAL(doubleClicked()) ); +} + + +ImageViewController::~ImageViewController() { + delete d->mBuilder; + delete d; +} + + +void ImageViewController::setFocus() { + QWidget* view; + if (d->mPlayerPart) { + view = d->mPlayerPart->widget(); + } else { + view = d->mImageView; + } + view->setFocus(); +} + + +void ImageViewController::slotLoaded() { + LOG(""); + if (d->mDocument->urlKind()==MimeTypeUtils::KIND_FILE) { + d->showPlayerPart(); + } else { + d->showImageView(); + } +} + + +void ImageViewController::setFullScreen(bool fullScreen) { + d->mFullScreen=fullScreen; + d->mImageView->setFullScreen(fullScreen); + + if (d->mFullScreen) { + d->restartAutoHideTimer(); + if (!d->mFullScreenBar) { + d->initFullScreenBar(); + } + } else { + d->mAutoHideTimer->stop(); + QApplication::restoreOverrideCursor(); + d->mCursorHidden=false; + } + + d->mToolBar->setHidden(d->mFullScreen); + if (d->mFullScreenBar) { + d->mFullScreenBar->setHidden(!d->mFullScreen); + } +} + + +void ImageViewController::setNormalCommonActions(const KActionPtrList& actions) { + KActionPtrList::ConstIterator + it=actions.begin(), + end=actions.end(); + + for (; it!=end; ++it) { + (*it)->plug(d->mToolBar); + } + d->mToolBar->insertLineSeparator(); +} + + +void ImageViewController::setFullScreenCommonActions(const KActionPtrList& actions) { + d->mFullScreenCommonActions=actions; +} + + +void ImageViewController::setImageViewActions(const KActionPtrList& actions) { + d->mImageViewActions=actions; +} + + +void ImageViewController::slotAutoHide() { + if (d->mFullScreenBar) { + // Do not auto hide if the cursor is over the bar + QPoint pos=d->mFullScreenBar->mapFromGlobal(QCursor::pos()); + if (d->mFullScreenBar->rect().contains(pos)) { + d->restartAutoHideTimer(); + return; + } + } + + // Only hide cursor if we are not over a dialog + QWidget* widget = KApplication::kApplication()->activeWindow(); + if (!widget || !widget->inherits("QDialog")) { + QApplication::setOverrideCursor(blankCursor); + d->mCursorHidden=true; + } +} + + +QWidget* ImageViewController::widget() const { + return d->mContainer; +} + + +void ImageViewController::updateFromSettings() { + d->mImageView->updateFromSettings(); +} + + +/** + * This event filter monitors mouse moves and make sure the position of the + * fullscreen bar is updated. + */ +bool ImageViewController::eventFilter(QObject* object, QEvent* event) { + if (!d->mFullScreen) return false; + if (!event->type()==QEvent::MouseMove) return false; + + // Check we must filter this object. This is an application filter, so we + // have to check we are not dealing with another object. + bool isAChildOfStack=false; + QObject* parentObject; + for (parentObject=object->parent(); parentObject; parentObject=parentObject->parent()) { + if (parentObject==d->mStack) { + isAChildOfStack=true; + break; + } + } + if (!isAChildOfStack) return false; + + d->updateFullScreenBarPosition(); + + if (event->type()==QEvent::MouseMove) { + d->mCursorHidden=false; + d->restartAutoHideTimer(); + } + + if (d->mCursorHidden) { + QApplication::setOverrideCursor(blankCursor,true); + } else { + QApplication::restoreOverrideCursor(); + } + + return false; +} + + +/** + * Little helper to plug an action if it exists + */ +inline void plugAction(QPopupMenu* menu, KActionCollection* actionCollection, const char* actionName) { + KAction* action=actionCollection->action(actionName); + if (action) action->plug(menu); +} + + +void ImageViewController::openImageViewContextMenu(const QPoint& pos) { + QPopupMenu menu(d->mImageView); + bool noImage=d->mDocument->filename().isEmpty(); + bool validImage=!d->mDocument->isNull(); + + // The fullscreen item is always there, to be able to leave fullscreen mode + // if necessary. But KParts may not have the action itself. + plugAction(&menu, d->mActionCollection, "fullscreen"); + + plugAction(&menu, d->mActionCollection, "slideshow"); + + if (validImage) { + menu.insertSeparator(); + + plugAction(&menu, d->mActionCollection, "view_zoom_to_fit"); + plugAction(&menu, d->mActionCollection, "view_zoom_in"); + plugAction(&menu, d->mActionCollection, "view_zoom_out"); + plugAction(&menu, d->mActionCollection, "view_actual_size"); + plugAction(&menu, d->mActionCollection, "view_zoom_lock"); + } + + menu.insertSeparator(); + + plugAction(&menu, d->mActionCollection, "first"); + plugAction(&menu, d->mActionCollection, "previous"); + plugAction(&menu, d->mActionCollection, "next"); + plugAction(&menu, d->mActionCollection, "last"); + + if (validImage) { + menu.insertSeparator(); + + QPopupMenu* editMenu=new QPopupMenu(&menu); + plugAction(editMenu, d->mActionCollection, "rotate_left"); + plugAction(editMenu, d->mActionCollection, "rotate_right"); + plugAction(editMenu, d->mActionCollection, "mirror"); + plugAction(editMenu, d->mActionCollection, "flip"); + plugAction(editMenu, d->mActionCollection, "adjust_bcg"); + menu.insertItem( i18n("Edit"), editMenu ); + + ExternalToolContext* externalToolContext= + ExternalToolManager::instance()->createContext( + this, d->mDocument->url()); + + menu.insertItem( + i18n("External Tools"), externalToolContext->popupMenu()); + } + + if (!noImage) { + menu.insertSeparator(); + + plugAction(&menu, d->mActionCollection, "file_rename"); + plugAction(&menu, d->mActionCollection, "file_copy"); + plugAction(&menu, d->mActionCollection, "file_move"); + plugAction(&menu, d->mActionCollection, "file_link"); + plugAction(&menu, d->mActionCollection, "file_delete"); + + menu.insertSeparator(); + + plugAction(&menu, d->mActionCollection, "file_properties"); + } + + menu.exec(pos); +} + + +} // namespace |