// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include // Qt #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include // Local #include #include #include #include #include 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 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(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(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