|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 2001-2002 Matthias Kretz <kretz@kde.org>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License version 2
|
|
|
|
as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
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; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kimagecanvas.h"
|
|
|
|
#include "kimageholder.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "config/defaults.h"
|
|
|
|
|
|
|
|
#include <tqcolor.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqapplication.h>
|
|
|
|
#include <tqwmatrix.h>
|
|
|
|
#include <tqtimer.h>
|
|
|
|
|
|
|
|
#include <kpixmap.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kgenericfactory.h>
|
|
|
|
#include <ksettings/dispatcher.h>
|
|
|
|
#include <kconfig.h>
|
|
|
|
|
|
|
|
#define KIMAGECANVAS_WIPESIZE 5
|
|
|
|
|
|
|
|
const int MOUSECURSORHIDETIME = 3000;
|
|
|
|
|
|
|
|
//extern bool tqt_use_xrender;
|
|
|
|
|
|
|
|
typedef KGenericFactory<KImageCanvas> KImageCanvasFactory;
|
|
|
|
K_EXPORT_COMPONENT_FACTORY( libkviewcanvas,
|
|
|
|
KImageCanvasFactory( "kviewcanvas" ) )
|
|
|
|
|
|
|
|
KImageCanvas::KImageCanvas( TQWidget * parent, const char * name, const TQStringList & )
|
|
|
|
: TQScrollView( parent, name, WResizeNoErase | WStaticContents )
|
|
|
|
, m_client( 0 )
|
|
|
|
, m_oldClient( 0 )
|
|
|
|
, m_image( 0 )
|
|
|
|
, m_imageTransformed( 0 )
|
|
|
|
, m_pixmap( 0 )
|
|
|
|
, m_pTimer( new TQTimer( this, "KImageCanvas/Timer" ) )
|
|
|
|
, m_maxsize( Defaults::maxSize )
|
|
|
|
, m_minsize( Defaults::minSize )
|
|
|
|
, m_currentsize( 0, 0 )
|
|
|
|
, m_zoom( 1.0 )
|
|
|
|
, m_fastscale( ! Defaults::smoothScaling )
|
|
|
|
, m_keepaspectratio( Defaults::keepAspectRatio )
|
|
|
|
, m_bImageChanged( false )
|
|
|
|
, m_bSizeChanged( false )
|
|
|
|
, m_bNeedNewPixmap( false )
|
|
|
|
, m_bCentered( Defaults::centerImage )
|
|
|
|
, m_bImageUpdateScheduled( false )
|
|
|
|
, m_bNewImage( false )
|
|
|
|
, m_iBlendTimerId( 0 )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
setFrameStyle( TQFrame::NoFrame );
|
|
|
|
setResizePolicy( TQScrollView::Manual );
|
|
|
|
setMinimumSize( 0, 0 );
|
|
|
|
setBgColor( Defaults::bgColor );
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( imageChanged() ), this, TQT_SLOT( slotImageChanged() ) );
|
|
|
|
connect( m_pTimer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( hideCursor() ) );
|
|
|
|
|
|
|
|
KSettings::Dispatcher::self()->registerInstance(
|
|
|
|
KImageCanvasFactory::instance(), TQT_TQOBJECT(this),
|
|
|
|
TQT_SLOT( loadSettings() ) );
|
|
|
|
|
|
|
|
viewport()->setFocusProxy( this );
|
|
|
|
clear();
|
|
|
|
|
|
|
|
TQWidget::setMouseTracking( true );
|
|
|
|
viewport()->setMouseTracking( true );
|
|
|
|
m_cursor.setShape( Qt::CrossCursor );
|
|
|
|
viewport()->setCursor( m_cursor );
|
|
|
|
m_pTimer->start( MOUSECURSORHIDETIME, true );
|
|
|
|
|
|
|
|
loadSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
KImageCanvas::~KImageCanvas()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
delete m_image; m_image = 0;
|
|
|
|
delete m_pixmap; m_pixmap = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setBgColor( const TQColor & color )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
viewport()->setPaletteBackgroundColor( color );
|
|
|
|
if( m_client )
|
|
|
|
m_client->setPaletteBackgroundColor( color );
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQColor & KImageCanvas::bgColor() const
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
return viewport()->paletteBackgroundColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
int KImageCanvas::imageDepth() const
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( ! m_image )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return m_image->depth();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQSize KImageCanvas::imageSize() const
|
|
|
|
{
|
|
|
|
//kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( ! m_image )
|
|
|
|
return TQSize( 0, 0 );
|
|
|
|
|
|
|
|
return m_matrix.isIdentity() ? m_image->size() : m_matrix.mapRect( TQRect( TQPoint(), m_image->size() ) ).size();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQSize KImageCanvas::currentSize() const
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( ! m_image )
|
|
|
|
return TQSize( 0, 0 );
|
|
|
|
|
|
|
|
return m_currentsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQImage * KImageCanvas::image() const
|
|
|
|
{
|
|
|
|
if( m_imageTransformed )
|
|
|
|
return m_imageTransformed;
|
|
|
|
return m_image;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQRect KImageCanvas::selection() const
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_client )
|
|
|
|
return m_selection;
|
|
|
|
else
|
|
|
|
return TQRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setCentered( bool centered )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_bCentered != centered )
|
|
|
|
{
|
|
|
|
m_bCentered = centered;
|
|
|
|
center();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setImage( const TQImage & newimage )
|
|
|
|
{
|
|
|
|
bool emitHasImage = m_image ? false : true;
|
|
|
|
m_matrix.reset();
|
|
|
|
matrixChanged();
|
|
|
|
delete m_image;
|
|
|
|
m_image = new TQImage( newimage );
|
|
|
|
m_bNewImage = true;
|
|
|
|
// don't emit the signal here - call the slot directly
|
|
|
|
slotImageChanged();
|
|
|
|
|
|
|
|
sizeFromZoom( m_zoom );
|
|
|
|
updateImage();
|
|
|
|
if( emitHasImage && m_image )
|
|
|
|
emit hasImage( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setImage( const TQImage & newimage, const TQSize & size )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << size << endl;
|
|
|
|
bool emitHasImage = m_image ? false : true;
|
|
|
|
m_matrix.reset();
|
|
|
|
matrixChanged();
|
|
|
|
delete m_image;
|
|
|
|
m_image = new TQImage( newimage );
|
|
|
|
m_bNewImage = true;
|
|
|
|
// don't emit the signal here - call the slot directly
|
|
|
|
slotImageChanged();
|
|
|
|
|
|
|
|
resizeImage( size );
|
|
|
|
updateImage();
|
|
|
|
if( emitHasImage && m_image )
|
|
|
|
emit hasImage( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setZoom( double zoom )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << zoom << endl;
|
|
|
|
if( m_image == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( zoom > 0.0 && m_zoom != zoom )
|
|
|
|
{
|
|
|
|
m_zoom = zoom;
|
|
|
|
sizeFromZoom( m_zoom );
|
|
|
|
emit zoomChanged( m_zoom );
|
|
|
|
updateImage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::boundImageTo( const TQSize & size )
|
|
|
|
{
|
|
|
|
bool keepAspectRatio = m_keepaspectratio;
|
|
|
|
m_keepaspectratio = true;
|
|
|
|
resizeImage( size );
|
|
|
|
m_keepaspectratio = keepAspectRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setMaximumImageSize( const TQSize & maxsize )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << maxsize << endl;
|
|
|
|
if( ( ! m_minsize.isEmpty() ) &&
|
|
|
|
( maxsize.width() < m_minsize.width() || maxsize.height() < m_minsize.height() ) )
|
|
|
|
{
|
|
|
|
kdWarning( 4620 ) << "the new maximum image size is smaller than the minimum size" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_maxsize = maxsize;
|
|
|
|
|
|
|
|
resizeImage( m_currentsize );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setMinimumImageSize( const TQSize & minsize )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << minsize << endl;
|
|
|
|
if( ( ! m_maxsize.isEmpty() ) &&
|
|
|
|
( minsize.width() > m_maxsize.width() || minsize.height() > m_maxsize.height() ) )
|
|
|
|
{
|
|
|
|
kdWarning( 4620 ) << "the new minimum image size is greater than the maximum size" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_minsize = minsize;
|
|
|
|
|
|
|
|
resizeImage( m_currentsize );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::resizeImage( const TQSize & newsize )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_image == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQSize size = newsize;
|
|
|
|
|
|
|
|
// check that it fits into min and max sizes
|
|
|
|
checkBounds( size );
|
|
|
|
|
|
|
|
// calculate the new zoom factor
|
|
|
|
zoomFromSize( size );
|
|
|
|
|
|
|
|
if( size != m_currentsize )
|
|
|
|
{
|
|
|
|
m_currentsize = size;
|
|
|
|
sizeChanged();
|
|
|
|
|
|
|
|
updateImage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::hideScrollbars( bool hidden )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( hidden )
|
|
|
|
{
|
|
|
|
setVScrollBarMode( AlwaysOff );
|
|
|
|
setHScrollBarMode( AlwaysOff );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setVScrollBarMode( Auto );
|
|
|
|
setHScrollBarMode( Auto );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setKeepAspectRatio( bool aspect )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_keepaspectratio = aspect;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int KImageCanvas::numOfBlendEffects() const
|
|
|
|
{
|
|
|
|
return Defaults::numOfBlendEffects;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString KImageCanvas::blendEffectDescription( unsigned int idx ) const
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
switch( idx )
|
|
|
|
{
|
|
|
|
case NoBlending:
|
|
|
|
kdWarning( 4620 ) << k_funcinfo << " shouldn't be called with an index of 0 - That's always not really defined\n";
|
|
|
|
return i18n( Defaults::blendEffectDescription[ 0 ] );
|
|
|
|
case AlphaBlend:
|
|
|
|
return i18n( Defaults::blendEffectDescription[ 5 ] );
|
|
|
|
case WipeFromLeft:
|
|
|
|
return i18n( Defaults::blendEffectDescription[ 1 ] );
|
|
|
|
case WipeFromRight:
|
|
|
|
return i18n( Defaults::blendEffectDescription[ 2 ] );
|
|
|
|
case WipeFromTop:
|
|
|
|
return i18n( Defaults::blendEffectDescription[ 3 ] );
|
|
|
|
case WipeFromBottom:
|
|
|
|
return i18n( Defaults::blendEffectDescription[ 4 ] );
|
|
|
|
}
|
|
|
|
kdError( 4620 ) << "Effect description for effect with index " << idx << " doesn't exist\n";
|
|
|
|
return TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KImageCanvas::eventFilter( TQObject * obj, TQEvent * ev )
|
|
|
|
{
|
|
|
|
if( ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(m_client) || TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(m_oldClient) ) && ev->type() == TQEvent::MouseMove )
|
|
|
|
mouseMoveEvent( TQT_TQMOUSEEVENT( ev ) );
|
|
|
|
return TQScrollView::eventFilter( obj, ev );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::setFastScale( bool fastscale )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_fastscale = fastscale;
|
|
|
|
if( m_fastscale )
|
|
|
|
{
|
|
|
|
// wo do scaling with a matrix now, so the m_imageTransformed isn't needed anymore
|
|
|
|
delete m_imageTransformed;
|
|
|
|
m_imageTransformed = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
matrixChanged(); // set the flag to dirty so that a new m_imageTransformed will be created
|
|
|
|
// else we very relyably get a crash
|
|
|
|
}
|
|
|
|
updateImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::clear()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
bool emitHasImage = m_image ? true : false;
|
|
|
|
delete m_image;
|
|
|
|
m_image = 0;
|
|
|
|
m_currentsize -= m_currentsize; //zero size
|
|
|
|
if( m_client )
|
|
|
|
m_client->clear();
|
|
|
|
if( emitHasImage && ! m_image )
|
|
|
|
emit hasImage( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::flipHorizontal( bool change )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_image == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( change )
|
|
|
|
{
|
|
|
|
TQWMatrix matrix( 1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F );
|
|
|
|
*m_image = m_image->xForm( matrix );
|
|
|
|
emit imageChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_matrix.scale( 1.0, -1.0 );
|
|
|
|
matrixChanged();
|
|
|
|
}
|
|
|
|
// size didn't change
|
|
|
|
updateImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::flipVertical( bool change )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_image == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( change )
|
|
|
|
{
|
|
|
|
TQWMatrix matrix( -1.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F );
|
|
|
|
*m_image = m_image->xForm( matrix );
|
|
|
|
emit imageChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_matrix.scale( -1.0, 1.0 );
|
|
|
|
matrixChanged();
|
|
|
|
}
|
|
|
|
// size didn't change
|
|
|
|
updateImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::rotate( double a, bool change )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_image == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( change )
|
|
|
|
{
|
|
|
|
TQWMatrix matrix;
|
|
|
|
matrix.rotate( a );
|
|
|
|
*m_image = m_image->xForm( matrix );
|
|
|
|
emit imageChanged();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_matrix.rotate( a );
|
|
|
|
matrixChanged();
|
|
|
|
}
|
|
|
|
//adjust m_currentsize
|
|
|
|
sizeFromZoom( m_zoom );
|
|
|
|
updateImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::checkBounds( TQSize & newsize )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_keepaspectratio )
|
|
|
|
{
|
|
|
|
// check that the new size has the same aspect ratio the original image had
|
|
|
|
TQSize origsize = imageSize();
|
|
|
|
double x1 = double( origsize.height() ) / double( newsize.height() );
|
|
|
|
double x2 = double( origsize.width() ) / double( newsize.width() );
|
|
|
|
if( ( newsize * x1 != origsize ) || ( newsize * x2 != origsize ) )
|
|
|
|
{
|
|
|
|
// not OK
|
|
|
|
kdDebug( 4620 ) << "checkBounds: the aspect ratio wasn't kept changing from " << newsize << endl;
|
|
|
|
// the user want's that the aspect ratio doesn't change. The
|
|
|
|
// question is: make it larger or smaller?
|
|
|
|
// we make it smaller (we depend on that in boundImageTo)
|
|
|
|
newsize = origsize / KMAX( x1, x2 );
|
|
|
|
kdDebug( 4620 ) << "checkBounds: to " << newsize << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( ( ! m_maxsize.isEmpty() ) &&
|
|
|
|
( newsize.width() > m_maxsize.width() || newsize.height() > m_maxsize.height() ) )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << "checkBounds: the new size is bigger than the max size" << endl;
|
|
|
|
if( m_keepaspectratio )
|
|
|
|
{
|
|
|
|
double x1 = double( m_maxsize.height() ) / double( newsize.height() );
|
|
|
|
double x2 = double( m_maxsize.width() ) / double( newsize.width() );
|
|
|
|
double x = KMIN( x1, x2 );//( x1 > x2 ) ? x2 : x1;
|
|
|
|
newsize *= x;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
newsize = newsize.boundedTo( m_maxsize );
|
|
|
|
}
|
|
|
|
if( ( ! m_minsize.isEmpty() ) &&
|
|
|
|
( newsize.width() < m_minsize.width() || newsize.height() < m_minsize.height() ) )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << "checkBounds: the new size is smaller than the min size" << endl;
|
|
|
|
if( m_keepaspectratio )
|
|
|
|
{
|
|
|
|
double x1 = double( m_minsize.height() ) / double( newsize.height() );
|
|
|
|
double x2 = double( m_minsize.width() ) / double( newsize.width() );
|
|
|
|
double x = KMAX( x1, x2 );//( x1 > x2 ) ? x1 : x2;
|
|
|
|
newsize *= x;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
newsize = newsize.expandedTo( m_minsize );
|
|
|
|
}
|
|
|
|
// if it still won't fit we have a problem: we can't keep the aspect ratio or we have
|
|
|
|
// to violate the min/max settings
|
|
|
|
if( ( ! m_maxsize.isEmpty() ) &&
|
|
|
|
( newsize.width() > m_maxsize.width() || newsize.height() > m_maxsize.height() ) )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << "checkBounds: Sorry, I can't keep the aspect ratio." << endl;
|
|
|
|
newsize = newsize.boundedTo( m_maxsize );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::zoomFromSize( const TQSize & newsize )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( ! m_image )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQSize originalsize = imageSize();
|
|
|
|
double widthzoom = double( newsize.width() ) / double( originalsize.width() );
|
|
|
|
double heightzoom = double( newsize.height() ) / double( originalsize.height() );
|
|
|
|
double zoom = ( widthzoom + heightzoom ) / 2;
|
|
|
|
if( zoom != m_zoom )
|
|
|
|
{
|
|
|
|
m_zoom = zoom;
|
|
|
|
emit zoomChanged( m_zoom );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::sizeFromZoom( double zoom )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( ! m_image )
|
|
|
|
return;
|
|
|
|
|
|
|
|
TQSize newsize = zoom * imageSize();
|
|
|
|
kdDebug( 4620 ) << "change size from " << imageSize() << " to " << newsize << endl;
|
|
|
|
resizeImage( newsize );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::updateImage()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( ! m_bImageUpdateScheduled )
|
|
|
|
TQTimer::singleShot( 0, this, TQT_SLOT( slotUpdateImage() ) );
|
|
|
|
m_bImageUpdateScheduled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::slotUpdateImage()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_bImageUpdateScheduled = false;
|
|
|
|
if( m_image == 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
//only update if something was changed
|
|
|
|
if( m_bImageChanged || m_bSizeChanged || m_bMatrixChanged )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << "actually updating the image now" << endl;
|
|
|
|
TQApplication::setOverrideCursor( WaitCursor );
|
|
|
|
if( m_bNewImage || ! m_client )
|
|
|
|
{
|
|
|
|
finishNewClient();
|
|
|
|
m_oldClient = m_client;
|
|
|
|
m_client = createNewClient();
|
|
|
|
}
|
|
|
|
m_client->setImage( pixmap() );
|
|
|
|
|
|
|
|
if( m_bSizeChanged || m_bNewImage )
|
|
|
|
{
|
|
|
|
TQSize sh = m_client->sizeHint();
|
|
|
|
if( ! sh.isValid() )
|
|
|
|
sh = TQSize( 0, 0 );
|
|
|
|
m_client->resize( sh );
|
|
|
|
resizeContents( sh.width(), sh.height() );
|
|
|
|
center();
|
|
|
|
}
|
|
|
|
TQRect drawRect = m_client->drawRect();
|
|
|
|
switch( m_iBlendEffect )
|
|
|
|
{
|
|
|
|
case NoBlending:
|
|
|
|
break;
|
|
|
|
case AlphaBlend:
|
|
|
|
break;
|
|
|
|
case WipeFromLeft:
|
|
|
|
drawRect.setRight( KIMAGECANVAS_WIPESIZE + contentsX() );
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
break;
|
|
|
|
case WipeFromRight:
|
|
|
|
drawRect.rLeft() += KMIN( drawRect.width() - KIMAGECANVAS_WIPESIZE, contentsX() + visibleWidth() );
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
break;
|
|
|
|
case WipeFromTop:
|
|
|
|
drawRect.setBottom( KIMAGECANVAS_WIPESIZE + contentsY() );
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
break;
|
|
|
|
case WipeFromBottom:
|
|
|
|
drawRect.setTop( KMIN( drawRect.height() - KIMAGECANVAS_WIPESIZE, contentsY() + visibleHeight() ) );
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m_client->update();
|
|
|
|
m_iBlendTimerId = startTimer( 5 );
|
|
|
|
TQApplication::restoreOverrideCursor();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_bNewImage = false;
|
|
|
|
m_bImageChanged = false;
|
|
|
|
m_bSizeChanged = false;
|
|
|
|
m_bMatrixChanged = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::mouseMoveEvent( TQMouseEvent * )
|
|
|
|
{
|
|
|
|
if( m_cursor.shape() == TQt::BlankCursor )
|
|
|
|
{
|
|
|
|
m_cursor.setShape( Qt::CrossCursor );
|
|
|
|
viewport()->setCursor( m_cursor );
|
|
|
|
if( m_client )
|
|
|
|
m_client->setCursor( m_cursor );
|
|
|
|
}
|
|
|
|
m_pTimer->start( MOUSECURSORHIDETIME, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KImageCanvas::resizeEvent( TQResizeEvent * ev )
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << "KImageCanvas resized to " << ev->size() << endl;
|
|
|
|
TQScrollView::resizeEvent( ev );
|
|
|
|
center();
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::contentsMousePressEvent( TQMouseEvent * ev )
|
|
|
|
{
|
|
|
|
if ( ev->button() == Qt::RightButton )
|
|
|
|
emit contextPress( ev->globalPos() );
|
|
|
|
TQScrollView::contentsMousePressEvent( ev );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::contentsWheelEvent( TQWheelEvent * ev )
|
|
|
|
{
|
|
|
|
//kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
// Ctrl+Wheelmouse changes the zoom.
|
|
|
|
// Wheelmouse scrolls around
|
|
|
|
if ( ev->state() & ControlButton )
|
|
|
|
{
|
|
|
|
int delta = ev->delta() / 120;
|
|
|
|
double zoom = m_zoom;
|
|
|
|
// make zoom a value of 1/16, 1/15, 1/14, .. , 1/2, 1, 2, 3, .. , 15, 16
|
|
|
|
bool done = false;
|
|
|
|
for( int i = 15; i > 0; --i )
|
|
|
|
{
|
|
|
|
if( zoom <= ( 1.0 / i ) )
|
|
|
|
{
|
|
|
|
if( zoom < ( 1.0 / ( i + 0.5 ) ) )
|
|
|
|
zoom = ( 1.0 / ( i + 1 ) );
|
|
|
|
else
|
|
|
|
zoom = ( 1.0 / i );
|
|
|
|
done = true;
|
|
|
|
// zoom = 1/16, 1/15, .. , 1/2, 1
|
|
|
|
double x = 1.0 / zoom - delta;
|
|
|
|
if( x == 0 )
|
|
|
|
zoom = 2.0;
|
|
|
|
else
|
|
|
|
zoom = 1.0 / x;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( ! done )
|
|
|
|
for( int i = 2; i < 17; ++i )
|
|
|
|
{
|
|
|
|
if( zoom < (double)i )
|
|
|
|
{
|
|
|
|
if( zoom < ( i - 0.5 ) )
|
|
|
|
zoom = i - 1.0;
|
|
|
|
else
|
|
|
|
zoom = (double)i;
|
|
|
|
done = true;
|
|
|
|
// zoom = 1, 2, .., 15, 16
|
|
|
|
zoom = zoom + delta;
|
|
|
|
if( zoom < 0.9 )
|
|
|
|
zoom = 0.5;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( ! done )
|
|
|
|
{
|
|
|
|
zoom = 16.0;
|
|
|
|
zoom = zoom + delta;
|
|
|
|
if( zoom > 16.0 )
|
|
|
|
zoom = 16.0;
|
|
|
|
}
|
|
|
|
kdDebug( 4620 ) << "Mousewheel: oldzoom = " << m_zoom << " newzoom = " << zoom << endl;
|
|
|
|
ev->accept();
|
|
|
|
bool oldscale = fastScale();
|
|
|
|
setFastScale( true );
|
|
|
|
setZoom( zoom );
|
|
|
|
setFastScale( oldscale );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
TQScrollView::contentsWheelEvent( ev );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::keyPressEvent( TQKeyEvent * ev )
|
|
|
|
{
|
|
|
|
//kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
switch( ev->key() )
|
|
|
|
{
|
|
|
|
case Key_Down:
|
|
|
|
ev->accept();
|
|
|
|
verticalScrollBar()->addLine();
|
|
|
|
break;
|
|
|
|
case Key_Up:
|
|
|
|
ev->accept();
|
|
|
|
verticalScrollBar()->subtractLine();
|
|
|
|
break;
|
|
|
|
case Key_Left:
|
|
|
|
ev->accept();
|
|
|
|
horizontalScrollBar()->subtractLine();
|
|
|
|
break;
|
|
|
|
case Key_Right:
|
|
|
|
ev->accept();
|
|
|
|
horizontalScrollBar()->addLine();
|
|
|
|
break;
|
|
|
|
case Key_PageUp:
|
|
|
|
ev->accept();
|
|
|
|
verticalScrollBar()->subtractPage();
|
|
|
|
break;
|
|
|
|
case Key_PageDown:
|
|
|
|
ev->accept();
|
|
|
|
verticalScrollBar()->addPage();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ev->ignore();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::timerEvent( TQTimerEvent * ev )
|
|
|
|
{
|
|
|
|
if( ev->timerId() == m_iBlendTimerId )
|
|
|
|
{
|
|
|
|
TQRect drawRect = m_client->drawRect();
|
|
|
|
switch( m_iBlendEffect )
|
|
|
|
{
|
|
|
|
case NoBlending:
|
|
|
|
finishNewClient();
|
|
|
|
break;
|
|
|
|
case AlphaBlend:
|
|
|
|
finishNewClient();
|
|
|
|
//if( tqt_use_xrender )
|
|
|
|
//{
|
|
|
|
//}
|
|
|
|
//else
|
|
|
|
//{
|
|
|
|
//kdWarning( 4620 ) << "no XRender" << endl;
|
|
|
|
//finishNewClient();
|
|
|
|
//}
|
|
|
|
break;
|
|
|
|
case WipeFromLeft:
|
|
|
|
drawRect.rRight() += KIMAGECANVAS_WIPESIZE;
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
m_client->update( drawRect.right() - KIMAGECANVAS_WIPESIZE, 0, KIMAGECANVAS_WIPESIZE, m_client->height() );
|
|
|
|
if( drawRect.right() >= contentsX() + visibleWidth() )
|
|
|
|
finishNewClient();
|
|
|
|
break;
|
|
|
|
case WipeFromRight:
|
|
|
|
drawRect.rLeft() -= KIMAGECANVAS_WIPESIZE;
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
m_client->update( drawRect.left(), 0, KIMAGECANVAS_WIPESIZE, m_client->height() );
|
|
|
|
if( drawRect.left() <= contentsX() )
|
|
|
|
finishNewClient();
|
|
|
|
break;
|
|
|
|
case WipeFromTop:
|
|
|
|
drawRect.rBottom() += KIMAGECANVAS_WIPESIZE;
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
m_client->update( 0, drawRect.bottom() - KIMAGECANVAS_WIPESIZE, m_client->width(), KIMAGECANVAS_WIPESIZE );
|
|
|
|
if( drawRect.bottom() >= contentsY() + visibleHeight() )
|
|
|
|
finishNewClient();
|
|
|
|
break;
|
|
|
|
case WipeFromBottom:
|
|
|
|
drawRect.rTop() -= KIMAGECANVAS_WIPESIZE;
|
|
|
|
m_client->setDrawRect( drawRect );
|
|
|
|
m_client->update( 0, drawRect.top(), m_client->width(), KIMAGECANVAS_WIPESIZE );
|
|
|
|
if( drawRect.top() <= contentsY() )
|
|
|
|
finishNewClient();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
kdFatal( 4620 ) << "unknown Blend Effect" << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
killTimer( ev->timerId() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::hideCursor()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_cursor.setShape( Qt::BlankCursor );
|
|
|
|
viewport()->setCursor( m_cursor );
|
|
|
|
if( m_client )
|
|
|
|
m_client->setCursor( m_cursor );
|
|
|
|
}
|
|
|
|
|
|
|
|
const KPixmap KImageCanvas::pixmap()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << ( m_bNeedNewPixmap ? "convert from Image" : "use old copy" ) << endl;
|
|
|
|
// create a new Pixmap in m_pixmap if needed
|
|
|
|
if( m_bNeedNewPixmap )
|
|
|
|
{
|
|
|
|
// only do it again if requested
|
|
|
|
m_bNeedNewPixmap = false;
|
|
|
|
// ok, the old one may go now
|
|
|
|
delete m_pixmap;
|
|
|
|
|
|
|
|
// if smoothscaling is wanted and the transformation matrix or the image
|
|
|
|
// itself changed...
|
|
|
|
if( ! m_fastscale && ( m_bMatrixChanged || m_bImageChanged ) )
|
|
|
|
{
|
|
|
|
delete m_imageTransformed;
|
|
|
|
// we create a new image transformed by the matrix
|
|
|
|
m_imageTransformed = new TQImage( m_matrix.isIdentity() ? *m_image : m_image->xForm( m_matrix ) );
|
|
|
|
kdDebug( 4620 ) << "Size of m_image: " << m_image->size() << endl;
|
|
|
|
kdDebug( 4620 ) << "Size of m_imageTransformed: " << m_imageTransformed->size() << endl;
|
|
|
|
}
|
|
|
|
// smoothScale or fast scaling via m_matrix
|
|
|
|
m_pixmap = new KPixmap();
|
|
|
|
m_pixmap->convertFromImage( m_fastscale ? *m_image : m_imageTransformed->smoothScale( m_currentsize ), KPixmap::ColorOnly );
|
|
|
|
}
|
|
|
|
if( m_fastscale )
|
|
|
|
{
|
|
|
|
// fast scaling is needed so we need to scale now
|
|
|
|
TQWMatrix matrix( m_matrix );
|
|
|
|
matrix.scale( m_zoom, m_zoom );
|
|
|
|
if( ! matrix.isIdentity() )
|
|
|
|
return m_pixmap->xForm( matrix );
|
|
|
|
}
|
|
|
|
return *m_pixmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::slotImageChanged()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_bImageChanged = true;
|
|
|
|
m_bNeedNewPixmap = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::loadSettings()
|
|
|
|
{
|
|
|
|
KConfigGroup cfgGroup( KImageCanvasFactory::instance()->config(),
|
|
|
|
"Settings" );
|
|
|
|
setFastScale( ! cfgGroup.readBoolEntry( "Smooth Scaling", ! fastScale() ) );
|
|
|
|
setKeepAspectRatio( cfgGroup.readBoolEntry( "Keep Aspect Ratio",
|
|
|
|
keepAspectRatio() ) );
|
|
|
|
setCentered( cfgGroup.readBoolEntry( "Center Image", centered() ) );
|
|
|
|
|
|
|
|
setBgColor( cfgGroup.readColorEntry( "Background Color", &bgColor() ) );
|
|
|
|
|
|
|
|
setMinimumImageSize( TQSize( cfgGroup.readNumEntry( "Minimum Width",
|
|
|
|
minimumImageSize().width() ), cfgGroup.readNumEntry(
|
|
|
|
"Minimum Height", minimumImageSize().height() ) ) );
|
|
|
|
setMaximumImageSize( TQSize( cfgGroup.readNumEntry( "Maximum Width",
|
|
|
|
maximumImageSize().width() ), cfgGroup.readNumEntry(
|
|
|
|
"Maximum Height", maximumImageSize().height() ) ) );
|
|
|
|
|
|
|
|
KConfigGroup blendConfig( KImageCanvasFactory::instance()->config(),
|
|
|
|
"Blend Effects" );
|
|
|
|
/* TODO
|
|
|
|
m_vEffects.clear();
|
|
|
|
for( unsigned int i = 1; i <= numOfBlendEffects(); ++i )
|
|
|
|
{
|
|
|
|
if( blendConfig.readBoolEntry( TQString::number( i ), false ) )
|
|
|
|
m_vEffects.push_back( i );
|
|
|
|
}
|
|
|
|
// and now tell the canvas what blend effect to use
|
|
|
|
switchBlendEffect();
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::selected( const TQRect & rect )
|
|
|
|
{
|
|
|
|
//kdDebug( 4620 ) << k_funcinfo << rect << endl;
|
|
|
|
m_selection = rect;
|
|
|
|
if( ! m_selection.isNull() )
|
|
|
|
{
|
|
|
|
m_selection.setTop( int( ( m_selection.top() + 0.5 ) / m_zoom ) );
|
|
|
|
m_selection.setLeft( int( ( m_selection.left() + 0.5 ) / m_zoom ) );
|
|
|
|
m_selection.setRight( int( ( m_selection.right() + 0.5 ) / m_zoom ) );
|
|
|
|
m_selection.setBottom( int( ( m_selection.bottom() + 0.5 ) / m_zoom ) );
|
|
|
|
}
|
|
|
|
//kdDebug( 4620 ) << "m_selection = " << m_selection << endl;
|
|
|
|
emit selectionChanged( m_selection );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::mapCursorPos( const TQPoint & pos )
|
|
|
|
{
|
|
|
|
TQPoint mapped( static_cast<int>( ( pos.x() + 0.5 ) / m_zoom ), static_cast<int>( ( pos.y() + 0.5 ) / m_zoom ) );
|
|
|
|
//kdDebug( 4620 ) << k_funcinfo << pos << " -> " << mapped << endl;
|
|
|
|
emit cursorPos( mapped );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::sizeChanged()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_bSizeChanged = true;
|
|
|
|
if( ! m_fastscale )
|
|
|
|
m_bNeedNewPixmap = true;
|
|
|
|
emit imageSizeChanged( m_currentsize );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::matrixChanged()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
m_bMatrixChanged = true;
|
|
|
|
m_bNeedNewPixmap = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::center()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
if( m_bCentered && m_client )
|
|
|
|
{
|
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
|
|
|
|
|
|
|
int scrollbarwidth = ( height() >= m_currentsize.height() ) ? 0 : verticalScrollBar()->width();
|
|
|
|
int scrollbarheight = ( width() - scrollbarwidth >= m_currentsize.width() ) ? 0 : horizontalScrollBar()->height();
|
|
|
|
scrollbarwidth = ( height() - scrollbarheight >= m_currentsize.height() ) ? 0 : verticalScrollBar()->width();
|
|
|
|
|
|
|
|
int availheight = height() - scrollbarheight;
|
|
|
|
int availwidth = width() - scrollbarwidth;
|
|
|
|
|
|
|
|
if( availwidth > m_currentsize.width() )
|
|
|
|
x = ( availwidth - m_currentsize.width() ) / 2;
|
|
|
|
if( availheight > m_currentsize.height() )
|
|
|
|
y = ( availheight - m_currentsize.height() ) / 2;
|
|
|
|
|
|
|
|
kdDebug( 4620 ) << "center with left top at " << x << ", " << y << endl;
|
|
|
|
moveChild( m_client, x, y );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KImageCanvas::finishNewClient()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
killTimer( m_iBlendTimerId );
|
|
|
|
if( m_client )
|
|
|
|
m_client->setDrawRect( m_client->rect() );
|
|
|
|
delete m_oldClient;
|
|
|
|
m_oldClient = 0;
|
|
|
|
emit showingImageDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
KImageHolder * KImageCanvas::createNewClient()
|
|
|
|
{
|
|
|
|
kdDebug( 4620 ) << k_funcinfo << endl;
|
|
|
|
KImageHolder * client = new KImageHolder( viewport() );
|
|
|
|
client->setMinimumSize( 0, 0 );
|
|
|
|
client->setMouseTracking( true );
|
|
|
|
client->installEventFilter( this );
|
|
|
|
setFocusProxy( client );
|
|
|
|
client->setFocusPolicy( TQ_StrongFocus );
|
|
|
|
client->setFocus();
|
|
|
|
|
|
|
|
addChild( client, 0, 0 );
|
|
|
|
|
|
|
|
connect( client, TQT_SIGNAL( contextPress( const TQPoint& ) ), TQT_SIGNAL( contextPress( const TQPoint& ) ) );
|
|
|
|
connect( client, TQT_SIGNAL( cursorPos( const TQPoint & ) ), TQT_SLOT( mapCursorPos( const TQPoint & ) ) );
|
|
|
|
connect( client, TQT_SIGNAL( selected( const TQRect & ) ), TQT_SLOT( selected( const TQRect & ) ) );
|
|
|
|
connect( client, TQT_SIGNAL( wannaScroll( int, int ) ), TQT_SLOT( scrollBy( int, int ) ) );
|
|
|
|
|
|
|
|
return client;
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "kimagecanvas.moc"
|
|
|
|
|
|
|
|
// vim:sw=4:ts=4
|