You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1252 lines
32 KiB
1252 lines
32 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 1998-2006 Carsten Pfeiffer <pfeiffer@kde.org>
|
|
|
|
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 <tqcheckbox.h>
|
|
#include <tqcursor.h>
|
|
#include <tqdrawutil.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqkeycode.h>
|
|
#include <tqpainter.h>
|
|
#include <tqpen.h>
|
|
#include <tqpopupmenu.h>
|
|
|
|
#ifdef KDE_USE_FINAL
|
|
#undef GrayScale
|
|
#undef Color
|
|
#endif
|
|
#include <tqrect.h>
|
|
#include <tqstring.h>
|
|
#include <tqstringlist.h>
|
|
#include <tqtimer.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
|
|
|
|
TQCursor *ImageWindow::s_handCursor = 0L;
|
|
|
|
ImageWindow::ImageWindow( ImData *_idata, ImlibData *id, TQWidget *parent,
|
|
const char *name )
|
|
: ImlibWidget( _idata, id, parent, name )
|
|
{
|
|
init();
|
|
}
|
|
|
|
ImageWindow::ImageWindow( ImData *_idata, TQWidget *parent, const char *name )
|
|
: ImlibWidget( _idata, parent, name )
|
|
{
|
|
init();
|
|
}
|
|
|
|
ImageWindow::~ImageWindow()
|
|
{
|
|
}
|
|
|
|
|
|
void ImageWindow::init()
|
|
{
|
|
setFocusPolicy( TQ_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 ) {
|
|
TQString file = locate( "appdata", "pics/handcursor.png" );
|
|
if ( !file.isEmpty() )
|
|
s_handCursor = new TQCursor( file );
|
|
else
|
|
s_handCursor = new TQCursor( arrowCursor );
|
|
}
|
|
|
|
setupActions();
|
|
imageCache->setMaxImages( kdata->maxCachedImages );
|
|
|
|
transWidget = 0L;
|
|
myIsFullscreen = false;
|
|
|
|
xpos = 0, ypos = 0;
|
|
m_numHeads = ScreenCount( x11Display() );
|
|
|
|
setAcceptDrops( true );
|
|
setBackgroundColor( kdata->backgroundColor );
|
|
|
|
static TQPixmap imageIcon = UserIcon( "imageviewer-medium" );
|
|
static TQPixmap 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(),
|
|
TQT_TQOBJECT(this), TQT_SLOT( slotRequestNext() ),
|
|
m_actions, "next_image" );
|
|
new KAction( i18n("Show Previous Image"), KStdAccel::prior(),
|
|
TQT_TQOBJECT(this), TQT_SLOT( slotRequestPrevious() ),
|
|
m_actions, "previous_image" );
|
|
|
|
new KAction( i18n("Delete Image"), SHIFT + Key_Delete,
|
|
TQT_TQOBJECT(this), TQT_SLOT( imageDelete() ),
|
|
m_actions, "delete_image" );
|
|
new KAction( i18n("Move Image to Trash"), Key_Delete,
|
|
TQT_TQOBJECT(this), TQT_SLOT( imageTrash() ),
|
|
m_actions, "trash_image" );
|
|
|
|
new KAction( i18n("Zoom In"), Key_Plus,
|
|
TQT_TQOBJECT(this), TQT_SLOT( zoomIn() ),
|
|
m_actions, "zoom_in" );
|
|
new KAction( i18n("Zoom Out"), Key_Minus,
|
|
TQT_TQOBJECT(this), TQT_SLOT( zoomOut() ),
|
|
m_actions, "zoom_out" );
|
|
new KAction( i18n("Restore Original Size"), Key_O,
|
|
TQT_TQOBJECT(this), TQT_SLOT( showImageOriginalSize() ),
|
|
m_actions, "original_size" );
|
|
new KAction( i18n("Maximize"), Key_M,
|
|
TQT_TQOBJECT(this), TQT_SLOT( maximize() ),
|
|
m_actions, "maximize" );
|
|
|
|
new KAction( i18n("Rotate 90 Degrees"), Key_9,
|
|
TQT_TQOBJECT(this), TQT_SLOT( rotate90() ),
|
|
m_actions, "rotate90" );
|
|
new KAction( i18n("Rotate 180 Degrees"), Key_8,
|
|
TQT_TQOBJECT(this), TQT_SLOT( rotate180() ),
|
|
m_actions, "rotate180" );
|
|
new KAction( i18n("Rotate 270 Degrees"), Key_7,
|
|
TQT_TQOBJECT(this), TQT_SLOT( rotate270() ),
|
|
m_actions, "rotate270" );
|
|
|
|
new KAction( i18n("Flip Horizontally"), Key_Asterisk,
|
|
TQT_TQOBJECT(this), TQT_SLOT( flipHoriz() ),
|
|
m_actions, "flip_horicontally" );
|
|
new KAction( i18n("Flip Vertically"), Key_Slash,
|
|
TQT_TQOBJECT(this), TQT_SLOT( flipVert() ),
|
|
m_actions, "flip_vertically" );
|
|
|
|
new KAction( i18n("Print Image..."), KStdAccel::print(),
|
|
TQT_TQOBJECT(this), TQT_SLOT( printImage() ),
|
|
m_actions, "print_image" );
|
|
KStdAction::saveAs( TQT_TQOBJECT(this), TQT_SLOT( saveImage() ),
|
|
m_actions, "save_image_as" );
|
|
|
|
KStdAction::close( TQT_TQOBJECT(this), TQT_SLOT( close() ),
|
|
m_actions, "close_image" );
|
|
// --------
|
|
new KAction( i18n("More Brightness"), Key_B,
|
|
TQT_TQOBJECT(this), TQT_SLOT( moreBrightness() ),
|
|
m_actions, "more_brightness" );
|
|
new KAction( i18n("Less Brightness"), SHIFT + Key_B,
|
|
TQT_TQOBJECT(this), TQT_SLOT( lessBrightness() ),
|
|
m_actions, "less_brightness" );
|
|
new KAction( i18n("More Contrast"), Key_C,
|
|
TQT_TQOBJECT(this), TQT_SLOT( moreContrast() ),
|
|
m_actions, "more_contrast" );
|
|
new KAction( i18n("Less Contrast"), SHIFT + Key_C,
|
|
TQT_TQOBJECT(this), TQT_SLOT( lessContrast() ),
|
|
m_actions, "less_contrast" );
|
|
new KAction( i18n("More Gamma"), Key_G,
|
|
TQT_TQOBJECT(this), TQT_SLOT( moreGamma() ),
|
|
m_actions, "more_gamma" );
|
|
new KAction( i18n("Less Gamma"), SHIFT + Key_G,
|
|
TQT_TQOBJECT(this), TQT_SLOT( lessGamma() ),
|
|
m_actions, "less_gamma" );
|
|
|
|
// --------
|
|
new KAction( i18n("Scroll Up"), Key_Up,
|
|
TQT_TQOBJECT(this), TQT_SLOT( scrollUp() ),
|
|
m_actions, "scroll_up" );
|
|
new KAction( i18n("Scroll Down"), Key_Down,
|
|
TQT_TQOBJECT(this), TQT_SLOT( scrollDown() ),
|
|
m_actions, "scroll_down" );
|
|
new KAction( i18n("Scroll Left"), Key_Left,
|
|
TQT_TQOBJECT(this), TQT_SLOT( scrollLeft() ),
|
|
m_actions, "scroll_left" );
|
|
new KAction( i18n("Scroll Right"), Key_Right,
|
|
TQT_TQOBJECT(this), TQT_SLOT( scrollRight() ),
|
|
m_actions, "scroll_right" );
|
|
// --------
|
|
new KAction( i18n("Pause Slideshow"), Key_P,
|
|
TQT_TQOBJECT(this), TQT_SLOT( pauseSlideShow() ),
|
|
m_actions, "kuick_slideshow_pause" );
|
|
|
|
KAction *fullscreenAction = KStdAction::fullScreen(TQT_TQOBJECT(this), TQT_SLOT( toggleFullscreen() ), m_actions, 0 );
|
|
|
|
KAction *reloadAction = new KAction( i18n("Reload Image"), KStdAccel::shortcut(KStdAccel::Reload),
|
|
TQT_TQOBJECT(this), TQT_SLOT( reload() ),
|
|
m_actions, "reload_image" );
|
|
|
|
new KAction( i18n("Properties"), ALT + Key_Return,
|
|
TQT_TQOBJECT(this), TQT_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();
|
|
|
|
TQString 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 )
|
|
{
|
|
TQRect 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:
|
|
{
|
|
TQString 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 ) ) {
|
|
TQString 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( TQWheelEvent *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( TQKeyEvent *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( TQKeyEvent *e )
|
|
{
|
|
if ( e->state() & ShiftButton ) { // Shift-key released
|
|
updateCursor();
|
|
if ( transWidget ) {
|
|
delete transWidget;
|
|
transWidget = 0L;
|
|
}
|
|
}
|
|
|
|
e->accept();
|
|
}
|
|
|
|
void ImageWindow::mousePressEvent( TQMouseEvent *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() == Qt::LeftButton ) {
|
|
if ( e->state() & ShiftButton )
|
|
updateCursor( ZoomCursor );
|
|
else
|
|
updateCursor( MoveCursor );
|
|
}
|
|
|
|
ImlibWidget::mousePressEvent( e );
|
|
}
|
|
|
|
void ImageWindow::contextMenuEvent( TQContextMenuEvent *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( TQMouseEvent *e )
|
|
{
|
|
if ( !(e->state() & Qt::LeftButton) ) { // only handle LeftButton actions
|
|
return;
|
|
}
|
|
|
|
if ( e->state() & ShiftButton ) {
|
|
|
|
if ( !transWidget ) {
|
|
transWidget = new TQWidget( this );
|
|
transWidget->setGeometry( 0, 0, width(), height() );
|
|
transWidget->setBackgroundMode( NoBackground );
|
|
}
|
|
|
|
transWidget->hide();
|
|
TQPainter p( transWidget );
|
|
// really required?
|
|
p.eraseRect( transWidget->rect() );
|
|
transWidget->show();
|
|
tqApp->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();
|
|
}
|
|
|
|
TQPen pen( TQt::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( TQMouseEvent *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() == Qt::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( TQFocusEvent *ev )
|
|
{
|
|
ImlibWidget::focusInEvent( ev );
|
|
emit sigFocusWindow( this );
|
|
}
|
|
|
|
|
|
void ImageWindow::resizeEvent( TQResizeEvent *e )
|
|
{
|
|
ImlibWidget::resizeEvent( e );
|
|
|
|
centerImage();
|
|
updateCursor();
|
|
}
|
|
|
|
|
|
void ImageWindow::dragEnterEvent( TQDragEnterEvent *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( TQDropEvent *e )
|
|
{
|
|
// FIXME - only preliminary drop-support for now
|
|
KURL::List list;
|
|
if ( KURLDrag::decode( e, list ) && !list.isEmpty()) {
|
|
TQString 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 TQPopupMenu( this );
|
|
|
|
m_actions->action("next_image")->plug( viewerMenu );
|
|
m_actions->action("previous_image")->plug( viewerMenu );
|
|
viewerMenu->insertSeparator();
|
|
|
|
brightnessMenu = new TQPopupMenu( viewerMenu );
|
|
m_actions->action("more_brightness")->plug(brightnessMenu);
|
|
m_actions->action("less_brightness")->plug(brightnessMenu);
|
|
|
|
contrastMenu = new TQPopupMenu( viewerMenu );
|
|
m_actions->action("more_contrast")->plug(contrastMenu);
|
|
m_actions->action("less_contrast")->plug(contrastMenu);
|
|
|
|
gammaMenu = new TQPopupMenu( 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;
|
|
TQCheckBox *keepSize = new TQCheckBox( i18n("Keep original image size"), 0L);
|
|
keepSize->setChecked( true );
|
|
KFileDialog dlg( m_saveDirectory, tmp.fileFilter, this, "filedialog", true
|
|
#if KDE_VERSION >= 310
|
|
,keepSize
|
|
#endif
|
|
);
|
|
|
|
TQString 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() == TQDialog::Accepted )
|
|
{
|
|
KURL url = dlg.selectedURL();
|
|
if ( url.isValid() )
|
|
{
|
|
if ( !saveImage( url, keepSize->isChecked() ) )
|
|
{
|
|
TQString 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() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TQString 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 )
|
|
tqSwap( w, h );
|
|
|
|
ImlibImage *saveIm = Imlib_clone_scaled_image( id, m_kuim->imlibImage(),
|
|
w, h );
|
|
bool success = false;
|
|
|
|
TQString saveFile;
|
|
if ( dest.isLocalFile() )
|
|
saveFile = dest.path();
|
|
else
|
|
{
|
|
TQString extension = TQFileInfo( dest.fileName() ).extension();
|
|
if ( !extension.isEmpty() )
|
|
extension.prepend( '.' );
|
|
|
|
KTempFile tmpFile( TQString(), 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,
|
|
TQFile::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();
|
|
|
|
TQSize s = maxImageSize();
|
|
int mw = s.width();
|
|
int mh = s.height();
|
|
|
|
if ( kuim->absRotation() == ROT_90 || kuim->absRotation() == ROT_270 )
|
|
tqSwap( 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();
|
|
}
|
|
}
|
|
|
|
TQSize 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 )
|
|
{
|
|
TQSize 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;
|
|
|
|
TQSize 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 ),
|
|
TQString(),
|
|
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() == TQt::BlankCursor;
|
|
}
|
|
|
|
#include "imagewindow.moc"
|