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 }