diff options
Diffstat (limited to 'kuickshow/src')
46 files changed, 8009 insertions, 0 deletions
diff --git a/kuickshow/src/Makefile.am b/kuickshow/src/Makefile.am new file mode 100644 index 00000000..57c92388 --- /dev/null +++ b/kuickshow/src/Makefile.am @@ -0,0 +1,31 @@ +## Makefile.am for kuickshow + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = kuickshow.la + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) +KDE_CXXFLAGS = $(IMLIB_CFLAGS) +METASOURCES = AUTO + +kuickshow_la_LDFLAGS = $(all_libraries) -module -avoid-version +kuickshow_la_LIBADD = $(LIB_KDEPRINT) $(LIB_IMLIB) +kuickshow_la_SOURCES = kuickshow.cpp \ + aboutwidget.cpp generalwidget.cpp kuickconfigdlg.cpp main.cpp \ + defaultswidget.cpp imagewindow.cpp kuickdata.cpp \ + imdata.cpp filefinder.cpp kurlwidget.cpp filewidget.cpp \ + kuick.cpp imlibwidget.cpp slideshowwidget.cpp printing.cpp \ + kuickfile.cpp kuickimage.cpp filecache.cpp + +# if you "make distclean", this files get removed. If you want to remove +# them while "make clean", use CLEANFILES +DISTCLEANFILES = $(METASOURCES) + +messages: + $(XGETTEXT) *.cpp *.h -o $(podir)/kuickshow.pot + +KDE_ICON = kuickshow + + # this is where the kdelnk file will go +xdg_apps_DATA = kuickshow.desktop diff --git a/kuickshow/src/aboutwidget.cpp b/kuickshow/src/aboutwidget.cpp new file mode 100644 index 00000000..2ae41102 --- /dev/null +++ b/kuickshow/src/aboutwidget.cpp @@ -0,0 +1,95 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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 <qdatetime.h> +#include <qevent.h> +#include <qglobal.h> +#include <qgroupbox.h> +#include <qlabel.h> + +#include <kwin.h> +#include <kstandarddirs.h> + +#include "imlibwidget.h" +#include "kurlwidget.h" +#include "version.h" + +#include "aboutwidget.h" + +AboutWidget::AboutWidget( QWidget *parent, const char *name ) + : QVBox( parent, name, Qt::WShowModal ) +{ + KWin::setType( winId(), NET::Override ); + KWin::setState( winId(), NET::SkipTaskbar ); + + setFrameStyle( WinPanel | Raised ); + + QGroupBox *gBox = new QGroupBox( 1, Horizontal, this); + gBox->setGeometry( 10, 10, width()-20, height()-20 ); + gBox->setAlignment( AlignHCenter ); + gBox->installEventFilter( this ); + + gBox->setPalette( QPalette( QColor( white ) ) ); + gBox->setBackgroundMode( PaletteBackground ); + + int hour = QTime::currentTime().hour(); + QString file; + + if ( hour >= 10 && hour < 16 ) + file = locate("appdata", "pics/kuickshow-day.jpg"); + else + file = locate("appdata", "pics/kuickshow-night.jpg"); + + QLabel *authors = new QLabel("Kuickshow " KUICKSHOWVERSION + " was brought to you by", gBox); + authors->setAlignment( AlignCenter ); + + m_homepage = new KURLWidget("Carsten Pfeiffer", gBox); + m_homepage->setURL( "http://devel-home.kde.org/~pfeiffer/kuickshow/" ); + m_homepage->setAlignment( AlignCenter ); + + QLabel *copy = new QLabel("(C) 1998-2006", gBox); + copy->setAlignment( AlignCenter ); + + ImlibWidget *im = new ImlibWidget( 0L, gBox, "KuickShow Logo" ); + if ( im->loadImage( file ) ) + im->setFixedSize( im->width(), im->height() ); + else { + delete im; + im = 0L; + qWarning( "KuickShow: about-image not found/unreadable." ); + } +} + +AboutWidget::~AboutWidget() +{ +} + +bool AboutWidget::eventFilter( QObject *o, QEvent *e ) +{ + if ( e->type() == QEvent::MouseButtonPress ) { + QMouseEvent *ev = static_cast<QMouseEvent*>( e ); + if ( !m_homepage->geometry().contains( ev->pos() ) ) { + deleteLater(); + return true; + } + } + + return QVBox::eventFilter( o, e ); +} +#include "aboutwidget.moc" diff --git a/kuickshow/src/aboutwidget.h b/kuickshow/src/aboutwidget.h new file mode 100644 index 00000000..b9bc79d1 --- /dev/null +++ b/kuickshow/src/aboutwidget.h @@ -0,0 +1,43 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef ABOUTWIDGET_H +#define ABOUTWIDGET_H + +#include <qevent.h> +#include <qvbox.h> + +class KURLWidget; + +class AboutWidget : public QVBox +{ + Q_OBJECT + +public: + AboutWidget(QWidget *parent = 0, const char *name = 0); + +protected: + ~AboutWidget(); + bool eventFilter( QObject*, QEvent * ); + +private: + KURLWidget *m_homepage; + +}; + +#endif diff --git a/kuickshow/src/defaultswidget.cpp b/kuickshow/src/defaultswidget.cpp new file mode 100644 index 00000000..69807251 --- /dev/null +++ b/kuickshow/src/defaultswidget.cpp @@ -0,0 +1,282 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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 <qcheckbox.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qvgroupbox.h> + +#include <kcombobox.h> +#include <kdialog.h> +#include <klocale.h> +#include <knuminput.h> +#include <kstandarddirs.h> + +#include "imlibwidget.h" +#include "defaultswidget.h" + +DefaultsWidget::DefaultsWidget( QWidget *parent, const char *name) + : QWidget( parent, name ) +{ + imFiltered = 0L; + + cbEnableMods = new QCheckBox( i18n("Apply default image modifications"), this ); + connect( cbEnableMods, SIGNAL( toggled(bool) ), SLOT( enableWidgets(bool) )); + + // create all the widgets + + gbScale = new QGroupBox( i18n("Scaling"), this ); + gbScale->setColumnLayout( 0, Qt::Horizontal ); + + cbDownScale = new QCheckBox( i18n("Shrink image to screen size, if larger"), + gbScale, "shrinktoscreen" ); + + cbUpScale = new QCheckBox( i18n("Scale image to screen size, if smaller, up to factor:"), gbScale, "upscale checkbox" ); + + sbMaxUpScaleFactor = new KIntNumInput( gbScale, "upscale factor" ); + sbMaxUpScaleFactor->setRange( 1, 100, 1, false ); + + connect(cbUpScale, SIGNAL( toggled(bool)), sbMaxUpScaleFactor, + SLOT( setEnabled(bool) )); + + // -- + + gbGeometry = new QGroupBox( i18n("Geometry"), this ); + gbGeometry->setColumnLayout( 0, Qt::Horizontal ); + + cbFlipVertically = new QCheckBox( i18n("Flip vertically"), gbGeometry ); + + cbFlipHorizontally = new QCheckBox( i18n("Flip horizontally"), gbGeometry ); + + lbRotate = new QLabel( i18n("Rotate image:"), gbGeometry ); + + comboRotate = new KComboBox( gbGeometry, "rotate combobox" ); + comboRotate->insertItem( i18n("0 Degrees") ); + comboRotate->insertItem( i18n("90 Degrees") ); + comboRotate->insertItem( i18n("180 Degrees") ); + comboRotate->insertItem( i18n("270 Degrees") ); + + // -- + + gbAdjust = new QVGroupBox( i18n("Adjustments"), this ); + + sbBrightness = new KIntNumInput( gbAdjust, "brightness spinbox" ); + sbBrightness->setRange( -256, 256, 1, true ); + sbBrightness->setLabel( i18n("Brightness:"), AlignVCenter ); + + sbContrast = new KIntNumInput( sbBrightness, 0,gbAdjust, 10, + "contrast spinbox"); + sbContrast->setRange( -256, 256, 1, true ); + sbContrast->setLabel( i18n("Contrast:"), AlignVCenter ); + + sbGamma = new KIntNumInput( sbContrast, 0, gbAdjust, 10, "gamma spinbox" ); + sbGamma->setRange( -256, 256, 1, true ); + sbGamma->setLabel( i18n("Gamma:"), AlignVCenter ); + + // -- + + gbPreview = new QGroupBox( i18n("Preview"), this ); + gbPreview->setAlignment( AlignCenter ); + + lbImOrig = new QLabel( i18n("Original"), gbPreview ); + imOrig = new ImlibWidget( 0L, gbPreview, "original image" ); + + lbImFiltered = new QLabel( i18n("Modified"), gbPreview ); + imFiltered = new ImlibWidget( 0L, imOrig->getImlibData(), gbPreview, "" ); + connect( imFiltered, SIGNAL( destroyed() ), SLOT( slotNoImage() )); + + //// + //////////////// + + + // layout management + QVBoxLayout *mainLayout = new QVBoxLayout( this, 0, + KDialog::spacingHint(), "main layout" ); + + QVBoxLayout *gbScaleLayout = new QVBoxLayout( gbScale->layout(), + KDialog::spacingHint()); + QVBoxLayout *gbGeometryLayout = new QVBoxLayout(gbGeometry->layout(), + KDialog::spacingHint()); + QGridLayout *gbPreviewLayout = new QGridLayout(gbPreview, 2, 3, 0, + KDialog::spacingHint()); + + QHBoxLayout *scaleLayout = new QHBoxLayout(); + QHBoxLayout *rotateLayout = new QHBoxLayout(); + + mainLayout->addWidget( cbEnableMods ); + mainLayout->addWidget( gbScale ); + QHBoxLayout *hl = new QHBoxLayout(); + hl->addWidget( gbGeometry ); + hl->addWidget( gbAdjust ); + mainLayout->addLayout( hl ); + mainLayout->addWidget( gbPreview ); + mainLayout->addStretch(); + + // -- + + gbScaleLayout->addWidget( cbDownScale ); + gbScaleLayout->addLayout( scaleLayout ); + + scaleLayout->addWidget( cbUpScale ); + scaleLayout->addWidget( sbMaxUpScaleFactor ); + + // -- + + gbGeometryLayout->addWidget( cbFlipVertically, 0, AlignLeft ); + gbGeometryLayout->addWidget( cbFlipHorizontally, 0, AlignLeft ); + gbGeometryLayout->addLayout( rotateLayout, 0 ); + + rotateLayout->addWidget( lbRotate, 0, AlignLeft ); + rotateLayout->addWidget( comboRotate, 0, AlignLeft ); + + // -- + + gbPreviewLayout->setMargin( 10 ); + gbPreviewLayout->setSpacing( KDialog::spacingHint() ); + gbPreviewLayout->addWidget( lbImOrig, 0, 0, AlignCenter ); + gbPreviewLayout->addWidget( imOrig, 1, 0, AlignCenter | AlignTop ); + gbPreviewLayout->addWidget( lbImFiltered, 0, 2, AlignCenter ); + gbPreviewLayout->addWidget( imFiltered, 1, 2, AlignCenter | AlignTop ); + + + //// + //////////////// + + // connect them all to the update slot + connect( cbDownScale, SIGNAL( clicked() ), SLOT( updatePreview() )); + connect( cbUpScale, SIGNAL( clicked() ), SLOT( updatePreview() )); + connect( cbFlipVertically, SIGNAL( clicked() ), SLOT( updatePreview() )); + connect( cbFlipHorizontally, SIGNAL( clicked() ), SLOT( updatePreview() )); + connect( sbMaxUpScaleFactor, SIGNAL( valueChanged(int) ), SLOT( updatePreview() )); + connect( sbBrightness, SIGNAL( valueChanged(int) ), SLOT( updatePreview() )); + connect( sbContrast, SIGNAL( valueChanged(int) ), SLOT( updatePreview() )); + connect( sbGamma, SIGNAL( valueChanged(int) ), SLOT( updatePreview() )); + + connect( comboRotate, SIGNAL( activated(int) ), SLOT( updatePreview() )); + + + QString filename = locate( "data", "kuickshow/pics/calibrate.png" ); + if ( !imOrig->loadImage( filename ) ) + imOrig = 0L; // FIXME - display some errormessage! + if ( !imFiltered->loadImage( filename ) ) + imFiltered = 0L; // FIXME - display some errormessage! + + loadSettings( *kdata ); + + if ( imOrig ) + imOrig->setFixedSize( imOrig->size() ); + if ( imFiltered ) + imFiltered->setFixedSize( imFiltered->size() ); + + mainLayout->activate(); +} + + +DefaultsWidget::~DefaultsWidget() +{ + // those need to be deleted in the right order, as imFiltered + // references ImlibData from imOrig + delete imFiltered; + delete imOrig; +} + +void DefaultsWidget::loadSettings( const KuickData& data ) +{ + cbDownScale->setChecked( data.downScale ); + cbUpScale->setChecked( data.upScale ); + sbMaxUpScaleFactor->setValue( data.maxUpScale ); + + cbFlipVertically->setChecked( data.flipVertically ); + cbFlipHorizontally->setChecked( data.flipHorizontally ); + + comboRotate->setCurrentItem( data.rotation ); + + ImData *id = data.idata; + + sbBrightness->setValue( id->brightness ); + sbContrast->setValue( id->contrast ); + sbGamma->setValue( id->gamma ); + + cbEnableMods->setChecked( data.isModsEnabled ); + enableWidgets( data.isModsEnabled ); + + updatePreview(); +} + +void DefaultsWidget::applySettings( KuickData& data ) +{ + data.isModsEnabled = cbEnableMods->isChecked(); + + data.downScale = cbDownScale->isChecked(); + data.upScale = cbUpScale->isChecked(); + data.maxUpScale = sbMaxUpScaleFactor->value(); + + data.flipVertically = cbFlipVertically->isChecked(); + data.flipHorizontally = cbFlipHorizontally->isChecked(); + + data.rotation = currentRotation(); + + ImData *id = data.idata; + + id->brightness = sbBrightness->value(); + id->contrast = sbContrast->value(); + id->gamma = sbGamma->value(); +} + +void DefaultsWidget::updatePreview() +{ + if ( !imFiltered ) + return; + + imFiltered->setAutoRender( false ); + + int flipMode = cbFlipHorizontally->isChecked() ? FlipHorizontal : FlipNone; + flipMode |= cbFlipVertically->isChecked() ? FlipVertical : FlipNone; + imFiltered->setFlipMode( flipMode ); + + Rotation rotation = cbEnableMods->isChecked() ? currentRotation() : ROT_0; + imFiltered->setRotation( rotation ); + + imFiltered->setBrightness( sbBrightness->value() ); + imFiltered->setContrast( sbContrast->value() ); + imFiltered->setGamma( sbGamma->value() ); + + imFiltered->updateImage(); + imFiltered->setAutoRender( true ); +} + + +void DefaultsWidget::enableWidgets( bool enable ) +{ + gbScale->setEnabled( enable ); + sbMaxUpScaleFactor->setEnabled( enable & cbUpScale->isChecked() ); + + gbGeometry->setEnabled( enable ); + gbAdjust->setEnabled( enable ); + gbPreview->setEnabled( enable ); + updatePreview(); +} + + +Rotation DefaultsWidget::currentRotation() const +{ + return (Rotation) comboRotate->currentItem(); +} + +#include "defaultswidget.moc" diff --git a/kuickshow/src/defaultswidget.h b/kuickshow/src/defaultswidget.h new file mode 100644 index 00000000..3febb098 --- /dev/null +++ b/kuickshow/src/defaultswidget.h @@ -0,0 +1,73 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef DEFAULTSWIDGET_H +#define DEFAULTSWIDGET_H + +#include "kuickdata.h" + +class ImlibWidget; +class ImData; + +class QCheckBox; +class QLabel; + +class KComboBox; +class KIntNumInput; + +class DefaultsWidget : public QWidget +{ + Q_OBJECT + +public: + DefaultsWidget( QWidget *parent, const char *name ); + ~DefaultsWidget(); + + void loadSettings( const KuickData& data ); + void applySettings( KuickData& data ); + +private: + Rotation currentRotation() const; + + QCheckBox *cbEnableMods; + + QGroupBox *gbScale; + QCheckBox *cbUpScale, *cbDownScale; + KIntNumInput *sbMaxUpScaleFactor; + + QGroupBox *gbAdjust; + KIntNumInput *sbBrightness, *sbContrast, *sbGamma; + + QGroupBox *gbGeometry; + QLabel *lbRotate; + KComboBox *comboRotate; + QCheckBox *cbFlipVertically, *cbFlipHorizontally; + + QGroupBox *gbPreview; + QLabel *lbImOrig, *lbImFiltered; + ImlibWidget *imOrig, *imFiltered; + + +private slots: + void updatePreview(); + void slotNoImage() { imFiltered = 0L; } + void enableWidgets( bool ); + +}; + +#endif diff --git a/kuickshow/src/filecache.cpp b/kuickshow/src/filecache.cpp new file mode 100644 index 00000000..c1c1affe --- /dev/null +++ b/kuickshow/src/filecache.cpp @@ -0,0 +1,83 @@ +#include <unistd.h> + +#include <qstring.h> + +#include <kdebug.h> +#include <kstandarddirs.h> +#include <ktempdir.h> + +#include "filecache.h" + +FileCache * FileCache::s_self; + +FileCache::FileCache() + : m_limit( 0 ), + m_tempDir( 0L ) +{ + m_files.setAutoDelete( true ); + 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 KURL& url ) +{ + QString urlString = url.prettyURL(); + KuickFile *file = m_files.find( 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 ) { + kdWarning() << "Unable to create temporary directory for KuickShow" << endl; + return QString::null; + } + } + + return m_tempDir->name(); +} + + +KTempDir * FileCache::createTempDir() +{ + QString tmpName = QString::fromLatin1( KGlobal::instance()->instanceName() ); + tmpName.append( QString::number( getpid() ) ); + QString dirName = locateLocal( "tmp", tmpName ); + KTempDir *dir = new KTempDir( dirName ); + dir->setAutoDelete( true ); + if ( dir->status() != 0L ) + { + delete dir; + return 0L; + } + + return dir; +} diff --git a/kuickshow/src/filecache.h b/kuickshow/src/filecache.h new file mode 100644 index 00000000..16e53261 --- /dev/null +++ b/kuickshow/src/filecache.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** $Id: .emacs,v 1.3 2006/02/20 15:06:53 gis Exp $ +** +** Created : 2006 +** +** Copyright (C) 2006 Carsten Pfeiffer <[email protected]> +** +****************************************************************************/ + +#ifndef FILECACHE_H +#define FILECACHE_H + +#include <qcache.h> + +#include "kuickfile.h" + +class KTempDir; + +class FileCache +{ +public: + static FileCache * self(); + static void shutdown(); + + KuickFile * getFile( const KURL& url ); + void setLimit( int numFiles ); + int getLimit() const { return m_limit; } + + /** + * @return the temporary directory or QString::null if none available + */ + QString tempDir(); + +private: + static FileCache *s_self; + FileCache(); + ~FileCache(); + + KTempDir * createTempDir(); + QCache<KuickFile> m_files; + + int m_limit; + KTempDir *m_tempDir; + +}; + +#endif // FILECACHE_H diff --git a/kuickshow/src/filefinder.cpp b/kuickshow/src/filefinder.cpp new file mode 100644 index 00000000..9dda0046 --- /dev/null +++ b/kuickshow/src/filefinder.cpp @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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 <qkeycode.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kcompletionbox.h> +#include <kurlcompletion.h> + +#include "filefinder.h" + +FileFinder::FileFinder( QWidget *parent, const char *name ) + : KLineEdit( parent, name ) +{ + // make this widget just as large, as the font is + 8 Pixels + int height = fontMetrics().height() + 8; + setFixedSize( 150, height ); + setFrame( true ); + + setHandleSignals( true ); // we want the completionbox signals + completionBox()->setTabHandling( true ); + + connect( completionBox(), SIGNAL( userCancelled(const QString&) ), + SLOT( hide() )); + connect( completionBox(), SIGNAL( activated( const QString& ) ), + SLOT( slotAccept( const QString& ))); + connect( this, SIGNAL( returnPressed( const QString& )), + SLOT( slotAccept( const QString& ) )); + + KURLCompletion *comp = new KURLCompletion(); + comp->setReplaceHome( true ); + comp->setReplaceEnv( true ); + setCompletionObject( comp, false ); + setAutoDeleteCompletionObject( true ); + setFocusPolicy( ClickFocus ); + + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, "GeneralConfiguration" ); + setCompletionMode( (KGlobalSettings::Completion) + config->readNumEntry( "FileFinderCompletionMode", + KGlobalSettings::completionMode())); +} + +FileFinder::~FileFinder() +{ + KConfig *config = KGlobal::config(); + KConfigGroupSaver cs( config, "GeneralConfiguration" ); + config->writeEntry( "FileFinderCompletionMode", completionMode() ); +} + +void FileFinder::focusOutEvent( QFocusEvent *e ) +{ + if ( e->reason() != QFocusEvent::Popup ) + hide(); +} + +void FileFinder::keyPressEvent( QKeyEvent *e ) +{ + int key = e->key(); + if ( key == Key_Escape ) { + hide(); + e->accept(); + } + + else { + KLineEdit::keyPressEvent( e ); + } +} + +void FileFinder::hide() +{ + KLineEdit::hide(); + parentWidget()->setFocus(); +} + +void FileFinder::slotAccept( const QString& dir ) +{ + hide(); + emit enterDir( dir ); +} + +#include "filefinder.moc" diff --git a/kuickshow/src/filefinder.h b/kuickshow/src/filefinder.h new file mode 100644 index 00000000..f75c69a4 --- /dev/null +++ b/kuickshow/src/filefinder.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef FILEFINDER_H +#define FILEFINDER_H + +#include <qevent.h> + +#include <klineedit.h> + +class KURLCompletion; + +class FileFinder : public KLineEdit +{ + Q_OBJECT + +public: + FileFinder( QWidget *parent=0, const char *name=0 ); + ~FileFinder(); + + KURLCompletion *completion() { + return static_cast<KURLCompletion*>( completionObject() ); + } + + virtual void hide(); + +signals: + void enterDir( const QString& ); + +protected: + virtual void focusOutEvent( QFocusEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + +private slots: + void slotAccept( const QString& ); + +}; + +#endif // FILEFINDER_H diff --git a/kuickshow/src/filewidget.cpp b/kuickshow/src/filewidget.cpp new file mode 100644 index 00000000..d4b99f0a --- /dev/null +++ b/kuickshow/src/filewidget.cpp @@ -0,0 +1,464 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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 <qkeycode.h> + +#include <kdeversion.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kpropertiesdialog.h> +#include <kurlcompletion.h> + +#include "filefinder.h" +#include "filewidget.h" +#include "kuickdata.h" +#include "kuickshow.h" + +#ifdef KeyPress +#undef KeyPress +#endif + +FileWidget::FileWidget( const KURL& url, QWidget *parent, const char *name ) + : KDirOperator( url, parent, name ), + m_validCompletion( false ), + m_fileFinder( 0L ) +{ + setEnableDirHighlighting( true ); + +#if KDE_VERSION >= 310 + setViewConfig( KGlobal::config(), "Filebrowser" ); +#endif + readConfig( KGlobal::config(), "Filebrowser" ); + setView( KFile::Default ); + + // setOnlyDoubleClickSelectsFiles( true ); + reloadConfiguration(); + + completionObject()->setCompletionMode( KGlobalSettings::CompletionAuto ); + dirCompletionObject()->setCompletionMode( KGlobalSettings::CompletionAuto); + + slotViewChanged(); + connect( this, SIGNAL( viewChanged( KFileView * )), + SLOT( slotViewChanged() )); + + connect( dirLister(), SIGNAL( clear() ), SLOT( slotItemsCleared() )); + connect( dirLister(), SIGNAL( deleteItem( KFileItem * ) ), + SLOT( slotItemDeleted( KFileItem *) )); + + connect( this, SIGNAL( fileHighlighted( const KFileItem * )), + SLOT( slotHighlighted( const KFileItem * ))); + + connect( this, SIGNAL(urlEntered(const KURL&)), + SLOT( slotURLEntered( const KURL& ))); + + // should actually be KDirOperator's job! + connect( this, SIGNAL( finishedLoading() ), SLOT( slotFinishedLoading() )); +} + +FileWidget::~FileWidget() +{ + delete m_fileFinder; +} + +void FileWidget::initActions() +{ + int index = 0; + KActionCollection *coll = actionCollection(); + KActionSeparator *sep = new KActionSeparator( coll, "kuicksep" ); + KActionMenu *menu = static_cast<KActionMenu*>( coll->action("popupMenu") ); + + menu->insert( coll->action("kuick_showInOtherWindow"), index++ ); + menu->insert( coll->action("kuick_showInSameWindow"), index++ ); + menu->insert( coll->action("kuick_showFullscreen"), index++ ); + menu->insert( sep, index++ ); + + // support for older kdelibs, remove somewhen... + if ( coll->action("kuick_delete") ) + menu->insert( coll->action("kuick_delete"), 9 ); + + // properties dialog is now in kfile, but not at the right position, + // so we move it to the real bottom + menu->remove( coll->action( "properties" ) ); + + QPopupMenu *pMenu = menu->popupMenu(); + int lastItemId = pMenu->idAt( pMenu->count() - 1 ); + QMenuItem *mItem = pMenu->findItem( lastItemId ); + if ( mItem && !mItem->isSeparator() ) + menu->insert( sep ); + + // those at the bottom + menu->insert( coll->action("kuick_print") ); + menu->insert( sep ); + menu->insert( 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! + KMimeType::List l = KMimeType::allMimeTypes(); + for (KMimeType::List::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::activatedMenu( const KFileItem *item, const QPoint& pos ) +{ + 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 ); + actionCollection()->action("properties")->setEnabled( item ); + + bool hasSelection = (item != 0L); + if ( actionCollection()->action("kuick_delete") ) + actionCollection()->action("kuick_delete")->setEnabled( hasSelection ); + + KDirOperator::activatedMenu( item, pos ); +} + +void FileWidget::findCompletion( const QString& text ) +{ + if ( text.at(0) == '/' || text.at(0) == '~' || + text.find('/') != -1 ) { + QString t = m_fileFinder->completion()->makeCompletion( text ); + + if (m_fileFinder->completionMode() == KGlobalSettings::CompletionPopup || + m_fileFinder->completionMode() == KGlobalSettings::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 ) + KDirOperator::setCurrentItem( file ); +} + +bool FileWidget::eventFilter( QObject *o, QEvent *e ) +{ + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *k = static_cast<QKeyEvent*>( e ); + + if ( (k->state() & (ControlButton | AltButton)) == 0 ) { + int key = k->key(); + if ( actionCollection()->action("delete")->shortcut().contains( key ) ) + { + k->accept(); + KFileItem *item = getCurrentItem( false ); + if ( item ) { + KFileItemList list; + list.append( item ); + del( list, (k->state() & ShiftButton) == 0 ); + } + return true; + } + + const QString& text = k->text(); + if ( !text.isEmpty() && text.unicode()->isPrint() ) { + k->accept(); + + if ( !m_fileFinder ) { + m_fileFinder = new FileFinder( this, "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 ); +} + + +// KIO::NetAccess::stat() does NOT give us the right mimetype, while +// KIO::NetAccess::mimetype() does. So we have this hacklet to tell +// showImage that the KFileItem is really an image. +#define IS_IMAGE 5 +#define MY_TYPE 55 + +bool FileWidget::isImage( const KFileItem *item ) +{ +// return item && !item->isDir(); + if ( item ) + { + return item->isReadable() && item->mimetype().startsWith( "image/") || + item->extraData( (void*) MY_TYPE ) == (void*) IS_IMAGE; + } + return false; +} + +void FileWidget::setImage( KFileItem& item, bool enable ) +{ + if ( enable ) + item.setExtraData( (void*) MY_TYPE, (void*) IS_IMAGE ); + else + item.removeExtraData( (void*) MY_TYPE ); +} + +KFileItem * FileWidget::gotoFirstImage() +{ + KFileItemListIterator it( *(fileView()->items()) ); + + while ( it.current() ) { + if ( isImage( it.current() ) ) { + setCurrentItem( it.current() ); + return it.current(); + } + ++it; + } + + return 0L; +} + +KFileItem * FileWidget::gotoLastImage() +{ + KFileItemListIterator it( *(fileView()->items()) ); + it.toLast(); + + while ( it.current() ) { + if ( isImage( it.current() ) ) { + setCurrentItem( it.current() ); + return it.current(); + } + --it; + } + + return 0L; +} + +KFileItem * FileWidget::getNext( bool go ) +{ + KFileItem *item = getItem( Next, true ); + if ( item ) { + if ( go ) + setCurrentItem( item ); + return item; + } + + return 0L; +} + +KFileItem * FileWidget::getPrevious( bool go ) +{ + KFileItem *item = getItem( Previous, true ); + if ( item ) { + if ( go ) + setCurrentItem( item ); + return item; + } + + return 0L; +} + +// returns 0L when there is no previous/next item/image +// this sucks! Use KFileView::currentFileItem() when implemented +KFileItem * FileWidget::getItem( WhichItem which, bool onlyImage ) const +{ + KFileItemListIterator it( *(fileView()->items()) ); + + while ( it.current() ) { // find the iterator to the current item + if ( it.current()->url() == m_currentURL ) + break; + + ++it; + } + + if ( it.current() ) { + switch ( which ) { + case Previous: { + --it; + while ( it.current() ) { + if ( isImage( it.current() ) || !onlyImage ) + return it.current(); + --it; + } + return 0L; // no previous item / image + } + + case Next: { + ++it; + while ( it.current() ) { + if ( isImage( it.current() ) || !onlyImage ) + return it.current(); + ++it; + } + return 0L; // no further item / image + } + + case Current: + default: + return it.current(); + } + } + + return 0L; +} + +void FileWidget::slotViewChanged() +{ + fileView()->widget()->installEventFilter( this ); +} + +void FileWidget::slotItemsCleared() +{ + m_currentURL = QString::null; +} + +void FileWidget::slotItemDeleted( KFileItem *item ) +{ + KFileItem *current = getCurrentItem( false ); + if ( item != current ) { + return; // all ok, we already have a new current item + } + + KFileItem *next = getNext(); + if ( !next ) + next = getPrevious(); + + if ( next ) + m_currentURL = next->url().url(); +} + +void FileWidget::slotHighlighted( const KFileItem *item ) +{ + m_currentURL = item->url().url(); +} + +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 ); + + KURL url; + url.setPath( dir ); + setURL( url, true ); + } + + else if ( text.find('/') != (int) text.length() -1 ) { // relative path + QString dir = m_fileFinder->completion()->replacedPath( text ); + KURL u( url(), dir ); + setURL( u, true ); + } + + else if ( m_validCompletion ) { + KFileItem *item = getCurrentItem( true ); + + if ( item ) { + if ( item->isDir() ) + setURL( item->url(), true ); + else + emit fileSelected( item ); + } + } +} + +void FileWidget::setCurrentItem( const KFileItem *item ) +{ + if ( item ) { + fileView()->setCurrentItem( item ); + fileView()->ensureItemVisible( item ); + } +} + +void FileWidget::setInitialItem( const QString& filename ) +{ + m_initialName = filename; +} + +void FileWidget::slotURLEntered( const KURL& url ) +{ + if ( m_fileFinder ) + m_fileFinder->completion()->setDir( url.path() ); +} + +void FileWidget::slotFinishedLoading() +{ + KFileItem *current = getCurrentItem( false ); + if ( !m_initialName.isEmpty() ) + setCurrentItem( m_initialName ); + else if ( !current ) + setCurrentItem( view()->items()->getFirst() ); + + m_initialName = QString::null; + 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() ); +} + +#include "filewidget.moc" diff --git a/kuickshow/src/filewidget.h b/kuickshow/src/filewidget.h new file mode 100644 index 00000000..a785fd85 --- /dev/null +++ b/kuickshow/src/filewidget.h @@ -0,0 +1,98 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef FILEWIDGET_H +#define FILEWIDGET_H + +#include <qevent.h> + +#include <kdiroperator.h> + +class FileFinder; +class KFileItem; + +class FileWidget : public KDirOperator +{ + Q_OBJECT + +public: + enum WhichItem { Previous, Next, Current }; + + FileWidget( const KURL& url, QWidget *parent = 0L, const char *name = 0L ); + ~FileWidget(); + + bool hasFiles() const; + void reloadConfiguration(); + + void setInitialItem( const QString& filename ); + + KFileItem *getCurrentItem( bool onlyImage ) const { + return getItem( Current, onlyImage ); + } + + void setCurrentItem( const KFileItem * ); + void setCurrentItem( const QString& filename ) { + KDirOperator::setCurrentItem( filename ); + } + + KFileItem * gotoFirstImage(); + KFileItem * gotoLastImage(); + KFileItem * getNext( bool go=true ); + KFileItem * getPrevious( bool go=true ); + + + KFileItem *getItem( WhichItem which, bool onlyImage ) const; + + static bool isImage( const KFileItem * ); + static void setImage( KFileItem& item, bool enable ); + + void initActions(); + +signals: + void finished(); + +protected: + virtual bool eventFilter( QObject *o, QEvent * ); + virtual void resizeEvent( QResizeEvent * ); + virtual void activatedMenu( const KFileItem *, const QPoint& ); + virtual QSize sizeHint() const; + +private slots: + void slotReturnPressed( const QString& text ); + void findCompletion( const QString& ); + void slotViewChanged(); + + void slotItemsCleared(); + void slotItemDeleted( KFileItem * ); + void slotHighlighted( const KFileItem * ); + + void slotURLEntered( const KURL& url ); + void slotFinishedLoading(); + +private: + KFileView * fileView() const { return (KFileView*) view(); } + + bool m_validCompletion; + FileFinder *m_fileFinder; + QString m_currentURL; + QString m_initialName; + +}; + + +#endif // FILEWIDGET_H diff --git a/kuickshow/src/generalwidget.cpp b/kuickshow/src/generalwidget.cpp new file mode 100644 index 00000000..3b718d69 --- /dev/null +++ b/kuickshow/src/generalwidget.cpp @@ -0,0 +1,163 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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 <qcheckbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qvgroupbox.h> + +#include <kapplication.h> +#include <kcolorbutton.h> +#include <kdialog.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klocale.h> +#include <knuminput.h> +#include <kurllabel.h> + +#include "generalwidget.h" + +GeneralWidget::GeneralWidget( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setSpacing( KDialog::spacingHint() ); + + QPixmap pixmap = UserIcon( "logo" ); + KURLLabel *logo = new KURLLabel( this ); + logo->setURL( "http://devel-home.kde.org/~pfeiffer/kuickshow/" ); + logo->setPixmap( pixmap ); + logo->setFixedSize( pixmap.size() ); + logo->setTipText( i18n( "Open KuickShow Website" ) ); + logo->setUseTips( true ); + + connect( logo, SIGNAL( leftClickedURL( const QString & ) ), + SLOT( slotURLClicked( const QString & ) ) ); + + layout->addWidget( logo, 0, AlignRight ); + + cbFullscreen = new QCheckBox( i18n("Fullscreen mode"), this, "boscreen" ); + + cbPreload = new QCheckBox( i18n("Preload next image"), this, "preload"); + cbLastdir = new QCheckBox( i18n("Remember last folder"), this, "restart_lastdir"); + + QGridLayout *gridLayout = new QGridLayout( 2, 2 ); + gridLayout->setSpacing( KDialog::spacingHint() ); + QLabel *l0 = new QLabel( i18n("Background color:"), this ); + colorButton = new KColorButton( this ); + + QLabel *l1 = new QLabel( i18n("Show only files with extension: "), this, "label" ); + editFilter = new KLineEdit( this, "filteredit" ); + + gridLayout->addWidget( l0, 0, 0 ); + gridLayout->addWidget( colorButton, 0, 1 ); + gridLayout->addWidget( l1, 1, 0 ); + gridLayout->addWidget( editFilter, 1, 1 ); + + layout->addWidget( cbFullscreen ); + layout->addWidget( cbPreload ); + layout->addWidget( cbLastdir ); + layout->addLayout( gridLayout ); + + //////////////////////////////////////////////////////////////////////// + + QVGroupBox *gbox2 = new QVGroupBox( i18n("Quality/Speed"), + this, "qualitybox" ); + layout->addWidget( gbox2 ); + layout->addStretch(); + + cbSmoothScale = new QCheckBox( i18n("Smooth scaling"), gbox2, "smoothscale" ); + cbFastRender = new QCheckBox( i18n("Fast rendering"), gbox2, "fastrender" ); + cbDither16bit = new QCheckBox( i18n("Dither in HiColor (15/16bit) modes"), + gbox2, "dither16bit" ); + + cbDither8bit = new QCheckBox( i18n("Dither in LowColor (<=8bit) modes"), + gbox2, "dither8bit" ); + + cbOwnPalette = new QCheckBox( i18n("Use own color palette"), + gbox2, "pal"); + connect( cbOwnPalette, SIGNAL( clicked() ), this, SLOT( useOwnPalette() ) ); + + cbFastRemap = new QCheckBox( i18n("Fast palette remapping"), gbox2, "remap"); + + maxCacheSpinBox = new KIntNumInput( gbox2, "editmaxcache" ); + maxCacheSpinBox->setLabel( i18n("Maximum cache size: "), AlignVCenter ); + maxCacheSpinBox->setSuffix( i18n( " MB" ) ); + maxCacheSpinBox->setSpecialValueText( i18n( "Unlimited" ) ); + maxCacheSpinBox->setRange( 0, 400, 1 ); + + loadSettings( *kdata ); + cbFullscreen->setFocus(); +} + +GeneralWidget::~GeneralWidget() +{ +} + +void GeneralWidget::slotURLClicked( const QString & url ) +{ + kapp->invokeBrowser( url ); +} + +void GeneralWidget::loadSettings( const KuickData& data ) +{ + ImData *idata = data.idata; + + colorButton->setColor( data.backgroundColor ); + editFilter->setText( data.fileFilter ); + cbFullscreen->setChecked( data.fullScreen ); + cbPreload->setChecked( data.preloadImage ); + cbLastdir->setChecked( data.startInLastDir ); + cbFastRemap->setChecked( idata->fastRemap ); + cbOwnPalette->setChecked( idata->ownPalette ); + cbSmoothScale->setChecked( idata->smoothScale ); + cbFastRender->setChecked( idata->fastRender ); + cbDither16bit->setChecked( idata->dither16bit ); + cbDither8bit->setChecked( idata->dither8bit ); + maxCacheSpinBox->setValue( idata->maxCache / 1024 ); + + useOwnPalette(); // enable/disable remap-checkbox +} + +void GeneralWidget::applySettings( KuickData& data) +{ + ImData *idata = data.idata; + + data.backgroundColor = colorButton->color(); + data.fileFilter = editFilter->text(); + data.fullScreen = cbFullscreen->isChecked(); + data.preloadImage = cbPreload->isChecked(); + data.startInLastDir = cbLastdir->isChecked(); + + idata->smoothScale = cbSmoothScale->isChecked(); + idata->fastRemap = cbFastRemap->isChecked(); + idata->ownPalette = cbOwnPalette->isChecked(); + idata->fastRender = cbFastRender->isChecked(); + idata->dither16bit = cbDither16bit->isChecked(); + idata->dither8bit = cbDither8bit->isChecked(); + + idata->maxCache = (uint) maxCacheSpinBox->value() * 1024; +} + +void GeneralWidget::useOwnPalette() +{ + cbFastRemap->setEnabled( cbOwnPalette->isChecked() ); +} + +#include "generalwidget.moc" diff --git a/kuickshow/src/generalwidget.h b/kuickshow/src/generalwidget.h new file mode 100644 index 00000000..e39eef18 --- /dev/null +++ b/kuickshow/src/generalwidget.h @@ -0,0 +1,62 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef GENERALWIDGET_H +#define GENERALWIDGET_H + +#include <qwidget.h> + +#include "kuickdata.h" + +class QCheckBox; +class KColorButton; +class KLineEdit; +class KIntNumInput; + + +class GeneralWidget : public QWidget +{ + Q_OBJECT + +public: + GeneralWidget( QWidget *parent, const char *name ); + ~GeneralWidget(); + + void loadSettings( const KuickData& data ); + void applySettings( KuickData& data ); + +private: + QCheckBox *cbFullscreen, *cbActiveWindow, *cbPreload, *cbLastdir; + QCheckBox *cbSmoothScale, *cbFastRemap, *cbFastRender; + QCheckBox *cbDither16bit, *cbDither8bit, *cbOwnPalette; + + KLineEdit *editFilter; + KIntNumInput *maxCacheSpinBox; + + KIntNumInput *sbMaxWidth, *sbMaxHeight; + KIntNumInput *sbZoomFactor; + + KColorButton *colorButton; + +private slots: + void useOwnPalette(); + void slotURLClicked( const QString & ); + +}; + +#endif diff --git a/kuickshow/src/hi16-app-kuickshow.png b/kuickshow/src/hi16-app-kuickshow.png Binary files differnew file mode 100644 index 00000000..9f764485 --- /dev/null +++ b/kuickshow/src/hi16-app-kuickshow.png diff --git a/kuickshow/src/hi22-app-kuickshow.png b/kuickshow/src/hi22-app-kuickshow.png Binary files differnew file mode 100644 index 00000000..d8750f31 --- /dev/null +++ b/kuickshow/src/hi22-app-kuickshow.png diff --git a/kuickshow/src/hi32-app-kuickshow.png b/kuickshow/src/hi32-app-kuickshow.png Binary files differnew file mode 100644 index 00000000..e70e573e --- /dev/null +++ b/kuickshow/src/hi32-app-kuickshow.png diff --git a/kuickshow/src/imagewindow.cpp b/kuickshow/src/imagewindow.cpp new file mode 100644 index 00000000..c63a3c67 --- /dev/null +++ b/kuickshow/src/imagewindow.cpp @@ -0,0 +1,1251 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2006 Carsten Pfeiffer <[email protected]> + + 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 <stdlib.h> + +#include <qcheckbox.h> +#include <qcursor.h> +#include <qdrawutil.h> +#include <qfileinfo.h> +#include <qkeycode.h> +#include <qpainter.h> +#include <qpen.h> +#include <qpopupmenu.h> + +#ifdef KDE_USE_FINAL +#undef GrayScale +#undef Color +#endif +#include <qrect.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtimer.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <kcursor.h> +#include <kdebug.h> +#include <kdeversion.h> +#ifdef KDE_USE_FINAL +#undef Unsorted +#endif +#include <kfiledialog.h> +#include <kiconloader.h> +#include <kimageeffect.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprinter.h> +#include <kpropertiesdialog.h> +#include <kstdaccel.h> +#include <kstdguiitem.h> +#include <kstandarddirs.h> +#include <kglobalsettings.h> +#include <ktempfile.h> +#include <kwin.h> +#include <netwm.h> +#include <kurldrag.h> +#include <kio/netaccess.h> + +#include "filecache.h" +#include "imagewindow.h" +#include "kuick.h" +#include "kuickdata.h" +#include "kuickfile.h" +#include "kuickimage.h" +#include "printing.h" + + +#undef GrayScale + +QCursor *ImageWindow::s_handCursor = 0L; + +ImageWindow::ImageWindow( ImData *_idata, ImlibData *id, QWidget *parent, + const char *name ) + : ImlibWidget( _idata, id, parent, name ) +{ + init(); +} + +ImageWindow::ImageWindow( ImData *_idata, QWidget *parent, const char *name ) + : ImlibWidget( _idata, parent, name ) +{ + init(); +} + +ImageWindow::~ImageWindow() +{ +} + + +void ImageWindow::init() +{ + setFocusPolicy( QWidget::StrongFocus ); + + KCursor::setAutoHideCursor( this, true, true ); + KCursor::setHideCursorDelay( 1500 ); + + // give the image window a different WM_CLASS + XClassHint hint; + hint.res_name = const_cast<char*>( kapp->name() ); + hint.res_class = const_cast<char*>( "ImageWindow" ); + XSetClassHint( x11Display(), winId(), &hint ); + + viewerMenu = 0L; + gammaMenu = 0L; + brightnessMenu = 0L; + contrastMenu = 0L; + + + m_actions = new KActionCollection( this ); + + if ( !s_handCursor ) { + QString file = locate( "appdata", "pics/handcursor.png" ); + if ( !file.isEmpty() ) + s_handCursor = new QCursor( file ); + else + s_handCursor = new QCursor( arrowCursor ); + } + + setupActions(); + imageCache->setMaxImages( kdata->maxCachedImages ); + + transWidget = 0L; + myIsFullscreen = false; + + xpos = 0, ypos = 0; + m_numHeads = ScreenCount( x11Display() ); + + setAcceptDrops( true ); + setBackgroundColor( kdata->backgroundColor ); + + static QPixmap imageIcon = UserIcon( "imageviewer-medium" ); + static QPixmap miniImageIcon = UserIcon( "imageviewer-small" ); + KWin::setIcons( winId(), imageIcon, miniImageIcon ); +} + +void ImageWindow::updateActions() +{ + m_actions->readShortcutSettings(); +} + +void ImageWindow::setupActions() +{ + new KAction( i18n("Show Next Image"), KStdAccel::next(), + this, SLOT( slotRequestNext() ), + m_actions, "next_image" ); + new KAction( i18n("Show Previous Image"), KStdAccel::prior(), + this, SLOT( slotRequestPrevious() ), + m_actions, "previous_image" ); + + new KAction( i18n("Delete Image"), SHIFT + Key_Delete, + this, SLOT( imageDelete() ), + m_actions, "delete_image" ); + new KAction( i18n("Move Image to Trash"), Key_Delete, + this, SLOT( imageTrash() ), + m_actions, "trash_image" ); + + new KAction( i18n("Zoom In"), Key_Plus, + this, SLOT( zoomIn() ), + m_actions, "zoom_in" ); + new KAction( i18n("Zoom Out"), Key_Minus, + this, SLOT( zoomOut() ), + m_actions, "zoom_out" ); + new KAction( i18n("Restore Original Size"), Key_O, + this, SLOT( showImageOriginalSize() ), + m_actions, "original_size" ); + new KAction( i18n("Maximize"), Key_M, + this, SLOT( maximize() ), + m_actions, "maximize" ); + + new KAction( i18n("Rotate 90 Degrees"), Key_9, + this, SLOT( rotate90() ), + m_actions, "rotate90" ); + new KAction( i18n("Rotate 180 Degrees"), Key_8, + this, SLOT( rotate180() ), + m_actions, "rotate180" ); + new KAction( i18n("Rotate 270 Degrees"), Key_7, + this, SLOT( rotate270() ), + m_actions, "rotate270" ); + + new KAction( i18n("Flip Horizontally"), Key_Asterisk, + this, SLOT( flipHoriz() ), + m_actions, "flip_horicontally" ); + new KAction( i18n("Flip Vertically"), Key_Slash, + this, SLOT( flipVert() ), + m_actions, "flip_vertically" ); + + new KAction( i18n("Print Image..."), KStdAccel::print(), + this, SLOT( printImage() ), + m_actions, "print_image" ); + KStdAction::saveAs( this, SLOT( saveImage() ), + m_actions, "save_image_as" ); + + KStdAction::close( this, SLOT( close() ), + m_actions, "close_image" ); + // -------- + new KAction( i18n("More Brightness"), Key_B, + this, SLOT( moreBrightness() ), + m_actions, "more_brightness" ); + new KAction( i18n("Less Brightness"), SHIFT + Key_B, + this, SLOT( lessBrightness() ), + m_actions, "less_brightness" ); + new KAction( i18n("More Contrast"), Key_C, + this, SLOT( moreContrast() ), + m_actions, "more_contrast" ); + new KAction( i18n("Less Contrast"), SHIFT + Key_C, + this, SLOT( lessContrast() ), + m_actions, "less_contrast" ); + new KAction( i18n("More Gamma"), Key_G, + this, SLOT( moreGamma() ), + m_actions, "more_gamma" ); + new KAction( i18n("Less Gamma"), SHIFT + Key_G, + this, SLOT( lessGamma() ), + m_actions, "less_gamma" ); + + // -------- + new KAction( i18n("Scroll Up"), Key_Up, + this, SLOT( scrollUp() ), + m_actions, "scroll_up" ); + new KAction( i18n("Scroll Down"), Key_Down, + this, SLOT( scrollDown() ), + m_actions, "scroll_down" ); + new KAction( i18n("Scroll Left"), Key_Left, + this, SLOT( scrollLeft() ), + m_actions, "scroll_left" ); + new KAction( i18n("Scroll Right"), Key_Right, + this, SLOT( scrollRight() ), + m_actions, "scroll_right" ); + // -------- + new KAction( i18n("Pause Slideshow"), Key_P, + this, SLOT( pauseSlideShow() ), + m_actions, "kuick_slideshow_pause" ); + + KAction *fullscreenAction = KStdAction::fullScreen(this, SLOT( toggleFullscreen() ), m_actions, 0 ); + + KAction *reloadAction = new KAction( i18n("Reload Image"), KStdAccel::shortcut(KStdAccel::Reload), + this, SLOT( reload() ), + m_actions, "reload_image" ); + + new KAction( i18n("Properties"), ALT + Key_Return, + this, SLOT( slotProperties() ), + m_actions, "properties" ); + + m_actions->readShortcutSettings(); + + // Unfortunately there is no KAction::setShortcutDefault() :-/ + // so add Key_Return as fullscreen shortcut _after_ readShortcutSettings() + addAlternativeShortcut(fullscreenAction, Key_Return); + addAlternativeShortcut(reloadAction, Key_Enter); +} + +void ImageWindow::addAlternativeShortcut(KAction *action, int key) +{ + KShortcut cut( action->shortcut() ); + if (cut == action->shortcutDefault()) { + cut.append(KKey(key)); + action->setShortcut(cut); + } +} + +void ImageWindow::showWindow() +{ + if ( myIsFullscreen ) + showFullScreen(); + else + showNormal(); +} + +void ImageWindow::setFullscreen( bool enable ) +{ + xpos = 0; ypos = 0; + +// if ( enable && !myIsFullscreen ) { // set Fullscreen +// showFullScreen(); +// } +// else if ( !enable && myIsFullscreen ) { // go into window mode +// showNormal(); +// } + + myIsFullscreen = enable; +// centerImage(); // ### really necessary (multihead!) +} + + +void ImageWindow::updateGeometry( int imWidth, int imHeight ) +{ +// qDebug("::updateGeometry: %i, %i", imWidth, imHeight); + // XMoveWindow( x11Display(), win, 0, 0 ); + XResizeWindow( x11Display(), win, imWidth, imHeight ); + + if ( imWidth != width() || imHeight != height() ) { + if ( myIsFullscreen ) { + centerImage(); + } + else { // window mode + // XMoveWindow( x11Display(), win, 0, 0 ); + resizeOptimal( imWidth, imHeight ); // also centers the image + } + } + else { // image size == widget size + xpos = 0; ypos = 0; + XMoveWindow( x11Display(), win, 0, 0 ); + } + + updateCursor(); + + QString caption = i18n( "Filename (Imagewidth x Imageheight)", + "%3 (%1 x %2)" ); + caption = caption.arg( m_kuim->originalWidth() ). + arg( m_kuim->originalHeight() ).arg( m_kuim->url().prettyURL() ); + setCaption( kapp->makeStdCaption( caption ) ); +} + + +void ImageWindow::centerImage() +{ + int w, h; + if ( myIsFullscreen ) + { + QRect desktopRect = KGlobalSettings::desktopGeometry( this ); + w = desktopRect.width(); + h = desktopRect.height(); + } + else + { + w = width(); + h = height(); + } + + xpos = w/2 - imageWidth()/2; + ypos = h/2 - imageHeight()/2; + + XMoveWindow( x11Display(), win, xpos, ypos ); + + // Modified by Evan for his Multi-Head (2 screens) + // This should center on the first head +// if ( myIsFullscreen && m_numHeads > 1 && ((m_numHeads % 2) == 0) ) +// xpos = ((width()/m_numHeads) / 2) - imageWidth()/2; +// else +// xpos = width()/2 - imageWidth()/2; + +// ypos = height()/2 - imageHeight()/2; +// XMoveWindow( x11Display(), win, xpos, ypos ); +} + + +void ImageWindow::scrollImage( int x, int y, bool restrict ) +{ + xpos += x; + ypos += y; + + int cwlocal = width(); + int chlocal = height(); + + int iw = imageWidth(); + int ih = imageHeight(); + + if ( myIsFullscreen || width() > desktopWidth() ) + cwlocal = desktopWidth(); + + if ( myIsFullscreen || height() > desktopHeight() ) + chlocal = desktopHeight(); + + if ( restrict ) { // don't allow scrolling in certain cases + if ( x != 0 ) { // restrict x-movement + if ( iw <= cwlocal ) + xpos -= x; // restore previous position + else if ( (xpos <= 0) && (xpos + iw <= cwlocal) ) + xpos = cwlocal - iw; + else if ( (xpos + iw >= cwlocal) && xpos >= 0 ) + xpos = 0; + } + + if ( y != 0 ) { // restrict y-movement + if ( ih <= chlocal ) + ypos -= y; + else if ( (ypos <= 0) && (ypos + ih <= chlocal) ) + ypos = chlocal - ih; + else if ( (ypos + ih >= chlocal) && ypos >= 0 ) + ypos = 0; + } + } + + XMoveWindow( x11Display(), win, xpos, ypos ); + XClearArea( x11Display(), win, xpos, ypos, iw, ih, false ); + showImage(); +} + + +// image loading performs: +// --------------------- +// loadImageInternal(); +// reset image mods +// load image from disk / get from cache +// loaded(); // apply modifications, scale +// render pixmap +// +// updateWidget(); +// XUnmapWindow(); +// XSetWindowBackgroundPixmap() +// resize window to fit image size, center image +// XClearWindow(); // repaint +// XMapWindow(), XSync(); +// +bool ImageWindow::showNextImage( const KURL& url ) +{ + KuickFile *file = FileCache::self()->getFile( url ); + switch ( file->waitForDownload( this ) ) { + case KuickFile::ERROR: + { + QString tmp = i18n("Unable to download the image from %1.").arg(url.prettyURL()); + emit sigImageError( file, tmp ); + return false; + } + case KuickFile::CANCELED: + return false; // just abort, no error message + default: + break; // go on... + } + + return showNextImage( file ); +} + +bool ImageWindow::showNextImage( KuickFile *file ) +{ + if ( !loadImage( file ) ) { + QString tmp = i18n("Unable to load the image %1.\n" + "Perhaps the file format is unsupported or " + "your Imlib is not installed properly.").arg(file->url().prettyURL()); + emit sigImageError( file, tmp ); + return false; + } + + else { + // updateWidget( true ); // already called from loadImage() + if ( !isVisible() ) + showWindow(); + + showImage(); + return true; + } +} + +void ImageWindow::reload() +{ + showNextImage( currentFile() ); +} + +void ImageWindow::pauseSlideShow() +{ + emit pauseSlideShowSignal(); +} + +void ImageWindow::addBrightness( int factor ) +{ + if ( factor == 0 ) + return; + + int oldValue = mod.brightness - ImlibOffset; + setBrightness( oldValue + (idata->brightnessFactor * (int) factor) ); +} + +void ImageWindow::addContrast( int factor ) +{ + if ( factor == 0 ) + return; + + int oldValue = mod.contrast - ImlibOffset; + setContrast( oldValue + (idata->contrastFactor * (int) factor) ); +} + +void ImageWindow::addGamma( int factor ) +{ + if ( factor == 0 ) + return; + + int oldValue = mod.gamma - ImlibOffset; + setGamma( oldValue + (idata->gammaFactor * (int) factor) ); +} + + +//////////// +//// +// slots for keyboard/popupmenu actions + + +void ImageWindow::scrollUp() +{ + scrollImage( 0, 20 * kdata->scrollSteps ); +} + +void ImageWindow::scrollDown() +{ + scrollImage( 0, - 20 * kdata->scrollSteps ); +} + +void ImageWindow::scrollLeft() +{ + scrollImage( 20 * kdata->scrollSteps, 0 ); +} + +void ImageWindow::scrollRight() +{ + scrollImage( - 20 * kdata->scrollSteps, 0 ); +} + +/// + +void ImageWindow::zoomIn() +{ + zoomImage( kdata->zoomSteps ); +} + +void ImageWindow::zoomOut() +{ + Q_ASSERT( kdata->zoomSteps != 0 ); + zoomImage( 1.0 / kdata->zoomSteps ); +} + +/// + +void ImageWindow::moreBrightness() +{ + addBrightness( kdata->brightnessSteps ); +} + +void ImageWindow::moreContrast() +{ + addContrast( kdata->contrastSteps ); +} + +void ImageWindow::moreGamma() +{ + addGamma( kdata->gammaSteps ); +} + + +void ImageWindow::lessBrightness() +{ + addBrightness( - kdata->brightnessSteps ); +} + +void ImageWindow::lessContrast() +{ + addContrast( - kdata->contrastSteps ); +} + +void ImageWindow::lessGamma() +{ + addGamma( - kdata->gammaSteps ); +} + +void ImageWindow::imageDelete() +{ + emit deleteImage(this); +} + +void ImageWindow::imageTrash() +{ + emit trashImage(this); +} + +/// + + + + +///////////// +//// +// event handlers + +void ImageWindow::wheelEvent( QWheelEvent *e ) +{ + e->accept(); + static const int WHEEL_DELTA = 120; + int delta = e->delta(); + + if ( delta == 0 ) + return; + + int steps = delta / WHEEL_DELTA; + emit requestImage( this, -steps ); +} + +void ImageWindow::keyPressEvent( QKeyEvent *e ) +{ + uint key = e->key(); + + if ( key == Key_Shift ) + updateCursor( ZoomCursor ); + + if ( key == Key_Escape || KStdAccel::close().contains( KKey( e ) ) ) + close( true ); + else if ( KStdAccel::save().contains( KKey( e ) ) ) + saveImage(); + + else { + e->ignore(); + return; + } + + e->accept(); +} + +void ImageWindow::keyReleaseEvent( QKeyEvent *e ) +{ + if ( e->state() & ShiftButton ) { // Shift-key released + updateCursor(); + if ( transWidget ) { + delete transWidget; + transWidget = 0L; + } + } + + e->accept(); +} + +void ImageWindow::mousePressEvent( QMouseEvent *e ) +{ + xmove = e->x(); // for moving the image with the mouse + ymove = e->y(); + + xzoom = xmove; // for zooming with the mouse + yzoom = ymove; + + xposPress = xmove; + yposPress = ymove; + + if ( e->button() == LeftButton ) { + if ( e->state() & ShiftButton ) + updateCursor( ZoomCursor ); + else + updateCursor( MoveCursor ); + } + + ImlibWidget::mousePressEvent( e ); +} + +void ImageWindow::contextMenuEvent( QContextMenuEvent *e ) +{ + e->accept(); + + if ( !viewerMenu ) + setPopupMenu(); + + viewerMenu->popup( e->globalPos() ); +} + +void ImageWindow::updateCursor( KuickCursor cursor ) +{ + switch ( cursor ) + { + case ZoomCursor: + setCursor( arrowCursor ); // need a magnify-cursor + break; + case MoveCursor: + setCursor( *s_handCursor ); + break; + case DefaultCursor: + default: + if ( isCursorHidden() ) + return; + + if ( imageWidth() > width() || imageHeight() > height() ) + setCursor( *s_handCursor ); + else + setCursor( arrowCursor ); + break; + } +} + +void ImageWindow::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !(e->state() & LeftButton) ) { // only handle LeftButton actions + return; + } + + if ( e->state() & ShiftButton ) { + + if ( !transWidget ) { + transWidget = new QWidget( this ); + transWidget->setGeometry( 0, 0, width(), height() ); + transWidget->setBackgroundMode( NoBackground ); + } + + transWidget->hide(); + QPainter p( transWidget ); + // really required? + p.eraseRect( transWidget->rect() ); + transWidget->show(); + qApp->processOneEvent(); + + int width = e->x() - xposPress; + int height = e->y() - yposPress; + + if ( width < 0 ) { + width = abs( width ); + xzoom = e->x(); + } + + if ( height < 0 ) { + height = abs( height ); + yzoom = e->y(); + } + + QPen pen( Qt::white, 1, DashLine ); + p.setPen( pen ); // for drawing white dashed line + p.drawRect( xzoom, yzoom, width, height ); + p.setPen( DotLine ); // defaults to black dotted line pen + p.drawRect( xzoom, yzoom, width, height ); + p.flush(); + } + + else { // move the image + // scrolling with mouse + uint xtmp = e->x(); + uint ytmp = e->y(); + scrollImage( xtmp - xmove, ytmp - ymove ); + xmove = xtmp; + ymove = ytmp; + } +} + +void ImageWindow::mouseReleaseEvent( QMouseEvent *e ) +{ + updateCursor(); + + if ( transWidget ) { + // destroy the transparent widget, used for showing the rectangle (zoom) + delete transWidget; + transWidget = 0L; + } + + // only proceed if shift-Key is still pressed + if ( !(e->button() == LeftButton && e->state() & ShiftButton) ) + return; + + int neww, newh, topX, topY, botX, botY; + float factor, factorx, factory; + + // zoom into the selected area + uint x = e->x(); + uint y = e->y(); + + if ( xposPress == x || yposPress == y ) + return; + + if ( xposPress > x ) { + topX = x; + botX = xposPress; + } + else { + topX = xposPress; + botX = x; + } + + if ( yposPress > y ) { + topY = y; + botY = yposPress; + } + else { + topY = yposPress; + botY = y; + } + + neww = botX - topX; + newh = botY - topY; + + factorx = ((float) width() / (float) neww); + factory = ((float) height() / (float) newh); + + if ( factorx < factory ) // use the smaller factor + factor = factorx; + else factor = factory; + + uint w = 0; // shut up compiler! + uint h = 0; + w = (uint) ( factor * (float) imageWidth() ); + h = (uint) ( factor * (float) imageHeight() ); + + if ( !canZoomTo( w, h ) ) + return; + + int xtmp = - (int) (factor * abs(xpos - topX) ); + int ytmp = - (int) (factor * abs(ypos - topY) ); + + // if image has different ratio (width()/height()), center it + int xcenter = (width() - (int) (neww * factor)) / 2; + int ycenter = (height() - (int) (newh * factor)) / 2; + + xtmp += xcenter; + ytmp += ycenter; + + m_kuim->resize( w, h, idata->smoothScale ? KuickImage::SMOOTH : KuickImage::FAST ); + XResizeWindow( x11Display(), win, w, h ); + updateWidget( false ); + + xpos = xtmp; ypos = ytmp; + + XMoveWindow( x11Display(), win, xpos, ypos ); + scrollImage( 1, 1, true ); // unrestricted scrolling +} + + +void ImageWindow::focusInEvent( QFocusEvent *ev ) +{ + ImlibWidget::focusInEvent( ev ); + emit sigFocusWindow( this ); +} + + +void ImageWindow::resizeEvent( QResizeEvent *e ) +{ + ImlibWidget::resizeEvent( e ); + + centerImage(); + updateCursor(); +} + + +void ImageWindow::dragEnterEvent( QDragEnterEvent *e ) +{ + // if ( e->provides( "image/*" ) ) // can't do this right now with Imlib + if ( e->provides( "text/uri-list" ) ) + e->accept(); + else + e->ignore(); +} + + +void ImageWindow::dropEvent( QDropEvent *e ) +{ + // FIXME - only preliminary drop-support for now + KURL::List list; + if ( KURLDrag::decode( e, list ) && !list.isEmpty()) { + QString tmpFile; + const KURL &url = list.first(); + if (KIO::NetAccess::download( url, tmpFile, this ) ) + { + loadImage( tmpFile ); + KIO::NetAccess::removeTempFile( tmpFile ); + } + updateWidget(); + e->accept(); + } + else + e->ignore(); +} + + +//////////////////// +///////// +// misc stuff + +void ImageWindow::setPopupMenu() +{ + viewerMenu = new QPopupMenu( this ); + + m_actions->action("next_image")->plug( viewerMenu ); + m_actions->action("previous_image")->plug( viewerMenu ); + viewerMenu->insertSeparator(); + + brightnessMenu = new QPopupMenu( viewerMenu ); + m_actions->action("more_brightness")->plug(brightnessMenu); + m_actions->action("less_brightness")->plug(brightnessMenu); + + contrastMenu = new QPopupMenu( viewerMenu ); + m_actions->action("more_contrast")->plug(contrastMenu); + m_actions->action("less_contrast")->plug(contrastMenu); + + gammaMenu = new QPopupMenu( viewerMenu ); + m_actions->action("more_gamma")->plug(gammaMenu); + m_actions->action("less_gamma")->plug(gammaMenu); + + m_actions->action("zoom_in")->plug( viewerMenu ); + m_actions->action("zoom_out")->plug( viewerMenu ); + m_actions->action("original_size")->plug( viewerMenu ); + m_actions->action("maximize")->plug( viewerMenu ); + + viewerMenu->insertSeparator(); + m_actions->action("rotate90")->plug( viewerMenu ); + m_actions->action("rotate180")->plug( viewerMenu ); + m_actions->action("rotate270")->plug( viewerMenu ); + + viewerMenu->insertSeparator(); + m_actions->action("flip_vertically")->plug( viewerMenu ); + m_actions->action("flip_horicontally")->plug( viewerMenu ); + viewerMenu->insertSeparator(); + viewerMenu->insertItem( i18n("Brightness"), brightnessMenu ); + viewerMenu->insertItem( i18n("Contrast"), contrastMenu ); + viewerMenu->insertItem( i18n("Gamma"), gammaMenu ); + viewerMenu->insertSeparator(); + + m_actions->action("delete_image")->plug( viewerMenu ); + m_actions->action("print_image")->plug( viewerMenu ); + m_actions->action("save_image_as")->plug( viewerMenu ); + m_actions->action("properties")->plug( viewerMenu ); + + viewerMenu->insertSeparator(); + m_actions->action("close_image")->plug( viewerMenu ); +} + +void ImageWindow::printImage() +{ + if ( !m_kuim ) + return; + + if ( !Printing::printImage( *this, this ) ) + { + KMessageBox::sorry( this, i18n("Unable to print the image."), + i18n("Printing Failed") ); + } +} + +void ImageWindow::saveImage() +{ + if ( !m_kuim ) + return; + + KuickData tmp; + QCheckBox *keepSize = new QCheckBox( i18n("Keep original image size"), 0L); + keepSize->setChecked( true ); + KFileDialog dlg( m_saveDirectory, tmp.fileFilter, this, "filedialog", true +#if KDE_VERSION >= 310 + ,keepSize +#endif + ); + + QString selection = m_saveDirectory.isEmpty() ? + m_kuim->url().url() : + m_kuim->url().fileName(); + dlg.setOperationMode( KFileDialog::Saving ); + dlg.setMode( KFile::File ); + dlg.setSelection( selection ); + dlg.setCaption( i18n("Save As") ); + if ( dlg.exec() == QDialog::Accepted ) + { + KURL url = dlg.selectedURL(); + if ( url.isValid() ) + { + if ( !saveImage( url, keepSize->isChecked() ) ) + { + QString tmp = i18n("Couldn't save the file.\n" + "Perhaps the disk is full, or you don't " + "have write permission to the file."); + KMessageBox::sorry( this, tmp, i18n("File Saving Failed")); + } + else + { + if ( url.equals( m_kuim->url() )) { + Imlib_apply_modifiers_to_rgb( id, m_kuim->imlibImage() ); + } + } + } + } + + QString lastDir = dlg.baseURL().path(+1); + if ( lastDir != m_saveDirectory ) + m_saveDirectory = lastDir; + +#if KDE_VERSION < 310 + delete keepSize; +#endif +} + +bool ImageWindow::saveImage( const KURL& dest, bool keepOriginalSize ) +{ + int w = keepOriginalSize ? m_kuim->originalWidth() : m_kuim->width(); + int h = keepOriginalSize ? m_kuim->originalHeight() : m_kuim->height(); + if ( m_kuim->absRotation() == ROT_90 || m_kuim->absRotation() == ROT_270 ) + qSwap( w, h ); + + ImlibImage *saveIm = Imlib_clone_scaled_image( id, m_kuim->imlibImage(), + w, h ); + bool success = false; + + QString saveFile; + if ( dest.isLocalFile() ) + saveFile = dest.path(); + else + { + QString extension = QFileInfo( dest.fileName() ).extension(); + if ( !extension.isEmpty() ) + extension.prepend( '.' ); + + KTempFile tmpFile( QString::null, extension ); + if ( tmpFile.status() != 0 ) + return false; + tmpFile.close(); + if ( tmpFile.status() != 0 ) + return false; + saveFile = tmpFile.name(); + } + + if ( saveIm ) + { + Imlib_apply_modifiers_to_rgb( id, saveIm ); + success = Imlib_save_image( id, saveIm, + QFile::encodeName( saveFile ).data(), + NULL ); + if ( success && !dest.isLocalFile() ) + { + if ( isFullscreen() ) + toggleFullscreen(); // otherwise upload window would block us invisibly + success = KIO::NetAccess::upload( saveFile, dest, const_cast<ImageWindow*>( this ) ); + } + + Imlib_kill_image( id, saveIm ); + } + + return success; +} + +void ImageWindow::toggleFullscreen() +{ + setFullscreen( !myIsFullscreen ); + showWindow(); +} + +void ImageWindow::loaded( KuickImage *kuim ) +{ + if ( !kdata->isModsEnabled ) { + // ### BUG: should be "restorePreviousSize" + kuim->restoreOriginalSize(); + } + else + { + autoRotate( kuim ); + autoScale( kuim ); + } +} + +// upscale/downscale depending on configuration +void ImageWindow::autoScale( KuickImage *kuim ) +{ + int newW = kuim->originalWidth(); + int newH = kuim->originalHeight(); + + QSize s = maxImageSize(); + int mw = s.width(); + int mh = s.height(); + + if ( kuim->absRotation() == ROT_90 || kuim->absRotation() == ROT_270 ) + qSwap( newW, newH ); + + bool doIt = false; + + if ( kdata->upScale ) + { + if ( (newW < mw) && (newH < mh) ) + { + doIt = true; + + float ratio1, ratio2; + int maxUpScale = kdata->maxUpScale; + + ratio1 = (float) mw / (float) newW; + ratio2 = (float) mh / (float) newH; + ratio1 = (ratio1 < ratio2) ? ratio1 : ratio2; + if ( maxUpScale > 0 ) + ratio1 = (ratio1 < maxUpScale) ? ratio1 : maxUpScale; + newH = (int) ((float) newH * ratio1); + newW = (int) ((float) newW * ratio1); + } + } + + if ( kdata->downScale ) + { + // eventually set width and height to the best/max possible screen size + if ( (newW > mw) || (newH > mh) ) + { + doIt = true; + + if ( newW > mw ) + { + float ratio = (float) newW / (float) newH; + newW = mw; + newH = (int) ((float) newW / ratio); + } + + // the previously calculated "h" might be larger than screen + if ( newH > mh ) { + float ratio = (float) newW / (float) newH; + newH = mh; + newW = (int) ((float) newH * ratio); + } + } + } + + if ( doIt ) + kuim->resize( newW, newH, idata->smoothScale ? KuickImage::SMOOTH : KuickImage::FAST ); +} + +// only called when kdata->isModsEnabled is true +bool ImageWindow::autoRotate( KuickImage *kuim ) +{ + if ( kdata->autoRotation && ImlibWidget::autoRotate( kuim ) ) + return true; + + else // rotation by metadata not available or not configured + { + // only apply default mods to newly loaded images + + // ### actually we should have a dirty flag ("neverManuallyFlipped") + if ( kuim->flipMode() == FlipNone ) + { + int flipMode = 0; + if ( kdata->flipVertically ) + flipMode |= FlipVertical; + if ( kdata->flipHorizontally ) + flipMode |= FlipHorizontal; + + kuim->flipAbs( flipMode ); + } + + if ( kuim->absRotation() == ROT_0 ) + kuim->rotateAbs( kdata->rotation ); + } + + return true; +} + +int ImageWindow::desktopWidth( bool totalScreen ) const +{ + if ( myIsFullscreen || totalScreen ) + { + return KGlobalSettings::desktopGeometry(topLevelWidget()).width(); + } else + return Kuick::workArea().width(); +} + + +int ImageWindow::desktopHeight( bool totalScreen ) const +{ + if ( myIsFullscreen || totalScreen ) { + return KGlobalSettings::desktopGeometry(topLevelWidget()).height(); + } else { + return Kuick::workArea().height(); + } +} + +QSize ImageWindow::maxImageSize() const +{ + if ( myIsFullscreen ) { + return KGlobalSettings::desktopGeometry(topLevelWidget()).size(); + } + else { + return Kuick::workArea().size() - Kuick::frameSize( winId() ); + } +} + +void ImageWindow::resizeOptimal( int w, int h ) +{ + QSize s = maxImageSize(); + int mw = s.width(); + int mh = s.height(); + int neww = (w >= mw) ? mw : w; + int newh = (h >= mh) ? mh : h; + + if ( neww == width() && newh == height() ) + centerImage(); + else + resize( neww, newh ); // also centers the image +} + +void ImageWindow::maximize() +{ + if ( !m_kuim ) + return; + + bool oldUpscale = kdata->upScale; + bool oldDownscale = kdata->downScale; + + kdata->upScale = true; + kdata->downScale = true; + + autoScale( m_kuim ); + updateWidget( true ); + + if ( !myIsFullscreen ) + resizeOptimal( imageWidth(), imageHeight() ); + + kdata->upScale = oldUpscale; + kdata->downScale = oldDownscale; +} + +bool ImageWindow::canZoomTo( int newWidth, int newHeight ) +{ + if ( !ImlibWidget::canZoomTo( newWidth, newHeight ) ) + return false; + + QSize desktopSize = KGlobalSettings::desktopGeometry(topLevelWidget()).size(); + + int desktopArea = desktopSize.width() * desktopSize.height(); + int imageArea = newWidth * newHeight; + + if ( imageArea > desktopArea * kdata->maxZoomFactor ) + { + return KMessageBox::warningContinueCancel( + this, + i18n("You are about to view a very large image (%1 x %2 pixels), which can be very resource-consuming and even make your computer hang.\nDo you want to continue?") + .arg( newWidth ).arg( newHeight ), + QString::null, + KStdGuiItem::cont(), + "ImageWindow_confirm_very_large_window" + ) == KMessageBox::Continue; + } + + return true; +} + +void ImageWindow::rotated( KuickImage *kuim, int rotation ) +{ + if ( !m_kuim ) + return; + + ImlibWidget::rotated( kuim, rotation ); + + if ( rotation == ROT_90 || rotation == ROT_270 ) + autoScale( kuim ); // ### BUG: only autoScale when configured! +} + +void ImageWindow::slotProperties() +{ + (void) new KPropertiesDialog( currentFile()->url(), this, "props dialog", true ); +} + +void ImageWindow::setBusyCursor() +{ + // avoid busy cursor in fullscreen mode + if ( !isFullscreen() ) + ImlibWidget::setBusyCursor(); +} + +void ImageWindow::restoreCursor() +{ + // avoid busy cursor in fullscreen mode + if ( !isFullscreen() ) + ImlibWidget::restoreCursor(); +} + +bool ImageWindow::isCursorHidden() const +{ + return cursor().shape() == Qt::BlankCursor; +} + +#include "imagewindow.moc" diff --git a/kuickshow/src/imagewindow.h b/kuickshow/src/imagewindow.h new file mode 100644 index 00000000..4a823a0a --- /dev/null +++ b/kuickshow/src/imagewindow.h @@ -0,0 +1,176 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef IMAGEWINDOW_H +#define IMAGEWINDOW_H + +#include <qevent.h> + +#include <kaction.h> +#include <kurl.h> + +#include "imlibwidget.h" + +class QCursor; +class QPopupMenu; +class QRect; +class QString; +class QTimer; +class QWidget; + +class KuickFile; + +class ImageWindow : public ImlibWidget +{ + Q_OBJECT + +public: + ImageWindow( ImData *_idata, ImlibData *id, QWidget *parent=0, + const char *name=0 ); + ImageWindow( ImData *_idata=0, QWidget *parent=0, const char *name=0 ); + + bool showNextImage( KuickFile * file ); + bool showNextImage( const KURL& url ); + void scrollImage( int, int, bool restrict=true ); + void setFullscreen( bool ); + bool isFullscreen() const { return myIsFullscreen; } + + void addBrightness( int ); + void addContrast( int ); + void addGamma( int ); + + void updateActions(); + + KActionCollection * actionCollection() const { return m_actions; } + + /** + * Resizes image to @p w, @p h, but takes into account the workarea, so + * it won't ever get a bigger size than the workarea. + */ + void resizeOptimal( int w, int h ); + void autoScale( KuickImage *kuim ); + virtual bool autoRotate( KuickImage *kuim ); + + bool saveImage( const KURL& dest, bool keepOriginalSize ); + +public slots: + void zoomIn(); + void zoomOut(); + void moreBrightness(); + void lessBrightness(); + void moreContrast(); + void lessContrast(); + void moreGamma(); + void lessGamma(); + void scrollUp(); + void scrollDown(); + void scrollLeft(); + void scrollRight(); + void printImage(); + void toggleFullscreen(); + void maximize(); + void imageDelete(); + void imageTrash(); + +signals: + void sigFocusWindow( ImageWindow * ); + // go advance images back/forth + void requestImage( ImageWindow *, int advance ); + void deleteImage(ImageWindow *viewer); + void trashImage(ImageWindow *viewer); + +protected: + ~ImageWindow(); // deletes itself, just call close( true ); + + void init(); + void centerImage(); + void addAlternativeShortcut( KAction *action, int key ); + virtual void updateGeometry( int imWidth, int imHeight ); + virtual void loaded( KuickImage * ); + virtual bool canZoomTo( int newWidth, int newHeight ); + virtual void rotated( KuickImage *kuim, int rotation ); + + virtual void wheelEvent( QWheelEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void keyReleaseEvent( QKeyEvent * ); + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void mouseMoveEvent( QMouseEvent * ); + virtual void focusInEvent( QFocusEvent * ); + virtual void resizeEvent( QResizeEvent * ); + virtual void dragEnterEvent( QDragEnterEvent * ); + virtual void dropEvent( QDropEvent * ); + virtual void contextMenuEvent( QContextMenuEvent * ); + + void showWindow(); + enum KuickCursor { DefaultCursor = 0, ZoomCursor, MoveCursor }; + void updateCursor( KuickCursor cursor = DefaultCursor ); + + // popupmenu entries + uint itemViewerZoomMax, itemViewerZoomOrig, itemViewerZoomIn; + uint itemViewerZoomOut, itemViewerFlipH, itemViewerProps; + uint itemRotate90, itemRotate180, itemRotate270; + uint itemViewerFlipV, itemViewerPrint; + uint itemViewerSave, itemViewerClose; + uint itemBrightnessPlus, itemBrightnessMinus; + uint itemContrastPlus, itemContrastMinus; + uint itemGammaPlus, itemGammaMinus; + + + uint xmove, ymove; // used for scrolling the image with the mouse + int xpos, ypos; // top left corner of the image + int xzoom, yzoom; // used for zooming the image with the mouse + uint xposPress, yposPress; + + + QPopupMenu *viewerMenu, *gammaMenu, *brightnessMenu, *contrastMenu; + QWidget *transWidget; + + +protected slots: + void saveImage(); + void slotRequestNext() { emit requestImage( this, +1 ); } + void slotRequestPrevious() { emit requestImage( this, -1 ); } + void reload(); + void slotProperties(); + void pauseSlideShow(); + virtual void setBusyCursor(); + virtual void restoreCursor(); + +signals: + void pauseSlideShowSignal(); + +private: + int desktopWidth( bool totalScreen = false ) const; + int desktopHeight( bool totalScreen = false ) const; + QSize maxImageSize() const; + void setupActions(); + void setPopupMenu(); + bool isCursorHidden() const; + + bool myIsFullscreen; + int m_numHeads; + QString m_saveDirectory; + + KActionCollection *m_actions; + + static QCursor * s_handCursor; +}; + + +#endif // IMAGEWINDOW_H diff --git a/kuickshow/src/imdata.cpp b/kuickshow/src/imdata.cpp new file mode 100644 index 00000000..a2e89283 --- /dev/null +++ b/kuickshow/src/imdata.cpp @@ -0,0 +1,92 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2001 Carsten Pfeiffer <[email protected]> + + 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 <stdlib.h> + +#include <kconfig.h> + +#include "imdata.h" + + +ImData::ImData() +{ + ownPalette = true; + fastRemap = true; + fastRender = true; + dither16bit = false; + dither8bit = true; + smoothScale = false; + maxCache = 10240; + + gamma = 0; + brightness = 0; + contrast = 0; + + gammaFactor = 10; + brightnessFactor = 10; + contrastFactor = 10; +} + + +void ImData::load( KConfig *kc ) +{ + ImData def; + + kc->setGroup( "ImlibConfiguration" ); + + ownPalette = kc->readBoolEntry( "UseOwnPalette", def.ownPalette ); + fastRemap = kc->readBoolEntry( "FastRemapping", def.fastRemap ); + fastRender = kc->readBoolEntry( "FastRendering", def.fastRender ); + dither16bit = kc->readBoolEntry( "Dither16Bit", def.dither16bit ); + dither8bit = kc->readBoolEntry( "Dither8Bit", def.dither8bit ); + smoothScale = kc->readBoolEntry( "SmoothScaling", def.smoothScale ); + + maxCache = kc->readNumEntry( "MaxCacheSize", 10240 ); + + gamma = kc->readNumEntry( "GammaDefault", 0 ); + brightness = kc->readNumEntry( "BrightnessDefault", 0 ); + contrast = kc->readNumEntry( "ContrastDefault", 0 ); + + gammaFactor = abs( kc->readNumEntry( "GammaFactor", 10 ) ); + brightnessFactor = abs( kc->readNumEntry( "BrightnessFactor", 10 ) ); + contrastFactor = abs( kc->readNumEntry( "ContrastFactor", 10 ) ); +} + + +void ImData::save( KConfig *kc ) +{ + kc->setGroup( "ImlibConfiguration" ); + + kc->writeEntry( "UseOwnPalette", ownPalette ); + kc->writeEntry( "FastRemapping", fastRemap ); + kc->writeEntry( "FastRendering", fastRender ); + kc->writeEntry( "Dither16Bit", dither16bit ); + kc->writeEntry( "Dither8Bit", dither8bit ); + kc->writeEntry( "MaxCacheSize", maxCache ); + kc->writeEntry( "SmoothScaling", smoothScale ); + + kc->writeEntry( "GammaDefault", gamma ); + kc->writeEntry( "BrightnessDefault", brightness ); + kc->writeEntry( "ContrastDefault", contrast ); + + kc->writeEntry( "GammaFactor", gammaFactor ); + kc->writeEntry( "BrightnessFactor", brightnessFactor ); + kc->writeEntry( "ContrastFactor", contrastFactor ); + + kc->sync(); +} diff --git a/kuickshow/src/imdata.h b/kuickshow/src/imdata.h new file mode 100644 index 00000000..c6dc6a5c --- /dev/null +++ b/kuickshow/src/imdata.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 1998,1999,2000,2001 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef IMBLIBCONFIG_H +#define IMBLIBCONFIG_H + +class KConfig; +class ImData +{ +public: + ImData(); + ~ImData() {}; + + void load( KConfig *kc ); + void save( KConfig *kc ); + + // new stuff.......... + + int gamma; + int brightness; + int contrast; + + // ----------------------- + + bool ownPalette :1; + bool fastRemap :1; + bool fastRender :1; + bool dither16bit :1; + bool dither8bit :1; + bool smoothScale :1; + + + uint gammaFactor; + uint brightnessFactor; + uint contrastFactor; + + uint maxCache; + +}; + + +#endif diff --git a/kuickshow/src/imlibwidget.cpp b/kuickshow/src/imlibwidget.cpp new file mode 100644 index 00000000..5b5cb84b --- /dev/null +++ b/kuickshow/src/imlibwidget.cpp @@ -0,0 +1,715 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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 "kuickdata.h" + +#include <sys/time.h> +#include <unistd.h> + +#include <stdlib.h> +#include <assert.h> + +#include <qcolor.h> +#include <qfile.h> +#include <qglobal.h> +#include <qimage.h> +#include <qobject.h> +#include <qpalette.h> + +#include <kcursor.h> +#include <kdebug.h> +#include <kfilemetainfo.h> +#include <kimageio.h> + +#include "filecache.h" +#include "kuickfile.h" +#include "kuickimage.h" +#include "imlibwidget.h" + +const int ImlibWidget::ImlibOffset = 256; + +ImlibWidget::ImlibWidget( ImData *_idata, QWidget *parent, const char *name ) : + QWidget( parent, name, WDestructiveClose ) +{ + 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(x11Display(), x11Screen()); + + 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( x11Display(), &par ); + + init(); +} + + +ImlibWidget::ImlibWidget( ImData *_idata, ImlibData *_id, QWidget *parent, + const char *name ) + : QWidget( parent, name, WDestructiveClose ) +{ + 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."); + + setAutoRender( true ); + + setPalette( QPalette( myBackgroundColor )); + setBackgroundMode( PaletteBackground ); + + imageCache = new ImageCache( id, 4 ); // cache 4 images (FIXME?) + connect( imageCache, SIGNAL( sigBusy() ), SLOT( setBusyCursor() )); + connect( imageCache, SIGNAL( sigIdle() ), SLOT( restoreCursor() )); + + win = XCreateSimpleWindow(x11Display(), winId(), 0,0,w,h,0,0,0); +} + +ImlibWidget::~ImlibWidget() +{ + delete imageCache; + if ( deleteImlibData && id ) free ( id ); + if ( win ) XDestroyWindow( x11Display(), win ); + if ( deleteImData ) delete idata; +} + +KURL ImlibWidget::url() const +{ + if ( m_kuickFile ) + return m_kuickFile->url(); + + return KURL(); +} + +KuickFile * ImlibWidget::currentFile() const +{ + return m_kuickFile; +} + +// tries to load "file" and returns the according KuickImage * +// or 0L if unsuccessful +// Note that the given file MUST already be downloaded prior to calling this function +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, mod ); + if ( !kuim ) {// couldn't load file, maybe corrupt or wrong format + kdWarning() << "ImlibWidget: can't load image " << file->url().prettyURL() << endl; + return 0L; + } + + loaded( kuim ); // maybe upscale/downscale/rotate in subclasses + + return kuim; +} + +// overridden in subclass +void ImlibWidget::loaded( KuickImage * ) +{ +} + +bool ImlibWidget::loadImage( const KURL& 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 KURL& 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( x11Display(), win ); + XSync( x11Display(), 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 metadatas( kuim->file().localFile() ); + if ( !metadatas.isValid() ) + return false; + + KFileMetaInfoItem metaitem = metadatas.item("Orientation"); + if ( !metaitem.isValid() +#if QT_VERSION >= 0x030100 + || metaitem.value().isNull() +#endif + ) + 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( x11Display(), win );// remove the old image -> no flicker + + XSetWindowBackgroundPixmap( x11Display(), win, m_kuim->pixmap() ); + + if ( geometryUpdate ) + updateGeometry( m_kuim->width(), m_kuim->height() ); + + XClearWindow( x11Display(), win ); + + showImage(); +} + + +// here we just use the size of m_kuim, may be overridden in subclass +void ImlibWidget::updateGeometry( int w, int h ) +{ + XMoveWindow( x11Display(), win, 0, 0 ); // center? + XResizeWindow( x11Display(), 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( false); // FIXME - false? 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 ( ownCursor() ) + m_oldCursor = cursor(); + else + m_oldCursor = QCursor(); + + setCursor( KCursor::waitCursor() ); +} + +void ImlibWidget::restoreCursor() +{ + if ( cursor().shape() == KCursor::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, WFlags f, const QPoint& p, bool showIt ) +{ + XWindowAttributes attr; + XGetWindowAttributes( x11Display(), win, &attr ); + XUnmapWindow( x11Display(), win ); + XReparentWindow( x11Display(), win, attr.root, 0, 0 ); + QWidget::reparent( parent, f, p, showIt ); + XReparentWindow( x11Display(), win, winId(), attr.x, attr.y ); + if( attr.map_state != IsUnmapped ) + XMapWindow( x11Display(), win ); +} + +void ImlibWidget::rotated( KuickImage *, int ) +{ +} + +//---------- + + +// uhh ugly, we have two lists to map from filename to KuickImage :-/ +ImageCache::ImageCache( ImlibData *id, int maxImages ) +{ + myId = id; + idleCount = 0; + myMaxImages = maxImages; + kuickList.setAutoDelete( true ); + fileList.clear(); + kuickList.clear(); +} + + +ImageCache::~ImageCache() +{ + kuickList.clear(); + fileList.clear(); +} + + +void ImageCache::setMaxImages( int maxImages ) +{ + myMaxImages = maxImages; + int count = kuickList.count(); + while ( count > myMaxImages ) { + kuickList.removeLast(); + fileList.remove( fileList.fromLast() ); + count--; + } +} + +void ImageCache::slotBusy() +{ + if ( idleCount == 0 ) + emit sigBusy(); + + idleCount++; +} + +void ImageCache::slotIdle() +{ + idleCount--; + + if ( idleCount == 0 ) + emit sigIdle(); +} + + +KuickImage * ImageCache::getKuimage( KuickFile * file, + ImlibColorModifier mod ) +{ + 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.findIndex( 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.take( index ); + kuickList.insert( 0, kuim ); + fileList.remove( file ); + fileList.prepend( file ); + } + + return kuim; + } + + if ( !kuim ) { + 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.latin1(), +// (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() > (uint) myMaxImages ) { +// qDebug(":::: now removing from cache: %s", (*fileList.fromLast()).latin1()); + kuickList.removeLast(); + fileList.remove( fileList.fromLast() ); + } + + 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 +{ + kdDebug() << "Trying to load " << fileName << " with KImageIO..." << endl; + + KImageIO::registerFormats(); + + QImage image( fileName ); + if ( image.isNull() ) + return 0L; + if ( image.depth() != 32 ) { + image.setAlphaBuffer(false); + image = image.convertDepth(32); + + 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<QRgb *>( 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; +} + +#include "imlibwidget.moc" diff --git a/kuickshow/src/imlibwidget.h b/kuickshow/src/imlibwidget.h new file mode 100644 index 00000000..5f6e89e1 --- /dev/null +++ b/kuickshow/src/imlibwidget.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** $Id$ +** +** ImlibWidget: maps an Xlib window with Imlib's contents on a QWidget +** +** Created : 98 +** +** Copyright (C) 1998-2001 by Carsten Pfeiffer. All rights reserved. +** +****************************************************************************/ + +#ifndef IMLIBWIDGET_H +#define IMLIBWIDGET_H + +#include <qvariant.h> + +#include <qcursor.h> +#include <qevent.h> +#include <qptrlist.h> +#include <qtimer.h> +#include <qwidget.h> + +#include <kurl.h> + +// #include those AFTER Qt-includes! +#include <Imlib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +// #include <X11/extensions/shape.h> + +#include "imdata.h" +#include "kuickdata.h" + +class KuickFile; +class KuickImage; + +class ImageCache : public QObject +{ + Q_OBJECT + +public: + ImageCache( ImlibData *id, int maxImages=1 ); + ~ImageCache(); + + void setMaxImages( int maxImages ); + int maxImages() const { return myMaxImages; } + + KuickImage * getKuimage( KuickFile * file, ImlibColorModifier ); + +private: + ImlibImage * loadImageWithQt( const QString& filename ) const; + + int myMaxImages; + QValueList<KuickFile*>fileList; + QPtrList<KuickImage> kuickList; + // QPtrList<ImlibImage> imList; + ImlibData * myId; + int idleCount; + +private slots: + void slotBusy(); + void slotIdle(); + +signals: + void sigBusy(); + void sigIdle(); + +}; + + +// ------------------------------------------ + +class QColor; + +class ImlibWidget : public QWidget +{ + Q_OBJECT + +public: + + ImlibWidget( ImData *_idata=0, QWidget *parent=0, const char *name=0 ); + ImlibWidget( ImData *_idata, ImlibData *id, QWidget *parent=0, + const char *name=0 ); + virtual ~ImlibWidget(); + + KURL url() const; + KuickFile * currentFile() const; + bool loadImage( KuickFile * file ); + bool loadImage( const KURL& url ); + bool cacheImage(const KURL& url ); + void zoomImage( float ); + void setBrightness( int ); + void setContrast( int ); + void setGamma( int ); + void setRotation( Rotation ); + void setFlipMode( int mode ); + + int brightness() const; // ### no impl! + int contrast() const; // ### no impl! + int gamma() const; // ### no impl! + Rotation rotation() const; + FlipMode flipMode() const; + + int imageWidth() const; + int imageHeight() const; + + void setAutoRender( bool enable ) { isAutoRendering = enable;} + bool isAutoRenderEnabled() const { return isAutoRendering; } + void setMaxImageCache( int ); + int maxImageCache() const { return myMaxImageCache; } + const QColor& backgroundColor() const; + void setBackgroundColor( const QColor& ); + + /** + * @return true if auto-rotation is not possible, e.g. because no metadata + * about orientation is available + */ + virtual bool autoRotate( KuickImage *kuim ); + + ImlibData* getImlibData() const { return id; } + + virtual void reparent( QWidget* parent, WFlags f, const QPoint& p, bool showIt = FALSE ); + +public slots: + void rotate90(); + void rotate270(); + void rotate180(); + void flipHoriz(); + void flipVert(); + void showImageOriginalSize(); + inline void updateImage() { updateWidget( true ); } + + +protected: + KuickImage * loadImageInternal( KuickFile * file ); + void showImage(); + void setImageModifier(); + void rotate( int ); + void updateWidget( bool geometryUpdate=true ); + virtual void updateGeometry( int width, int height ); + virtual void loaded( KuickImage * ); + virtual bool canZoomTo( int newWidth, int newHeight ); + virtual void rotated( KuickImage *kuim, int rotation ); + + void closeEvent( QCloseEvent * ); + + inline void autoUpdate( bool geometryUpdate=false ) { + if ( isAutoRendering ) + updateWidget( geometryUpdate ); + } + + bool stillResizing, deleteImData, deleteImlibData; + bool imlibModifierChanged; + + KuickImage *m_kuim; + ImageCache *imageCache; + ImlibData *id; + ImData *idata; + Window win; + ImlibColorModifier mod; + + KuickFile *m_kuickFile; + QCursor m_oldCursor; + + static const int ImlibOffset; + + +private: + void init(); + bool isAutoRendering; + int myMaxImageCache; + QColor myBackgroundColor; + + +protected slots: + bool cacheImage( KuickFile *file ); + virtual void setBusyCursor(); + virtual void restoreCursor(); + + +signals: + void sigImageError( const KuickFile * file, const QString& ); + +}; + + +#endif diff --git a/kuickshow/src/kuick.cpp b/kuickshow/src/kuick.cpp new file mode 100644 index 00000000..43eadb50 --- /dev/null +++ b/kuickshow/src/kuick.cpp @@ -0,0 +1,4 @@ +#include "kuick.h" + +Kuick * Kuick::s_self = 0L; +QSize Kuick::s_frameSize; diff --git a/kuickshow/src/kuick.h b/kuickshow/src/kuick.h new file mode 100644 index 00000000..bc86d5d2 --- /dev/null +++ b/kuickshow/src/kuick.h @@ -0,0 +1,70 @@ +/* This file is part of the KDE project + Copyright (C) 2000,2001,2002 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef KUICKGLOBALS_H +#define KUICKGLOBALS_H + +#include <qrect.h> +#include <qsize.h> + +#include <kwin.h> +#include <kwinmodule.h> + +class Kuick +{ +public: + static QRect workArea() { + return self()->winModule.workArea(); + } + + static QSize frameSize( WId win = 0L ) { + if ( win ) { + KWin::WindowInfo info = KWin::windowInfo(win, NET::WMKDEFrameStrut | NET::WMGeometry); + int wborder = info.frameGeometry().width() - info.geometry().width(); + int hborder = info.frameGeometry().height() - info.geometry().height(); + + if ( wborder || hborder ) { // we get a 0,0 border when not shown + s_frameSize.setWidth( wborder ); + s_frameSize.setHeight( hborder ); + } + } + + if ( !s_frameSize.isValid() ) + return QSize( 0, 0 ); + + return s_frameSize; + } + + static Kuick * self() { + if ( !s_self ) { + s_self = new Kuick; + } + return s_self; + } + + KWinModule winModule; + +private: + Kuick() {} + static Kuick * s_self; + + static QSize s_frameSize; +}; + + +#endif // KUICKGLOBALS_H diff --git a/kuickshow/src/kuickconfigdlg.cpp b/kuickshow/src/kuickconfigdlg.cpp new file mode 100644 index 00000000..6ece8883 --- /dev/null +++ b/kuickshow/src/kuickconfigdlg.cpp @@ -0,0 +1,99 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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. +*/ + + +#ifdef index +#undef index +#endif +#include "kuickconfigdlg.h" + +#include <qkeycode.h> +#include <qvbox.h> + +#include <kaccel.h> +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> + +#include "imagewindow.h" +#include "defaultswidget.h" +#include "generalwidget.h" +#include "slideshowwidget.h" + +#include "kuickdata.h" + + +KuickConfigDialog::KuickConfigDialog( KActionCollection *_coll, QWidget *parent, + const char *name, bool modal ) + : KDialogBase( Tabbed, i18n("Configure"), + Help | Default | Ok | Apply | Cancel, Ok, + parent, name, modal ) +{ + coll = _coll; + QVBox *box = addVBoxPage( i18n("&General") ); + generalWidget = new GeneralWidget( box, "general widget" ); + + box = addVBoxPage( i18n("&Modifications") ); + defaultsWidget = new DefaultsWidget( box, "defaults widget" ); + + box = addVBoxPage( i18n("&Slideshow") ); + slideshowWidget = new SlideShowWidget( box, "slideshow widget" ); + + box = addVBoxPage( i18n("&Viewer Shortcuts") ); + + imageWindow = new ImageWindow(); // just to get the accel... + imageWindow->hide(); + + imageKeyChooser = new KKeyChooser( imageWindow->actionCollection(), box ); + + box = addVBoxPage( i18n("Bro&wser Shortcuts") ); + browserKeyChooser = new KKeyChooser( coll, box ); + + connect( this, SIGNAL( defaultClicked() ), SLOT( resetDefaults() )); +} + +KuickConfigDialog::~KuickConfigDialog() +{ + imageWindow->close( true ); +} + +void KuickConfigDialog::applyConfig() +{ + generalWidget->applySettings( *kdata ); + defaultsWidget->applySettings( *kdata ); + slideshowWidget->applySettings( *kdata ); + + imageKeyChooser->save(); + browserKeyChooser->save(); + + KGlobal::config()->sync(); +} + + +void KuickConfigDialog::resetDefaults() +{ + KuickData data; + generalWidget->loadSettings( data ); + defaultsWidget->loadSettings( data ); + slideshowWidget->loadSettings( data ); + + imageKeyChooser->allDefault(); + browserKeyChooser->allDefault(); +} + +#include "kuickconfigdlg.moc" diff --git a/kuickshow/src/kuickconfigdlg.h b/kuickshow/src/kuickconfigdlg.h new file mode 100644 index 00000000..884d91dc --- /dev/null +++ b/kuickshow/src/kuickconfigdlg.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef KUICKCONFIGDLG_H +#define KUICKCONFIGDLG_H + +#include <qevent.h> + +#include <kkeydialog.h> + +#include <kdialogbase.h> + +class GeneralWidget; +class DefaultsWidget; +class SlideShowWidget; +class ImageWindow; + +class KuickConfigDialog : public KDialogBase +{ + Q_OBJECT + +public: + KuickConfigDialog( KActionCollection *coll, QWidget *parent=0, + const char *name=0, bool modal=true); + ~KuickConfigDialog(); + + void applyConfig(); + +private slots: + void resetDefaults(); + +private: + DefaultsWidget *defaultsWidget; + GeneralWidget *generalWidget; + SlideShowWidget *slideshowWidget; + KKeyChooser *imageKeyChooser, *browserKeyChooser; + KActionCollection *coll; + + ImageWindow *imageWindow; + +}; + +#endif diff --git a/kuickshow/src/kuickdata.cpp b/kuickshow/src/kuickdata.cpp new file mode 100644 index 00000000..c6aa47c2 --- /dev/null +++ b/kuickshow/src/kuickdata.cpp @@ -0,0 +1,177 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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 <stdlib.h> + +#include <qcolor.h> +#include <kconfig.h> +#include <kglobal.h> + +#include "kuickdata.h" + + +KuickData::KuickData() +{ + fileFilter = "*.jpeg *.jpg *.gif *.xpm *.ppm *.pgm *.pbm *.pnm *.png *.bmp *.psd *.eim *.tif *.tiff *.xcf";// *.mng"; + slideDelay = 3000; + slideshowCycles = 1; + slideshowFullscreen = true; + slideshowStartAtFirst = true; + + preloadImage = true; + + isModsEnabled = true; + fullScreen = false; + autoRotation = true; + downScale = true; + upScale = false; + flipVertically = false; + flipHorizontally = false; + + maxUpScale = 3; + rotation = ROT_0; + + brightnessSteps = 1; + contrastSteps = 1; + gammaSteps = 1; + scrollSteps = 1; + zoomSteps = 1.5; + + maxZoomFactor = 4.0; + + maxCachedImages = 4; + backgroundColor = Qt::black; + + startInLastDir = true; + + idata = new ImData; +} + +KuickData::~KuickData() +{ + delete idata; +} + + +void KuickData::load() +{ + KConfig *kc = KGlobal::config(); + + KuickData def; + + kc->setGroup( "GeneralConfiguration" ); + fileFilter = kc->readEntry( "FileFilter", def.fileFilter ); + slideDelay = kc->readNumEntry( "SlideShowDelay", def.slideDelay ); + slideshowCycles = kc->readUnsignedNumEntry( "SlideshowCycles", 1 ); + slideshowFullscreen = kc->readBoolEntry( "SlideshowFullscreen", true ); + slideshowStartAtFirst = kc->readBoolEntry("SlideshowStartAtFirst", true ); + + preloadImage = kc->readBoolEntry( "PreloadNextImage", def.preloadImage ); + + fullScreen = kc->readBoolEntry( "Fullscreen", def.fullScreen); + autoRotation = kc->readBoolEntry( "AutoRotation", def.autoRotation); + downScale = kc->readBoolEntry( "ShrinkToScreenSize", def.downScale ); + upScale = kc->readBoolEntry( "ZoomToScreenSize", def.upScale ); + flipVertically = kc->readBoolEntry( "FlipVertically", def.flipVertically ); + flipHorizontally = kc->readBoolEntry( "FlipHorizontally", + def.flipHorizontally ); + maxUpScale = kc->readNumEntry( "MaxUpscale Factor", def.maxUpScale ); + rotation = (Rotation) kc->readNumEntry( "Rotation", def.rotation ); + + isModsEnabled = kc->readBoolEntry( "ApplyDefaultModifications", + def.isModsEnabled ); + + brightnessSteps = kc->readNumEntry("BrightnessStepSize",def.brightnessSteps); + contrastSteps = kc->readNumEntry("ContrastStepSize", def.contrastSteps); + gammaSteps = kc->readNumEntry("GammaStepSize", def.gammaSteps); + scrollSteps = kc->readNumEntry("ScrollingStepSize", def.scrollSteps); + zoomSteps = kc->readDoubleNumEntry("ZoomStepSize", def.zoomSteps); + + maxZoomFactor = kc->readDoubleNumEntry( "MaximumZoomFactorByDesktop", def.maxZoomFactor ); + + maxCachedImages = kc->readUnsignedNumEntry( "MaxCachedImages", + def.maxCachedImages ); + backgroundColor = kc->readColorEntry( "BackgroundColor", &Qt::black ); + + startInLastDir = kc->readBoolEntry( "StartInLastDir", true); + + idata->load( kc ); + + // compatibility with KuickShow <= 0.8.3 + switch ( rotation ) + { + case 90: + rotation = ROT_90; + break; + case 180: + rotation = ROT_180; + break; + case 270: + rotation = ROT_270; + break; + default: + if ( (rotation < ROT_0) || (rotation > ROT_270) ) + rotation = ROT_0; + break; + } +} + + +void KuickData::save() +{ + KConfig *kc = KGlobal::config(); + kc->setGroup( "GeneralConfiguration" ); + + kc->writeEntry( "FileFilter", fileFilter ); + kc->writeEntry( "SlideShowDelay", slideDelay ); + kc->writeEntry( "SlideshowCycles", slideshowCycles ); + kc->writeEntry( "SlideshowFullscreen", slideshowFullscreen ); + kc->writeEntry( "SlideshowStartAtFirst", slideshowStartAtFirst ); + + kc->writeEntry( "PreloadNextImage", preloadImage ); + + kc->writeEntry( "Fullscreen", fullScreen ); + kc->writeEntry( "AutoRotation", autoRotation ); + kc->writeEntry( "ShrinkToScreenSize", downScale ); + kc->writeEntry( "ZoomToScreenSize", upScale ); + kc->writeEntry( "FlipVertically", flipVertically ); + kc->writeEntry( "FlipHorizontally", flipHorizontally ); + kc->writeEntry( "MaxUpscale Factor", maxUpScale ); + kc->writeEntry( "Rotation", rotation ); + + kc->writeEntry( "ApplyDefaultModifications", isModsEnabled ); + + + kc->writeEntry( "BrightnessStepSize", brightnessSteps ); + kc->writeEntry( "ContrastStepSize", contrastSteps ); + kc->writeEntry( "GammaStepSize", gammaSteps ); + + kc->writeEntry( "ScrollingStepSize", scrollSteps ); + kc->writeEntry( "ZoomStepSize", zoomSteps ); + + kc->writeEntry( "MaximumZoomFactorByDesktop", maxZoomFactor ); + + kc->writeEntry( "MaxCachedImages", maxCachedImages ); + kc->writeEntry( "BackgroundColor", backgroundColor ); + + kc->writeEntry( "StartInLastDir", startInLastDir ); + + idata->save( kc ); + + kc->sync(); +} diff --git a/kuickshow/src/kuickdata.h b/kuickshow/src/kuickdata.h new file mode 100644 index 00000000..d05b3e99 --- /dev/null +++ b/kuickshow/src/kuickdata.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2003 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef KUICKDATA_H +#define KUICKDATA_H + +#include <qcolor.h> +#include <qstring.h> + +#include "imdata.h" + +class KConfig; + +// values are also used as combobox index defaultswidget.* +enum Rotation { ROT_0=0, ROT_90=1, ROT_180=2, ROT_270=3 }; + +// hmm, global declaration for now +enum FlipMode { FlipNone = 0, FlipHorizontal = 1, FlipVertical = 2 }; + +class KuickData +{ +public: + KuickData(); + ~KuickData(); + + void load(); + void save(); + + + ImData *idata; + + QString fileFilter; + uint slideDelay; + uint slideshowCycles; + bool slideshowFullscreen :1; + bool slideshowStartAtFirst :1; + + int brightnessSteps; + int contrastSteps; + int gammaSteps; + + int scrollSteps; + float zoomSteps; + + bool startInLastDir :1; + + bool preloadImage :1; + bool autoRotation :1; + bool fullScreen :1; + + // default image modifications + bool isModsEnabled :1; + + bool flipVertically :1; + bool flipHorizontally :1; + bool downScale :1; + bool upScale :1; + int maxUpScale; + float maxZoomFactor; + uint maxCachedImages; + Rotation rotation; + + QColor backgroundColor; + + +}; + + +extern KuickData* kdata; + +#endif diff --git a/kuickshow/src/kuickfile.cpp b/kuickshow/src/kuickfile.cpp new file mode 100644 index 00000000..1dd259d6 --- /dev/null +++ b/kuickshow/src/kuickfile.cpp @@ -0,0 +1,194 @@ +#include <qfile.h> + +#include <kdebug.h> +#include <kdeversion.h> +#include <kinstance.h> +#include <klocale.h> +#include <kprogress.h> +#include <kio/job.h> +#include <kio/netaccess.h> +#include <ktempfile.h> + +#include "filecache.h" +#include "kuickfile.h" + +KuickFile::KuickFile(const KURL& url) + : QObject(), + m_url( url ), + m_job( 0L ), + m_progress( 0L ), + m_currentProgress( 0 ) +{ + if ( m_url.isLocalFile()) + m_localFile = m_url.path(); + else { + const KURL& mostLocal = KIO::NetAccess::mostLocalURL( m_url, 0L ); + 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.findRev('.'); + if ( extIndex > 0 ) + ext = fileName.mid( extIndex + 1 ); + + QString tempDir = FileCache::self()->tempDir(); + KTempFile tempFile( tempDir, ext ); + tempFile.setAutoDelete( tempDir.isNull() ); // in case there is no proper tempdir, make sure to delete those files! + if ( tempFile.status() != 0 ) + return false; + + tempFile.close(); + if ( tempFile.status() != 0 ) + return false; + + KURL destURL; + destURL.setPath( tempFile.name() ); + + m_job = KIO::file_copy( m_url, destURL, -1, true, false, false ); // handling progress ourselves + m_job->setAutoErrorHandlingEnabled( true ); + connect( m_job, SIGNAL( result( KIO::Job * )), SLOT( slotResult( KIO::Job * ) )); + connect( m_job, SIGNAL( percent( KIO::Job *, unsigned long )), SLOT( slotProgress( KIO::Job *, 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; + } + + KProgressDialog *dialog = new KProgressDialog( parent ); + dialog->setModal( true ); + dialog->setCaption( i18n("Downloading %1...").arg( m_url.fileName() ) ); + dialog->setLabel( i18n("Please wait while downloading\n%1").arg( m_url.prettyURL() )); + dialog->setAllowCancel( true ); + dialog->setAutoClose( true ); + + m_progress = dialog->progressBar(); + m_progress->setTotalSteps( 100 ); // percent + m_progress->setProgress( m_currentProgress ); + dialog->exec(); + bool canceled = dialog->wasCancelled(); + delete dialog; + m_progress = 0L; + + 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( KIO::Job *job ) +{ + if (job != m_job) { // huh? + return; + } + + m_job = 0L; + + if ( job->error() != 0 ) { + m_currentProgress = 0; + + if ( job->error() != KIO::ERR_USER_CANCELED ) + kdWarning() << "ERROR: KuickFile::slotResult: " << job->errorString() << endl; + + QString canceledFile = static_cast<KIO::FileCopyJob*>(job)->destURL().path(); + QFile::remove( canceledFile ); + m_progress->topLevelWidget()->hide(); + } + else { + m_localFile = static_cast<KIO::FileCopyJob*>(job)->destURL().path(); + emit downloaded( this ); // before closing the progress dialog + + if ( m_progress ) { + m_progress->setProgress( 100 ); +#define BUGGY_VERSION KDE_MAKE_VERSION(3,5,2) + if ( KDE::version() <= BUGGY_VERSION ) { + m_progress->topLevelWidget()->hide(); // ### workaround broken KProgressDialog + } + } + } +} + +void KuickFile::slotProgress( KIO::Job *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->setProgress( (int) percent ); +} + +bool operator==( const KuickFile& first, const KuickFile& second ) { + return first.url().equals( second.url() ); +} + +#include "kuickfile.moc" diff --git a/kuickshow/src/kuickfile.h b/kuickshow/src/kuickfile.h new file mode 100644 index 00000000..957db6ee --- /dev/null +++ b/kuickshow/src/kuickfile.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** $Id: .emacs,v 1.3 2002/02/20 15:06:53 gis Exp $ +** +** Created : 2006 +** +** Copyright (C) 2006 Carsten Pfeiffer <[email protected]> +** +****************************************************************************/ + +#ifndef KUICKFILE_H +#define KUICKFILE_H + +#include <qobject.h> +#include <qstring.h> + +#include <kurl.h> +#include <kprogress.h> + +namespace KIO { + class Job; + class FileCopyJob; +} + +class KuickFile : public QObject +{ + Q_OBJECT + +public: + enum DownloadStatus + { + OK = 1, + CANCELED, + ERROR + }; + + KuickFile(const KURL& url); + + /** + * Cleans up resources and removes any temporary file, if available. + */ + ~KuickFile(); + + const KURL& url() const { return m_url; } + + + QString localFile() const; + + bool download(); + + /** + * @return true if download is in progress + */ + bool isDownloading() const { return m_job != 0L; } + + /** + * @return true if a local file is available, that is, + * @ref #localFile will return a non-empty name + * ### HERE ADD mostlylocal thing! + */ + bool isAvailable() const { return !localFile().isEmpty(); } + + /** + * @return true if @ref #isAvailable() returns true AND @ref #url() is a remote URL, + * i.e. the file really has been downloaded. + */ + bool hasDownloaded() const; + + /** + * Opens a modal dialog window, blocking user interaction until the download + * has finished. If the file is already available, this function will return true + * immediately. + * @return true when the download has finished or false when the user aborted the dialog + */ + KuickFile::DownloadStatus waitForDownload( QWidget *parent ); + +// bool needsDownload(); + +signals: + /** + * Signals that download has finished for that file. Will only be emitted for non-local files! + */ + void downloaded( KuickFile * ); + +private slots: + void slotResult( KIO::Job *job ); + void slotProgress( KIO::Job *job, unsigned long percent ); + +private: + KURL m_url; + QString m_localFile; + KIO::FileCopyJob *m_job; + KProgress *m_progress; + int m_currentProgress; + +}; + +bool operator==( const KuickFile& first, const KuickFile& second ); + +#endif // KUICKFILE_H diff --git a/kuickshow/src/kuickglobals.h b/kuickshow/src/kuickglobals.h new file mode 100644 index 00000000..4ea2ff3c --- /dev/null +++ b/kuickshow/src/kuickglobals.h @@ -0,0 +1,33 @@ +/* This file is part of the KDE project + Copyright (C) 2001,2002 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef KUICKGLOBALS_H +#define KUICKGLOBALS_H + +#include <kwinmodule.h> + +class KuickGlobals +{ +public: + KWinModule winModule; + + +}; + + +#endif // KUICKGLOBALS_H diff --git a/kuickshow/src/kuickimage.cpp b/kuickshow/src/kuickimage.cpp new file mode 100644 index 00000000..29e5de1c --- /dev/null +++ b/kuickshow/src/kuickimage.cpp @@ -0,0 +1,527 @@ +#include "kuickimage.h" + +KuickImage::KuickImage( const KuickFile * file, ImlibImage *im, ImlibData *id) + : QObject( 0L, 0L ) +{ + myFile = file; + myOrigIm = 0L; + myIm = im; + myId = id; + myPixmap = 0L; + myWidth = im->rgb_width; + myHeight = im->rgb_height; + myIsDirty = true; + + myOrigWidth = myWidth; + myOrigHeight = myHeight; + myRotation = ROT_0; + myFlipMode = FlipNone; +} + +KuickImage::~KuickImage() +{ + if ( myPixmap ) + Imlib_free_pixmap( myId, myPixmap ); + + if ( myOrigIm ) + { + Imlib_destroy_image( myId, myOrigIm ); + Imlib_kill_image( myId, myIm ); // kill scaled image (### really? analyze!) + } + else + Imlib_destroy_image( myId, myIm ); +} + + +Pixmap& KuickImage::pixmap() +{ + if ( myIsDirty ) + renderPixmap(); + + return myPixmap; +} + + +void KuickImage::renderPixmap() +{ + if ( !myIsDirty ) + return; + +// qDebug("### rendering: %s", myFilename.latin1()); + + if ( myPixmap ) + Imlib_free_pixmap( myId, myPixmap ); + + emit startRendering(); + +// #ifndef NDEBUG +// struct timeval tms1, tms2; +// gettimeofday( &tms1, NULL ); +// #endif + + Imlib_render( myId, myIm, myWidth, myHeight ); + myPixmap = Imlib_move_image( myId, myIm ); + +// #ifndef NDEBUG +// gettimeofday( &tms2, NULL ); +// qDebug("*** rendering image: %s, took %ld ms", myFilename.latin1(), +// (tms2.tv_usec - tms1.tv_usec)/1000); +// #endif + + emit stoppedRendering(); + + myIsDirty = false; +} + + +void KuickImage::rotate( Rotation rot ) +{ + if ( rot == ROT_180 ) { // rotate 180 degrees + Imlib_flip_image_horizontal( myId, myIm ); + Imlib_flip_image_vertical( myId, myIm ); + } + + else if ( rot == ROT_90 || rot == ROT_270 ) { + qSwap( myWidth, myHeight ); + Imlib_rotate_image( myId, myIm, -1 ); + + if ( rot == ROT_90 ) // rotate 90 degrees + Imlib_flip_image_horizontal( myId, myIm ); + else if ( rot == ROT_270 ) // rotate 270 degrees + Imlib_flip_image_vertical( myId, myIm ); + } + + myRotation = (Rotation) ((myRotation + rot) % 4); + myIsDirty = true; +} + + +bool KuickImage::rotateAbs( Rotation rot ) +{ + if ( myRotation == rot ) + return false; + + int diff = rot - myRotation; + bool clockWise = (diff > 0); + + switch( abs(diff) ) { + case ROT_90: + rotate( clockWise ? ROT_90 : ROT_270 ); + break; + case ROT_180: + rotate( ROT_180 ); + break; + case ROT_270: + rotate( clockWise ? ROT_270 : ROT_90 ); + break; + } + + return true; +} + +void KuickImage::flip( FlipMode flipMode ) +{ + if ( flipMode & FlipHorizontal ) + Imlib_flip_image_horizontal( myId, myIm ); + if ( flipMode & FlipVertical ) + Imlib_flip_image_vertical( myId, myIm ); + + myFlipMode = (FlipMode) (myFlipMode ^ flipMode); + myIsDirty = true; +} + +bool KuickImage::flipAbs( int mode ) +{ + if ( myFlipMode == mode ) + return false; + + bool changed = false; + + if ( ((myFlipMode & FlipHorizontal) && !(mode & FlipHorizontal)) || + (!(myFlipMode & FlipHorizontal) && (mode & FlipHorizontal)) ) { + Imlib_flip_image_horizontal( myId, myIm ); + changed = true; + } + + if ( ((myFlipMode & FlipVertical) && !(mode & FlipVertical)) || + (!(myFlipMode & FlipVertical) && (mode & FlipVertical)) ) { + Imlib_flip_image_vertical( myId, myIm ); + changed = true; + } + + if ( changed ) { + myFlipMode = (FlipMode) mode; + myIsDirty = true; + return true; + } + + return false; +} + + +void KuickImage::restoreOriginalSize() +{ + if (myWidth == myOrigWidth && myHeight == myOrigHeight) + return; + +// qDebug("-- restoreOriginalSize"); + + if ( myOrigIm != 0L ) + { + Imlib_destroy_image( myId, myIm ); + myIm = myOrigIm; + myOrigIm = 0L; + } + + myWidth = myOrigWidth; + myHeight = myOrigHeight; + myIsDirty = true; + + if ( myRotation == ROT_90 || myRotation == ROT_270 ) + qSwap( myWidth, myHeight ); +} + +void KuickImage::resize( int width, int height, KuickImage::ResizeMode mode ) +{ + if ( myWidth == width && myHeight == height ) + return; + + if ( mode == KuickImage::SMOOTH ) + { + if ( !smoothResize( width, height ) ) + fastResize( width, height ); + } + else + { + fastResize( width, height ); + } +} + + +void KuickImage::fastResize( int width, int height ) +{ +// qDebug("-- fastResize: %i x %i", width, height ); + + // lazy resizing (only done when rendering pixmap) + myWidth = width; + myHeight = height; + myIsDirty = true; +} + +bool KuickImage::smoothResize( int newWidth, int newHeight ) +{ +// qDebug("-- smoothResize: %i x %i", newWidth, newHeight); + + QImage *image = newQImage(); + // Note: QImage::ScaleMin seems to have a bug (off-by-one, sometimes results in width being 1 pixel too small) + QImage scaledImage = image->smoothScale(newWidth, newHeight, QImage::ScaleFree); + + delete image; + + + ImlibImage *newIm = toImage( myId, scaledImage ); + if ( newIm ) + { + if ( myOrigIm == 0 ) + myOrigIm = myIm; + + myIm = newIm; + myWidth = newWidth; + myHeight = newHeight; + myIsDirty = true; + return true; + } + + return false; +} + +QImage * KuickImage::newQImage() const +{ + ImlibImage *im; + +// qDebug("-- newQImage"); + + if ( myOrigIm != 0L && myRotation == ROT_0 && myFlipMode == FlipNone ) + { + // use original image if no other modifications have been applied + // ### use orig image always and reapply mods? + im = myOrigIm; + } + else + { + im = myIm; + } + + int w = im->rgb_width; + int h = im->rgb_height; + + QImage *image = new QImage( w, h, 32 ); + uchar *rgb = im->rgb_data; + QRgb **destImageData = reinterpret_cast<QRgb**>( image->jumpTable() ); + + + int byteIndex = 0; + int destLineIndex = 0; + int destByteIndex = 0; + for ( int pixel = 0; pixel < (w * h); pixel++ ) + { + if ( pixel != 0 && (pixel % w) == 0 ) + { + destLineIndex++; + destByteIndex = 0; + } + + uchar r = rgb[byteIndex++]; + uchar g = rgb[byteIndex++]; + uchar b = rgb[byteIndex++]; + + QRgb rgbPixel = qRgb( r, g, b ); + destImageData[destLineIndex][destByteIndex++] = rgbPixel; + } + + return image; +} + +ImlibImage * KuickImage::toImage( ImlibData *id, QImage& image ) +{ + if ( image.isNull() ) + return 0L; + + if ( image.depth() != 32 ) + { + image.setAlphaBuffer(false); + image = image.convertDepth(32); + + 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<QRgb *>( 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( id, newImageData, NULL, + image.width(), image.height() ); + + delete [] newImageData; + + return im; +} + + +#if 0 +bool KuickImage::smoothResize( int newWidth, int newHeight ) +{ + int numPixels = newWidth * newHeight; + const int NUM_BYTES_NEW = 3; // 24 bpp + uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW]; + + // ### endianness + // myIm : old image, old size + + + ///////////////////////////////////////////////// +// int w = myOrigWidth; //myViewport.width(); + //int h = myOrigHeight; //myViewport.height(); + + //QImage dst(w, h, myIm->depth(), myIm->numColors(), myIm->bitOrder()); + + //QRgb *scanline; + + int basis_ox, basis_oy, basis_xx, basis_yy; + + // ### we only scale with a fixed factor for x and y anyway + double scalex = newWidth / (double) myOrigWidth; + double scaley = newHeight / (double) myOrigHeight; + +// basis_ox=(int) (myViewport.left() * 4096.0 / scalex); +// basis_oy=(int) (myViewport.top() * 4096.0 / scaley); + basis_ox = 0; + basis_oy = 0; + basis_xx = (int) (4096.0 / scalex); + basis_yy = (int) (4096.0 / scaley); + + //qDebug("Basis: (%d, %d), (%d, 0), (0, %d)", basis_ox, basis_oy, basis_xx, basis_yy); + + int x2, y2; + + int max_x2 = (myOrigWidth << 12); + int max_y2 = (myOrigHeight << 12); + +// QRgb background = idata->backgroundColor.rgb(); + +// QRgb **imdata = (QRgb **) myIm->jumpTable(); +// QRgb *imdata = reinterpret_cast<QRgb*>( myIm->rgb_data ); + uchar *imdata = myIm->rgb_data; + + + int y = 0; + + +// for (;;) //fill the top of the target pixmap with the background color +// { +// y2 = basis_oy + y * basis_yy; +// +// if ((y2 >= 0 && (y2 >> 12) < myIm->height()) || y >= h) +// break; +// +// scanline = (QRgb*) dst.scanLine(y); +// for (int i = 0; i < w; i++) +// *(scanline++) = background; //qRgb(0,255,0); +// y++; +// } + + for (; y < newHeight; y++) + { +// scanline = (QRgb*) dst.scanLine(y); + + x2 = basis_ox; + y2 = basis_oy + y * basis_yy; + + if (y2 >= max_y2) + break; + + int x = 0; + +// while ((x2 < 0 || (x2 >> 12) >= myIm->width()) && x < w) //fill the left of the target pixmap with the background color +// { +// *(scanline++) = background; //qRgb(0,0,255); +// x2 += basis_xx; +// x++; +// } + + int top = y2 >> 12; + int bottom = top + 1; + if (bottom >= myOrigHeight) + bottom--; + +// for (; x < w; x++) + for (; x < newWidth; x++) // ### myOrigWidth orig + { + int left = x2 >> 12; + int right = left + 1; + + if (right >= myOrigWidth) + right = myOrigWidth - 1; + + unsigned int wx = x2 & 0xfff; //12 bits of precision for reasons which will become clear + unsigned int wy = y2 & 0xfff; //12 bits of precision + + unsigned int iwx = 0xfff - wx; + unsigned int iwy = 0xfff - wy; + + QRgb tl = 0, tr = 0, bl = 0, br = 0; + int ind = 0; + ind = (left + top * myOrigWidth) * 3; + tl = (imdata[ind] << 16); + tl |= (imdata[ind + 1] << 8); + tl |= (imdata[ind + 2] << 0); + int bar = imdata[ind + 2] << 8; + bar = qBlue(bar); + + ind = (right + top * myOrigWidth) * 3; + tr = (imdata[ind] << 16); + tr |= (imdata[ind + 1] << 8); + tr |= (imdata[ind + 2] << 0); + bar = imdata[ind + 2] << 8; + + ind = (left + bottom * myOrigWidth) * 3; + bl = (imdata[ind] << 16); + bl |= (imdata[ind + 1] << 8); + bl |= (imdata[ind + 2] << 0); + bar = imdata[ind + 2] << 8; + + ind = (right + bottom * myOrigWidth) * 3; + br = (imdata[ind] << 16); + br |= (imdata[ind + 1] << 8); + br |= (imdata[ind + 2] << 0); +// tl=imdata[top][left]; +// tr=imdata[top][right]; +// bl=imdata[bottom][left]; +// br=imdata[bottom][right]; + + /* + tl=getValidPixel(myIm, left, top, x, y); //these calls are expensive + tr=getValidPixel(myIm, right, top, x, y); //use them to debug segfaults in this function + bl=getValidPixel(myIm, left, bottom, x, y); + br=getValidPixel(myIm, right, bottom, x, y); + */ + + unsigned int r = (unsigned int) (qRed(tl) * iwx * iwy + qRed(tr) * wx* iwy + qRed(bl) * iwx * wy + qRed(br) * wx * wy); // NB 12+12+8 == 32 + unsigned int g = (unsigned int) (qGreen(tl) * iwx * iwy + qGreen(tr) * wx * iwy + qGreen(bl) * iwx * wy + qGreen(br) * wx * wy); + unsigned int b = (unsigned int) (qBlue(tl) * iwx * iwy + qBlue(tr) * wx * iwy + qBlue(bl) * iwx * wy + qBlue(br) * wx * wy); + + // ### endianness + //we're actually off by one in 255 here! (254 instead of 255) + int foo = r >> 24; + foo = g >> 24; + foo = b >> 24; + newImageData[(y * newWidth * 3) + (x * 3) + 0] = (r >> 24); + newImageData[(y * newWidth * 3) + (x * 3) + 1] = (g >> 24); + newImageData[(y * newWidth * 3) + (x * 3) + 2] = (b >> 24); +// *(scanline++) = qRgb(r >> 24, g >> 24, b >> 24); //we're actually off by one in 255 here + + x2 += basis_xx; + + if (x2 > max_x2) + { + x++; + break; + } + + } + +// while (x < w) //fill the right of each scanline with the background colour +// { +// *(scanline++) = background; //qRgb(255,0,0); +// x++; +// } + } + +// for (;;) //fill the bottom of the target pixmap with the background color +// { +// y2 = basis_oy + y * basis_yy; +// +// if (y >= h) +// break; +// +// scanline = (QRgb*) dst.scanLine(y); +// for (int i = 0; i < w; i++) +// *(scanline++) = background; //qRgb(255,255,0); +// y++; +// } + + // ### keep orig image somewhere but delete all scaled images! + ImlibImage *newIm = Imlib_create_image_from_data( myId, newImageData, NULL, + newWidth, newHeight ); + delete[] newImageData; + + if ( newIm ) + { + myScaledIm = newIm; + myIsDirty = true; + myWidth = newWidth; + myHeight = newHeight; + } + + return myIm != 0L; +// return dst.copy(); +} +#endif + +#include "kuickimage.moc" diff --git a/kuickshow/src/kuickimage.h b/kuickshow/src/kuickimage.h new file mode 100644 index 00000000..745facf4 --- /dev/null +++ b/kuickshow/src/kuickimage.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** $Id: .emacs,v 1.3 2002/02/20 15:06:53 gis Exp $ +** +** Created : 2002 +** +** Copyright (C) 2002 Carsten Pfeiffer <[email protected]> +** +****************************************************************************/ + +#ifndef KUICKIMAGE_H +#define KUICKIMAGE_H + +#include <qimage.h> +#include <qobject.h> + +#include <kurl.h> + +#include "kuickdata.h" +#include "kuickfile.h" + +// #include those AFTER Qt-includes! +#include <Imlib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +// #include <X11/extensions/shape.h> + + +class KuickImage : public QObject +{ + Q_OBJECT + +public: + enum ResizeMode { FAST, SMOOTH }; + + KuickImage( const KuickFile * file, ImlibImage *im, ImlibData *id ); + ~KuickImage(); + + int width() const { return myWidth; } + int height() const { return myHeight; } + int originalWidth() const { return myOrigWidth; } + int originalHeight() const { return myOrigHeight; } + + void resize( int width, int height, KuickImage::ResizeMode mode ); + void restoreOriginalSize(); + void rotate( Rotation rot ); + bool rotateAbs( Rotation rot ); + void flip( FlipMode flipMode ); + bool flipAbs( int mode ); + ImlibImage * imlibImage() const { return myIm; } + Pixmap& pixmap(); + void renderPixmap(); +// const QString& filename() const { return myFilename;} + const KuickFile& file() const { return *myFile; } + const KURL& url() const { return myFile->url(); } + + void setDirty( bool d ) { myIsDirty = d; } + bool isDirty() const { return myIsDirty; } + Rotation absRotation() const { return myRotation; } + FlipMode flipMode() const { return myFlipMode; } + + static ImlibImage * toImage( ImlibData *id, QImage& image ); + +private: + void fastResize( int newWidth, int newHeight ); + bool smoothResize( int width, int height ); + /** + * Note: caller must delete it! + */ + QImage * newQImage() const; + + const KuickFile * myFile; + + int myWidth; + int myHeight; + ImlibImage * myOrigIm; + ImlibImage * myIm; + ImlibData * myId; + Pixmap myPixmap; + bool myIsDirty; + + int myOrigWidth; + int myOrigHeight; + Rotation myRotation; + FlipMode myFlipMode; + +signals: + void startRendering(); + void stoppedRendering(); +}; + + +#endif // KUICKIMAGE_H diff --git a/kuickshow/src/kuickshow.cpp b/kuickshow/src/kuickshow.cpp new file mode 100644 index 00000000..7e8718fb --- /dev/null +++ b/kuickshow/src/kuickshow.cpp @@ -0,0 +1,1443 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2006 Carsten Pfeiffer <[email protected]> + + 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 <stdio.h> +#include <assert.h> + +#include <qdir.h> +#include <qdesktopwidget.h> +#include <qdialog.h> +#include <qglobal.h> +#include <qkeycode.h> +#include <qlayout.h> +#include <qsize.h> +#include <qstring.h> + +#include <kaboutdata.h> +#include <kaccel.h> +#include <kaction.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <kconfig.h> +#include <kcursor.h> +#include <kdeversion.h> +#include <kfiledialog.h> +#include <kfilemetainfo.h> +#include <kglobal.h> +#include <khelpmenu.h> +#include <kiconloader.h> +#include <kio/netaccess.h> +#include <klocale.h> +#include <kmenubar.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kprotocolinfo.h> +#include <kpropertiesdialog.h> +#include <kprotocolinfo.h> +#include <kstatusbar.h> +#include <kstdaction.h> +#include <kstandarddirs.h> +#include <kstartupinfo.h> +#include <ktoolbar.h> +#include <kurlcombobox.h> +#include <kurlcompletion.h> +#include <kurldrag.h> +#include <kwin.h> +#include <kstdguiitem.h> + +#include <kdebug.h> + +#include "aboutwidget.h" +#include "filewidget.h" +#include "filecache.h" +#include "imdata.h" +#include "imagewindow.h" +#include "imlibwidget.h" +#include "kuick.h" +#include "kuickfile.h" + +#ifdef index +#undef index +#endif + +#include "kuickconfigdlg.h" +#include "kuickdata.h" +#include "kuickshow.h" +#include "version.h" + +#ifdef KeyPress +#undef KeyPress +#endif + +KuickData* kdata; + +static const int URL_ITEM = 0; +static const int META_ITEM = 1; + +QValueList<ImageWindow*> KuickShow::s_viewers; + +KuickShow::KuickShow( const char *name ) + : KMainWindow( 0L, name ), + m_slideshowCycle( 1 ), + fileWidget( 0L ), + dialog( 0L ), + id( 0L ), + m_viewer( 0L ), + oneWindowAction( 0L ), + m_accel( 0L ), + m_delayedRepeatItem( 0L ), + m_slideShowStopped(false) +{ + aboutWidget = 0L; + kdata = new KuickData; + kdata->load(); + + initImlib(); + resize( 400, 500 ); + + m_slideTimer = new QTimer( this ); + connect( m_slideTimer, SIGNAL( timeout() ), SLOT( nextSlide() )); + + + KConfig *kc = KGlobal::config(); + + bool isDir = false; // true if we get a directory on the commandline + + // parse commandline options + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + // files to display + // either a directory to display, an absolute path, a relative path, or a URL + KURL startDir; + startDir.setPath( QDir::currentDirPath() + '/' ); + + 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, + i18n("Do you really want to display this 1 image at the same time? This might be quite resource intensive and could overload your computer.<br>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.<br>If you choose %1, only the first image will be shown.", numArgs).arg(KStdGuiItem::no().plainText()), + i18n("Display Multiple Images?")) + != KMessageBox::Yes ) + { + numArgs = 1; + } + } + + for ( int i = 0; i < numArgs; i++ ) { + KURL url = args->url( i ); + KFileItem item( KFileItem::Unknown, KFileItem::Unknown, url, false ); + + // 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() ) + { + KMimeType::Ptr mime = KMimeType::findByURL( url ); + QString name = mime->name(); + if ( name == "application/octet-stream" ) // unknown -> stat() + name = KIO::NetAccess::mimetype( url, this ); + + // 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/" ) ) + { + FileWidget::setImage( item, true ); + showImage( &item, true, false, 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) || args->isSet( "lastfolder" )) { + kc->setGroup( "SessionSettings"); + startDir = kc->readPathEntry( "CurrentDirectory", startDir.url() ); + } + + if ( s_viewers.isEmpty() || isDir ) { + initGUI( startDir ); + if (!kapp->isRestored()) // during session management, readProperties() will show() + show(); + } + + else { // don't show browser, when image on commandline + hide(); + KStartupInfo::appStarted(); + } +} + + +KuickShow::~KuickShow() +{ + saveSettings(); + + if ( m_viewer ) + m_viewer->close( true ); + + FileCache::shutdown(); + free( id ); + kapp->quit(); + + delete kdata; +} + +// TODO convert to use xmlui file +void KuickShow::initGUI( const KURL& startDir ) +{ + KURL startURL( startDir ); + if ( !KProtocolInfo::supportsListing( startURL ) ) + startURL = KURL(); + + fileWidget = new FileWidget( startURL, this, "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 KURL& )), + this, SLOT( dirSelected( const KURL& )) ); + + + fileWidget->setAcceptDrops(true); + connect( fileWidget, SIGNAL( dropped( const KFileItem *, QDropEvent *, const KURL::List & )), + this, SLOT( slotDropped( const KFileItem *, QDropEvent *, const KURL::List &)) ); + + // setup actions + KAction *open = KStdAction::open( this, SLOT( slotOpenURL() ), + coll, "openURL" ); + + KAction *print = KStdAction::print( this, SLOT( slotPrint() ), + coll, "kuick_print" ); + print->setText( i18n("Print Image...") ); + + KAction *configure = new KAction( i18n("Configure %1...").arg( KGlobal::instance()->aboutData()->programName() ), "configure", + KShortcut(), + this, SLOT( configuration() ), + coll, "kuick_configure" ); + KAction *slide = new KAction( i18n("Start Slideshow" ), "ksslide", + KShortcut( Key_F2 ), + this, SLOT( startSlideShow() ), + coll, "kuick_slideshow" ); + KAction *about = new KAction( i18n( "About KuickShow" ), "about", + KShortcut(), + this, SLOT( about() ), coll, "about" ); + + oneWindowAction = new KToggleAction( i18n("Open Only One Image Window"), + "window_new", + KShortcut( CTRL+Key_N ), coll, + "kuick_one window" ); + + m_toggleBrowserAction = new KToggleAction( i18n("Show File Browser"), KShortcut( Key_Space ), coll, "toggleBrowser" ); + m_toggleBrowserAction->setCheckedState(i18n("Hide File Browser")); + connect( m_toggleBrowserAction, SIGNAL( toggled( bool ) ), + SLOT( toggleBrowser() )); + + KAction *showInOther = new KAction( i18n("Show Image"), KShortcut(), + this, SLOT( slotShowInOtherWindow() ), + coll, "kuick_showInOtherWindow" ); + KAction *showInSame = new KAction( i18n("Show Image in Active Window"), + KShortcut(), + this, SLOT( slotShowInSameWindow() ), + coll, "kuick_showInSameWindow" ); + KAction *showFullscreen = new KAction( i18n("Show Image in Fullscreen Mode"), + KShortcut(), this, SLOT( slotShowFullscreen() ), + coll, "kuick_showFullscreen" ); + + KAction *quit = KStdAction::quit( this, SLOT(slotQuit()), coll, "quit"); + + // remove QString::null parameter -- ellis + coll->readShortcutSettings( QString::null ); + m_accel = coll->accel(); + + // menubar + KMenuBar *mBar = menuBar(); + QPopupMenu *fileMenu = new QPopupMenu( mBar, "file" ); + open->plug( fileMenu ); + showInOther->plug( fileMenu ); + showInSame->plug( fileMenu ); + showFullscreen->plug( fileMenu ); + fileMenu->insertSeparator(); + slide->plug( fileMenu ); + print->plug( fileMenu ); + fileMenu->insertSeparator(); + quit->plug( fileMenu ); + + QPopupMenu *editMenu = new QPopupMenu( mBar, "edit" ); + coll->action("mkdir")->plug( editMenu ); + coll->action("delete")->plug( editMenu ); + editMenu->insertSeparator(); + coll->action("properties")->plug( editMenu ); + + + // remove the Sorting submenu (and the separator below) + // from the main contextmenu + KActionMenu *sortingMenu = static_cast<KActionMenu*>( coll->action("sorting menu")); + KActionMenu *mainActionMenu = static_cast<KActionMenu*>( coll->action("popupMenu")); + QPopupMenu *mainPopup = mainActionMenu->popupMenu(); + int sortingIndex = mainPopup->indexOf( sortingMenu->itemId( 0 ) ); + int separatorId = mainPopup->idAt( sortingIndex + 1 ); + QMenuItem *separatorItem = mainPopup->findItem( separatorId ); + if ( separatorItem && separatorItem->isSeparator() ) + mainPopup->removeItem( separatorId ); + mainActionMenu->remove( sortingMenu ); + + // add the sorting menu and a separator into the View menu + KActionMenu *viewActionMenu = static_cast<KActionMenu*>( coll->action("view menu")); + viewActionMenu->popupMenu()->insertSeparator( 0 ); + sortingMenu->plug( viewActionMenu->popupMenu(), 0 ); // on top of the menu + + + QPopupMenu *settingsMenu = new QPopupMenu( mBar, "settings" ); + configure->plug( settingsMenu ); + + mBar->insertItem( i18n("&File"), fileMenu ); + mBar->insertItem( i18n("&Edit"), editMenu ); + viewActionMenu->plug( mBar ); + mBar->insertItem( i18n("&Settings"), settingsMenu ); + + // toolbar + KToolBar *tBar = toolBar(); + tBar->setText( i18n( "Main Toolbar" ) ); + + coll->action("up")->plug( tBar ); + coll->action("back")->plug( tBar ); + coll->action("forward")->plug( tBar ); + coll->action("home")->plug( tBar ); + coll->action("reload")->plug( tBar ); + + tBar->insertSeparator(); + + coll->action( "short view" )->plug( tBar ); + coll->action( "detailed view" )->plug( tBar ); + coll->action( "preview")->plug( tBar ); + + tBar->insertSeparator(); + configure->plug( tBar ); + slide->plug( tBar ); + tBar->insertSeparator(); + oneWindowAction->plug( tBar ); + print->plug( tBar ); + tBar->insertSeparator(); + about->plug( tBar ); + + QPopupMenu *help = helpMenu( QString::null, false ); + mBar->insertItem( KStdGuiItem::help().text() , help ); + + + KStatusBar* sBar = statusBar(); + sBar->insertItem( " ", URL_ITEM, 10 ); + sBar->insertItem( " ", META_ITEM, 2 ); + sBar->setItemAlignment(URL_ITEM, QLabel::AlignVCenter | QLabel::AlignLeft); + + fileWidget->setFocus(); + + KConfig *kc = KGlobal::config(); + kc->setGroup("SessionSettings"); + bool oneWindow = kc->readBoolEntry("OpenImagesInActiveWindow", true ); + oneWindowAction->setChecked( oneWindow ); + + tBar->show(); + + // Address box in address tool bar + KToolBar *addressToolBar = toolBar( "address_bar" ); + const int ID_ADDRESSBAR = 1; + + cmbPath = new KURLComboBox( KURLComboBox::Directories, + true, addressToolBar, "address_combo_box" ); + KURLCompletion *cmpl = new KURLCompletion( KURLCompletion::DirCompletion ); + cmbPath->setCompletionObject( cmpl ); + cmbPath->setAutoDeleteCompletionObject( true ); + + addressToolBar->insertWidget( ID_ADDRESSBAR, 1, cmbPath); + addressToolBar->setItemAutoSized( ID_ADDRESSBAR ); + + connect( cmbPath, SIGNAL( urlActivated( const KURL& )), + this, SLOT( slotSetURL( const KURL& ))); + connect( cmbPath, SIGNAL( returnPressed()), + this, SLOT( slotURLComboReturnPressed())); + + + fileWidget->initActions(); + fileWidget->clearHistory(); + dirSelected( fileWidget->url() ); + + setCentralWidget( fileWidget ); + setupGUI( KMainWindow::Save ); + + coll->action( "reload" )->setShortcut( KStdAccel::reload() ); + coll->action( "short view" )->setShortcut(Key_F6); + coll->action( "detailed view" )->setShortcut(Key_F7); + coll->action( "show hidden" )->setShortcut(Key_F8); + coll->action( "mkdir" )->setShortcut(Key_F10); + coll->action( "preview" )->setShortcut(Key_F11); + coll->action( "separate dirs" )->setShortcut(Key_F12); +} + +void KuickShow::redirectDeleteAndTrashActions(KActionCollection *coll) +{ + KAction *action = coll->action("delete"); + if (action) + { + action->disconnect(fileWidget); + connect(action, SIGNAL(activated()), this, SLOT(slotDeleteCurrentImage())); + } + + action = coll->action("trash"); + if (action) + { + action->disconnect(fileWidget); + connect(action, SIGNAL(activated()), this, SLOT(slotTrashCurrentImage())); + } +} + +void KuickShow::slotSetURL( const KURL& url ) +{ + fileWidget->setURL( url, true ); +} + +void KuickShow::slotURLComboReturnPressed() +{ + KURL where = KURL::fromPathOrURL( cmbPath->currentText() ); + slotSetURL( where ); +} + +void KuickShow::viewerDeleted() +{ + ImageWindow *viewer = (ImageWindow*) sender(); + s_viewers.remove( viewer ); + if ( viewer == m_viewer ) + m_viewer = 0L; + + if ( !haveBrowser() && s_viewers.isEmpty() ) { + saveSettings(); + FileCache::shutdown(); + ::exit(0); + } + + else if ( haveBrowser() ) { + setActiveWindow(); + // 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 *fi ) +{ + KFileItem *item = const_cast<KFileItem *>( fi ); + statusBar()->changeItem( item->getStatusBarInfo(), URL_ITEM ); + bool image = FileWidget::isImage( fi ); + + QString meta; + if ( image ) + { + KFileMetaInfo info = item->metaInfo(); + if ( info.isValid() ) + { + meta = info.item( KFileMimeTypeInfo::Size ).string(); + KFileMetaInfoGroup group = info.group( "Technical" ); + if ( group.isValid() ) + { + QString bpp = group.item( "BitDepth" ).string(); + if ( !bpp.isEmpty() ) + meta.append( ", " ).append( bpp ); + } + } + } + statusBar()->changeItem( meta, META_ITEM ); + + 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 KURL& url ) +{ + if ( url.isLocalFile() ) + setCaption( url.path() ); + else + setCaption( url.prettyURL() ); + + cmbPath->setURL( url ); + statusBar()->changeItem( url.prettyURL(), URL_ITEM ); +} + +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 ) +{ + newWindow |= !m_viewer; + fullscreen |= (newWindow && kdata->fullScreen); + if ( FileWidget::isImage( fi ) ) { + + if ( newWindow ) { + m_viewer = new ImageWindow( kdata->idata, id, 0L, "image window" ); + m_viewer->setFullscreen( fullscreen ); + s_viewers.append( m_viewer ); + + 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; + +// file->waitForDownload( this ); +// QString filename; +// KIO::NetAccess::download(fi->url(), filename, this); + + if ( !safeViewer->showNextImage( fi->url() ) ) { + m_viewer = safeViewer; + safeViewer->close( true ); // 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 ) { + KFileItem *item = 0L; // don't move cursor + item = fileWidget->getItem( FileWidget::Next, true ); + if ( item ) + 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; + KFileItem *item = fileWidget->getCurrentItem(false); + list.append (item); + + if (KMessageBox::warningContinueCancel( + parent, + i18n("<qt>Do you really want to delete\n <b>'%1'</b>?</qt>").arg(item->url().pathOrURL()), + i18n("Delete File"), + KStdGuiItem::del(), + "Kuick_delete_current_image") + != KMessageBox::Continue) + { + return; + } + + tryShowNextImage(); + fileWidget->del(list, false, false); +} + +void KuickShow::performTrashCurrentImage(QWidget *parent) +{ + assert(fileWidget != 0L); + + KFileItemList list; + KFileItem *item = fileWidget->getCurrentItem(false); + if (!item) return; + + list.append (item); + + if (KMessageBox::warningContinueCancel( + parent, + i18n("<qt>Do you really want to trash\n <b>'%1'</b>?</qt>").arg(item->url().pathOrURL()), + i18n("Trash File"), + KGuiItem(i18n("to trash", "&Trash"),"edittrash"), + "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) + 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) + 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 ) { + 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 ) { // last image + if ( m_slideshowCycle < kdata->slideshowCycles + || kdata->slideshowCycles == 0 ) { + item = fileWidget->gotoFirstImage(); + if ( item ) { + nextSlide( item ); + m_slideshowCycle++; + return; + } + } + + m_viewer->close( true ); + fileWidget->actionCollection()->action("kuick_slideshow")->setEnabled( true ); + return; + } + + nextSlide( item ); +} + +void KuickShow::nextSlide( 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 ) + return; + + KFileItemListIterator it( *items ); + + // don't show the image, just print + ImageWindow *iw = new ImageWindow( 0, id, this, "printing image" ); + KFileItem *item; + while ( (item = it.current()) ) { + if (FileWidget::isImage( item ) && iw->loadImage( item->url() )) + iw->printImage(); + ++it; + } + + iw->close( true ); +} + +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 KURL::List &urls) +{ + KURL::List::ConstIterator it = urls.begin(); + for ( ; it != urls.end(); ++it ) + { + KFileItem item( KFileItem::Unknown, KFileItem::Unknown, *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() +{ + KMainWindow::show(); + (void) Kuick::frameSize( winId() ); +} + +void KuickShow::slotAdvanceImage( ImageWindow *view, int steps ) +{ + KFileItem *item = 0L; // to be shown + KFileItem *item_next = 0L; // 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 ) { // 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<QKeyEvent *>( e ); + + if ( k ) { + if ( KStdAccel::quit().contains( KKey( k ) ) ) { + saveSettings(); + deleteAllViewers(); + FileCache::shutdown(); + ::exit(0); + } + else if ( KStdAccel::help().contains( KKey( k ) ) ) { + appHelpActivated(); + return true; + } + } + + + ImageWindow *window = dynamic_cast<ImageWindow*>( 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 = 0L; // the image to be shown + KFileItem *item_next = 0L; // the image to be cached + + if ( k ) { // keypress + ret = true; + int key = k->key(); + + // Key_Shift shouldn't load the browser in nobrowser mode, it + // is used for zooming in the imagewindow + // Key_Alt shouldn't either - otherwise Alt+F4 doesn't work, the + // F4 gets eaten (by NetAccess' modal dialog maybe?) + + if ( !fileWidget ) + { + if ( key != Key_Escape && key != Key_Shift && key != Key_Alt ) + { + KuickFile *file = m_viewer->currentFile(); +// QFileInfo fi( m_viewer->filename() ); +// start.setPath( fi.dirPath( true ) ); + initGUI( file->url().upURL() ); + + // 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() ) + { + fileWidget->setCurrentItem( file->url().fileName() ); + 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().fileName() ); + connect( fileWidget, SIGNAL( finished() ), + SLOT( slotReplayEvent() )); + } + + return true; + } + + return KMainWindow::eventFilter( o, e ); + } + + // we definitely have a fileWidget here! + + KKey kkey( k ); + if ( key == Key_Home || KStdAccel::home().contains( kkey ) ) + { + item = fileWidget->gotoFirstImage(); + item_next = fileWidget->getNext( false ); + } + + else if ( key == Key_End || KStdAccel::end().contains( kkey ) ) + { + item = fileWidget->gotoLastImage(); + item_next = fileWidget->getPrevious( false ); + } + + else if ( fileWidget->actionCollection()->action("delete")->shortcut().contains( key )) + { + kdDebug() << "WOW, deletion happens here!" << endl; +// KFileItem *cur = fileWidget->getCurrentItem( false ); + (void) fileWidget->getCurrentItem( false ); + item = fileWidget->getNext( false ); // don't move + if ( !item ) + item = fileWidget->getPrevious( false ); + KFileItem it( KFileItem::Unknown, KFileItem::Unknown, + m_viewer->url() ); + KFileItemList list; + list.append( &it ); + if ( fileWidget->del(list, window, + (k->state() & ShiftButton) == 0) == 0L ) + return true; // aborted deletion + + // ### check failure asynchronously and restore old item? + fileWidget->setCurrentItem( item ); + } + + else if ( m_toggleBrowserAction->shortcut().contains( key ) ) + { + toggleBrowser(); + return true; // don't pass keyEvent + } + + else + ret = false; + + + if ( FileWidget::isImage( item ) ) { +// QString filename; +// KIO::NetAccess::download(item->url(), filename, this); + m_viewer->showNextImage( item->url() ); + + if ( kdata->preloadImage && item_next ) { // 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<QMouseEvent*>( e ); + if ( ev->button() == LeftButton ) + { + if ( s_viewers.count() == 1 ) + { + if ( !fileWidget ) + { +// KURL start; +// QFileInfo fi( window->filename() ); +// start.setPath( fi.dirPath( true ) ); + initGUI( window->currentFile()->url().fileName() ); + } + show(); + raise(); + } + + window->close( true ); + + ev->accept(); + ret = true; + } + } + + } // isA ImageWindow + + + if ( ret ) + return true; + + return KMainWindow::eventFilter( o, e ); +} + +void KuickShow::configuration() +{ + if ( !m_accel ) { + KURL start; + start.setPath( QDir::homeDirPath() ); + initGUI( KURL::fromPathOrURL( QDir::homeDirPath() ) ); + } + + dialog = new KuickConfigDialog( fileWidget->actionCollection(), 0L, + "dialog", false ); + dialog->resize( 540, 510 ); + dialog->setIcon( kapp->miniIcon() ); + + connect( dialog, SIGNAL( okClicked() ), + this, SLOT( slotConfigApplied() ) ); + connect( dialog, SIGNAL( applyClicked() ), + this, SLOT( slotConfigApplied() ) ); + connect( dialog, SIGNAL( finished() ), + this, SLOT( slotConfigClosed() ) ); + + fileWidget->actionCollection()->action( "kuick_configure" )->setEnabled( false ); + dialog->show(); +} + + +void KuickShow::slotConfigApplied() +{ + dialog->applyConfig(); + + initImlib(); + kdata->save(); + + ImageWindow *viewer; + QValueListIterator<ImageWindow*> it = s_viewers.begin(); + while ( it != s_viewers.end() ) { + viewer = *it; + viewer->updateActions(); + ++it; + } + + fileWidget->reloadConfiguration(); +} + + +void KuickShow::slotConfigClosed() +{ + dialog->delayedDestruct(); + fileWidget->actionCollection()->action( "kuick_configure" )->setEnabled( true ); +} + +void KuickShow::about() +{ + if ( !aboutWidget ) + aboutWidget = new AboutWidget( 0L, "about" ); + + aboutWidget->adjustSize(); + +#if KDE_VERSION >= 310 + KDialog::centerOnScreen( aboutWidget ); +#else +// Not fixed because it must be dead code now. + QDesktopWidget *desktop = QApplication::desktop(); + int screen = desktop->screenNumber( aboutWidget ); + if ( screen == -1 ) + screen = desktop->primaryScreen(); + + QRect r = desktop->screenGeometry( screen ); + aboutWidget->move( r.center().x() - aboutWidget->width()/2, + r.center().y() - aboutWidget->height()/2 ); +#endif + + aboutWidget->show(); +} + +// ------ sessionmanagement - load / save current directory ----- +void KuickShow::readProperties( KConfig *kc ) +{ + assert( fileWidget ); // from SM, we should always have initGUI on startup + QString dir = kc->readPathEntry( "CurrentDirectory" ); + if ( !dir.isEmpty() ) { + fileWidget->setURL( KURL::fromPathOrURL( dir ), true ); + fileWidget->clearHistory(); + } + + const KURL& listedURL = fileWidget->url(); + QStringList images = kc->readPathListEntry( "Images shown" ); + QStringList::Iterator it; + bool hasCurrentURL = false; + + for ( it = images.begin(); it != images.end(); ++it ) { + KFileItem item( KFileItem::Unknown, KFileItem::Unknown, KURL::fromPathOrURL( *it ), false ); + 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().fileName() ); + hasCurrentURL = true; + } + } + + bool visible = kc->readBoolEntry( "Browser visible", false ); + if ( visible || s_viewers.isEmpty() ) + show(); +} + +void KuickShow::saveProperties( KConfig *kc ) +{ + kc->writeEntry( "Browser visible", fileWidget && fileWidget->isVisible() ); + if (fileWidget) + kc->writePathEntry( "CurrentDirectory", fileWidget->url().url() ); + + QStringList urls; + QValueListIterator<ImageWindow*> it; + for ( it = s_viewers.begin(); it != s_viewers.end(); ++it ) + { + const KURL& url = (*it)->currentFile()->url(); + if ( url.isLocalFile() ) + urls.append( url.path() ); + else + urls.append( url.prettyURL() ); // ### check if writePathEntry( prettyURL ) works! + } + + kc->writePathEntry( "Images shown", urls ); +} + +// -------------------------------------------------------------- + +void KuickShow::saveSettings() +{ + KConfig *kc = KGlobal::config(); + + kc->setGroup("SessionSettings"); + if ( oneWindowAction ) + kc->writeEntry( "OpenImagesInActiveWindow", oneWindowAction->isChecked() ); + + if ( fileWidget ) { + kc->writePathEntry( "CurrentDirectory", fileWidget->url().prettyURL() ); // ### was url().url() + fileWidget->writeConfig( kc, "Filebrowser" ); + } + + kc->sync(); +} + + +void KuickShow::messageCantLoadImage( const KuickFile *, const QString& message ) +{ + m_viewer->clearFocus(); + KMessageBox::information( m_viewer, message, i18n("Error"), "kuick_cant_load_image" ); +} + +void KuickShow::initImlib() +{ + ImData *idata = kdata->idata; + ImlibInitParams par; + initImlibParams( idata, &par ); + + id = Imlib_init_with_params( x11Display(), &par ); + if ( !id ) { + initImlibParams( idata, &par ); + + qWarning("*** KuickShow: Whoops, can't initialize imlib, trying my own palettefile now."); + QString paletteFile = locate( "data", "kuickshow/im_palette.pal" ); + // ### - does the qstrdup() cure the segfault in imlib eventually? + char *file = qstrdup( paletteFile.local8Bit() ); + par.palettefile = file; + par.flags |= PARAMS_PALETTEFILE; + + qWarning("Palettefile: %s", par.palettefile ); + + id = Imlib_init_with_params( x11Display(), &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(x11Display(), x11Screen()); + + 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; + + // ### WORKAROUND for QIconView bug in Qt <= 3.0.3 at least + if ( fileWidget && fileWidget->view() ) { + QWidget *widget = fileWidget->view()->widget(); + if ( widget->inherits( "QIconView" ) || widget->child(0, "QIconView" ) ){ + fileWidget->setSorting( fileWidget->sorting() ); + } + } + // -------------------------------------------------------------- +} + +void KuickShow::replayAdvance(DelayedRepeatEvent *event) +{ + // ### 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() ); + } + } + // -------------------------------------------------------------- + + slotAdvanceImage( event->viewer, *(int *) (event->data) ); +} + +void KuickShow::delayAction(DelayedRepeatEvent *event) +{ + if (m_delayedRepeatItem) + return; + + m_delayedRepeatItem = event; + + KURL url = event->viewer->currentFile()->url(); +// QFileInfo fi( event->viewer->filename() ); +// start.setPath( fi.dirPath( true ) ); + initGUI( url.upURL() ); + + // see eventFilter() for explanation and similar code + if ( fileWidget->dirLister()->isFinished() && + fileWidget->dirLister()->rootItem() ) + { + fileWidget->setCurrentItem( url.fileName() ); + QTimer::singleShot( 0, this, SLOT( doReplay())); + } + else + { + fileWidget->setInitialItem( url.fileName() ); + 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: + kdWarning() << "doReplay: unknown action -- ignoring: " << m_delayedRepeatItem->action << endl; + 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(); + KWin::activateWindow( winId() ); // ### this should not be necessary +// setFocus(); + } + else if ( !s_viewers.isEmpty() ) + hide(); +} + +void KuickShow::slotOpenURL() +{ + KFileDialog dlg(QString::null, kdata->fileFilter, this, "filedialog", true); + dlg.setMode( KFile::Files | KFile::Directory ); + dlg.setCaption( i18n("Select Files or Folder to Open") ); + + if ( dlg.exec() == QDialog::Accepted ) + { + KURL::List urls = dlg.selectedURLs(); + KURL::List::ConstIterator it = urls.begin(); + for ( ; it != urls.end(); ++it ) + { + KFileItem item( KFileItem::Unknown, KFileItem::Unknown, *it ); + if ( FileWidget::isImage( &item ) ) + showImage( &item, true ); + else + fileWidget->setURL( *it, true ); + } + } +} + +void KuickShow::deleteAllViewers() +{ + QValueListIterator<ImageWindow*> it = s_viewers.begin(); + for ( ; it != s_viewers.end(); ++it ) { + (*it)->disconnect( SIGNAL( destroyed() ), this, SLOT( viewerDeleted() )); + (*it)->close( true ); + } + + s_viewers.clear(); + m_viewer = 0L; +} + +KActionCollection * KuickShow::actionCollection() const +{ + if ( fileWidget ) + return fileWidget->actionCollection(); + + return KMainWindow::actionCollection(); +} + +#include "kuickshow.moc" diff --git a/kuickshow/src/kuickshow.desktop b/kuickshow/src/kuickshow.desktop new file mode 100644 index 00000000..b2e2ee37 --- /dev/null +++ b/kuickshow/src/kuickshow.desktop @@ -0,0 +1,93 @@ +[Desktop Entry] +Name=Kuickshow +Name[af]=Vinnig-vertoon +Name[ar]=عرض سريع +Name[eo]=Rapidmontrilo +Name[fr]=KuickShow +Name[hi]=क्विक-शो +Name[it]=KuickShow +Name[ko]=K퀵쇼 +Name[ne]=क्विकशो +Name[pt_BR]=KRápida Exibição +Name[ta]=விரைவுகாட்சி +Name[th]=ดูภาพด่วน - K +Name[ven]=Musumbedzo wa kuick +Name[zh_TW]=Kuickshow 快秀 +Name[zu]=I Kuickshow +Exec=kuickshow %i %m -caption "%c" %U +Icon=kuickshow +Type=Application +MimeType=image/gif;image/x-xpm;image/x-xbm;image/jpeg;image/png;image/tiff;image/x-bmp;image/x-psd;image/x-eim;image/x-portable-bitmap;image/x-portable-pixmap;image/x-portable-greymap; +DocPath=kuickshow/index.html +Terminal=false +InitialPreference=6 + +GenericName=Image Viewer +GenericName[af]=Beeld Aansig +GenericName[ar]=عارض صور +GenericName[bg]=Преглед на изображения +GenericName[br]=Gweler ar skeudennoù +GenericName[bs]=Preglednik slika +GenericName[ca]=Visualitzador d'imatges +GenericName[cs]=Prohlížeč obrázků +GenericName[cy]=Gwelydd Delweddau +GenericName[da]=Billedfremviser +GenericName[de]=Bildbetrachter +GenericName[el]=Προβολέας εικόνων +GenericName[eo]=Bildorigardilo +GenericName[es]=Visor de imágenes +GenericName[et]=Pildifailide näitaja +GenericName[eu]=Irudi ikustailua +GenericName[fa]=مشاهدهگر تصویر +GenericName[fi]=Kuvannäytin +GenericName[fr]=Afficheur d'images +GenericName[ga]=Amharcán Íomhánna +GenericName[gl]=Visor de imaxes +GenericName[he]=מציג תמונות +GenericName[hi]=छवि प्रदर्शक +GenericName[hr]=Preglednik slika +GenericName[hu]=Képnézegető +GenericName[is]=Myndaskoðari +GenericName[it]=Visore di immagini +GenericName[ja]=画像ビューア +GenericName[kk]=Кескіндерді қарау +GenericName[km]=កម្មវិធីមើលរូបភាព +GenericName[lt]=Paveikslėlių žiūriklis +GenericName[lv]=Attēlu Skatītājs +GenericName[ms]=Paparan Imej +GenericName[mt]=Werrej tal-istampi +GenericName[nb]=Bildefremviser +GenericName[nds]=Bildkieker +GenericName[ne]=छवि दर्शक +GenericName[nl]=Afbeeldingenweergaveprogramma +GenericName[nn]=Biletvisar +GenericName[nso]=Molebeledi wa Ponagalo +GenericName[pa]=ਚਿੱਤਰ ਦਰਸ਼ਕ +GenericName[pl]=Przeglądarka obrazków +GenericName[pt]=Visualizador de Imagens +GenericName[pt_BR]=Visualizador de Imagens +GenericName[ro]=Vizualizor de imagini +GenericName[ru]=Просмотр изображений +GenericName[rw]=Mugaraza Shusho +GenericName[se]=Govvačájeheaddji +GenericName[sk]=Prehliadač obrázkov +GenericName[sl]=Pregledovalnik slik +GenericName[sr]=Приказивач слика +GenericName[sr@Latn]=Prikazivač slika +GenericName[sv]=Bildvisare +GenericName[ta]=பிம்ப காட்சி +GenericName[tg]=Намоиши тасвирот +GenericName[th]=เครื่องมือแสดงภาพ +GenericName[tr]=Resim Göstericisi +GenericName[uk]=Переглядач зображень +GenericName[uz]=Rasm koʻruvchi +GenericName[uz@cyrillic]=Расм кўрувчи +GenericName[ven]=Tshivhoni tsha Mutaleli +GenericName[wa]=Håyneu d' imådjes +GenericName[xh]=Umboniseli Womfanekiso +GenericName[zh_CN]=图片查看程序 +GenericName[zh_HK]=圖像檢視器 +GenericName[zh_TW]=影像檢視程式 +GenericName[zu]=Umbonisi Womfanekiso +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Graphics; diff --git a/kuickshow/src/kuickshow.h b/kuickshow/src/kuickshow.h new file mode 100644 index 00000000..d6239b64 --- /dev/null +++ b/kuickshow/src/kuickshow.h @@ -0,0 +1,178 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2006 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef KUICKSHOW_H +#define KUICKSHOW_H + +#include <qevent.h> +#include <qguardedptr.h> +#include <qstring.h> +#include <qvaluelist.h> + +#include <kfileitem.h> +#include <kmainwindow.h> +#include <kurl.h> + +#include <Imlib.h> + +#include "aboutwidget.h" + +class FileWidget; +class ImageWindow; +class ImData; +class KuickConfigDialog; + +class KAccel; +class KConfig; +class KToggleAction; +class AboutWidget; + +class KURL; +class KURLComboBox; + +class KuickFile; + +class DelayedRepeatEvent +{ +public: + DelayedRepeatEvent( ImageWindow *view, QKeyEvent *ev ) { + viewer = view; + event = ev; + } + DelayedRepeatEvent( ImageWindow *view, int action, void *data ) { + this->viewer = view; + this->action = action; + this->data = data; + this->event = 0L; + } + + ~DelayedRepeatEvent() { + delete event; + } + + enum Action + { + DeleteCurrentFile, + TrashCurrentFile, + AdvanceViewer + }; + + ImageWindow *viewer; + QKeyEvent *event; + int action; + void *data; +}; + + +class KuickShow : public KMainWindow +{ + Q_OBJECT + +public: + KuickShow( const char *name=0 ); + ~KuickShow(); + + virtual void show(); + static QValueList<ImageWindow*> s_viewers; + + // overridden to make KDCOPActionProxy work -- all our actions are not + // in the mainwindow's collection, but in the filewidget's. + virtual KActionCollection* actionCollection() const; + + +protected: + virtual void readProperties( KConfig * ); + void initImlibParams( ImData *, ImlibInitParams * ); + void tryShowNextImage(); + +private slots: + void toggleBrowser(); + void slotQuit() { delete this; } + void slotPrint(); + void slotConfigApplied(); + void slotConfigClosed(); + void messageCantLoadImage( const KuickFile * file, const QString& message); + bool showImage(const KFileItem *, bool newWindow = false, + bool fullscreen = false, bool moveToTopLeft = true ); + void showFileItem( ImageWindow *, const KFileItem * ); + void slotHighlighted( const KFileItem * ); + void slotSelected( const KFileItem * ); + void dirSelected( const KURL& ); + void configuration(); + void about(); + void startSlideShow(); + void pauseSlideShow(); + void nextSlide(); + void nextSlide( KFileItem *item ); + void viewerDeleted(); + void slotDropped( const KFileItem *, QDropEvent *, const KURL::List &); + void slotSetActiveViewer( ImageWindow *i ) { m_viewer = i; } + void slotAdvanceImage( ImageWindow *, int steps ); + + void slotShowInSameWindow(); + void slotShowInOtherWindow(); + void slotShowFullscreen(); + + void slotReplayEvent(); + void slotOpenURL(); + void slotSetURL( const KURL& ); + void slotURLComboReturnPressed(); +// void invalidateImages( const KFileItemList& items ); + void slotDeleteCurrentImage(ImageWindow *viewer); + void slotTrashCurrentImage(ImageWindow *viewer); + void slotDeleteCurrentImage(); + void slotTrashCurrentImage(); + + void doReplay(); + +private: + void initGUI( const KURL& startDir ); + bool eventFilter( QObject *, QEvent * ); + void initImlib(); + void saveProperties( KConfig * ); + void saveSettings(); + bool haveBrowser() const; + void delayedRepeatEvent( ImageWindow *, QKeyEvent * ); + void abortDelayedEvent(); + void deleteAllViewers(); + void redirectDeleteAndTrashActions(KActionCollection *coll); + + void delayAction(DelayedRepeatEvent *event); + void replayAdvance(DelayedRepeatEvent *event); + + void performDeleteCurrentImage(QWidget *parent); + void performTrashCurrentImage(QWidget *parent); + + uint viewItem, renameItem, deleteItem, printItem; + uint m_slideshowCycle; + + FileWidget *fileWidget; + KURLComboBox *cmbPath; + KuickConfigDialog *dialog; + ImlibData *id; + ImageWindow *m_viewer; + KToggleAction *oneWindowAction; + KAccel *m_accel; + DelayedRepeatEvent *m_delayedRepeatItem; + QTimer *m_slideTimer; + bool m_slideShowStopped; + KToggleAction *m_toggleBrowserAction; + QGuardedPtr<AboutWidget> aboutWidget; +}; + +#endif diff --git a/kuickshow/src/kurlwidget.cpp b/kuickshow/src/kurlwidget.cpp new file mode 100644 index 00000000..0a23fa3a --- /dev/null +++ b/kuickshow/src/kurlwidget.cpp @@ -0,0 +1,42 @@ +/* This file is part of the KDE project + Copyright (C) 1998,1999,2000 Carsten Pfeiffer <[email protected]> + + 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 <qtooltip.h> + +#include <kurl.h> +#include <krun.h> + +#include "kurlwidget.h" + +KURLWidget::KURLWidget(const QString& text, QWidget *parent, const char *name) + : KURLLabel( parent, name ) +{ + setText( text ); + connect( this, SIGNAL( leftClickedURL() ), SLOT( run() )); + setUseTips( true ); +} + +void KURLWidget::run() +{ + KURL ku( url() ); + if ( ku.isValid() ) { + (void) new KRun( ku, this ); + } +} + +#include "kurlwidget.moc" diff --git a/kuickshow/src/kurlwidget.h b/kuickshow/src/kurlwidget.h new file mode 100644 index 00000000..2f35e009 --- /dev/null +++ b/kuickshow/src/kurlwidget.h @@ -0,0 +1,36 @@ +/* This file is part of the KDE project + Copyright (C) 1998,1999,2000 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef KURLWIDGET_H +#define KURLWIDGET_H + +#include <kurllabel.h> + +class KURLWidget : public KURLLabel +{ + Q_OBJECT + +public: + KURLWidget( const QString& text, QWidget *, const char *name=0 ); + +protected slots: + virtual void run(); + +}; + +#endif diff --git a/kuickshow/src/main.cpp b/kuickshow/src/main.cpp new file mode 100644 index 00000000..dfb462f0 --- /dev/null +++ b/kuickshow/src/main.cpp @@ -0,0 +1,66 @@ +/* This file is part of the KDE project + Copyright (C) 1998-2002 Carsten Pfeiffer <[email protected]> + + 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 <qstring.h> + +#include <kaboutdata.h> +#include <kapplication.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <kdebug.h> + +#include "kuickshow.h" +#include "version.h" + +static KCmdLineOptions options[] = +{ + { "lastfolder", I18N_NOOP("Start in the last visited folder, not the " + "current working folder."), 0 }, + { "d", 0, 0 }, // short option for --lastdir + { "+[files]", I18N_NOOP("Optional image filenames/urls to show"), 0 }, + KCmdLineLastOption +}; + +extern "C" KDE_EXPORT int kdemain(int argc, char **argv) +{ + KAboutData about( + "kuickshow", I18N_NOOP( "KuickShow" ), KUICKSHOWVERSION, + I18N_NOOP("A fast and versatile image viewer" ), + KAboutData::License_GPL, "(c) 1998-2006, Carsten Pfeiffer", + 0 /*text*/, "http://devel-home.kde.org/~pfeiffer/" ); + + about.addAuthor( "Carsten Pfeiffer", 0, "[email protected]", + "http://devel-home.kde.org/~pfeiffer/" ); + about.addCredit( "Rober Hamberger", 0, "[email protected]" ); + about.addCredit( "Thorsten Scheuermann", 0, "[email protected]" ); + + KCmdLineArgs::init( argc, argv, &about ); + KCmdLineArgs::addCmdLineOptions( options ); + + KApplication app; + + if ( app.isRestored() ) { + (new KuickShow())->restore( 1, false ); // don't show automatically + } + else { + KuickShow *k = new KuickShow( "kuickshow" ); + app.setMainWidget( k ); + } + + return app.exec(); +} diff --git a/kuickshow/src/mainwidget.cpp b/kuickshow/src/mainwidget.cpp new file mode 100644 index 00000000..caa6700c --- /dev/null +++ b/kuickshow/src/mainwidget.cpp @@ -0,0 +1,43 @@ +/* This file is part of the KDE project + Copyright (C) 1998,1999 Carsten Pfeiffer <[email protected]> + + 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 "fileview.h" +#include "kuickdata.h" +#include "mainwidget.h" + + +MainWidget::MainWidget( QString startDir, QWidget *parent, + const char *name ) : QWidget ( parent, name ) +{ + box = new FileView( startDir, true, (QDir::Dirs | QDir::Files), + this, "fileview" ); +} + + +MainWidget::~MainWidget() +{ + +} + + +// for now, no layout managers +void MainWidget::resizeEvent( QResizeEvent * ) +{ + box->resize( width(), height() ); +} +#include "mainwidget.moc" diff --git a/kuickshow/src/mainwidget.h b/kuickshow/src/mainwidget.h new file mode 100644 index 00000000..36285dd8 --- /dev/null +++ b/kuickshow/src/mainwidget.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE project + Copyright (C) 1998 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include <qevent.h> +#include <qstring.h> +#include <qwidget.h> + +class FileView; + +class MainWidget : public QWidget +{ + Q_OBJECT + +public: + MainWidget( QString, QWidget *parent, const char *name=0L); + ~MainWidget(); + + FileView* getFileBox() { return box; } + +protected: + virtual void resizeEvent( QResizeEvent * ); + +private: + FileView *box; + +}; + + +#endif diff --git a/kuickshow/src/printing.cpp b/kuickshow/src/printing.cpp new file mode 100644 index 00000000..be57ca7f --- /dev/null +++ b/kuickshow/src/printing.cpp @@ -0,0 +1,338 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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 <qcheckbox.h> +#include <qfont.h> +#include <qfontmetrics.h> +#include <qgrid.h> +#include <qhbox.h> +#include <qlayout.h> +#include <qimage.h> +#include <kimageeffect.h> +#include <qpaintdevicemetrics.h> +#include <qpainter.h> +#include <qradiobutton.h> +#include <qvbuttongroup.h> +#include <qcolor.h> + +#include <kcombobox.h> +#include <kdialog.h> +#include <klocale.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <knuminput.h> +#include <kprinter.h> +#include <ktempfile.h> + +#include "imagewindow.h" +#include "printing.h" +#include "version.h" + +bool Printing::printImage( ImageWindow& imageWin, QWidget *parent ) +{ + QString imageURL = imageWin.url().prettyURL(); + KPrinter printer; + printer.setDocName( imageURL ); + printer.setCreator( "KuickShow-" KUICKSHOWVERSION ); + + KPrinter::addDialogPage( new KuickPrintDialogPage( parent, "kuick page")); + + if ( printer.setup( parent, i18n("Print %1").arg(printer.docName().section('/', -1)) ) ) + { + KTempFile tmpFile( QString::null, ".png" ); + if ( tmpFile.status() == 0 ) + { + tmpFile.setAutoDelete( true ); + if ( imageWin.saveImage( tmpFile.name(), true ) ) + return printImageWithQt( tmpFile.name(), printer, + imageURL ); + } + + return false; + } + + return true; // user aborted +} + +bool Printing::printImageWithQt( const QString& filename, KPrinter& printer, + const QString& originalFileName ) +{ + QImage image( filename ); + if ( image.isNull() ) { + kdWarning() << "Can't load image: " << filename << " for printing.\n"; + return false; + } + + QPainter p; + p.begin( &printer ); + + QPaintDeviceMetrics metrics( &printer ); + p.setFont( KGlobalSettings::generalFont() ); + QFontMetrics fm = p.fontMetrics(); + + int w = metrics.width(); + int h = metrics.height(); + + QString t = "true"; + QString f = "false"; + + // Black & white print? + if ( printer.option( "app-kuickshow-blackwhite" ) != f) { + image = image.convertDepth( 1, Qt::MonoOnly | Qt::ThresholdDither | Qt::AvoidDither ); + } + + int filenameOffset = 0; + bool printFilename = printer.option( "app-kuickshow-printFilename" ) != f; + if ( printFilename ) { + filenameOffset = fm.lineSpacing() + 14; + h -= filenameOffset; // filename goes into one line! + } + + // + // shrink image to pagesize, if necessary + // + bool shrinkToFit = (printer.option( "app-kuickshow-shrinkToFit" ) != f); + QSize imagesize = image.size(); + if ( shrinkToFit && (image.width() > w || image.height() > h) ) { + imagesize.scale( w, h, QSize::ScaleMin ); + } + + + // + // align image + // + bool ok = false; + int alignment = printer.option("app-kuickshow-alignment").toInt( &ok ); + if ( !ok ) + 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 = metrics.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, const char *name ) + : KPrintDialogPage( parent, name ) +{ + setTitle( i18n("Image Settings") ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setMargin( KDialog::marginHint() ); + layout->setSpacing( KDialog::spacingHint() ); + + m_addFileName = new QCheckBox( i18n("Print fi&lename below image"), this); + m_addFileName->setChecked( true ); + layout->addWidget( m_addFileName ); + + m_blackwhite = new QCheckBox ( i18n("Print image in &black and white"), this); + m_blackwhite->setChecked( false ); + layout->addWidget (m_blackwhite ); + + QVButtonGroup *group = new QVButtonGroup( i18n("Scaling"), this ); + group->setRadioButtonExclusive( true ); + layout->addWidget( group ); + // m_shrinkToFit = new QRadioButton( i18n("Shrink image to &fit, if necessary"), group ); + m_shrinkToFit = new QCheckBox( i18n("Shrink image to &fit, if necessary"), group ); + m_shrinkToFit->setChecked( true ); + + QWidget *widget = new QWidget( group ); + QGridLayout *grid = new QGridLayout( widget, 3, 3 ); + grid->addColSpacing( 0, 30 ); + grid->setColStretch( 0, 0 ); + grid->setColStretch( 1, 1 ); + grid->setColStretch( 2, 10 ); + + m_scale = new QRadioButton( i18n("Print e&xact size: "), widget ); + m_scale->setEnabled( false ); // ### + grid->addMultiCellWidget( m_scale, 0, 0, 0, 1 ); + group->insert( m_scale ); + connect( m_scale, SIGNAL( toggled( bool )), SLOT( toggleScaling( bool ))); + + m_units = new KComboBox( false, widget, "unit combobox" ); + grid->addWidget( m_units, 0, 2, AlignLeft ); + m_units->insertItem( i18n("Millimeters") ); + m_units->insertItem( i18n("Centimeters") ); + m_units->insertItem( i18n("Inches") ); + + m_width = new KIntNumInput( widget, "exact width" ); + grid->addWidget( m_width, 1, 1 ); + m_width->setLabel( i18n("&Width:" ) ); + m_width->setMinValue( 1 ); + + m_height = new KIntNumInput( widget, "exact height" ); + grid->addWidget( m_height, 2, 1 ); + m_height->setLabel( i18n("&Height:" ) ); + m_height->setMinValue( 1 ); +} + +KuickPrintDialogPage::~KuickPrintDialogPage() +{ +} + +void KuickPrintDialogPage::getOptions( QMap<QString,QString>& opts, + bool /*incldef*/ ) +{ + QString t = "true"; + QString f = "false"; + +// ### opts["app-kuickshow-alignment"] = ; + opts["app-kuickshow-printFilename"] = m_addFileName->isChecked() ? t : f; + opts["app-kuickshow-blackwhite"] = m_blackwhite->isChecked() ? t : f; + opts["app-kuickshow-shrinkToFit"] = m_shrinkToFit->isChecked() ? t : f; + opts["app-kuickshow-scale"] = m_scale->isChecked() ? t : f; + opts["app-kuickshow-scale-unit"] = m_units->currentText(); + opts["app-kuickshow-scale-width-pixels"] = QString::number( scaleWidth() ); + opts["app-kuickshow-scale-height-pixels"] = QString::number( scaleHeight() ); +} + +void KuickPrintDialogPage::setOptions( const QMap<QString,QString>& opts ) +{ + QString t = "true"; + QString f = "false"; + + m_addFileName->setChecked( opts["app-kuickshow-printFilename"] != f ); + // This sound strange, but if I copy the code on the line above, the checkbox + // was always checked. And this isn't the wanted behavior. So, with this works. + // KPrint magic ;-) + m_blackwhite->setChecked ( false ); + m_shrinkToFit->setChecked( opts["app-kuickshow-shrinkToFit"] != f ); + m_scale->setChecked( opts["app-kuickshow-scale"] == t ); + + m_units->setCurrentItem( opts["app-kuickshow-scale-unit"] ); + + bool ok; + int val = opts["app-kuickshow-scale-width-pixels"].toInt( &ok ); + if ( ok ) + setScaleWidth( val ); + val = opts["app-kuickshow-scale-height-pixels"].toInt( &ok ); + if ( ok ) + setScaleHeight( val ); + + if ( m_scale->isChecked() == m_shrinkToFit->isChecked() ) + m_shrinkToFit->setChecked( !m_scale->isChecked() ); + + // ### re-enable when implementednn + toggleScaling( false && m_scale->isChecked() ); +} + +void KuickPrintDialogPage::toggleScaling( bool enable ) +{ + m_width->setEnabled( enable ); + m_height->setEnabled( enable ); + m_units->setEnabled( enable ); +} + +int KuickPrintDialogPage::scaleWidth() const +{ + return fromUnitToPixels( m_width->value() ); +} + +int KuickPrintDialogPage::scaleHeight() const +{ + return fromUnitToPixels( m_height->value() ); +} + +void KuickPrintDialogPage::setScaleWidth( int pixels ) +{ + m_width->setValue( (int) pixelsToUnit( pixels ) ); +} + +void KuickPrintDialogPage::setScaleHeight( int pixels ) +{ + m_width->setValue( (int) pixelsToUnit( pixels ) ); +} + +int KuickPrintDialogPage::fromUnitToPixels( float /*value*/ ) const +{ + return 1; // ### +} + +float KuickPrintDialogPage::pixelsToUnit( int /*pixels*/ ) const +{ + return 1.0; // ### +} + +#include "printing.moc" diff --git a/kuickshow/src/printing.h b/kuickshow/src/printing.h new file mode 100644 index 00000000..eb679f7e --- /dev/null +++ b/kuickshow/src/printing.h @@ -0,0 +1,85 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef PRINTING_H +#define PRINTING_H + +#include <qfontmetrics.h> +#include <qstring.h> + +#include <kdeprint/kprintdialogpage.h> + +class QCheckBox; +class QRadioButton; +class KComboBox; +class KPrinter; +class KIntNumInput; + +class ImageWindow; + + +class Printing +{ +public: + static bool printImage( ImageWindow& imageWin, QWidget *parent = 0L); + static bool printImageWithQt( const QString& filename, KPrinter& printer, + const QString& originalFileName ); + +private: + static void addConfigPages(); + static QString minimizeString( QString text, const QFontMetrics& metrics, + int maxWidth ); + +}; + +class KuickPrintDialogPage : public KPrintDialogPage +{ + Q_OBJECT + +public: + KuickPrintDialogPage( QWidget *parent = 0L, const char *name = 0 ); + ~KuickPrintDialogPage(); + + virtual void getOptions(QMap<QString,QString>& opts, bool incldef = false); + virtual void setOptions(const QMap<QString,QString>& opts); + +private slots: + void toggleScaling( bool enable ); + +private: + // return values in pixels! + int scaleWidth() const; + int scaleHeight() const; + + void setScaleWidth( int pixels ); + void setScaleHeight( int pixels ); + + int fromUnitToPixels( float val ) const; + float pixelsToUnit( int pixels ) const; + + QCheckBox *m_shrinkToFit; + QRadioButton *m_scale; + KIntNumInput *m_width; + KIntNumInput *m_height; + KComboBox *m_units; + QCheckBox *m_addFileName; + QCheckBox *m_blackwhite; + +}; + +#endif // PRINTING_H diff --git a/kuickshow/src/slideshowwidget.cpp b/kuickshow/src/slideshowwidget.cpp new file mode 100644 index 00000000..447b996e --- /dev/null +++ b/kuickshow/src/slideshowwidget.cpp @@ -0,0 +1,80 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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 <qcheckbox.h> +#include <qlayout.h> + +#include <kdialog.h> +#include <klocale.h> +#include <knuminput.h> + +#include "slideshowwidget.h" + + +SlideShowWidget::SlideShowWidget( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ +// setTitle( i18n("Slideshow") ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setSpacing( KDialog::spacingHint() ); + + m_fullScreen = new QCheckBox( i18n("Switch to &full-screen"), this ); + m_startWithCurrent = new QCheckBox( i18n("S&tart with current image"), this); + + m_delayTime = new KIntNumInput( this, "delay time" ); + m_delayTime->setLabel( i18n("De&lay between slides:") ); + m_delayTime->setSuffix( i18n(" sec") ); + m_delayTime->setRange( 0, 60 * 60 ); // 1 hour + m_delayTime->setSpecialValueText( i18n("Wait for key") ); + + m_cycles = new KIntNumInput( m_delayTime, 1, this ); + m_cycles->setLabel( i18n("&Iterations (0 = infinite):") ); + m_cycles->setSpecialValueText( i18n("infinite") ); + m_cycles->setRange( 0, 500 ); + + layout->addWidget( m_fullScreen ); + layout->addWidget( m_startWithCurrent ); + layout->addWidget( m_delayTime ); + layout->addWidget( m_cycles ); + layout->addStretch( 1 ); + + loadSettings( *kdata ); +} + +SlideShowWidget::~SlideShowWidget() +{ +} + +void SlideShowWidget::loadSettings( const KuickData& data ) +{ + m_delayTime->setValue( data.slideDelay / 1000 ); + m_cycles->setValue( data.slideshowCycles ); + m_fullScreen->setChecked( data.slideshowFullscreen ); + m_startWithCurrent->setChecked( !data.slideshowStartAtFirst ); +} + +void SlideShowWidget::applySettings( KuickData& data ) +{ + data.slideDelay = m_delayTime->value() * 1000; + data.slideshowCycles = m_cycles->value(); + data.slideshowFullscreen = m_fullScreen->isChecked(); + data.slideshowStartAtFirst = !m_startWithCurrent->isChecked(); +} + +#include "slideshowwidget.moc" diff --git a/kuickshow/src/slideshowwidget.h b/kuickshow/src/slideshowwidget.h new file mode 100644 index 00000000..c651103d --- /dev/null +++ b/kuickshow/src/slideshowwidget.h @@ -0,0 +1,44 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Carsten Pfeiffer <[email protected]> + + 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. +*/ + +#ifndef SLIDESHOWWIDGET_H +#define SLIDESHOWWIDGET_H + +#include "kuickdata.h" + +class QCheckBox; +class KIntNumInput; + +class SlideShowWidget : public QWidget +{ + Q_OBJECT +public: + SlideShowWidget( QWidget *parent, const char *name ); + ~SlideShowWidget(); + + virtual void loadSettings( const KuickData& data ); + virtual void applySettings( KuickData& data ); + +private: + KIntNumInput *m_delayTime; + KIntNumInput *m_cycles; + QCheckBox *m_fullScreen; + QCheckBox *m_startWithCurrent; +}; + +#endif // SLIDESHOWWIDGET_H diff --git a/kuickshow/src/version.h b/kuickshow/src/version.h new file mode 100644 index 00000000..2465967f --- /dev/null +++ b/kuickshow/src/version.h @@ -0,0 +1,3 @@ +#ifndef KUICKSHOWVERSION +#define KUICKSHOWVERSION "0.8.13" +#endif |