diff --git a/CMakeLists.txt b/CMakeLists.txt index d9c41e5..b8f16dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,37 +1,41 @@ cmake_minimum_required(VERSION 3.0) project(kuickshow) -find_package(ECM REQUIRED NO_MODULE) +set(QT_MIN_VERSION "5.4.0") +set(KF5_MIN_VERSION "5.10.0") + +find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) +include(ECMQtDeclareLoggingCategory) include(FeatureSummary) -find_package(Qt5 REQUIRED +find_package(Qt5 ${QT_MIN_VERSION} REQUIRED Core Gui PrintSupport X11Extras ) -find_package(KF5 REQUIRED COMPONENTS +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS DocTools I18n IconThemes Init KIO WindowSystem XmlGui ) find_package(IMLIB REQUIRED) add_subdirectory( src ) add_subdirectory( misc ) add_subdirectory( pics ) add_subdirectory( icons ) add_subdirectory( doc ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bfabd9c..7dc2892 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,70 +1,77 @@ # KuickShow main application set(CMAKE_AUTOMOC ON) set(kuickshow_KDEINIT_SRCS kuickshow.cpp aboutwidget.cpp generalwidget.cpp kuickconfigdlg.cpp kuickfile.cpp filecache.cpp main.cpp defaultswidget.cpp imagewindow.cpp kuickdata.cpp kuickimage.cpp imdata.cpp filefinder.cpp kuickurlwidget.cpp filewidget.cpp kuick.cpp imlibwidget.cpp slideshowwidget.cpp imagemods.cpp printing.cpp openfilesanddirsdialog.cpp ) ki18n_wrap_ui(kuickshow_KDEINIT_SRCS aboutwidget.ui printing_page.ui generalwidget.ui defaultswidget.ui slideshowwidget.ui ) +ecm_qt_declare_logging_category(kuickshow_KDEINIT_SRCS + HEADER kuickshow_debug.h + IDENTIFIER KUICKSHOW_LOG + CATEGORY_NAME org.kde.kuickshow + DEFAULT_SEVERITY Warning +) # target kdeinit_kuickshow kf5_add_kdeinit_executable( kuickshow ${kuickshow_KDEINIT_SRCS}) target_compile_options(kdeinit_kuickshow PRIVATE -std=c++11 -Wall --pedantic) target_compile_definitions(kdeinit_kuickshow PRIVATE ${IMLIB_DEFINITIONS} -DQT_NO_URL_CAST_FROM_STRING) target_include_directories(kdeinit_kuickshow PRIVATE ${IMLIB_INCLUDE_DIRS}) target_link_libraries(kdeinit_kuickshow Qt5::Core Qt5::Gui Qt5::PrintSupport Qt5::X11Extras KF5::I18n KF5::IconThemes KF5::KIOCore KF5::KIOWidgets KF5::KIOFileWidgets KF5::WindowSystem KF5::XmlGui ${X11_LIBRARIES} ${IMLIB_LIBRARIES} ) install(TARGETS kdeinit_kuickshow ${INSTALL_TARGETS_DEFAULT_ARGS} ) # target kuickshow target_link_libraries( kuickshow kdeinit_kuickshow ) install(TARGETS kuickshow ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### install files ############### install( PROGRAMS org.kde.kuickshow.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +install( FILES kuickshow.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) diff --git a/src/filecache.cpp b/src/filecache.cpp index effd2e5..4467179 100644 --- a/src/filecache.cpp +++ b/src/filecache.cpp @@ -1,112 +1,112 @@ /* This file is part of the KDE project Copyright (C) 2003,2009 Carsten Pfeiffer 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filecache.h" #include -#include #include #include #include #include #include +#include "kuickshow_debug.h" #include "kuickfile.h" FileCache* FileCache::s_self = nullptr; FileCache::FileCache() : m_limit( 0 ), m_tempDir( 0L ) { m_files.setMaxCost( 100 ); // good default? ### make configurable? } FileCache::~FileCache() { delete m_tempDir; } void FileCache::shutdown() { if ( s_self ) { delete s_self; s_self = 0L; } } FileCache * FileCache::self() { if ( !s_self ) s_self = new FileCache(); return s_self; } KuickFile * FileCache::getFile( const QUrl& url ) { QString urlString = url.toDisplayString(); KuickFile *file = m_files.object( urlString ); if ( !file ) { file = new KuickFile( url ); m_files.insert( urlString, file ); } return file; } QString FileCache::tempDir() { if ( !m_tempDir ) { m_tempDir = createTempDir(); if ( !m_tempDir ) { qWarning("Unable to create temporary directory for KuickShow"); return QString::null; } } return m_tempDir->path() + QLatin1Char('/'); } QTemporaryFile* FileCache::createTempFile(const QString& suffix, const QString& prefix) { QString nameTemplate = tempDir(); if(nameTemplate.isEmpty()) return nullptr; nameTemplate += prefix + QStringLiteral("XXXXXX") + suffix; return new QTemporaryFile(nameTemplate); } QTemporaryDir* FileCache::createTempDir() { QString nameTemplate = QStringLiteral("%1/%2_%3_XXXXXX") .arg(QDir::tempPath()) .arg(QCoreApplication::applicationName()) .arg(QCoreApplication::applicationPid()); QTemporaryDir* dir = new QTemporaryDir(nameTemplate); if(!dir->isValid()) { delete dir; return 0L; } return dir; } diff --git a/src/filewidget.cpp b/src/filewidget.cpp index f94b387..19993a5 100644 --- a/src/filewidget.cpp +++ b/src/filewidget.cpp @@ -1,486 +1,486 @@ /* This file is part of the KDE project Copyright (C) 1998-2003 Carsten Pfeiffer 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filewidget.h" #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include "filefinder.h" #include "kuickdata.h" +#include "kuickshow_debug.h" #include "kuickshow.h" FileWidget::FileWidget( const QUrl& url, QWidget *parent ) : KDirOperator( url, parent ), m_validCompletion( false ), m_fileFinder( 0L ), m_fileItemActions( 0L ) { setEnableDirHighlighting( true ); KConfigGroup group(KSharedConfig::openConfig(), "Filebrowser"); setViewConfig( group ); readConfig( group ); setView( KFile::Default ); // setOnlyDoubleClickSelectsFiles( true ); reloadConfiguration(); completionObject()->setCompletionMode( KCompletion::CompletionAuto ); dirCompletionObject()->setCompletionMode( KCompletion::CompletionAuto); slotViewChanged(); connect( this, SIGNAL( viewChanged( QAbstractItemView * )), SLOT( slotViewChanged() )); connect( dirLister(), SIGNAL( clear() ), SLOT( slotItemsCleared() )); connect( dirLister(), SIGNAL( itemsDeleted( const KFileItemList& ) ), SLOT( slotItemsDeleted( const KFileItemList& ) )); connect( this, SIGNAL( fileHighlighted( const KFileItem& )), SLOT( slotHighlighted( const KFileItem& ))); connect( this, SIGNAL(urlEntered(const QUrl&)), SLOT( slotURLEntered( const QUrl& ))); // should actually be KDirOperator's job! connect( this, SIGNAL( finishedLoading() ), SLOT( slotFinishedLoading() )); connect( this, SIGNAL( contextMenuAboutToShow( const KFileItem&, QMenu *) ), SLOT( slotContextMenu( const KFileItem&, QMenu *))); } FileWidget::~FileWidget() { delete m_fileFinder; } void FileWidget::initActions() { KActionCollection *coll = actionCollection(); KActionMenu *menu = static_cast( coll->action("popupMenu") ); menu->addAction(coll->action("kuick_showInOtherWindow")); menu->addAction(coll->action("kuick_showInSameWindow")); menu->addAction(coll->action("kuick_showFullscreen")); menu->addSeparator(); // properties dialog is now in kfile, but not at the right position, // so we move it to the real bottom menu->menu()->removeAction( coll->action( "properties" ) ); /* KMenu *pMenu = menu->menu(); int lastItemId = pMenu->idAt( pMenu->count() - 1 ); QMenuItem *mItem = pMenu->findItem( lastItemId ); if ( mItem && !mItem->isSeparator() ) menu->addSeparator(); */ // those at the bottom menu->addAction(coll->action("kuick_print") ); menu->addSeparator(); menu->addAction(coll->action("properties") ); } void FileWidget::reloadConfiguration() { if ( kdata->fileFilter != nameFilter() ) { // At first, our list must have folders QStringList mimes; mimes.append("inode/directory"); // Then, all the images! QMimeDatabase mimedb; QList l = mimedb.allMimeTypes(); for (QList::const_iterator it = l.begin(); it != l.end(); ++it) if (it->name().startsWith( "image/" )) mimes.append( it->name() ); // Ok, show what we've done setMimeFilter (mimes); updateDir(); } } bool FileWidget::hasFiles() const { return (numFiles() > 0); } void FileWidget::slotContextMenu( const KFileItem& item, QMenu *popupMenu ) { bool image = isImage( item ); actionCollection()->action("kuick_showInSameWindow")->setEnabled( image ); actionCollection()->action("kuick_showInOtherWindow")->setEnabled( image ); actionCollection()->action("kuick_showFullscreen")->setEnabled( image ); actionCollection()->action("kuick_print")->setEnabled( image ); KActionCollection *coll = actionCollection(); KActionMenu *menu = static_cast( coll->action("popupMenu") ); menu->addAction(coll->action("kuick_showInOtherWindow")); menu->addAction(coll->action("kuick_showInSameWindow")); menu->addAction(coll->action("kuick_showFullscreen")); menu->addSeparator(); if (!item.isNull()) { KFileItemList items; items.append(item); KFileItemListProperties properties( items ); if ( !m_fileItemActions ) { m_fileItemActions = new KFileItemActions( this ); m_fileItemActions->setParentWidget( this ); } m_fileItemActions->setItemListProperties( properties ); m_fileItemActions->addOpenWithActionsTo( menu->menu(), QString() ); } // properties dialog is now in kfile, but not at the right position, // so we move it to the real bottom menu->menu()->removeAction( coll->action( "properties" ) ); // those at the bottom menu->addAction(coll->action("kuick_print") ); menu->addSeparator(); menu->addAction(coll->action("properties") ); } void FileWidget::findCompletion( const QString& text ) { if ( text.at(0) == '/' || text.at(0) == '~' || text.indexOf('/') != -1 ) { QString t = m_fileFinder->completion()->makeCompletion( text ); if (m_fileFinder->completionMode() == KCompletion::CompletionPopup || m_fileFinder->completionMode() == KCompletion::CompletionPopupAuto) m_fileFinder->setCompletedItems( m_fileFinder->completion()->allMatches() ); else if ( !t.isNull() ) m_fileFinder->setCompletedText( t ); return; } QString file = makeDirCompletion( text ); if ( file.isNull() ) file = makeCompletion( text ); m_validCompletion = !file.isNull(); if ( m_validCompletion ) { QUrl completeUrl = url(); completeUrl.setPath(completeUrl.adjusted(QUrl::RemoveFilename).path() + file); KDirOperator::setCurrentItem( completeUrl ); } } bool FileWidget::eventFilter( QObject *o, QEvent *e ) { if ( e->type() == QEvent::KeyPress ) { QKeyEvent *k = static_cast( e ); if ( (k->modifiers() & (Qt::ControlModifier | Qt::AltModifier)) == 0 ) { int key = k->key(); if ( actionCollection()->action("delete")->shortcuts().contains( key ) ) { k->accept(); KFileItem item = getCurrentItem( false ); if ( !item.isNull() ) { KFileItemList list; list.append( item ); del( list, this, (k->modifiers() & Qt::ShiftModifier) == 0 ); } return true; } const QString& text = k->text(); if ( !text.isEmpty() && text.unicode()->isPrint() ) { k->accept(); if ( !m_fileFinder ) { m_fileFinder = new FileFinder( this ); m_fileFinder->setObjectName( "file finder" ); connect( m_fileFinder, SIGNAL( completion(const QString&)), SLOT( findCompletion( const QString& ))); connect( m_fileFinder, SIGNAL( enterDir( const QString& ) ), SLOT( slotReturnPressed( const QString& ))); m_fileFinder->move( width() - m_fileFinder->width(), height() - m_fileFinder->height() ); } bool first = m_fileFinder->isHidden(); m_fileFinder->setText( text ); m_fileFinder->raise(); m_fileFinder->show(); m_fileFinder->setFocus(); if ( first ) findCompletion( text ); return true; } } k->ignore(); } return KDirOperator::eventFilter( o, e ); } bool FileWidget::isImage( const KFileItem& item ) { // return item && !item.isDir(); if ( !item.isNull() ) { return item.isReadable() && item.mimetype().startsWith( "image/"); } return false; } KFileItem FileWidget::gotoFirstImage() { QModelIndex modelIndex = view()->model()->index( 0, 0 ); while ( modelIndex.isValid() ) { KFileItem item = fileItemFor(modelIndex); if ( isImage( item ) ) { setCurrentItem( item ); return item; } modelIndex = modelIndex.sibling( modelIndex.row() + 1, modelIndex.column() ); } return KFileItem(); } KFileItem FileWidget::gotoLastImage() { QAbstractItemModel *model = view()->model(); int numRows = model->rowCount(); QModelIndex index = model->index(numRows - 1, 0); // last item while ( index.isValid() ) { KFileItem fileItem = fileItemFor( index ); if (isImage( fileItem )) { setCurrentItem( fileItem ); return fileItem; } index = index.parent(); } return KFileItem(); } KFileItem FileWidget::getNext( bool go ) { KFileItem item = getItem( Next, true ); if ( !item.isNull() ) { if ( go ) setCurrentItem( item ); return item; } return KFileItem(); } KFileItem FileWidget::getPrevious( bool go ) { KFileItem item = getItem( Previous, true ); if ( !item.isNull() ) { if ( go ) setCurrentItem( item ); return item; } return KFileItem(); } // returns a null item when there is no previous/next item/image // this sucks! Use KFileView::currentFileItem() when implemented KFileItem FileWidget::getItem( WhichItem which, bool onlyImage ) const { QModelIndex currentIndex = view()->currentIndex(); if ( !currentIndex.isValid() ) { qDebug("no current index"); return KFileItem(); } QModelIndex index = currentIndex; KFileItem item; const int column = index.column(); item = fileItemFor( currentIndex ); if ( item.isNull() ) qDebug("### current item is null: %s, %s", index.data().typeName(), qUtf8Printable(currentIndex.data().value())); switch( which ) { case Previous: { index = index.sibling( index.row() - 1, column ); while ( index.isValid() ) { item = fileItemFor(index); if ( !item.isNull() && (!onlyImage || isImage( item )) ) { return item; } index = index.sibling( index.row() - 1, column ); } return KFileItem(); // no previous item / image } case Next: { index = index.sibling( index.row() + 1, column ); while ( index.isValid() ) { item = fileItemFor(index); if ( !item.isNull() && (!onlyImage || isImage( item )) ) { return item; } index = index.sibling( index.row() + 1, column ); } return KFileItem(); // no further item / image } case Current: default: return fileItemFor(currentIndex); } return KFileItem(); } void FileWidget::slotViewChanged() { view()->installEventFilter( this ); } void FileWidget::slotItemsCleared() { m_currentURL = QString::null; } void FileWidget::slotItemsDeleted( const KFileItemList& items ) { KFileItem current = getCurrentItem( false ); if ( !items.contains(current) ) { return; // all ok, we already have a new current item } KFileItem next = getNext(); if ( next.isNull() ) next = getPrevious(); if ( !next.isNull() ) m_currentURL = next.url().url(); } void FileWidget::slotHighlighted( const KFileItem& item ) { if ( !item.isNull() ) { m_currentURL = item.url().url(); } else { m_currentURL = QString(); } } void FileWidget::slotReturnPressed( const QString& t ) { // we need a / at the end, otherwise replacedPath() will cut off the dir, // assuming it is a filename QString text = t; if ( text.at( text.length()-1 ) != '/' ) text += '/'; if ( text.at(0) == '/' || text.at(0) == '~' ) { QString dir = m_fileFinder->completion()->replacedPath( text ); setUrl( QUrl::fromLocalFile(dir), true ); } else if ( text.indexOf('/') != (int) text.length() -1 ) { // relative path QString dir = m_fileFinder->completion()->replacedPath( text ); QUrl u = url().resolved(QUrl(dir)); setUrl( u, true ); } else if ( m_validCompletion ) { KFileItem item = getCurrentItem( true ); if ( !item.isNull() ) { if ( item.isDir() ) setUrl( item.url(), true ); else emit fileSelected( item ); } } } void FileWidget::setInitialItem( const QUrl& url ) { m_initialName = url; } void FileWidget::slotURLEntered( const QUrl& url ) { if ( m_fileFinder ) m_fileFinder->completion()->setDir( url ); } void FileWidget::slotFinishedLoading() { const KFileItem& current = getCurrentItem( false ); if ( !m_initialName.isEmpty() ) setCurrentItem( m_initialName ); else if ( current.isNull() ) { QModelIndex first = view()->model()->index(0, 0); if (first.isValid()) { KFileItem item = first.data(Qt::UserRole).value(); if (!item.isNull()) { setCurrentItem( item ); } } } m_initialName = QUrl(); emit finished(); } QSize FileWidget::sizeHint() const { return QSize( 300, 300 ); } void FileWidget::resizeEvent( QResizeEvent *e ) { KDirOperator::resizeEvent( e ); if ( m_fileFinder ) m_fileFinder->move( width() - m_fileFinder->width(), height() - m_fileFinder->height() ); } diff --git a/src/imlibwidget.cpp b/src/imlibwidget.cpp index f02217a..a6d1213 100644 --- a/src/imlibwidget.cpp +++ b/src/imlibwidget.cpp @@ -1,728 +1,728 @@ /* This file is part of the KDE project Copyright (C) 1998-2002 Carsten Pfeiffer 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "imlibwidget.h" #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include "filecache.h" #include "kuickdata.h" #include "kuickfile.h" #include "kuickimage.h" +#include "kuickshow_debug.h" #include "imagemods.h" const int ImlibWidget::ImlibOffset = 256; ImlibWidget::ImlibWidget( ImData *_idata, QWidget *parent ) : QWidget( parent ) { idata = _idata; deleteImData = false; deleteImlibData = true; if ( !idata ) { // if no imlib configuration was given, create one ourself idata = new ImData; deleteImData = true; } ImlibInitParams par; // PARAMS_PALETTEOVERRIDE taken out because of segfault in imlib :o( par.flags = ( PARAMS_REMAP | PARAMS_VISUALID | PARAMS_FASTRENDER | PARAMS_HIQUALITY | PARAMS_DITHER | PARAMS_IMAGECACHESIZE | PARAMS_PIXMAPCACHESIZE ); Visual* defaultvis = DefaultVisual(getX11Display(), getX11Screen()); par.paletteoverride = idata->ownPalette ? 1 : 0; par.remap = idata->fastRemap ? 1 : 0; par.fastrender = idata->fastRender ? 1 : 0; par.hiquality = idata->dither16bit ? 1 : 0; par.dither = idata->dither8bit ? 1 : 0; par.visualid = defaultvis->visualid; uint maxcache = idata->maxCache; // 0 == no cache par.imagecachesize = maxcache * 1024; par.pixmapcachesize = maxcache * 1024; id = Imlib_init_with_params( getX11Display(), &par ); init(); } ImlibWidget::ImlibWidget( ImData *_idata, ImlibData *_id, QWidget *parent ) : QWidget( parent ) { id = _id; idata = _idata; deleteImData = false; deleteImlibData = false; if ( !idata ) { idata = new ImData; deleteImData = true; } init(); } void ImlibWidget::init() { int w = 1; // > 0 for XCreateWindow int h = 1; myBackgroundColor = Qt::black; m_kuim = 0L; m_kuickFile = 0L; if ( !id ) qFatal("ImlibWidget: Imlib not initialized, aborting."); setAttribute( Qt::WA_DeleteOnClose ); setAutoRender( true ); setPalette( QPalette( myBackgroundColor )); setBackgroundRole( QPalette::Window ); imageCache = new ImageCache( id, 4 ); // cache 4 images (FIXME?) connect( imageCache, SIGNAL( sigBusy() ), SLOT( setBusyCursor() )); connect( imageCache, SIGNAL( sigIdle() ), SLOT( restoreCursor() )); win = XCreateSimpleWindow(getX11Display(), winId(), 0,0,w,h,0,0,0); } ImlibWidget::~ImlibWidget() { delete imageCache; if ( deleteImlibData && id ) free ( id ); if ( win ) XDestroyWindow( getX11Display(), win ); if ( deleteImData ) delete idata; } QUrl ImlibWidget::url() const { if ( m_kuickFile ) return m_kuickFile->url(); return QUrl(); } KuickFile * ImlibWidget::currentFile() const { return m_kuickFile; } // tries to load "filename" and returns the according KuickImage * // or 0L if unsuccessful KuickImage * ImlibWidget::loadImageInternal( KuickFile * file ) { assert( file->isAvailable() ); // apply default image modifications mod.brightness = idata->brightness + ImlibOffset; mod.contrast = idata->contrast + ImlibOffset; mod.gamma = idata->gamma + ImlibOffset; KuickImage *kuim = imageCache->getKuimage( file ); bool wasCached = true; if ( !kuim ) { wasCached = false; kuim = imageCache->loadImage( file, mod ); } if ( !kuim ) {// couldn't load file, maybe corrupt or wrong format qWarning("ImlibWidget: can't load image %s", qUtf8Printable(file->url().toDisplayString())); return 0L; } loaded( kuim, wasCached ); // maybe upscale/downscale/rotate in subclasses return kuim; } // overridden in subclass void ImlibWidget::loaded( KuickImage *, bool /*wasCached*/ ) { } bool ImlibWidget::loadImage( const QUrl& url ) { return loadImage( FileCache::self()->getFile( url )); } bool ImlibWidget::loadImage( KuickFile * file ) { if ( file->waitForDownload( this ) != KuickFile::OK) return false; KuickImage *kuim = loadImageInternal( file ); // FIXME - check everywhere if we have a kuim or not! if ( kuim ) { m_kuim = kuim; autoUpdate( true ); // -> updateWidget() -> updateGeometry() m_kuickFile = file; return true; } return false; } bool ImlibWidget::cacheImage( const QUrl& url ) { // qDebug("cache image: %s", url.url().latin1()); KuickFile *file = FileCache::self()->getFile( url ); if ( file->isAvailable() ) return cacheImage( file ); else { if ( !file->download() ) { return false; } connect( file, SIGNAL( downloaded( KuickFile * )), SLOT( cacheImage( KuickFile * )) ); return true; // optimistic } } bool ImlibWidget::cacheImage( KuickFile * file ) { // qDebug("cache image: %s", file->url().url().latin1()); KuickImage *kuim = loadImageInternal( file ); if ( kuim ) { kuim->renderPixmap(); return true; } return false; } void ImlibWidget::showImage() { XMapWindow( getX11Display(), win ); XSync( getX11Display(), False ); } // -256..256 void ImlibWidget::setBrightness( int factor ) { mod.brightness = factor + ImlibOffset; setImageModifier(); autoUpdate(); } // -256..256 void ImlibWidget::setContrast( int factor ) { mod.contrast = factor + ImlibOffset; setImageModifier(); autoUpdate(); } // -256..256 void ImlibWidget::setGamma( int factor ) { mod.gamma = factor + ImlibOffset; setImageModifier(); autoUpdate(); } Rotation ImlibWidget::rotation() const { return m_kuim ? m_kuim->absRotation() : ROT_0; } FlipMode ImlibWidget::flipMode() const { return m_kuim ? m_kuim->flipMode() : FlipNone; } void ImlibWidget::zoomImage( float factor ) { if ( factor == 1 || factor == 0 || !m_kuim ) return; int newWidth = (int) (factor * (float) m_kuim->width()); int newHeight = (int) (factor * (float) m_kuim->height()); if ( canZoomTo( newWidth, newHeight ) ) { m_kuim->resize( newWidth, newHeight, idata->smoothScale ? KuickImage::SMOOTH : KuickImage::FAST ); autoUpdate( true ); } } bool ImlibWidget::canZoomTo( int newWidth, int newHeight ) { if ( newWidth <= 2 || newHeight <= 2 ) // minimum size for an image is 2x2 pixels return false; return true; } void ImlibWidget::showImageOriginalSize() { if ( !m_kuim ) return; m_kuim->restoreOriginalSize(); autoUpdate( true ); showImage(); } bool ImlibWidget::autoRotate( KuickImage *kuim ) { /* KFileMetaInfo disappered in KF5. * TODO: find alternative to KFileMetaInfo KFileMetaInfo metadatas( kuim->file().localFile() ); if ( !metadatas.isValid() ) return false; KFileMetaInfoItem metaitem = metadatas.item("Orientation"); if ( !metaitem.isValid() || metaitem.value().isNull() ) return false; switch ( metaitem.value().toInt() ) { // Orientation: // 1: normal // 2: flipped horizontally // 3: ROT 180 // 4: flipped vertically // 5: ROT 90 -> flip horizontally // 6: ROT 90 // 7: ROT 90 -> flip vertically // 8: ROT 270 case 1: default: kuim->rotateAbs( ROT_0 ); break; case 2: kuim->flipAbs( FlipHorizontal ); break; case 3: kuim->rotateAbs( ROT_180 ); break; case 4: kuim->flipAbs( FlipVertical ); break; case 5: kuim->rotateAbs( ROT_90 ); kuim->flipAbs( FlipHorizontal ); break; case 6: kuim->rotateAbs( ROT_90 ); break; case 7: kuim->rotateAbs( ROT_90 ); kuim->flipAbs( FlipVertical ); break; case 8: kuim->rotateAbs( ROT_270 ); break; } */ return true; } void ImlibWidget::setRotation( Rotation rot ) { if ( m_kuim ) { if ( m_kuim->rotateAbs( rot ) ) autoUpdate( true ); } } // slots connected to Accels and popupmenu void ImlibWidget::rotate90() { if ( !m_kuim ) return; m_kuim->rotate( ROT_90 ); rotated( m_kuim, ROT_90 ); autoUpdate( true ); } void ImlibWidget::rotate180() { if ( !m_kuim ) return; m_kuim->rotate( ROT_180 ); rotated( m_kuim, ROT_180 ); autoUpdate(); } void ImlibWidget::rotate270() { if ( !m_kuim ) return; m_kuim->rotate( ROT_270 ); rotated( m_kuim, ROT_270 ); autoUpdate( true ); } // should this go into a subclass? void ImlibWidget::flipHoriz() { if ( !m_kuim ) return; m_kuim->flip( FlipHorizontal ); autoUpdate(); } void ImlibWidget::flipVert() { if ( !m_kuim ) return; m_kuim->flip( FlipVertical ); autoUpdate(); } // end slots void ImlibWidget::setFlipMode( int mode ) { if ( !m_kuim ) return; if ( m_kuim->flipAbs( mode ) ) autoUpdate(); } void ImlibWidget::updateWidget( bool geometryUpdate ) { if ( !m_kuim ) return; // if ( geometryUpdate ) // XUnmapWindow( getX11Display(), win );// remove the old image -> no flicker XSetWindowBackgroundPixmap( getX11Display(), win, m_kuim->pixmap() ); if ( geometryUpdate ) updateGeometry( m_kuim->width(), m_kuim->height() ); XClearWindow( getX11Display(), win ); showImage(); } // here we just use the size of m_kuim, may be overridden in subclass void ImlibWidget::updateGeometry( int w, int h ) { XMoveWindow( getX11Display(), win, 0, 0 ); // center? XResizeWindow( getX11Display(), win, w, h ); resize( w, h ); } void ImlibWidget::closeEvent( QCloseEvent *e ) { e->accept(); QWidget::closeEvent( e ); } void ImlibWidget::setBackgroundColor( const QColor& color ) { myBackgroundColor = color; setPalette( QPalette( myBackgroundColor )); repaint(); // FIXME - necessary at all? } const QColor& ImlibWidget::backgroundColor() const { return myBackgroundColor; } void ImlibWidget::setImageModifier() { if ( !m_kuim ) return; Imlib_set_image_modifier( id, m_kuim->imlibImage(), &mod ); m_kuim->setDirty( true ); } int ImlibWidget::imageWidth() const { return m_kuim ? m_kuim->width() : 0; } int ImlibWidget::imageHeight() const { return m_kuim ? m_kuim->height() : 0; } void ImlibWidget::setBusyCursor() { if ( testAttribute( Qt::WA_SetCursor ) ) m_oldCursor = cursor(); else m_oldCursor = QCursor(); setCursor( QCursor( Qt::WaitCursor ) ); } void ImlibWidget::restoreCursor() { if ( cursor().shape() == QCursor(Qt::WaitCursor).shape() ) // only if nobody changed the cursor in the meantime! setCursor( m_oldCursor ); } /* // Reparenting a widget in Qt in fact means destroying the old X window of the widget // and creating a new one. And since the X window used for the Imlib image is a child // of this widget's X window, destroying this widget's X window would mean also // destroying the Imlib image X window. Therefore it needs to be temporarily reparented // away and reparented back to the new X window. // Reparenting may happen e.g. when doing the old-style (non-NETWM) fullscreen changes. void ImlibWidget::reparent( QWidget* parent, Qt::WFlags f, const QPoint& p, bool showIt ) { XWindowAttributes attr; XGetWindowAttributes( getX11Display(), win, &attr ); XUnmapWindow( getX11Display(), win ); XReparentWindow( getX11Display(), win, attr.root, 0, 0 ); QWidget::reparent( parent, f, p, showIt ); XReparentWindow( getX11Display(), win, winId(), attr.x, attr.y ); if( attr.map_state != IsUnmapped ) XMapWindow( getX11Display(), win ); } */ void ImlibWidget::rotated( KuickImage *, int ) { } int ImlibWidget::getX11Screen() const { return QApplication::desktop()->screenNumber(this); } //---------- // uhh ugly, we have two lists to map from filename to KuickImage :-/ ImageCache::ImageCache( ImlibData *id, int maxImages ) { myId = id; idleCount = 0; myMaxImages = maxImages; } ImageCache::~ImageCache() { while ( !kuickList.isEmpty() ) { delete kuickList.takeFirst(); } fileList.clear(); } void ImageCache::setMaxImages( int maxImages ) { myMaxImages = maxImages; int count = kuickList.count(); while ( count > myMaxImages ) { delete kuickList.takeLast(); fileList.removeLast(); count--; } } void ImageCache::slotBusy() { if ( idleCount == 0 ) emit sigBusy(); idleCount++; } void ImageCache::slotIdle() { idleCount--; if ( idleCount == 0 ) emit sigIdle(); } KuickImage * ImageCache::getKuimage( KuickFile * file ) { if ( !file ) return 0L; assert( file->isAvailable() ); // debug build if ( file->waitForDownload( 0L ) != KuickFile::OK ) // and for users return 0L; KuickImage *kuim = 0L; int index = fileList.indexOf( file ); if ( index != -1 ) { if ( index == 0 ) kuim = kuickList.at( 0 ); // need to reorder the lists, otherwise we might delete the current // image when a new one is cached and the current one is the last! else { kuim = kuickList.takeAt( index ); kuickList.insert( 0, kuim ); fileList.removeAll( file ); fileList.prepend( file ); } return kuim; } return 0L; } KuickImage * ImageCache::loadImage( KuickFile * file, ImlibColorModifier mod) { KuickImage *kuim = 0L; if ( !file || !file->isAvailable() ) return 0L; slotBusy(); // #ifndef NDEBUG // struct timeval tms1, tms2; // gettimeofday( &tms1, NULL ); // #endif ImlibImage *im = Imlib_load_image( myId, QFile::encodeName( file->localFile() ).data() ); // #ifndef NDEBUG // gettimeofday( &tms2, NULL ); // qDebug("*** LOADING image: %s, took %ld ms", file.toLatin1(), // (tms2.tv_usec - tms1.tv_usec)/1000); // #endif slotIdle(); if ( !im ) { slotBusy(); im = loadImageWithQt( file->localFile() ); slotIdle(); if ( !im ) return 0L; } Imlib_set_image_modifier( myId, im, &mod ); kuim = new KuickImage( file, im, myId ); connect( kuim, SIGNAL( startRendering() ), SLOT( slotBusy() )); connect( kuim, SIGNAL( stoppedRendering() ), SLOT( slotIdle() )); kuickList.insert( 0, kuim ); fileList.prepend( file ); if ( kuickList.count() > myMaxImages ) { // qDebug(":::: now removing from cache: %s", (*fileList.fromLast()).toLatin1()); delete kuickList.takeLast(); fileList.removeLast(); } return kuim; } // Note: the returned image's filename will not be the real filename (which it usually // isn't anyway, according to Imlib's sources). ImlibImage * ImageCache::loadImageWithQt( const QString& fileName ) const { qDebug("Trying to load %s with KImageIO...", qUtf8Printable(fileName)); QImage image( fileName ); if ( image.isNull() ) return 0L; if ( image.depth() != 32 ) { image = image.convertToFormat(QImage::Format_RGB32); if ( image.isNull() ) return 0L; } // convert to 24 bpp (discard alpha) int numPixels = image.width() * image.height(); const int NUM_BYTES_NEW = 3; // 24 bpp uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW]; uchar *newData = newImageData; int w = image.width(); int h = image.height(); for (int y = 0; y < h; y++) { QRgb *scanLine = reinterpret_cast( image.scanLine(y) ); for (int x = 0; x < w; x++) { const QRgb& pixel = scanLine[x]; *(newData++) = qRed(pixel); *(newData++) = qGreen(pixel); *(newData++) = qBlue(pixel); } } ImlibImage *im = Imlib_create_image_from_data( myId, newImageData, NULL, image.width(), image.height() ); delete[] newImageData; return im; } diff --git a/src/kuickfile.cpp b/src/kuickfile.cpp index 8a22112..26de1ab 100644 --- a/src/kuickfile.cpp +++ b/src/kuickfile.cpp @@ -1,208 +1,208 @@ /* This file is part of the KDE project Copyright (C) 2003,2009 Carsten Pfeiffer 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kuickfile.h" +#include "kuickshow_debug.h" #include #include #include -#include #include #include #include #include #include "filecache.h" KuickFile::KuickFile(const QUrl& url) : QObject(), m_url( url ), m_job( 0L ), m_progress( 0L ), m_currentProgress( 0 ) { if ( m_url.isLocalFile()) m_localFile = m_url.path(); else { QUrl mostLocal; KIO::StatJob* job = KIO::mostLocalUrl(m_url); connect(job, &KIO::StatJob::result, [job, &mostLocal]() { if(!job->error()) mostLocal = job->mostLocalUrl(); }); job->exec(); if ( mostLocal.isValid() && mostLocal.isLocalFile() ) m_localFile = mostLocal.path(); } } KuickFile::~KuickFile() { delete m_job; if ( hasDownloaded() ) QFile::remove( m_localFile ); } QString KuickFile::localFile() const { // Note: never call isAvailable() from here, directly or indirectly if ( isDownloading() ) return QString::null; return m_localFile; } bool KuickFile::hasDownloaded() const { return !m_url.isLocalFile() && isAvailable() && m_job != 0L; } // ### need an API for refreshing the file? bool KuickFile::download() { if ( m_url.isLocalFile() || isAvailable() ) return true; if ( isDownloading() ) return true; // reinitialize m_localFile = QString::null; m_currentProgress = 0; QString ext; QString fileName = m_url.fileName(); int extIndex = fileName.lastIndexOf('.'); if ( extIndex > 0 ) ext = fileName.mid( extIndex ); QScopedPointer tempFilePtr(FileCache::self()->createTempFile(ext)); if(tempFilePtr.isNull() || !tempFilePtr->open()) return false; QUrl destURL = QUrl::fromLocalFile(tempFilePtr->fileName()); // we don't need the actual temp file, just its unique name tempFilePtr.reset(); m_job = KIO::file_copy( m_url, destURL, -1, KIO::HideProgressInfo | KIO::Overwrite ); // handling progress ourselves // m_job->setAutoErrorHandlingEnabled( true ); connect( m_job, SIGNAL( result( KJob * )), SLOT( slotResult( KJob * ) )); connect( m_job, SIGNAL( percent( KJob *, unsigned long )), SLOT( slotProgress( KJob *, unsigned long ) )); // TODO: generify background/foreground downloading? return m_job != 0L; } KuickFile::DownloadStatus KuickFile::waitForDownload( QWidget *parent ) { if ( isAvailable() ) return OK; if ( !isDownloading() ) { if ( !download() ) return ERROR; } QProgressDialog *dialog = new QProgressDialog( parent ); dialog->setWindowTitle( i18n("Downloading %1...", m_url.fileName() ) ); dialog->setLabelText( i18n("Please wait while downloading\n%1", m_url.toDisplayString() )); dialog->setAutoClose( true ); dialog->setMaximum( 100 ); // percent dialog->setValue( m_currentProgress ); m_progress = dialog; dialog->exec(); m_progress = nullptr; bool canceled = dialog->wasCanceled(); delete dialog; if ( canceled && m_job ) { m_job->kill(); m_job = 0L; m_currentProgress = 0; } // ### when aborted, remove KuickImage from FileCache? if ( canceled ) return CANCELED; if ( !isAvailable() ) return ERROR; // ### use custom progress dialog with OK, SKIP, CANCEL? return OK; } void KuickFile::slotResult( KJob *job ) { if (job != m_job) { // huh? return; } m_job = 0L; if ( job->error() != 0 ) { m_currentProgress = 0; if ( job->error() != KIO::ERR_USER_CANCELED ) qWarning("ERROR: KuickFile::slotResult: %s", qUtf8Printable(job->errorString())); QString canceledFile = static_cast(job)->destUrl().path(); QFile::remove( canceledFile ); m_progress->hide(); } else { m_localFile = static_cast(job)->destUrl().path(); emit downloaded( this ); // before closing the progress dialog if ( m_progress ) { m_progress->setValue( 100 ); } } } void KuickFile::slotProgress( KJob *job, unsigned long percent ) { if (job != m_job) { // huh? return; } m_currentProgress = percent; if ( !m_progress ) return; // only set 100% in slotResult. Otherwise, the progress dialog would be closed // before slotResult() is called. if ( percent >= 100 ) percent = 99; m_progress->setValue( (int) percent ); } bool operator==( const KuickFile& first, const KuickFile& second ) { return first.url() == second.url(); } diff --git a/src/kuickshow.categories b/src/kuickshow.categories new file mode 100644 index 0000000..83d48ed --- /dev/null +++ b/src/kuickshow.categories @@ -0,0 +1 @@ +org.kde.kuickshow Kuickshow diff --git a/src/kuickshow.cpp b/src/kuickshow.cpp index 6581651..f87f9d6 100644 --- a/src/kuickshow.cpp +++ b/src/kuickshow.cpp @@ -1,1452 +1,1452 @@ /* This file is part of the KDE project Copyright (C) 1998-2006 Carsten Pfeiffer 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kuickshow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aboutwidget.h" #include "filecache.h" #include "filewidget.h" #include "imagewindow.h" #include "imdata.h" #include "imlibwidget.h" #include "kuick.h" #include "kuickconfigdlg.h" #include "kuickdata.h" #include "kuickfile.h" +#include "kuickshow_debug.h" #include "openfilesanddirsdialog.h" KuickData* kdata; QList KuickShow::s_viewers; KuickShow::KuickShow( const char *name ) : KXmlGuiWindow( 0L ), m_slideshowCycle( 1 ), fileWidget( 0L ), dialog( 0L ), id( 0L ), m_viewer( 0L ), oneWindowAction( 0L ), m_delayedRepeatItem( 0L ), m_slideShowStopped(false) { setObjectName(name); aboutWidget = 0L; kdata = new KuickData; kdata->load(); initImlib(); resize( 400, 500 ); m_slideTimer = new QTimer( this ); connect( m_slideTimer, SIGNAL( timeout() ), SLOT( nextSlide() )); KSharedConfig::Ptr kc = KSharedConfig::openConfig(); bool isDir = false; // true if we get a directory on the commandline // parse commandline options QCommandLineParser* parser = reinterpret_cast(qApp->property("cmdlineParser").value()); // files to display // either a directory to display, an absolute path, a relative path, or a URL QUrl startDir = QUrl::fromLocalFile(QDir::currentPath() + '/'); QStringList args = parser->positionalArguments(); int numArgs = args.count(); if ( numArgs >= 10 ) { // Even though the 1st i18n string will never be used, it needs to exist for plural handling - mhunter if ( KMessageBox::warningYesNo( this, i18np("Do you really want to display this 1 image at the same time? This might be quite resource intensive and could overload your computer.
If you choose %1, only the first image will be shown.", "Do you really want to display these %n images at the same time? This might be quite resource intensive and could overload your computer.
If you choose %1, only the first image will be shown.", numArgs).arg(KStandardGuiItem::no().plainText()), i18n("Display Multiple Images?")) != KMessageBox::Yes ) { numArgs = 1; } } QMimeDatabase mimedb; for ( int i = 0; i < numArgs; i++ ) { QUrl url = QUrl::fromUserInput(args.value(i), QDir::currentPath(), QUrl::AssumeLocalFile); KFileItem item( url ); // for remote URLs, we don't know if it's a file or directory, but // FileWidget::isImage() should correct in most cases. // For non-local non-images, we just assume directory. if ( FileWidget::isImage( item ) ) { showImage( item, true, false, true ); // show in new window, not fullscreen-forced and move to 0,0 // showImage( &item, true, false, false ); // show in new window, not fullscreen-forced and not moving to 0,0 } else if ( item.isDir() ) { startDir = url; isDir = true; } // need to check remote files else if ( !url.isLocalFile() ) { QMimeType mime = mimedb.mimeTypeForUrl( url ); QString name = mime.name(); if ( name == "application/octet-stream" ) { // unknown -> stat() KIO::MimetypeJob* job = KIO::mimetype(url); KJobWidgets::setWindow(job, this); connect(job, &KIO::MimetypeJob::result, [job, &name]() { if(!job->error()) name = job->mimetype(); }); job->exec(); } // text/* is a hack for bugs.kde.org-attached-images urls. // The real problem here is that NetAccess::mimetype does a HTTP HEAD, which doesn't // always return the right mimetype. The rest of KDE start a get() instead.... if ( name.startsWith( "image/" ) || name.startsWith( "text/" ) ) { showImage( item, true, false, true, true ); } else // assume directory, KDirLister will tell us if we can't list { startDir = url; isDir = true; } } // else // we don't handle local non-images } if ( (kdata->startInLastDir && args.count() == 0) || parser->isSet( "lastfolder" )) { KConfigGroup sessGroup(kc, "SessionSettings"); startDir = QUrl(sessGroup.readPathEntry( "CurrentDirectory", startDir.url() )); } if ( s_viewers.isEmpty() || isDir ) { initGUI( startDir ); if (!qApp->isSessionRestored()) // during session management, readProperties() will show() show(); } else { // don't show browser, when image on commandline hide(); KStartupInfo::appStarted(); } } KuickShow::~KuickShow() { saveSettings(); delete m_viewer; FileCache::shutdown(); free( id ); qApp->quit(); delete kdata; } // TODO convert to use xmlui file void KuickShow::initGUI( const QUrl& startDir ) { QUrl startURL( startDir ); if ( !KProtocolManager::supportsListing( startURL ) ) startURL = QUrl(); fileWidget = new FileWidget( startDir, this ); fileWidget->setObjectName( QString::fromLatin1( "MainWidget" ) ); setFocusProxy( fileWidget ); KActionCollection *coll = fileWidget->actionCollection(); redirectDeleteAndTrashActions(coll); connect( fileWidget, SIGNAL( fileSelected( const KFileItem& ) ), this, SLOT( slotSelected( const KFileItem& ) )); connect( fileWidget, SIGNAL( fileHighlighted( const KFileItem& )), this, SLOT( slotHighlighted( const KFileItem& ) )); connect( fileWidget, SIGNAL( urlEntered( const QUrl& )), this, SLOT( dirSelected( const QUrl& )) ); fileWidget->setAcceptDrops(true); connect( fileWidget, SIGNAL( dropped( const KFileItem&, QDropEvent *, const QList & )), this, SLOT( slotDropped( const KFileItem&, QDropEvent *, const QList &)) ); // setup actions QAction *open = KStandardAction::open( this, SLOT( slotOpenURL() ), coll ); coll->addAction( "openURL", open ); QAction *print = KStandardAction::print( this, SLOT( slotPrint() ), coll ); coll->addAction( "kuick_print", print ); print->setText( i18n("Print Image...") ); QAction *configure = coll->addAction( "kuick_configure" ); configure->setText( i18n("Configure %1...", QApplication::applicationDisplayName() ) ); configure->setIcon( QIcon::fromTheme( "configure" ) ); connect( configure, SIGNAL( triggered() ), this, SLOT( configuration() ) ); QAction *slide = coll->addAction( "kuick_slideshow" ); slide->setText( i18n("Start Slideshow" ) ); slide->setIcon( QIcon::fromTheme("ksslide" )); coll->setDefaultShortcut(slide, Qt::Key_F2); connect( slide, SIGNAL( triggered() ), this, SLOT( startSlideShow() )); QAction *about = coll->addAction( "about" ); about->setText( i18n( "About KuickShow" ) ); about->setIcon( QIcon::fromTheme("about") ); connect( about, SIGNAL( triggered() ), this, SLOT( about() ) ); oneWindowAction = coll->add( "kuick_one window" ); oneWindowAction->setText( i18n("Open Only One Image Window") ); oneWindowAction->setIcon( QIcon::fromTheme( "window-new" ) ); coll->setDefaultShortcut(oneWindowAction, Qt::CTRL+Qt::Key_N); m_toggleBrowserAction = coll->add( "toggleBrowser" ); m_toggleBrowserAction->setText( i18n("Show File Browser") ); coll->setDefaultShortcut(m_toggleBrowserAction, Qt::Key_Space); m_toggleBrowserAction->setCheckedState(KGuiItem(i18n("Hide File Browser"))); connect( m_toggleBrowserAction, SIGNAL( toggled( bool ) ), SLOT( toggleBrowser() )); QAction *showInOther = coll->addAction( "kuick_showInOtherWindow" ); showInOther->setText( i18n("Show Image") ); connect( showInOther, SIGNAL( triggered() ), SLOT( slotShowInOtherWindow() )); QAction *showInSame = coll->addAction( "kuick_showInSameWindow" ); showInSame->setText( i18n("Show Image in Active Window") ); connect( showInSame, SIGNAL( triggered() ), this, SLOT( slotShowInSameWindow() ) ); QAction *showFullscreen = coll->addAction( "kuick_showFullscreen" ); showFullscreen->setText( i18n("Show Image in Fullscreen Mode") ); connect( showFullscreen, SIGNAL( triggered() ), this, SLOT( slotShowFullscreen() ) ); QAction *defaultInlinePreview = coll->action( "inline preview" ); KToggleAction *inlinePreviewAction = coll->add( "kuick_inlinePreview" ); inlinePreviewAction->setText( defaultInlinePreview->text() ); inlinePreviewAction->setIcon( defaultInlinePreview->icon() ); connect( inlinePreviewAction, SIGNAL( toggled(bool) ), this, SLOT( slotToggleInlinePreview(bool) ) ); QAction *quit = KStandardAction::quit( this, SLOT(slotQuit()), coll); coll->addAction( "quit", quit ); // remove QString::null parameter -- ellis // coll->readShortcutSettings( QString::null ); // menubar QMenuBar *mBar = menuBar(); QMenu *fileMenu = new QMenu( i18n("&File"), mBar ); fileMenu->setObjectName( QString::fromLatin1( "file" ) ); fileMenu->addAction(open); fileMenu->addAction(showInOther); fileMenu->addAction(showInSame); fileMenu->addAction(showFullscreen); fileMenu->addSeparator(); fileMenu->addAction(slide); fileMenu->addAction(print); fileMenu->addSeparator(); fileMenu->addAction(quit); QMenu *editMenu = new QMenu( i18n("&Edit"), mBar ); editMenu->setObjectName( QString::fromLatin1( "edit" ) ); editMenu->addAction(coll->action("mkdir")); editMenu->addAction(coll->action("trash")); editMenu->addSeparator(); editMenu->addAction(coll->action("properties")); // add the sorting menu and a separator into the View menu KActionMenu *viewActionMenu = static_cast( coll->action("view menu")); viewActionMenu->menu()->addSeparator(); KActionMenu *sortingMenu = static_cast( coll->action("sorting menu")); // viewActionMenu->menu()->addAction(sortingMenu); //, 0 ); // on top of the menu QMenu *settingsMenu = new QMenu( i18n("&Settings"), mBar ); settingsMenu->setObjectName( QString::fromLatin1( "settings" ) ); settingsMenu->addAction(configure); mBar->addMenu( fileMenu ); mBar->addMenu( editMenu ); mBar->addAction( viewActionMenu ); mBar->addMenu( settingsMenu ); // toolbar KToolBar *tBar = toolBar(i18n("Main Toolbar")); tBar->addAction(coll->action("up")); tBar->addAction(coll->action("back")); tBar->addAction(coll->action("forward")); tBar->addAction(coll->action("home")); tBar->addAction(coll->action("reload")); tBar->addSeparator(); // Address box in address tool bar KToolBar *addressToolBar = toolBar( "address_bar" ); cmbPath = new KUrlComboBox( KUrlComboBox::Directories, true, addressToolBar ); KUrlCompletion *cmpl = new KUrlCompletion( KUrlCompletion::DirCompletion ); cmbPath->setCompletionObject( cmpl ); cmbPath->setAutoDeleteCompletionObject( true ); addressToolBar->addWidget( cmbPath ); connect( cmbPath, SIGNAL( urlActivated( const QUrl& )), this, SLOT( slotSetURL( const QUrl& ))); connect( cmbPath, SIGNAL( returnPressed()), this, SLOT( slotURLComboReturnPressed())); tBar->addSeparator(); tBar->addAction(coll->action( "short view" )); tBar->addAction(coll->action( "detailed view" )); tBar->addAction(inlinePreviewAction); tBar->addAction(coll->action( "preview")); tBar->addSeparator(); tBar->addAction(slide); tBar->addSeparator(); tBar->addAction(oneWindowAction); tBar->addAction(print); tBar->addSeparator(); tBar->addAction(about); KHelpMenu* help = new KHelpMenu(this, QString(), false); mBar->addMenu(help->menu()); sblblUrlInfo = createStatusBarLabel(10); sblblMetaInfo = createStatusBarLabel(2); fileWidget->setFocus(); KConfigGroup kc(KSharedConfig::openConfig(), "SessionSettings"); bool oneWindow = kc.readEntry("OpenImagesInActiveWindow", true ); oneWindowAction->setChecked( oneWindow ); tBar->show(); fileWidget->initActions(); fileWidget->clearHistory(); dirSelected( fileWidget->url() ); setCentralWidget( fileWidget ); setupGUI( KXmlGuiWindow::Save ); coll->setDefaultShortcuts(coll->action( "reload" ), KStandardShortcut::reload()); coll->setDefaultShortcut(coll->action( "short view" ), Qt::Key_F6); coll->setDefaultShortcut(coll->action( "detailed view" ), Qt::Key_F7); //coll->setDefaultShortcut(coll->action( "show hidden" ), Qt::Key_F8); coll->setDefaultShortcut(coll->action( "mkdir" ), Qt::Key_F10); coll->setDefaultShortcut(coll->action( "preview" ), Qt::Key_F11); //coll->setDefaultShortcut(coll->action( "separate dirs" ), Qt::Key_F12); } QLabel* KuickShow::createStatusBarLabel(int stretch) { QLabel* label = new QLabel(this); label->setFixedHeight(fontMetrics().height() + 2); // copied from KStatusBar::insertItem statusBar()->addWidget(label, stretch); return label; } void KuickShow::redirectDeleteAndTrashActions(KActionCollection *coll) { QAction *action = coll->action("delete"); if (action) { action->disconnect(fileWidget); connect(action, SIGNAL(triggered()), this, SLOT(slotDeleteCurrentImage())); } action = coll->action("trash"); if (action) { action->disconnect(fileWidget); connect(action, SIGNAL(triggered()), this, SLOT(slotTrashCurrentImage())); } } void KuickShow::slotSetURL( const QUrl& url ) { fileWidget->setUrl( url, true ); } void KuickShow::slotURLComboReturnPressed() { QUrl where = QUrl::fromUserInput( cmbPath->currentText(), QString(), QUrl::AssumeLocalFile ); slotSetURL( where ); } void KuickShow::viewerDeleted() { ImageWindow *viewer = (ImageWindow*) sender(); s_viewers.removeAll( viewer ); if ( viewer == m_viewer ) m_viewer = 0L; if ( !haveBrowser() && s_viewers.isEmpty() ) { saveSettings(); FileCache::shutdown(); ::exit(0); } else if ( haveBrowser() ) { activateWindow(); // This setFocus() call causes problems in the combiview (always the // directory view on the left gets the focus, which is not desired) // fileWidget->setFocus(); } if ( fileWidget ) // maybe a slideshow was stopped --> enable the action again fileWidget->actionCollection()->action("kuick_slideshow")->setEnabled( true ); m_slideTimer->stop(); } void KuickShow::slotHighlighted( const KFileItem& item ) { QString statusBarInfo = item.isNull() ? QString() : item.getStatusBarInfo(); sblblUrlInfo->setText(statusBarInfo); bool image = FileWidget::isImage( item ); QString meta; if ( image ) { /* KFileMetaInfo disappered in KF5. * TODO: find alternative to KFileMetaInfo KFileMetaInfo info; // code snippet copied from KFileItem::metaInfo (KDE4) if(item.isRegularFile() || item.isDir()) { bool isLocalUrl; QUrl url(item.mostLocalUrl(&isLocalUrl)); info = KFileMetaInfo(url.toLocalFile(), item.mimetype(), KFileMetaInfo::ContentInfo | KFileMetaInfo::TechnicalInfo); } if ( info.isValid() ) { meta = info.item("sizeurl").value().toString(); const QString bpp = info.item( "BitDepth" ).value().toString(); if ( !bpp.isEmpty() ) meta.append( ", " ).append( bpp ); } */ } sblblMetaInfo->setText(meta); fileWidget->actionCollection()->action("kuick_print")->setEnabled( image ); fileWidget->actionCollection()->action("kuick_showInSameWindow")->setEnabled( image ); fileWidget->actionCollection()->action("kuick_showInOtherWindow")->setEnabled( image ); fileWidget->actionCollection()->action("kuick_showFullscreen")->setEnabled( image ); } void KuickShow::dirSelected( const QUrl& url ) { if ( url.isLocalFile() ) setCaption( url.path() ); else setCaption( url.toDisplayString() ); cmbPath->setUrl( url ); sblblUrlInfo->setText(url.toDisplayString()); } void KuickShow::slotSelected( const KFileItem& item ) { showImage( item, !oneWindowAction->isChecked() ); } // downloads item if necessary void KuickShow::showFileItem( ImageWindow * /*view*/, const KFileItem * /*item*/ ) { } bool KuickShow::showImage( const KFileItem& fi, bool newWindow, bool fullscreen, bool moveToTopLeft, bool ignoreFileType ) { newWindow |= !m_viewer; fullscreen |= (newWindow && kdata->fullScreen); if ( ignoreFileType || FileWidget::isImage( fi ) ) { if ( newWindow ) { m_viewer = new ImageWindow( kdata->idata, id, 0L ); m_viewer->setObjectName( QString::fromLatin1("image window") ); m_viewer->setFullscreen( fullscreen ); s_viewers.append( m_viewer ); connect( m_viewer, SIGNAL( nextSlideRequested() ), this, SLOT( nextSlide() )); connect( m_viewer, SIGNAL( destroyed() ), SLOT( viewerDeleted() )); connect( m_viewer, SIGNAL( sigFocusWindow( ImageWindow *) ), this, SLOT( slotSetActiveViewer( ImageWindow * ) )); connect( m_viewer, SIGNAL( sigImageError(const KuickFile *, const QString& ) ), this, SLOT( messageCantLoadImage(const KuickFile *, const QString &) )); connect( m_viewer, SIGNAL( requestImage( ImageWindow *, int )), this, SLOT( slotAdvanceImage( ImageWindow *, int ))); connect( m_viewer, SIGNAL( pauseSlideShowSignal() ), this, SLOT( pauseSlideShow() ) ); connect( m_viewer, SIGNAL (deleteImage (ImageWindow *)), this, SLOT (slotDeleteCurrentImage (ImageWindow *))); connect( m_viewer, SIGNAL (trashImage (ImageWindow *)), this, SLOT (slotTrashCurrentImage (ImageWindow *))); if ( s_viewers.count() == 1 && moveToTopLeft ) { // we have to move to 0x0 before showing _and_ // after showing, otherwise we get some bogus geometry() m_viewer->move( Kuick::workArea().topLeft() ); } m_viewer->installEventFilter( this ); } // for some strange reason, m_viewer sometimes changes during the // next few lines of code, so as a workaround, we use safeViewer here. // This happens when calling KuickShow with two or more remote-url // arguments on the commandline, where the first one is loaded properly // and the second isn't (e.g. because it is a pdf or something else, // Imlib can't load). ImageWindow *safeViewer = m_viewer; if ( !safeViewer->showNextImage( fi.url() ) ) { m_viewer = safeViewer; delete safeViewer; // couldn't load image, close window } else { // safeViewer->setFullscreen( fullscreen ); if ( newWindow ) { // safeViewer->show(); if ( !fullscreen && s_viewers.count() == 1 && moveToTopLeft ) { // the WM might have moved us after showing -> strike back! // move the first image to 0x0 workarea coord safeViewer->move( Kuick::workArea().topLeft() ); } } if ( kdata->preloadImage && fileWidget ) { // don't move cursor KFileItem item = fileWidget->getItem( FileWidget::Next, true ); if ( !item.isNull() ) safeViewer->cacheImage( item.url() ); } m_viewer = safeViewer; return true; } // m_viewer created successfully } // isImage return false; } void KuickShow::slotDeleteCurrentImage() { performDeleteCurrentImage(fileWidget); } void KuickShow::slotTrashCurrentImage() { performTrashCurrentImage(fileWidget); } void KuickShow::slotDeleteCurrentImage(ImageWindow *viewer) { if (!fileWidget) { delayAction(new DelayedRepeatEvent(viewer, DelayedRepeatEvent::DeleteCurrentFile, 0L)); return; } performDeleteCurrentImage(viewer); } void KuickShow::slotTrashCurrentImage(ImageWindow *viewer) { if (!fileWidget) { delayAction(new DelayedRepeatEvent(viewer, DelayedRepeatEvent::TrashCurrentFile, 0L)); return; } performTrashCurrentImage(viewer); } void KuickShow::performDeleteCurrentImage(QWidget *parent) { assert(fileWidget != 0L); KFileItemList list; const KFileItem &item = fileWidget->getCurrentItem(false); list.append (item); if (KMessageBox::warningContinueCancel( parent, i18n("Do you really want to delete\n '%1'?", item.url().toDisplayString(QUrl::PreferLocalFile)), i18n("Delete File"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), "Kuick_delete_current_image") != KMessageBox::Continue) { return; } tryShowNextImage(); fileWidget->del(list, 0, false); } void KuickShow::performTrashCurrentImage(QWidget *parent) { assert(fileWidget != 0L); KFileItemList list; const KFileItem& item = fileWidget->getCurrentItem(false); if (item.isNull()) return; list.append (item); if (KMessageBox::warningContinueCancel( parent, i18n("Do you really want to trash\n '%1'?", item.url().toDisplayString(QUrl::PreferLocalFile)), i18n("Trash File"), KGuiItem(i18nc("to trash", "&Trash"),"edittrash"), KStandardGuiItem::cancel(), "Kuick_trash_current_image") != KMessageBox::Continue) { return; } tryShowNextImage(); fileWidget->trash(list, parent, false, false); } void KuickShow::tryShowNextImage() { // move to next file item even if we have no viewer KFileItem next = fileWidget->getNext(true); if (next.isNull()) next = fileWidget->getPrevious(true); // ### why is this necessary at all? Why does KDirOperator suddenly re-read the // entire directory after a file was deleted/trashed!? (KDirNotify is the reason) if (!m_viewer) return; if (!next.isNull()) showImage(next, false); else { if (!haveBrowser()) { // ### when simply calling toggleBrowser(), this main window is completely messed up QTimer::singleShot(0, this, SLOT(toggleBrowser())); } m_viewer->deleteLater(); } } void KuickShow::startSlideShow() { KFileItem item = kdata->slideshowStartAtFirst ? fileWidget->gotoFirstImage() : fileWidget->getCurrentItem(false); if ( !item.isNull() ) { m_slideshowCycle = 1; fileWidget->actionCollection()->action("kuick_slideshow")->setEnabled( false ); showImage( item, !oneWindowAction->isChecked(), kdata->slideshowFullscreen ); if(kdata->slideDelay) m_slideTimer->start( kdata->slideDelay ); } } void KuickShow::pauseSlideShow() { if(m_slideShowStopped) { if(kdata->slideDelay) m_slideTimer->start( kdata->slideDelay ); m_slideShowStopped = false; } else { m_slideTimer->stop(); m_slideShowStopped = true; } } void KuickShow::nextSlide() { if ( !m_viewer ) { m_slideshowCycle = 1; fileWidget->actionCollection()->action("kuick_slideshow")->setEnabled( true ); return; } KFileItem item = fileWidget->getNext( true ); if ( item.isNull() ) { // last image if ( m_slideshowCycle < kdata->slideshowCycles || kdata->slideshowCycles == 0 ) { item = fileWidget->gotoFirstImage(); if ( !item.isNull() ) { nextSlide( item ); m_slideshowCycle++; return; } } delete m_viewer; fileWidget->actionCollection()->action("kuick_slideshow")->setEnabled( true ); return; } nextSlide( item ); } void KuickShow::nextSlide( const KFileItem& item ) { m_viewer->showNextImage( item.url() ); if(kdata->slideDelay) m_slideTimer->start( kdata->slideDelay ); } // prints the selected files in the filebrowser void KuickShow::slotPrint() { const KFileItemList items = fileWidget->selectedItems(); if ( items.isEmpty() ) return; KFileItemList::const_iterator it = items.constBegin(); const KFileItemList::const_iterator end = items.constEnd(); // don't show the image, just print ImageWindow *iw = new ImageWindow( 0, id, this ); iw->setObjectName( QString::fromLatin1("printing image")); KFileItem item; for ( ; it != end; ++it ) { item = (*it); if (FileWidget::isImage( item ) && iw->loadImage( item.url())) iw->printImage(); } delete iw; } void KuickShow::slotShowInOtherWindow() { showImage( fileWidget->getCurrentItem( false ), true ); } void KuickShow::slotShowInSameWindow() { showImage( fileWidget->getCurrentItem( false ), false ); } void KuickShow::slotShowFullscreen() { showImage( fileWidget->getCurrentItem( false ), false, true ); } void KuickShow::slotDropped( const KFileItem&, QDropEvent *, const QList &urls) { QList::ConstIterator it = urls.constBegin(); for ( ; it != urls.constEnd(); ++it ) { KFileItem item( *it ); if ( FileWidget::isImage( item ) ) showImage( item, true ); else fileWidget->setUrl( *it, true ); } } // try to init the WM border as it is 0,0 when the window is not shown yet. void KuickShow::show() { KXmlGuiWindow::show(); (void) Kuick::frameSize( winId() ); } void KuickShow::slotAdvanceImage( ImageWindow *view, int steps ) { KFileItem item; // to be shown KFileItem item_next; // to be cached if ( steps == 0 ) return; // the viewer might not be available yet. Factor this out somewhen. if ( !fileWidget ) { if ( m_delayedRepeatItem ) return; delayAction(new DelayedRepeatEvent( view, DelayedRepeatEvent::AdvanceViewer, new int(steps) )); return; } if ( steps > 0 ) { for ( int i = 0; i < steps; i++ ) item = fileWidget->getNext( true ); item_next = fileWidget->getNext( false ); } else if ( steps < 0 ) { for ( int i = steps; i < 0; i++ ) item = fileWidget->getPrevious( true ); item_next = fileWidget->getPrevious( false ); } if ( FileWidget::isImage( item ) ) { // QString filename; // KIO::NetAccess::download(item->url(), filename, this); view->showNextImage( item.url() ); if (m_slideTimer->isActive() && kdata->slideDelay) m_slideTimer->start( kdata->slideDelay ); if ( kdata->preloadImage && !item_next.isNull() ) // preload next image if ( FileWidget::isImage( item_next ) ) view->cacheImage( item_next.url() ); } } bool KuickShow::eventFilter( QObject *o, QEvent *e ) { if ( m_delayedRepeatItem ) // we probably need to install an eventFilter over return true; // kapp, to make it really safe bool ret = false; int eventType = e->type(); QKeyEvent *k = 0L; if ( eventType == QEvent::KeyPress ) k = static_cast( e ); if ( k ) { if ( KStandardShortcut::quit().contains( k->key() ) ) { saveSettings(); deleteAllViewers(); FileCache::shutdown(); ::exit(0); } else if ( KStandardShortcut::help().contains( k->key() ) ) { appHelpActivated(); return true; } } ImageWindow *window = dynamic_cast( o ); if ( window ) { // The XWindow used to display Imlib's image is being resized when // switching images, causing enter- and leaveevents for this // ImageWindow, leading to the cursor being unhidden. So we simply // don't pass those events to KCursor to prevent that. if ( eventType != QEvent::Leave && eventType != QEvent::Enter ) KCursor::autoHideEventFilter( o, e ); m_viewer = window; QString img; KFileItem item; // the image to be shown KFileItem item_next; // the image to be cached if ( k ) { // keypress ret = true; int key = k->key(); // Qt::Key_Shift shouldn't load the browser in nobrowser mode, it // is used for zooming in the imagewindow // Qt::Key_Alt shouldn't either - otherwise Alt+F4 doesn't work, the // F4 gets eaten (by NetAccess' modal dialog maybe?) if ( !fileWidget ) { if ( key != Qt::Key_Escape && key != Qt::Key_Shift && key != Qt::Key_Alt ) { KuickFile *file = m_viewer->currentFile(); initGUI( KIO::upUrl(file->url()) ); // the fileBrowser will list the start-directory // asynchronously so we can't immediately continue. There // is no current-item and no next-item (actually no item // at all). So we tell the browser the initial // current-item and wait for it to tell us when it's ready. // Then we will replay this KeyEvent. delayedRepeatEvent( m_viewer, k ); // OK, once again, we have a problem with the now async and // sync KDirLister :( If the startDir is already cached by // KDirLister, we won't ever get that finished() signal // because it is emitted before we can connect(). So if // our dirlister has a rootFileItem, we assume the // directory is read already and simply call // slotReplayEvent() without the need for the finished() // signal. // see slotAdvanceImage() for similar code if ( fileWidget->dirLister()->isFinished() ) { if ( !fileWidget->dirLister()->rootItem().isNull() ) { fileWidget->setCurrentItem( file->url() ); QTimer::singleShot( 0, this, SLOT( slotReplayEvent())); } else // finished, but no root-item -- probably an error, kill repeat-item! { abortDelayedEvent(); } } else // not finished yet { fileWidget->setInitialItem( file->url() ); connect( fileWidget, SIGNAL( finished() ), SLOT( slotReplayEvent() )); } return true; } return KXmlGuiWindow::eventFilter( o, e ); } // we definitely have a fileWidget here! if ( key == Qt::Key_Home || KStandardShortcut::begin().contains( k->key() ) ) { item = fileWidget->gotoFirstImage(); item_next = fileWidget->getNext( false ); } else if ( key == Qt::Key_End || KStandardShortcut::end().contains( k->key() ) ) { item = fileWidget->gotoLastImage(); item_next = fileWidget->getPrevious( false ); } else if ( fileWidget->actionCollection()->action("delete")->shortcuts().contains( key )) { // KFileItem *cur = fileWidget->getCurrentItem( false ); (void) fileWidget->getCurrentItem( false ); item = fileWidget->getNext( false ); // don't move if ( item.isNull() ) item = fileWidget->getPrevious( false ); KFileItem it( m_viewer->url() ); KFileItemList list; list.append( it ); if ( fileWidget->del(list, window, (k->modifiers() & Qt::ShiftModifier) == 0) == 0L ) return true; // aborted deletion // ### check failure asynchronously and restore old item? fileWidget->setCurrentItem( item ); } else if ( m_toggleBrowserAction->shortcuts().contains( key ) ) { toggleBrowser(); return true; // don't pass keyEvent } else { ret = false; } if ( FileWidget::isImage( item ) ) { m_viewer->showNextImage( item.url() ); if ( kdata->preloadImage && !item_next.isNull() ) // preload next image if ( FileWidget::isImage( item_next ) ) m_viewer->cacheImage( item_next.url() ); ret = true; // don't pass keyEvent } } // keyPressEvent on ImageWindow // doubleclick closes image window // and shows browser when last window closed via doubleclick else if ( eventType == QEvent::MouseButtonDblClick ) { QMouseEvent *ev = static_cast( e ); if ( ev->button() == Qt::LeftButton ) { if ( s_viewers.count() == 1 ) { if ( !fileWidget ) { initGUI( window->currentFile()->url() ); } show(); raise(); } delete window; ev->accept(); ret = true; } } } // isA ImageWindow if ( ret ) return true; return KXmlGuiWindow::eventFilter( o, e ); } void KuickShow::configuration() { if ( !fileWidget ) { initGUI( QUrl::fromLocalFile(QDir::homePath()) ); } dialog = new KuickConfigDialog( fileWidget->actionCollection(), 0L, false ); dialog->setObjectName(QString::fromLatin1("dialog")); dialog->setWindowIcon( qApp->windowIcon() ); connect( dialog, SIGNAL( okClicked() ), this, SLOT( slotConfigApplied() ) ); connect( dialog, SIGNAL( applyClicked() ), this, SLOT( slotConfigApplied() ) ); connect( dialog, SIGNAL( finished(int) ), this, SLOT( slotConfigClosed() ) ); fileWidget->actionCollection()->action( "kuick_configure" )->setEnabled( false ); dialog->show(); } void KuickShow::slotConfigApplied() { dialog->applyConfig(); initImlib(); kdata->save(); ImageWindow *viewer; QList::ConstIterator it = s_viewers.constBegin(); while ( it != s_viewers.constEnd() ) { viewer = *it; viewer->updateActions(); ++it; } fileWidget->reloadConfiguration(); } void KuickShow::slotConfigClosed() { dialog->deleteLater(); fileWidget->actionCollection()->action( "kuick_configure" )->setEnabled( true ); } void KuickShow::about() { if ( !aboutWidget ) { aboutWidget = new AboutWidget( 0L ); aboutWidget->setObjectName( QString::fromLatin1( "about" ) ); } aboutWidget->adjustSize(); // code copied from KDialog::centerOnScreen() (KDE4) QDesktopWidget* desktop = QApplication::desktop(); QRect screenRect = desktop->screenGeometry(desktop->screenNumber(this)); aboutWidget->move(screenRect.center().x() - aboutWidget->width() / 2, screenRect.center().y() - aboutWidget->height() / 2); aboutWidget->show(); } // ------ sessionmanagement - load / save current directory ----- void KuickShow::readProperties( const KConfigGroup& kc ) { assert( fileWidget ); // from SM, we should always have initGUI on startup QString dir = kc.readPathEntry( "CurrentDirectory", QString() ); if ( !dir.isEmpty() ) { fileWidget->setUrl( QUrl( dir ), true ); fileWidget->clearHistory(); } QUrl listedURL = fileWidget->url(); const QStringList images = kc.readPathEntry( "Images shown", QStringList() ); QStringList::const_iterator it; bool hasCurrentURL = false; for ( it = images.constBegin(); it != images.constEnd(); ++it ) { KFileItem item( (QUrl( *it )) ); if ( item.isReadable() ) { if (showImage( item, true )) { // Set the current URL in the file widget, if possible if ( !hasCurrentURL && listedURL.isParentOf( item.url() )) { fileWidget->setInitialItem( item.url() ); hasCurrentURL = true; } } } } if ( !s_viewers.isEmpty() ) { bool visible = kc.readEntry( "Browser visible", true ); if ( !visible ) hide(); } } void KuickShow::saveProperties( KConfigGroup& kc ) { kc.writeEntry( "Browser visible", fileWidget && fileWidget->isVisible() ); if (fileWidget) kc.writePathEntry( "CurrentDirectory", fileWidget->url().url() ); QStringList urls; QList::ConstIterator it; for ( it = s_viewers.constBegin(); it != s_viewers.constEnd(); ++it ) { const QUrl url = (*it)->currentFile()->url(); if ( url.isLocalFile() ) urls.append( url.path() ); else urls.append( url.toDisplayString() ); // ### check if writePathEntry( prettyUrl ) works! } kc.writePathEntry( "Images shown", urls ); } // -------------------------------------------------------------- void KuickShow::saveSettings() { KSharedConfig::Ptr kc = KSharedConfig::openConfig(); KConfigGroup sessGroup(kc, "SessionSettings"); if ( oneWindowAction ) sessGroup.writeEntry( "OpenImagesInActiveWindow", oneWindowAction->isChecked() ); if ( fileWidget ) { sessGroup.writePathEntry( "CurrentDirectory", fileWidget->url().toDisplayString() ); KConfigGroup group( kc, "Filebrowser" ); fileWidget->writeConfig( group); } kc->sync(); } void KuickShow::messageCantLoadImage( const KuickFile *, const QString& message ) { m_viewer->clearFocus(); KMessageBox::sorry( m_viewer, message, i18n("Image Error") ); } void KuickShow::initImlib() { ImData *idata = kdata->idata; ImlibInitParams par; initImlibParams( idata, &par ); id = Imlib_init_with_params( getX11Display(), &par ); if ( !id ) { initImlibParams( idata, &par ); qWarning("*** KuickShow: Whoops, can't initialize imlib, trying my own palettefile now."); QString paletteFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kuickshow/im_palette.pal"); // FIXME - does the qstrdup() cure the segfault in imlib eventually? char *file = qstrdup( paletteFile.toLocal8Bit() ); par.palettefile = file; par.flags |= PARAMS_PALETTEFILE; qWarning("Palettefile: %s", par.palettefile ); id = Imlib_init_with_params( getX11Display(), &par ); if ( !id ) { QString tmp = i18n("Unable to initialize \"Imlib\".\n" "Start kuickshow from the command line " "and look for error messages.\n" "The program will now quit."); KMessageBox::error( this, tmp, i18n("Fatal Imlib Error") ); FileCache::shutdown(); ::exit(1); } } } void KuickShow::initImlibParams( ImData *idata, ImlibInitParams *par ) { par->flags = ( PARAMS_REMAP | PARAMS_VISUALID | PARAMS_SHAREDMEM | PARAMS_SHAREDPIXMAPS | PARAMS_FASTRENDER | PARAMS_HIQUALITY | PARAMS_DITHER | PARAMS_IMAGECACHESIZE | PARAMS_PIXMAPCACHESIZE ); Visual* defaultvis = DefaultVisual(getX11Display(), getX11Screen()); par->paletteoverride = idata->ownPalette ? 1 : 0; par->remap = idata->fastRemap ? 1 : 0; par->fastrender = idata->fastRender ? 1 : 0; par->hiquality = idata->dither16bit ? 1 : 0; par->dither = idata->dither8bit ? 1 : 0; par->sharedmem = 1; par->sharedpixmaps = 1; par->visualid = defaultvis->visualid; uint maxcache = idata->maxCache; // 0 == no cache par->imagecachesize = maxcache * 1024; par->pixmapcachesize = maxcache * 1024; } bool KuickShow::haveBrowser() const { return fileWidget && fileWidget->isVisible(); } void KuickShow::delayedRepeatEvent( ImageWindow *w, QKeyEvent *e ) { m_delayedRepeatItem = new DelayedRepeatEvent( w, new QKeyEvent( *e ) ); } void KuickShow::abortDelayedEvent() { delete m_delayedRepeatItem; m_delayedRepeatItem = 0L; } void KuickShow::slotReplayEvent() { disconnect( fileWidget, SIGNAL( finished() ), this, SLOT( slotReplayEvent() )); DelayedRepeatEvent *e = m_delayedRepeatItem; m_delayedRepeatItem = 0L; // otherwise, eventFilter aborts eventFilter( e->viewer, e->event ); delete e; // -------------------------------------------------------------- } void KuickShow::replayAdvance(DelayedRepeatEvent *event) { #if 0 // ### WORKAROUND for QIconView bug in Qt <= 3.0.3 at least // Sigh. According to qt-bugs, they won't fix this bug ever. So you can't // rely on sorting to be correct before the QIconView has been show()n. if ( fileWidget && fileWidget->view() ) { QWidget *widget = fileWidget->view()->widget(); if ( widget->inherits( "QIconView" ) || widget->child(0, "QIconView" ) ){ fileWidget->setSorting( fileWidget->sorting() ); } } #endif // -------------------------------------------------------------- slotAdvanceImage( event->viewer, *(int *) (event->data) ); } void KuickShow::delayAction(DelayedRepeatEvent *event) { if (m_delayedRepeatItem) return; m_delayedRepeatItem = event; QUrl url = event->viewer->currentFile()->url(); // QFileInfo fi( event->viewer->filename() ); // start.setPath( fi.dirPath( true ) ); initGUI( KIO::upUrl(url) ); // see eventFilter() for explanation and similar code if ( fileWidget->dirLister()->isFinished() && !fileWidget->dirLister()->rootItem().isNull() ) { fileWidget->setCurrentItem( url ); QTimer::singleShot( 0, this, SLOT( doReplay())); } else { fileWidget->setInitialItem( url ); connect( fileWidget, SIGNAL( finished() ), SLOT( doReplay() )); } } void KuickShow::doReplay() { if (!m_delayedRepeatItem) return; disconnect( fileWidget, SIGNAL( finished() ), this, SLOT( doReplay() )); switch (m_delayedRepeatItem->action) { case DelayedRepeatEvent::DeleteCurrentFile: performDeleteCurrentImage((QWidget *) m_delayedRepeatItem->data); break; case DelayedRepeatEvent::TrashCurrentFile: performTrashCurrentImage((QWidget *) m_delayedRepeatItem->data); break; case DelayedRepeatEvent::AdvanceViewer: replayAdvance(m_delayedRepeatItem); break; default: qWarning("doReplay: unknown action -- ignoring: %d", m_delayedRepeatItem->action); break; } delete m_delayedRepeatItem; m_delayedRepeatItem = 0L; } void KuickShow::toggleBrowser() { if ( !haveBrowser() ) { if ( m_viewer && m_viewer->isFullscreen() ) m_viewer->setFullscreen( false ); fileWidget->resize( size() ); // ### somehow fileWidget isn't resized!? show(); raise(); KWindowSystem::activateWindow( winId() ); // ### this should not be necessary // setFocus(); } else if ( !s_viewers.isEmpty() ) hide(); } void KuickShow::slotOpenURL() { OpenFilesAndDirsDialog dlg(this, i18n("Select Files or Folder to Open")); dlg.setNameFilter(i18n("Image Files (%1)").arg(kdata->fileFilter)); if(dlg.exec() != QDialog::Accepted) return; QList urls = dlg.selectedUrls(); if(urls.isEmpty()) return; for( auto url : urls ) { KFileItem item( url ); if ( FileWidget::isImage( item ) ) showImage( item, true ); else fileWidget->setUrl( url, true ); } } void KuickShow::slotToggleInlinePreview(bool on) { int iconSize; if (on) { iconSize = KIconLoader::SizeEnormous; } else { iconSize = KIconLoader::SizeSmall; } fileWidget->setIconsZoom( iconSize ); fileWidget->setInlinePreviewShown(on); QAction *defaultInlinePreview = fileWidget->actionCollection()->action( "inline preview" ); defaultInlinePreview->setChecked(on); // fileWidget->actionCollection("short view") } void KuickShow::deleteAllViewers() { QList::Iterator it = s_viewers.begin(); for ( ; it != s_viewers.end(); ++it ) { (*it)->disconnect( SIGNAL( destroyed() ), this, SLOT( viewerDeleted() )); delete (*it); } s_viewers.clear(); m_viewer = 0L; } KActionCollection * KuickShow::actionCollection() const { if ( fileWidget ) return fileWidget->actionCollection(); return KXmlGuiWindow::actionCollection(); } int KuickShow::getX11Screen() const { return QApplication::desktop()->screenNumber(this); } diff --git a/src/printing.cpp b/src/printing.cpp index 5355afd..bddbc91 100644 --- a/src/printing.cpp +++ b/src/printing.cpp @@ -1,327 +1,327 @@ /* This file is part of the KDE project Copyright (C) 2002 Carsten Pfeiffer 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "printing.h" #include #include -#include #include #include #include #include #include #include #include #include #include #include #include "filecache.h" #include "imagewindow.h" +#include "kuickshow_debug.h" #include "version.h" bool Printing::printImage( ImageWindow& imageWin, QWidget *parent ) { QString imageURL = imageWin.url().toDisplayString(); QPrinter printer; printer.setDocName( imageURL ); printer.setCreator( "KuickShow-" KUICKSHOWVERSION ); KuickPrintDialogPage* dialogPage = new KuickPrintDialogPage( parent ); dialogPage->setObjectName(QString::fromLatin1("kuick page")); QPrintDialog *printDialog = new QPrintDialog(&printer, parent); printDialog->setOptionTabs(QList() << dialogPage); printDialog->setWindowTitle(i18n("Print %1", printer.docName().section('/', -1))); if (printDialog->exec()) { QScopedPointer tmpFilePtr(FileCache::self()->createTempFile(QStringLiteral(".png"))); if ( tmpFilePtr->open() ) { if ( imageWin.saveImage( QUrl::fromLocalFile(tmpFilePtr->fileName()), true ) ) { bool success = printImageWithQt( tmpFilePtr->fileName(), printer, *dialogPage, imageURL); delete printDialog; return success; } } delete printDialog; return false; } delete printDialog; return true; // user aborted } bool Printing::printImageWithQt( const QString& filename, QPrinter& printer, KuickPrintDialogPage& dialogPage, const QString& originalFileName ) { QImage image( filename ); if ( image.isNull() ) { qWarning("Can't load image: %s for printing.", qUtf8Printable(filename)); return false; } QPainter p; p.begin( &printer ); p.setFont( QFontDatabase::systemFont(QFontDatabase::GeneralFont) ); QFontMetrics fm = p.fontMetrics(); int w = printer.width(); int h = printer.height(); // Black & white print? if ( dialogPage.printBlackWhite() ) { image = image.convertToFormat( QImage::Format_Mono, Qt::MonoOnly | Qt::ThresholdDither | Qt::AvoidDither ); } int filenameOffset = 0; bool printFilename = dialogPage.printFilename(); if ( printFilename ) { filenameOffset = fm.lineSpacing() + 14; h -= filenameOffset; // filename goes into one line! } // // shrink image to pagesize, if necessary // bool shrinkToFit = dialogPage.printShrinkToFit(); QSize imagesize = image.size(); if ( shrinkToFit && (image.width() > w || image.height() > h) ) { imagesize.scale( w, h, Qt::KeepAspectRatio ); } // // align image // // not currently implemented in print options //bool ok = false; //int alignment = printer.option("app-kuickshow-alignment").toInt( &ok ); //if ( !ok ) int alignment = Qt::AlignCenter; // default int x = 0; int y = 0; // ### need a GUI for this in KuickPrintDialogPage! // x - alignment if ( alignment & Qt::AlignHCenter ) x = (w - imagesize.width())/2; else if ( alignment & Qt::AlignLeft ) x = 0; else if ( alignment & Qt::AlignRight ) x = w - imagesize.width(); // y - alignment if ( alignment & Qt::AlignVCenter ) y = (h - imagesize.height())/2; else if ( alignment & Qt::AlignTop ) y = 0; else if ( alignment & Qt::AlignBottom ) y = h - imagesize.height(); // // perform the actual drawing // p.drawImage( QRect( x, y, imagesize.width(), imagesize.height()), image ); if ( printFilename ) { QString fname = minimizeString( originalFileName, fm, w ); if ( !fname.isEmpty() ) { int fw = fm.width( fname ); int x = (w - fw)/2; int y = printer.height() - filenameOffset/2; p.drawText( x, y, fname ); } } p.end(); return true; } QString Printing::minimizeString( QString text, const QFontMetrics& metrics, int maxWidth ) { if ( text.length() <= 5 ) return QString::null; // no sense to cut that tiny little string bool changed = false; while ( metrics.width( text ) > maxWidth ) { int mid = text.length() / 2; text.remove( mid, 2 ); // remove 2 characters in the middle changed = true; } if ( changed ) // add "..." in the middle { int mid = text.length() / 2; if ( mid <= 5 ) // sanity check return QString::null; text.replace( mid - 1, 3, "..." ); } return text; } /////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// KuickPrintDialogPage::KuickPrintDialogPage( QWidget *parent ) : QWidget( parent ) { ui = new Ui::KuickPrintDialogPage; ui->setupUi(this); connect( ui->scale, SIGNAL( toggled( bool )), SLOT( toggleScaling( bool ))); } KuickPrintDialogPage::~KuickPrintDialogPage() { delete ui; } // ### opts["app-kuickshow-alignment"] = ; bool KuickPrintDialogPage::printFilename() { return ui->addFileName->isChecked(); } void KuickPrintDialogPage::setPrintFilename( bool addFilename ) { ui->addFileName->setChecked( addFilename ); } bool KuickPrintDialogPage::printBlackWhite() { return ui->blackwhite->isChecked(); } void KuickPrintDialogPage::setPrintBlackWhite( bool blackWhite ) { ui->blackwhite->setChecked( blackWhite ); } bool KuickPrintDialogPage::printShrinkToFit() { return ui->shrinkToFit->isChecked(); } void KuickPrintDialogPage::setPrintShrinkToFit( bool shrinkToFit ) { toggleScaling( !shrinkToFit ); } bool KuickPrintDialogPage::printScale() { return ui->scale->isChecked(); } void KuickPrintDialogPage::setPrintScale( bool scale ) { toggleScaling( scale ); } QString KuickPrintDialogPage::printScaleUnit() { return ui->units->currentText(); } void KuickPrintDialogPage::setPrintScaleUnit( QString scaleUnit ) { for(int i = 0, count = ui->units->count(); i < count; i++) { if(ui->units->itemText(i) == scaleUnit) { ui->units->setCurrentIndex(i); return; } } ui->units->setCurrentIndex(-1); } int KuickPrintDialogPage::printScaleWidthPixels() { return scaleWidth(); } void KuickPrintDialogPage::setPrintScaleWidthPixels( int scaleWidth ) { setScaleWidth(scaleWidth); } int KuickPrintDialogPage::printScaleHeightPixels() { return scaleHeight(); } void KuickPrintDialogPage::setPrintScaleHeightPixels( int scaleHeight ) { setScaleHeight(scaleHeight); } void KuickPrintDialogPage::toggleScaling( bool enable ) { ui->shrinkToFit->setChecked( !enable ); ui->scale->setChecked( enable ); ui->width->setEnabled( enable ); ui->height->setEnabled( enable ); ui->units->setEnabled( enable ); } int KuickPrintDialogPage::scaleWidth() const { return fromUnitToPixels( ui->width->value() ); } int KuickPrintDialogPage::scaleHeight() const { return fromUnitToPixels( ui->height->value() ); } void KuickPrintDialogPage::setScaleWidth( int pixels ) { ui->width->setValue( (int) pixelsToUnit( pixels ) ); } void KuickPrintDialogPage::setScaleHeight( int pixels ) { ui->width->setValue( (int) pixelsToUnit( pixels ) ); } int KuickPrintDialogPage::fromUnitToPixels( float /*value*/ ) const { return 1; // ### } float KuickPrintDialogPage::pixelsToUnit( int /*pixels*/ ) const { return 1.0; // ### }