File indexing completed on 2024-05-05 16:39:01

0001 /* This file is part of the KDE project
0002    Copyright (C) 1998-2006 Carsten Pfeiffer <pfeiffer@kde.org>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public
0006    License as published by the Free Software Foundation, version 2.
0007 
0008    This program is distributed in the hope that it will be useful,
0009    but WITHOUT ANY WARRANTY; without even the implied warranty of
0010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0011     General Public License for more details.
0012 
0013    You should have received a copy of the GNU General Public License
0014    along with this program; see the file COPYING.  If not, write to
0015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0016    Boston, MA 02110-1301, USA.
0017 */
0018 
0019 #include "imagewindow.h"
0020 
0021 #include <KActionCollection>
0022 #include <KCursor>
0023 #include <KIconLoader>
0024 #include <KIO/StoredTransferJob>
0025 #include <KJobWidgets>
0026 #include <KLocalizedString>
0027 #include <KMessageBox>
0028 #include <KPropertiesDialog>
0029 #include <KStandardAction>
0030 #include <KStandardGuiItem>
0031 #include <KStandardShortcut>
0032 #include <KToggleFullScreenAction>
0033 #include <KUrlMimeData>
0034 #include <KWindowSystem>
0035 
0036 #include <QApplication>
0037 #include <QBitmap>
0038 #include <QCheckBox>
0039 #include <QContextMenuEvent>
0040 #include <QCursor>
0041 #include <QDesktopWidget>
0042 #include <QDragEnterEvent>
0043 #include <QDropEvent>
0044 #include <QFileDialog>
0045 #include <QFocusEvent>
0046 #include <QGridLayout>
0047 #include <QKeyEvent>
0048 #include <QMenu>
0049 #include <QMimeData>
0050 #include <QMouseEvent>
0051 #include <QPainter>
0052 #include <QPen>
0053 #include <QPixmap>
0054 #include <QRect>
0055 #include <QResizeEvent>
0056 #include <QScopedPointer>
0057 #include <QStandardPaths>
0058 #include <QString>
0059 #include <QStringList>
0060 #include <QTemporaryFile>
0061 #include <QTimer>
0062 #include <QUrl>
0063 #include <QWheelEvent>
0064 
0065 #include <stdlib.h>
0066 
0067 #include "filecache.h"
0068 #include "imagemods.h"
0069 #include "kuick.h"
0070 #include "kuickdata.h"
0071 #include "kuickimage.h"
0072 #include "printing.h"
0073 
0074 
0075 QCursor *ImageWindow::s_handCursor = 0L;
0076 
0077 ImageWindow::ImageWindow( ImData *_idata, ImlibData *id, QWidget *parent )
0078     : ImlibWidget( _idata, id, parent )
0079 {
0080     init();
0081 }
0082 
0083 ImageWindow::ImageWindow( ImData *_idata, QWidget *parent )
0084     : ImlibWidget( _idata, parent )
0085 {
0086     init();
0087 }
0088 
0089 ImageWindow::~ImageWindow()
0090 {
0091 }
0092 
0093 
0094 void ImageWindow::init()
0095 {
0096     setFocusPolicy( Qt::StrongFocus );
0097 
0098     KCursor::setAutoHideCursor( this, true, true );
0099     KCursor::setHideCursorDelay( 1500 );
0100 
0101     // give the image window a different WM_CLASS
0102     QByteArray appName = QCoreApplication::applicationName().toLocal8Bit();
0103     XClassHint hint;
0104     hint.res_name = appName.data();
0105     hint.res_class = const_cast<char*>( "ImageWindow" );
0106     XSetClassHint( getX11Display(), winId(), &hint );
0107 
0108     viewerMenu = 0L;
0109     gammaMenu = 0L;
0110     brightnessMenu = 0L;
0111     contrastMenu = 0L;
0112 
0113 
0114     m_actions = new KActionCollection( this );
0115     m_actions->addAssociatedWidget( this );
0116 
0117     if ( !s_handCursor ) {
0118         QString file = QStandardPaths::locate(QStandardPaths::AppDataLocation, "pics/handcursor.png");
0119         if ( !file.isEmpty() )
0120             s_handCursor = new QCursor( QPixmap(file) );
0121         else
0122             s_handCursor = new QCursor( Qt::ArrowCursor );
0123     }
0124 
0125     setupActions();
0126     imageCache->setMaxImages( kdata->maxCachedImages );
0127 
0128     transWidget    = 0L;
0129     myIsFullscreen = false;
0130 
0131     xpos = 0, ypos = 0;
0132     m_numHeads = ScreenCount( getX11Display() );
0133 
0134     setAcceptDrops( true );
0135     setBackgroundColor( kdata->backgroundColor );
0136 
0137     // TODO: static non-POD data - is this advisable?
0138     static QPixmap imageIcon = KIconLoader::global()->loadIcon("imageviewer-medium", KIconLoader::User);
0139     static QPixmap miniImageIcon = KIconLoader::global()->loadIcon( "imageviewer-small",KIconLoader::User);
0140     KWindowSystem::setIcons( winId(), imageIcon, miniImageIcon );
0141 }
0142 
0143 void ImageWindow::updateActions()
0144 {
0145     m_actions->readSettings();
0146 }
0147 
0148 void ImageWindow::setupActions()
0149 {
0150     QAction *nextImage = m_actions->addAction( "next_image" );
0151     nextImage->setText( i18n("Show Next Image") );
0152     m_actions->setDefaultShortcuts(nextImage, KStandardShortcut::next());
0153     connect( nextImage, SIGNAL( triggered() ), this, SLOT( slotRequestNext() ) );
0154 
0155     QAction* showPreviousImage = m_actions->addAction( "previous_image" );
0156     showPreviousImage->setText( i18n("Show Previous Image") );
0157     m_actions->setDefaultShortcuts(showPreviousImage, KStandardShortcut::prior());
0158     connect( showPreviousImage, SIGNAL( triggered() ), this, SLOT( slotRequestPrevious() ) );
0159 
0160     QAction* deleteImage = m_actions->addAction( "delete_image" );
0161     deleteImage->setText( i18n("Delete Image") );
0162     m_actions->setDefaultShortcut(deleteImage, QKeySequence(Qt::ShiftModifier | Qt::Key_Delete));
0163     connect( deleteImage, SIGNAL( triggered() ), this, SLOT( imageDelete() ) );
0164 
0165     QAction *trashImage = m_actions->addAction( "trash_image" );
0166     trashImage->setText( i18n("Move Image to Trash") );
0167     m_actions->setDefaultShortcut(trashImage, Qt::Key_Delete);
0168     connect( trashImage, SIGNAL( triggered() ), this, SLOT( imageTrash() ) );
0169 
0170 
0171     QAction* zoomIn = KStandardAction::zoomIn( this, SLOT( zoomIn() ), m_actions );
0172     m_actions->setDefaultShortcut(zoomIn, Qt::Key_Plus);
0173     m_actions->addAction( "zoom_in",  zoomIn );
0174 
0175     QAction *zoomOut = KStandardAction::zoomOut( this, SLOT( zoomOut() ), m_actions );
0176     m_actions->setDefaultShortcut(zoomOut, Qt::Key_Minus);
0177     m_actions->addAction( "zoom_out",  zoomOut );
0178 
0179     QAction *restoreSize = m_actions->addAction( "original_size" );
0180     restoreSize->setText( i18n("Restore Original Size") );
0181     m_actions->setDefaultShortcut(restoreSize, Qt::Key_O);
0182     connect( restoreSize, SIGNAL( triggered() ), this, SLOT( showImageOriginalSize() ) );
0183 
0184     QAction *maximize = m_actions->addAction( "maximize" );
0185     maximize->setText( i18n("Maximize") );
0186     m_actions->setDefaultShortcut(maximize, Qt::Key_M);
0187     connect( maximize, SIGNAL( triggered() ), this, SLOT( maximize() ) );
0188 
0189     QAction *rotate90 = m_actions->addAction( "rotate90" );
0190     rotate90->setText( i18n("Rotate 90 Degrees") );
0191     m_actions->setDefaultShortcut(rotate90, Qt::Key_9);
0192     connect( rotate90, SIGNAL( triggered() ), this, SLOT( rotate90() ) );
0193 
0194     QAction *rotate180 = m_actions->addAction( "rotate180" );
0195     rotate180->setText( i18n("Rotate 180 Degrees") );
0196     m_actions->setDefaultShortcut(rotate180, Qt::Key_8);
0197     connect( rotate180, SIGNAL( triggered() ), this, SLOT( rotate180() ) );
0198 
0199     QAction *rotate270 = m_actions->addAction( "rotate270" );
0200     rotate270->setText( i18n("Rotate 270 Degrees") );
0201     m_actions->setDefaultShortcut(rotate270, Qt::Key_7);
0202     connect( rotate270, SIGNAL( triggered() ), this, SLOT( rotate270() ) );
0203 
0204     QAction *flipHori = m_actions->addAction( "flip_horicontally" );
0205     flipHori->setText( i18n("Flip Horizontally") );
0206     m_actions->setDefaultShortcut(flipHori, Qt::Key_Asterisk);
0207     connect( flipHori, SIGNAL( triggered() ), this, SLOT( flipHoriz() ) );
0208 
0209     QAction *flipVeri = m_actions->addAction( "flip_vertically" );
0210     flipVeri->setText( i18n("Flip Vertically") );
0211     m_actions->setDefaultShortcut(flipVeri, Qt::Key_Slash);
0212     connect( flipVeri, SIGNAL( triggered() ), this, SLOT( flipVert() ) );
0213 
0214     QAction *printImage = m_actions->addAction( "print_image" );
0215     printImage->setText( i18n("Print Image...") );
0216     m_actions->setDefaultShortcuts(printImage, KStandardShortcut::print());
0217     connect( printImage, SIGNAL( triggered() ), this, SLOT( printImage() ) );
0218 
0219     QAction *a =  KStandardAction::saveAs( this, SLOT( saveImage() ), m_actions);
0220     m_actions->addAction( "save_image_as",  a );
0221 
0222     a = KStandardAction::close( this, SLOT( close() ), m_actions);
0223     m_actions->addAction( "close_image", a );
0224     // --------
0225     QAction *moreBrighteness = m_actions->addAction( "more_brightness" );
0226     moreBrighteness->setText( i18n("More Brightness") );
0227     m_actions->setDefaultShortcut(moreBrighteness, Qt::Key_B);
0228     connect( moreBrighteness, SIGNAL( triggered() ), this, SLOT( moreBrightness() ) );
0229 
0230     QAction *lessBrightness = m_actions->addAction( "less_brightness" );
0231     lessBrightness->setText(  i18n("Less Brightness") );
0232     m_actions->setDefaultShortcut(lessBrightness, Qt::SHIFT + Qt::Key_B);
0233     connect( lessBrightness, SIGNAL( triggered() ), this, SLOT( lessBrightness() ) );
0234 
0235     QAction *moreContrast = m_actions->addAction( "more_contrast" );
0236     moreContrast->setText( i18n("More Contrast") );
0237     m_actions->setDefaultShortcut(moreContrast, Qt::Key_C);
0238     connect( moreContrast, SIGNAL( triggered() ), this, SLOT( moreContrast() ) );
0239 
0240     QAction *lessContrast = m_actions->addAction( "less_contrast" );
0241     lessContrast->setText( i18n("Less Contrast") );
0242     m_actions->setDefaultShortcut(lessContrast, Qt::SHIFT + Qt::Key_C);
0243     connect( lessContrast, SIGNAL( triggered() ), this, SLOT( lessContrast() ) );
0244 
0245     QAction *moreGamma = m_actions->addAction( "more_gamma" );
0246     moreGamma->setText( i18n("More Gamma") );
0247     m_actions->setDefaultShortcut(moreGamma, Qt::Key_G);
0248     connect( moreGamma, SIGNAL( triggered() ), this, SLOT( moreGamma() ) );
0249 
0250     QAction *lessGamma = m_actions->addAction( "less_gamma" );
0251     lessGamma->setText( i18n("Less Gamma") );
0252     m_actions->setDefaultShortcut(lessGamma, Qt::SHIFT + Qt::Key_G);
0253     connect( lessGamma, SIGNAL( triggered() ), this, SLOT( lessGamma() ) );
0254 
0255     // --------
0256     QAction *scrollUp = m_actions->addAction( "scroll_up" );
0257     scrollUp->setText( i18n("Scroll Up") );
0258     m_actions->setDefaultShortcut(scrollUp, Qt::Key_Up);
0259     connect( scrollUp, SIGNAL( triggered() ), this, SLOT( scrollUp() ) );
0260 
0261     QAction *scrollDown = m_actions->addAction( "scroll_down" );
0262     scrollDown->setText( i18n("Scroll Down") );
0263     m_actions->setDefaultShortcut(scrollDown, Qt::Key_Down);
0264     connect( scrollDown, SIGNAL( triggered() ), this, SLOT( scrollDown() ) );
0265 
0266     QAction *scrollLeft = m_actions->addAction( "scroll_left" );
0267     scrollLeft->setText( i18n("Scroll Left") );
0268     m_actions->setDefaultShortcut(scrollLeft, Qt::Key_Left);
0269     connect( scrollLeft, SIGNAL( triggered() ), this, SLOT( scrollLeft() ) );
0270 
0271     QAction *scrollRight = m_actions->addAction( "scroll_right" );
0272     scrollRight->setText( i18n("Scroll Right") );
0273     m_actions->setDefaultShortcut(scrollRight, Qt::Key_Right);
0274     connect( scrollRight, SIGNAL( triggered() ), this, SLOT( scrollRight() ) );
0275     // --------
0276     QAction *pause = m_actions->addAction( "kuick_slideshow_pause" );
0277     pause->setText( i18n("Pause Slideshow") );
0278     m_actions->setDefaultShortcut(pause, Qt::Key_P);
0279     connect( pause, SIGNAL( triggered() ), this, SLOT( pauseSlideShow() ) );
0280 
0281     QAction *fullscreenAction = m_actions->addAction( KStandardAction::FullScreen, "fullscreen", this, SLOT( toggleFullscreen() ));
0282     QList<QKeySequence> shortcuts = fullscreenAction->shortcuts();
0283     if(!shortcuts.contains(Qt::Key_Return)) shortcuts << Qt::Key_Return;
0284     m_actions->setDefaultShortcuts(fullscreenAction, shortcuts);
0285 //    KAction *fullscreenAction = KStandardAction::fullScreen(this, SLOT( toggleFullscreen() ), m_actions);
0286 //    m_actions->addAction( "", fullscreenAction );
0287 
0288     QAction *reloadAction = m_actions->addAction( "reload_image" );
0289     reloadAction->setText( i18n("Reload Image") );
0290     if(!(shortcuts = KStandardShortcut::reload()).contains(Qt::Key_Enter)) shortcuts << Qt::Key_Enter;
0291     m_actions->setDefaultShortcuts(reloadAction, shortcuts);
0292     connect( reloadAction, SIGNAL( triggered() ), this, SLOT( reload() ) );
0293 
0294     QAction *properties = m_actions->addAction("properties" );
0295     properties->setText( i18n("Properties") );
0296     m_actions->setDefaultShortcut(properties, Qt::ALT + Qt::Key_Return);
0297     connect( properties, SIGNAL( triggered() ), this, SLOT( slotProperties() ) );
0298 
0299     m_actions->readSettings();
0300 }
0301 
0302 void ImageWindow::showWindow()
0303 {
0304     if ( myIsFullscreen )
0305         showFullScreen();
0306     else
0307         showNormal();
0308 }
0309 
0310 void ImageWindow::setFullscreen( bool enable )
0311 {
0312     xpos = 0; ypos = 0;
0313 
0314 //    if ( enable && !myIsFullscreen ) { // set Fullscreen
0315 //        showFullScreen();
0316 //    }
0317 //    else if ( !enable && myIsFullscreen ) { // go into window mode
0318 //        showNormal();
0319 //    }
0320 
0321     myIsFullscreen = enable;
0322 //    centerImage(); // ### really necessary (multihead!)
0323 }
0324 
0325 
0326 void ImageWindow::updateGeometry( int imWidth, int imHeight )
0327 {
0328 //     qDebug("::updateGeometry: %i, %i", imWidth, imHeight);
0329     //  XMoveWindow( getX11Display(), win, 0, 0 );
0330     XResizeWindow( getX11Display(), win, imWidth, imHeight );
0331 
0332     if ( imWidth != width() || imHeight != height() ) {
0333     if ( myIsFullscreen ) {
0334         centerImage();
0335     }
0336     else { // window mode
0337         // XMoveWindow( getX11Display(), win, 0, 0 );
0338         resizeOptimal( imWidth, imHeight ); // also centers the image
0339     }
0340     }
0341     else { // image size == widget size
0342     xpos = 0; ypos = 0;
0343     XMoveWindow( getX11Display(), win, 0, 0 );
0344     }
0345 
0346     updateCursor();
0347 
0348     QString caption = i18nc( "Filename (Imagewidth x Imageheight)",
0349                              "%3 (%1 x %2)",
0350                              m_kuim->originalWidth(), m_kuim->originalHeight(),
0351                              m_kuim->url().toDisplayString() );
0352     setWindowTitle( caption );
0353 }
0354 
0355 
0356 void ImageWindow::centerImage()
0357 {
0358     int w, h;
0359     if ( myIsFullscreen )
0360     {
0361         QRect desktopRect = QApplication::desktop()->screenGeometry(this);
0362         w = desktopRect.width();
0363         h = desktopRect.height();
0364     }
0365     else
0366     {
0367         w = width();
0368         h = height();
0369     }
0370 
0371     xpos = w/2 - imageWidth()/2;
0372     ypos = h/2 - imageHeight()/2;
0373 
0374     XMoveWindow( getX11Display(), win, xpos, ypos );
0375 
0376     // Modified by Evan for his Multi-Head (2 screens)
0377     // This should center on the first head
0378 //     if ( myIsFullscreen && m_numHeads > 1 && ((m_numHeads % 2) == 0) )
0379 //         xpos = ((width()/m_numHeads) / 2) - imageWidth()/2;
0380 //     else
0381 //         xpos = width()/2 - imageWidth()/2;
0382 
0383 //     ypos = height()/2 - imageHeight()/2;
0384 //     XMoveWindow( getX11Display(), win, xpos, ypos );
0385 }
0386 
0387 
0388 void ImageWindow::scrollImage( int x, int y, bool restrict )
0389 {
0390     xpos += x;
0391     ypos += y;
0392 
0393     int cwlocal = width();
0394     int chlocal = height();
0395 
0396     int iw = imageWidth();
0397     int ih = imageHeight();
0398 
0399     if ( myIsFullscreen || width() > desktopWidth() )
0400     cwlocal = desktopWidth();
0401 
0402     if ( myIsFullscreen || height() > desktopHeight() )
0403     chlocal = desktopHeight();
0404 
0405     if ( restrict ) { // don't allow scrolling in certain cases
0406     if ( x != 0 ) { // restrict x-movement
0407         if ( iw <= cwlocal )
0408         xpos -= x; // restore previous position
0409         else if ( (xpos <= 0) && (xpos + iw <= cwlocal) )
0410         xpos = cwlocal - iw;
0411         else if ( (xpos + iw >= cwlocal) && xpos >= 0 )
0412         xpos = 0;
0413     }
0414 
0415     if ( y != 0 ) { // restrict y-movement
0416         if ( ih <= chlocal )
0417         ypos -= y;
0418         else if ( (ypos <= 0) && (ypos + ih <= chlocal) )
0419         ypos = chlocal - ih;
0420         else if ( (ypos + ih >= chlocal) && ypos >= 0 )
0421         ypos = 0;
0422     }
0423     }
0424 
0425     XMoveWindow( getX11Display(), win, xpos, ypos );
0426     XClearArea( getX11Display(), win, xpos, ypos, iw, ih, false );
0427     showImage();
0428 }
0429 
0430 
0431 // image loading performs:
0432 // ---------------------
0433 // loadImageInternal();
0434 //     reset image mods
0435 //     load image from disk / get from cache
0436 //     loaded(); // apply modifications, scale
0437 //     render pixmap
0438 //
0439 // updateWidget();
0440 //     XUnmapWindow();
0441 //     XSetWindowBackgroundPixmap()
0442 //     resize window to fit image size, center image
0443 //     XClearWindow(); // repaint
0444 //     XMapWindow(), XSync();
0445 //
0446 bool ImageWindow::showNextImage( const QUrl& url )
0447 {
0448     KuickFile *file = FileCache::self()->getFile( url );
0449     switch ( file->waitForDownload( this ) ) {
0450         case KuickFile::ERROR:
0451         {
0452             QString tmp = i18n("Unable to download the image from %1.", url.toDisplayString());
0453             emit sigImageError( file, tmp );
0454             return false;
0455         }
0456         case KuickFile::CANCELED:
0457             return false; // just abort, no error message
0458         default:
0459             break; // go on...
0460     }
0461 
0462     return showNextImage( file );
0463 }
0464 
0465 bool ImageWindow::showNextImage( KuickFile *file )
0466 {
0467     if ( !loadImage( file ) ) {
0468         QString tmp = i18n("Unable to load the image %1.\n"
0469                        "Perhaps the file format is unsupported or "
0470                        "your Imlib is not installed properly.", file->url().toDisplayString());
0471         emit sigImageError( file, tmp );
0472     return false;
0473     }
0474 
0475     else {
0476     // updateWidget( true ); // already called from loadImage()
0477     if ( !isVisible() )
0478         showWindow();
0479 
0480 //  showImage();
0481     return true;
0482     }
0483 }
0484 
0485 void ImageWindow::reload()
0486 {
0487     showNextImage( currentFile() );
0488 }
0489 
0490 void ImageWindow::pauseSlideShow()
0491 {
0492     emit pauseSlideShowSignal();
0493 }
0494 
0495 void ImageWindow::addBrightness( int factor )
0496 {
0497     if ( factor == 0 )
0498     return;
0499 
0500     int oldValue = mod.brightness - ImlibOffset;
0501     setBrightness( oldValue + (idata->brightnessFactor * (int) factor) );
0502 }
0503 
0504 void ImageWindow::addContrast( int factor )
0505 {
0506     if ( factor == 0 )
0507     return;
0508 
0509     int oldValue = mod.contrast - ImlibOffset;
0510     setContrast( oldValue + (idata->contrastFactor * (int) factor) );
0511 }
0512 
0513 void ImageWindow::addGamma( int factor )
0514 {
0515     if ( factor == 0 )
0516     return;
0517 
0518     int oldValue = mod.gamma - ImlibOffset;
0519     setGamma( oldValue + (idata->gammaFactor * (int) factor) );
0520 }
0521 
0522 
0523 ////////////
0524 ////
0525 // slots for keyboard/popupmenu actions
0526 
0527 
0528 void ImageWindow::scrollUp()
0529 {
0530     scrollImage( 0, 20 * kdata->scrollSteps );
0531 }
0532 
0533 void ImageWindow::scrollDown()
0534 {
0535     scrollImage( 0, - 20 * kdata->scrollSteps );
0536 }
0537 
0538 void ImageWindow::scrollLeft()
0539 {
0540     scrollImage( 20 * kdata->scrollSteps, 0 );
0541 }
0542 
0543 void ImageWindow::scrollRight()
0544 {
0545     scrollImage( - 20 * kdata->scrollSteps, 0 );
0546 }
0547 
0548 ///
0549 
0550 void ImageWindow::zoomIn()
0551 {
0552     zoomImage( kdata->zoomSteps );
0553 }
0554 
0555 void ImageWindow::zoomOut()
0556 {
0557     Q_ASSERT( kdata->zoomSteps != 0 );
0558     zoomImage( 1.0 / kdata->zoomSteps );
0559 }
0560 
0561 ///
0562 
0563 void ImageWindow::moreBrightness()
0564 {
0565     addBrightness( kdata->brightnessSteps );
0566 }
0567 
0568 void ImageWindow::moreContrast()
0569 {
0570     addContrast( kdata->contrastSteps );
0571 }
0572 
0573 void ImageWindow::moreGamma()
0574 {
0575     addGamma( kdata->gammaSteps );
0576 }
0577 
0578 
0579 void ImageWindow::lessBrightness()
0580 {
0581     addBrightness( - kdata->brightnessSteps );
0582 }
0583 
0584 void ImageWindow::lessContrast()
0585 {
0586     addContrast( - kdata->contrastSteps );
0587 }
0588 
0589 void ImageWindow::lessGamma()
0590 {
0591     addGamma( - kdata->gammaSteps );
0592 }
0593 
0594 void ImageWindow::imageDelete()
0595 {
0596     emit deleteImage(this);
0597 }
0598 
0599 void ImageWindow::imageTrash()
0600 {
0601     emit trashImage(this);
0602 }
0603 
0604 ///
0605 
0606 
0607 
0608 
0609 /////////////
0610 ////
0611 // event handlers
0612 
0613 void ImageWindow::wheelEvent( QWheelEvent *e )
0614 {
0615     e->accept();
0616     static const int WHEEL_DELTA = 120;
0617     int delta = e->angleDelta().y();
0618 
0619     if ( delta == 0 )
0620         return;
0621 
0622     int steps = delta / WHEEL_DELTA;
0623     emit requestImage( this, -steps );
0624 }
0625 
0626 void ImageWindow::keyPressEvent( QKeyEvent *e )
0627 {
0628     uint key = e->key() | e->modifiers();
0629 
0630     if ( key == Qt::Key_Shift )
0631         updateCursor( ZoomCursor );
0632 
0633     if ( key == Qt::Key_Escape || KStandardShortcut::close().contains( key ) )
0634         close();
0635     else if ( KStandardShortcut::save().contains( key ) )
0636         saveImage();
0637     else if ( key == Qt::Key_Right || key == Qt::Key_Down )
0638         emit nextSlideRequested();
0639     else if ( key == Qt::Key_Left || key == Qt::Key_Up )
0640         emit prevSlideRequested(); // For future use...
0641 
0642     else {
0643         e->ignore();
0644         return;
0645     }
0646 
0647     e->accept();
0648 }
0649 
0650 void ImageWindow::keyReleaseEvent( QKeyEvent *e )
0651 {
0652     if ( e->modifiers() & Qt::ShiftModifier ) { // Shift-key released
0653         updateCursor();
0654         if ( transWidget ) {
0655             delete transWidget;
0656             transWidget = 0L;
0657         }
0658     }
0659 
0660     e->accept();
0661 }
0662 
0663 void ImageWindow::mousePressEvent( QMouseEvent *e )
0664 {
0665     xmove = e->x(); // for moving the image with the mouse
0666     ymove = e->y();
0667 
0668     xzoom = xmove;  // for zooming with the mouse
0669     yzoom = ymove;
0670 
0671     xposPress = xmove;
0672     yposPress = ymove;
0673 
0674     if ( e->button() == Qt::LeftButton ) {
0675         if ( e->modifiers() & Qt::ShiftModifier )
0676             updateCursor( ZoomCursor );
0677         else
0678             updateCursor( MoveCursor );
0679     }
0680 
0681     ImlibWidget::mousePressEvent( e );
0682 }
0683 
0684 void ImageWindow::contextMenuEvent( QContextMenuEvent *e )
0685 {
0686     e->accept();
0687 
0688     if ( !viewerMenu )
0689         setPopupMenu();
0690 
0691     viewerMenu->popup( e->globalPos() );
0692 }
0693 
0694 void ImageWindow::updateCursor( KuickCursor cursor )
0695 {
0696     switch ( cursor )
0697     {
0698         case ZoomCursor:
0699             setCursor( Qt::ArrowCursor ); // need a magnify-cursor
0700             break;
0701         case MoveCursor:
0702             setCursor( *s_handCursor );
0703             break;
0704         case DefaultCursor:
0705         default:
0706             if ( isCursorHidden() )
0707                 return;
0708 
0709             if ( imageWidth() > width() || imageHeight() > height() )
0710                 setCursor( *s_handCursor );
0711             else
0712                 setCursor( Qt::ArrowCursor );
0713             break;
0714     }
0715 }
0716 
0717 void ImageWindow::mouseMoveEvent( QMouseEvent *e )
0718 {
0719     if ( (e->buttons() != Qt::LeftButton) ) { // only handle Qt::LeftButton actions
0720     return;
0721     }
0722 
0723     // FIXME: the zoom-box doesn't work at all
0724     if ( false && (e->modifiers() & Qt::ShiftModifier) != 0 ) {
0725 
0726     if ( !transWidget ) {
0727         transWidget = new QWidget( this );
0728         transWidget->setGeometry( 0, 0, width(), height() );
0729         transWidget->setAttribute( Qt::WA_NoSystemBackground, true );
0730     }
0731 
0732     transWidget->hide();
0733     QPainter p( transWidget );
0734     // really required?
0735     p.eraseRect( transWidget->rect() );
0736     transWidget->show();
0737     //qApp->processOneEvent();
0738     qApp->processEvents( QEventLoop::ExcludeUserInputEvents );
0739 
0740     int width  = e->x() - xposPress;
0741     int height = e->y() - yposPress;
0742 
0743     if ( width < 0 ) {
0744         width = abs( width );
0745         xzoom = e->x();
0746     }
0747 
0748     if ( height < 0 ) {
0749         height = abs( height );
0750         yzoom = e->y();
0751     }
0752 
0753     QPen pen( Qt::white, 1, Qt::DashLine );
0754     p.setPen( pen );     // for drawing white dashed line
0755     p.drawRect( xzoom, yzoom, width, height );
0756     p.setPen( Qt::DotLine ); // defaults to black dotted line pen
0757     p.drawRect( xzoom, yzoom, width, height );
0758     }
0759 
0760     else { // move the image
0761     // scrolling with mouse
0762     uint xtmp = e->x();
0763     uint ytmp = e->y();
0764     scrollImage( xtmp - xmove, ytmp - ymove );
0765     xmove = xtmp;
0766     ymove = ytmp;
0767     }
0768 }
0769 
0770 void ImageWindow::mouseReleaseEvent( QMouseEvent *e )
0771 {
0772     updateCursor();
0773 
0774     if ( transWidget ) {
0775        // destroy the transparent widget, used for showing the rectangle (zoom)
0776     delete transWidget;
0777     transWidget = 0L;
0778     }
0779 
0780     // only proceed if shift-Key is still pressed
0781     if ( !(e->button() == Qt::LeftButton && e->modifiers() & Qt::ShiftModifier) )
0782     return;
0783 
0784     int neww, newh, topX, topY, botX, botY;
0785     float factor, factorx, factory;
0786 
0787     // zoom into the selected area
0788     uint x = e->x();
0789     uint y = e->y();
0790 
0791     if ( xposPress == x || yposPress == y )
0792     return;
0793 
0794     if ( xposPress > x ) {
0795     topX = x;
0796     botX = xposPress;
0797     }
0798     else {
0799     topX = xposPress;
0800     botX = x;
0801     }
0802 
0803     if ( yposPress > y ) {
0804     topY = y;
0805     botY = yposPress;
0806     }
0807     else {
0808     topY = yposPress;
0809     botY = y;
0810     }
0811 
0812     neww = botX - topX;
0813     newh = botY - topY;
0814 
0815     factorx = ((float) width() / (float) neww);
0816     factory = ((float) height() / (float) newh);
0817 
0818     if ( factorx < factory ) // use the smaller factor
0819     factor = factorx;
0820     else factor = factory;
0821 
0822     uint w = 0; // shut up compiler!
0823     uint h = 0;
0824     w = (uint) ( factor * (float) imageWidth() );
0825     h = (uint) ( factor * (float) imageHeight() );
0826 
0827     if ( !canZoomTo( w, h ) )
0828     return;
0829 
0830     int xtmp = - (int) (factor * abs(xpos - topX) );
0831     int ytmp = - (int) (factor * abs(ypos - topY) );
0832 
0833     // if image has different ratio (width()/height()), center it
0834     int xcenter = (width()  - (int) (neww * factor)) / 2;
0835     int ycenter = (height() - (int) (newh * factor)) / 2;
0836 
0837     xtmp += xcenter;
0838     ytmp += ycenter;
0839 
0840     m_kuim->resize( w, h, idata->smoothScale ? KuickImage::SMOOTH : KuickImage::FAST );
0841     XResizeWindow( getX11Display(), win, w, h );
0842     updateWidget( false );
0843 
0844     xpos = xtmp; ypos = ytmp;
0845 
0846     XMoveWindow( getX11Display(), win, xpos, ypos );
0847     scrollImage( 1, 1, true ); // unrestricted scrolling
0848 }
0849 
0850 
0851 void ImageWindow::focusInEvent( QFocusEvent *ev )
0852 {
0853     ImlibWidget::focusInEvent( ev );
0854     emit sigFocusWindow( this );
0855 }
0856 
0857 
0858 void ImageWindow::resizeEvent( QResizeEvent *e )
0859 {
0860     ImlibWidget::resizeEvent( e );
0861 
0862     centerImage();
0863     updateCursor();
0864 }
0865 
0866 
0867 void ImageWindow::dragEnterEvent( QDragEnterEvent *e )
0868 {
0869     //  if ( e->provides( "image/*" ) ) // can't do this right now with Imlib
0870     if ( e->mimeData()->hasFormat( "text/uri-list" ) )
0871     e->accept();
0872     else
0873     e->ignore();
0874 }
0875 
0876 
0877 void ImageWindow::dropEvent( QDropEvent *e )
0878 {
0879     // FIXME - only preliminary drop-support for now
0880     QList<QUrl> list = KUrlMimeData::urlsFromMimeData(e->mimeData());
0881     if ( !list.isEmpty()) {
0882         for(const QUrl& url : list) {
0883             if(url.isValid()) {
0884                 loadImage(url);
0885                 break;
0886             }
0887         }
0888     updateWidget();
0889     e->accept();
0890     }
0891     else
0892     e->ignore();
0893 }
0894 
0895 
0896 ////////////////////
0897 /////////
0898 // misc stuff
0899 
0900 void ImageWindow::setPopupMenu()
0901 {
0902     viewerMenu = new QMenu( this );
0903 
0904     viewerMenu->addAction(m_actions->action("next_image"));
0905     viewerMenu->addAction(m_actions->action("previous_image"));
0906     viewerMenu->addSeparator();
0907 
0908     brightnessMenu = new QMenu( i18n("Brightness"), viewerMenu );
0909     brightnessMenu->addAction(m_actions->action("more_brightness"));
0910     brightnessMenu->addAction(m_actions->action("less_brightness"));
0911 
0912     contrastMenu = new QMenu( i18n("Contrast"), viewerMenu );
0913     contrastMenu->addAction(m_actions->action("more_contrast"));
0914     contrastMenu->addAction(m_actions->action("less_contrast"));
0915 
0916     gammaMenu = new QMenu( i18n("Gamma"), viewerMenu );
0917     gammaMenu->addAction(m_actions->action("more_gamma"));
0918     gammaMenu->addAction(m_actions->action("less_gamma"));
0919 
0920     viewerMenu->addAction(m_actions->action("zoom_in"));
0921     viewerMenu->addAction(m_actions->action("zoom_out"));
0922     viewerMenu->addAction(m_actions->action("original_size"));
0923     viewerMenu->addAction(m_actions->action("maximize"));
0924 
0925     viewerMenu->addSeparator();
0926     viewerMenu->addAction(m_actions->action("rotate90"));
0927     viewerMenu->addAction(m_actions->action("rotate180"));
0928     viewerMenu->addAction(m_actions->action("rotate270"));
0929 
0930     viewerMenu->addSeparator();
0931     viewerMenu->addAction(m_actions->action("flip_vertically"));
0932     viewerMenu->addAction(m_actions->action("flip_horicontally"));
0933     viewerMenu->addSeparator();
0934     viewerMenu->addMenu( brightnessMenu );
0935     viewerMenu->addMenu( contrastMenu );
0936     viewerMenu->addMenu( gammaMenu );
0937     viewerMenu->addSeparator();
0938 
0939     viewerMenu->addAction(m_actions->action("delete_image"));
0940     viewerMenu->addAction(m_actions->action("print_image"));
0941     viewerMenu->addAction(m_actions->action("save_image_as"));
0942     viewerMenu->addAction(m_actions->action("properties"));
0943 
0944     viewerMenu->addSeparator();
0945     viewerMenu->addAction(m_actions->action("close_image"));
0946 }
0947 
0948 void ImageWindow::printImage()
0949 {
0950     if ( !m_kuim )
0951         return;
0952 
0953     if ( !Printing::printImage( *this, this ) )
0954     {
0955         KMessageBox::error( this, i18n("Unable to print the image."),
0956                             i18n("Printing Failed") );
0957     }
0958 }
0959 
0960 void ImageWindow::saveImage()
0961 {
0962     if ( !m_kuim )
0963         return;
0964 
0965     KuickData tmp;
0966     QCheckBox *keepSize = new QCheckBox( i18n("Keep original image size"), 0L);
0967     keepSize->setChecked( true );
0968 
0969     QFileDialog dlg(this);
0970     dlg.setWindowTitle( i18n("Save As") );
0971     dlg.setOption(QFileDialog::DontUseNativeDialog);
0972     dlg.setAcceptMode(QFileDialog::AcceptSave);
0973     dlg.setNameFilter(i18n("Image Files (%1)").arg(tmp.fileFilter));
0974     dlg.setDirectoryUrl(QUrl::fromUserInput(m_saveDirectory, QDir::currentPath(), QUrl::AssumeLocalFile));
0975 
0976     // insert the checkbox below the filter box
0977     if(QGridLayout* gl = qobject_cast<QGridLayout*>(dlg.layout())) {
0978         gl->addWidget(keepSize, gl->rowCount(), 0, 1, gl->columnCount());
0979     }
0980 
0981     QString selection = m_saveDirectory.isEmpty() ?
0982                             m_kuim->url().url() :
0983                             m_kuim->url().fileName();
0984     dlg.selectFile( selection );
0985     if ( dlg.exec() == QDialog::Accepted )
0986     {
0987         QList<QUrl> urls = dlg.selectedUrls();
0988         QUrl url = urls.value(0);
0989         if ( url.isValid() )
0990         {
0991             if ( !saveImage( url, keepSize->isChecked() ) )
0992             {
0993                 QString tmp = i18n("Could not save the file.\n"
0994                                    "Perhaps the disk is full, or you do not "
0995                                    "have write permission to the file.");
0996                 KMessageBox::error( this, tmp, i18n("File Saving Failed"));
0997             }
0998             else
0999             {
1000                 if ( url == m_kuim->url() ) {
1001                     Imlib_apply_modifiers_to_rgb( id, m_kuim->imlibImage() );
1002                 }
1003             }
1004         }
1005     }
1006 
1007     QString lastDir = dlg.directoryUrl().toDisplayString();
1008     if ( lastDir != m_saveDirectory )
1009         m_saveDirectory = lastDir;
1010 }
1011 
1012 bool ImageWindow::saveImage( const QUrl& dest, bool keepOriginalSize )
1013 {
1014     int w = keepOriginalSize ? m_kuim->originalWidth()  : m_kuim->width();
1015     int h = keepOriginalSize ? m_kuim->originalHeight() : m_kuim->height();
1016     if ( m_kuim->absRotation() == ROT_90 || m_kuim->absRotation() == ROT_270 )
1017         qSwap( w, h );
1018 
1019     ImlibImage *saveIm = Imlib_clone_scaled_image( id, m_kuim->imlibImage(),
1020                                                    w, h );
1021     bool success = false;
1022 
1023     QString saveFile;
1024     if ( dest.isLocalFile() )
1025         saveFile = dest.path();
1026     else
1027     {
1028 
1029         QString extension = QFileInfo( dest.fileName() ).completeSuffix();
1030         if(!extension.isEmpty()) extension.prepend('.');
1031         QScopedPointer<QTemporaryFile> tmpFilePtr(FileCache::self()->createTempFile(extension));
1032         if(tmpFilePtr.isNull()) return false;
1033 
1034         tmpFilePtr->setAutoRemove(false);
1035         if ( !tmpFilePtr->open() )
1036             return false;
1037         saveFile = tmpFilePtr->fileName();
1038         tmpFilePtr->close();
1039     }
1040 
1041     if ( saveIm )
1042     {
1043         Imlib_apply_modifiers_to_rgb( id, saveIm );
1044         success = Imlib_save_image( id, saveIm,
1045                                     QFile::encodeName( saveFile ).data(),
1046                                     NULL );
1047         if ( success && !dest.isLocalFile() )
1048         {
1049             if ( isFullscreen() )
1050                 toggleFullscreen(); // otherwise upload window would block us invisibly
1051 
1052             QFile sourceFile(saveFile);
1053             if(!sourceFile.open(QIODevice::ReadOnly)) {
1054                 // TODO: implement better error handling
1055                 qWarning("failed to open file \"%s\": %s", qUtf8Printable(saveFile), qUtf8Printable(sourceFile.errorString()));
1056                 return false;
1057             }
1058 
1059             KIO::StoredTransferJob* job = KIO::storedPut(&sourceFile, dest, -1);
1060             KJobWidgets::setWindow(job, this);
1061             success = job->exec();
1062 
1063             sourceFile.close();
1064         }
1065 
1066         Imlib_kill_image( id, saveIm );
1067     }
1068 
1069     return success;
1070 }
1071 
1072 void ImageWindow::toggleFullscreen()
1073 {
1074     setFullscreen( !myIsFullscreen );
1075     showWindow();
1076 }
1077 
1078 void ImageWindow::loaded( KuickImage *kuim, bool wasCached )
1079 {
1080     if (wasCached)
1081     {
1082         return; // keep it as it is
1083     }
1084 
1085     if ( !ImageMods::restoreFor( kuim, idata ) )
1086     {
1087         // if no cached image modifications are available, apply the default modifications
1088         if ( !kdata->isModsEnabled ) {
1089             // ### BUG: should be "restorePreviousSize"
1090             kuim->restoreOriginalSize();
1091         }
1092         else
1093         {
1094             autoRotate( kuim );
1095             autoScale( kuim );
1096         }
1097     }
1098 }
1099 
1100 // upscale/downscale depending on configuration
1101 void ImageWindow::autoScale( KuickImage *kuim )
1102 {
1103     int newW = kuim->originalWidth();
1104     int newH = kuim->originalHeight();
1105 
1106     QSize s = maxImageSize();
1107     int mw = s.width();
1108     int mh = s.height();
1109 
1110     if ( kuim->absRotation() == ROT_90 || kuim->absRotation() == ROT_270 )
1111         qSwap( newW, newH );
1112 
1113     bool doIt = false;
1114 
1115     if ( kdata->upScale )
1116     {
1117     if ( (newW < mw) && (newH < mh) )
1118         {
1119             doIt = true;
1120 
1121         float ratio1, ratio2;
1122         int maxUpScale = kdata->maxUpScale;
1123 
1124         ratio1 = (float) mw / (float) newW;
1125         ratio2 = (float) mh / (float) newH;
1126         ratio1 = (ratio1 < ratio2) ? ratio1 : ratio2;
1127         if ( maxUpScale > 0 )
1128         ratio1 = (ratio1 < maxUpScale) ? ratio1 : maxUpScale;
1129         newH = (int) ((float) newH * ratio1);
1130         newW = (int) ((float) newW * ratio1);
1131     }
1132     }
1133 
1134     if ( kdata->downScale )
1135     {
1136     // eventually set width and height to the best/max possible screen size
1137     if ( (newW > mw) || (newH > mh) )
1138         {
1139             doIt = true;
1140 
1141         if ( newW > mw )
1142             {
1143         float ratio = (float) newW / (float) newH;
1144         newW = mw;
1145         newH = (int) ((float) newW / ratio);
1146         }
1147 
1148         // the previously calculated "h" might be larger than screen
1149         if ( newH > mh ) {
1150         float ratio = (float) newW / (float) newH;
1151         newH = mh;
1152         newW = (int) ((float) newH * ratio);
1153         }
1154     }
1155     }
1156 
1157     if ( doIt )
1158         kuim->resize( newW, newH, idata->smoothScale ? KuickImage::SMOOTH : KuickImage::FAST );
1159 }
1160 
1161 // only called when kdata->isModsEnabled is true
1162 bool ImageWindow::autoRotate( KuickImage *kuim )
1163 {
1164     if ( kdata->autoRotation && ImlibWidget::autoRotate( kuim ) )
1165         return true;
1166 
1167     else // rotation by metadata not available or not configured
1168     {
1169         // only apply default mods to newly loaded images
1170 
1171         // ### actually we should have a dirty flag ("neverManuallyFlipped")
1172         if ( kuim->flipMode() == FlipNone )
1173         {
1174             int flipMode = 0;
1175             if ( kdata->flipVertically )
1176                 flipMode |= FlipVertical;
1177             if ( kdata->flipHorizontally )
1178                 flipMode |= FlipHorizontal;
1179 
1180             kuim->flipAbs( flipMode );
1181         }
1182 
1183         if ( kuim->absRotation() == ROT_0 )
1184             kuim->rotateAbs( kdata->rotation );
1185     }
1186 
1187     return true;
1188 }
1189 
1190 int ImageWindow::desktopWidth( bool totalScreen ) const
1191 {
1192     if ( myIsFullscreen || totalScreen )
1193     {
1194         return QApplication::desktop()->screenGeometry(topLevelWidget()).width();
1195     } else
1196     return Kuick::workArea().width();
1197 }
1198 
1199 
1200 int ImageWindow::desktopHeight( bool totalScreen ) const
1201 {
1202     if ( myIsFullscreen || totalScreen ) {
1203         return QApplication::desktop()->screenGeometry(topLevelWidget()).height();
1204     } else {
1205     return Kuick::workArea().height();
1206     }
1207 }
1208 
1209 QSize ImageWindow::maxImageSize() const
1210 {
1211     if ( myIsFullscreen ) {
1212         return QApplication::desktop()->screenGeometry(topLevelWidget()).size();
1213     }
1214     else {
1215     return Kuick::workArea().size() - Kuick::frameSize( winId() );
1216     }
1217 }
1218 
1219 void ImageWindow::resizeOptimal( int w, int h )
1220 {
1221     QSize s = maxImageSize();
1222     int mw = s.width();
1223     int mh = s.height();
1224     int neww = (w >= mw) ? mw : w;
1225     int newh = (h >= mh) ? mh : h;
1226 
1227     if ( neww == width() && newh == height() )
1228     centerImage();
1229     else
1230     resize( neww, newh ); // also centers the image
1231 }
1232 
1233 void ImageWindow::maximize()
1234 {
1235     if ( !m_kuim )
1236     return;
1237 
1238     bool oldUpscale = kdata->upScale;
1239     bool oldDownscale = kdata->downScale;
1240 
1241     kdata->upScale = true;
1242     kdata->downScale = true;
1243 
1244     autoScale( m_kuim );
1245     updateWidget( true );
1246 
1247     if ( !myIsFullscreen )
1248     resizeOptimal( imageWidth(), imageHeight() );
1249 
1250     kdata->upScale = oldUpscale;
1251     kdata->downScale = oldDownscale;
1252 }
1253 
1254 bool ImageWindow::canZoomTo( int newWidth, int newHeight )
1255 {
1256     if ( !ImlibWidget::canZoomTo( newWidth, newHeight ) )
1257         return false;
1258 
1259     QSize desktopSize = QApplication::desktop()->screenGeometry(topLevelWidget()).size();
1260 
1261     int desktopArea = desktopSize.width() * desktopSize.height();
1262     int imageArea = newWidth * newHeight;
1263 
1264     if ( imageArea > desktopArea * kdata->maxZoomFactor )
1265     {
1266         return KMessageBox::warningContinueCancel(
1267             this,
1268             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?",
1269             newWidth, newHeight ),
1270             QString(),
1271             KStandardGuiItem::cont(),
1272             KStandardGuiItem::cancel(),
1273             "ImageWindow_confirm_very_large_window"
1274             ) == KMessageBox::Continue;
1275     }
1276 
1277     return true;
1278 }
1279 
1280 void ImageWindow::rotated( KuickImage *kuim, int rotation )
1281 {
1282     if ( !m_kuim )
1283         return;
1284 
1285     ImlibWidget::rotated( kuim, rotation );
1286 
1287     if ( rotation == ROT_90 || rotation == ROT_270 )
1288         autoScale( kuim ); // ### BUG: only autoScale when configured!
1289 }
1290 
1291 void ImageWindow::slotProperties()
1292 {
1293     KPropertiesDialog dlg( currentFile()->url(), this );
1294     dlg.exec();
1295 }
1296 
1297 void ImageWindow::setBusyCursor()
1298 {
1299     // avoid busy cursor in fullscreen mode
1300     if ( !isFullscreen() )
1301         ImlibWidget::setBusyCursor();
1302 }
1303 
1304 void ImageWindow::restoreCursor()
1305 {
1306     // avoid busy cursor in fullscreen mode
1307     if ( !isFullscreen() )
1308         ImlibWidget::restoreCursor();
1309 }
1310 
1311 bool ImageWindow::isCursorHidden() const
1312 {
1313     return cursor().shape() == Qt::BlankCursor;
1314 }