KDEUI
kpixmapregionselectorwidget.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 /* NOTE: There are two copies of this .h and the .cpp file, with subtle differences. 00021 * One copy is in kdelibs/kdeui, and the other copy is in kdepim/libkdepim 00022 * This is because kdepim has to remain backwards compatible. Any changes 00023 * to either file should be made to the other. 00024 */ 00025 00026 #include "kpixmapregionselectorwidget.h" 00027 #include <QtGui/QPainter> 00028 #include <QtGui/QColor> 00029 #include <QtGui/QImage> 00030 #include <QLabel> 00031 #include <QtGui/QLayout> 00032 #include <QRubberBand> 00033 #include <kdebug.h> 00034 #include <kicon.h> 00035 #include <klocale.h> 00036 #include <kmenu.h> 00037 #include <kaction.h> 00038 #include <stdlib.h> 00039 #include <QtGui/QCursor> 00040 #include <QtGui/QApplication> 00041 #include <QMouseEvent> 00042 #include "kactioncollection.h" 00043 00044 class KPixmapRegionSelectorWidget::Private 00045 { 00046 public: 00047 Private(KPixmapRegionSelectorWidget *q): q(q) {} 00048 00049 KPixmapRegionSelectorWidget *q; 00050 00055 void updatePixmap(); 00056 00057 QRect calcSelectionRectangle( const QPoint &startPoint, const QPoint & endPoint ); 00058 00059 enum CursorState { None=0, Resizing, Moving }; 00060 CursorState m_state; 00061 00062 QPixmap m_unzoomedPixmap; 00063 QPixmap m_originalPixmap; 00064 QPixmap m_linedPixmap; 00065 QRect m_selectedRegion; 00066 QLabel *m_label; 00067 00068 QPoint m_tempFirstClick; 00069 double m_forcedAspectRatio; 00070 00071 int m_maxWidth, m_maxHeight; 00072 double m_zoomFactor; 00073 00074 QRubberBand *m_rubberBand; 00075 }; 00076 00077 KPixmapRegionSelectorWidget::KPixmapRegionSelectorWidget( QWidget *parent) 00078 : QWidget( parent ), d(new Private(this)) 00079 { 00080 QHBoxLayout * hboxLayout=new QHBoxLayout( this ); 00081 00082 hboxLayout->addStretch(); 00083 QVBoxLayout * vboxLayout=new QVBoxLayout(); 00084 hboxLayout->addItem(vboxLayout); 00085 00086 vboxLayout->addStretch(); 00087 d->m_label = new QLabel(this); 00088 d->m_label->setAttribute(Qt::WA_NoSystemBackground,true);//setBackgroundMode( Qt::NoBackground ); 00089 d->m_label->installEventFilter( this ); 00090 00091 vboxLayout->addWidget(d->m_label); 00092 vboxLayout->addStretch(); 00093 00094 hboxLayout->addStretch(); 00095 00096 d->m_forcedAspectRatio=0; 00097 00098 d->m_zoomFactor=1.0; 00099 d->m_rubberBand = new QRubberBand(QRubberBand::Rectangle, d->m_label); 00100 d->m_rubberBand->hide(); 00101 } 00102 00103 KPixmapRegionSelectorWidget::~KPixmapRegionSelectorWidget() 00104 { 00105 delete d; 00106 } 00107 00108 QPixmap KPixmapRegionSelectorWidget::pixmap() const 00109 { 00110 return d->m_unzoomedPixmap; 00111 } 00112 00113 void KPixmapRegionSelectorWidget::setPixmap( const QPixmap &pixmap ) 00114 { 00115 Q_ASSERT(!pixmap.isNull()); //This class isn't designed to deal with null pixmaps. 00116 d->m_originalPixmap = pixmap; 00117 d->m_unzoomedPixmap = pixmap; 00118 d->m_label->setPixmap( pixmap ); 00119 resetSelection(); 00120 } 00121 00122 void KPixmapRegionSelectorWidget::resetSelection() 00123 { 00124 d->m_selectedRegion = d->m_originalPixmap.rect(); 00125 d->m_rubberBand->hide(); 00126 d->updatePixmap(); 00127 } 00128 00129 QRect KPixmapRegionSelectorWidget::selectedRegion() const 00130 { 00131 return d->m_selectedRegion; 00132 } 00133 00134 void KPixmapRegionSelectorWidget::setSelectedRegion(const QRect &rect) 00135 { 00136 if (!rect.isValid()) resetSelection(); 00137 else 00138 { 00139 d->m_selectedRegion=rect; 00140 d->updatePixmap(); 00141 00142 QRect r=unzoomedSelectedRegion(); 00143 } 00144 } 00145 00146 void KPixmapRegionSelectorWidget::Private::updatePixmap() 00147 { 00148 Q_ASSERT(!m_originalPixmap.isNull()); 00149 if (m_originalPixmap.isNull()) { m_label->setPixmap(m_originalPixmap); return; } 00150 if (m_selectedRegion.width()>m_originalPixmap.width()) m_selectedRegion.setWidth( m_originalPixmap.width() ); 00151 if (m_selectedRegion.height()>m_originalPixmap.height()) m_selectedRegion.setHeight( m_originalPixmap.height() ); 00152 00153 QPainter painter; 00154 if (m_linedPixmap.isNull()) 00155 { 00156 m_linedPixmap = m_originalPixmap; 00157 QPainter p(&m_linedPixmap); 00158 p.setCompositionMode(QPainter::CompositionMode_SourceAtop); 00159 p.fillRect(m_linedPixmap.rect(), QColor(0, 0, 0, 100)); 00160 } 00161 00162 QPixmap pixmap = m_linedPixmap; 00163 painter.begin(&pixmap); 00164 painter.drawPixmap( m_selectedRegion.topLeft(), 00165 m_originalPixmap, m_selectedRegion ); 00166 00167 00168 painter.end(); 00169 00170 m_label->setPixmap(pixmap); 00171 00172 qApp->sendPostedEvents(0,QEvent::LayoutRequest); 00173 00174 if (m_selectedRegion == m_originalPixmap.rect())//d->m_label->rect()) //### CHECK! 00175 m_rubberBand->hide(); 00176 else 00177 { 00178 m_rubberBand->setGeometry(QRect(m_selectedRegion.topLeft(), 00179 m_selectedRegion.size())); 00180 00181 /* m_rubberBand->setGeometry(QRect(m_label -> mapToGlobal(m_selectedRegion.topLeft()), 00182 m_selectedRegion.size())); 00183 */ 00184 if (m_state!=None) m_rubberBand->show(); 00185 } 00186 00187 } 00188 00189 00190 KMenu *KPixmapRegionSelectorWidget::createPopupMenu() 00191 { 00192 KMenu *popup=new KMenu(this ); 00193 KActionCollection *actions=new KActionCollection(popup); 00194 popup->setObjectName( "PixmapRegionSelectorPopup"); 00195 popup->addTitle(i18n("Image Operations")); 00196 00197 QAction *action = actions->addAction("rotateclockwise"); 00198 action->setText(i18n("&Rotate Clockwise")); 00199 action->setIcon( KIcon( "object-rotate-right" ) ); 00200 connect( action, SIGNAL( triggered( bool ) ), this, SLOT(rotateClockwise()) ); 00201 00202 popup->addAction(action); 00203 00204 action = actions->addAction("rotatecounterclockwise"); 00205 action->setText(i18n("Rotate &Counterclockwise")); 00206 action->setIcon( KIcon( "object-rotate-left" ) ); 00207 connect( action, SIGNAL( triggered( bool ) ), this, SLOT(rotateCounterclockwise()) ); 00208 00209 popup->addAction(action); 00210 00211 /* 00212 I wonder if it would be appropriate to have here an "Open with..." option to 00213 edit the image (antlarr) 00214 */ 00215 return popup; 00216 } 00217 00218 void KPixmapRegionSelectorWidget::rotate(RotateDirection direction) 00219 { 00220 int w=d->m_originalPixmap.width(); 00221 int h=d->m_originalPixmap.height(); 00222 QImage img=d->m_unzoomedPixmap.toImage(); 00223 if(direction == Rotate90) 00224 img = img.transformed(QTransform().rotate(90.0)); 00225 else if(direction == Rotate180) 00226 img = img.transformed(QTransform().rotate(180.0)); 00227 else 00228 img = img.transformed(QTransform().rotate(270.0)); 00229 00230 d->m_unzoomedPixmap=QPixmap::fromImage(img); 00231 00232 img=d->m_originalPixmap.toImage(); 00233 if(direction == Rotate90) 00234 img = img.transformed(QTransform().rotate(90.0)); 00235 else if(direction == Rotate180) 00236 img = img.transformed(QTransform().rotate(180.0)); 00237 else 00238 img = img.transformed(QTransform().rotate(270.0)); 00239 00240 d->m_originalPixmap=QPixmap::fromImage(img); 00241 00242 d->m_linedPixmap=QPixmap(); 00243 00244 if (d->m_forcedAspectRatio>0 && d->m_forcedAspectRatio!=1) 00245 resetSelection(); 00246 else 00247 { 00248 switch (direction) 00249 { 00250 case ( Rotate90 ): 00251 { 00252 int x=h-d->m_selectedRegion.y()-d->m_selectedRegion.height(); 00253 int y=d->m_selectedRegion.x(); 00254 d->m_selectedRegion.setRect(x, y, d->m_selectedRegion.height(), d->m_selectedRegion.width() ); 00255 d->updatePixmap(); 00256 // qApp->sendPostedEvents(0,QEvent::LayoutRequest); 00257 // updatePixmap(); 00258 00259 } break; 00260 case ( Rotate270 ): 00261 { 00262 int x=d->m_selectedRegion.y(); 00263 int y=w-d->m_selectedRegion.x()-d->m_selectedRegion.width(); 00264 d->m_selectedRegion.setRect(x, y, d->m_selectedRegion.height(), d->m_selectedRegion.width() ); 00265 d->updatePixmap(); 00266 // qApp->sendPostedEvents(0,QEvent::LayoutRequest); 00267 // updatePixmap(); 00268 } break; 00269 default: resetSelection(); 00270 } 00271 } 00272 00273 emit pixmapRotated(); 00274 } 00275 00276 void KPixmapRegionSelectorWidget::rotateClockwise() 00277 { 00278 rotate(Rotate90); 00279 } 00280 00281 void KPixmapRegionSelectorWidget::rotateCounterclockwise() 00282 { 00283 rotate(Rotate270); 00284 } 00285 00286 bool KPixmapRegionSelectorWidget::eventFilter(QObject *obj, QEvent *ev) 00287 { 00288 if ( ev->type() == QEvent::MouseButtonPress ) 00289 { 00290 QMouseEvent *mev= (QMouseEvent *)(ev); 00291 //kDebug() << QString("click at %1,%2").arg( mev->x() ).arg( mev->y() ); 00292 00293 if ( mev->button() == Qt::RightButton ) 00294 { 00295 KMenu *popup = createPopupMenu( ); 00296 popup->exec( mev->globalPos() ); 00297 delete popup; 00298 return true; 00299 } 00300 00301 QCursor cursor; 00302 00303 if ( d->m_selectedRegion.contains( mev->pos() ) 00304 && d->m_selectedRegion!=d->m_originalPixmap.rect() ) 00305 { 00306 d->m_state=Private::Moving; 00307 cursor.setShape( Qt::SizeAllCursor ); 00308 d->m_rubberBand->show(); 00309 } 00310 else 00311 { 00312 d->m_state=Private::Resizing; 00313 cursor.setShape( Qt::CrossCursor ); 00314 } 00315 QApplication::setOverrideCursor(cursor); 00316 00317 d->m_tempFirstClick=mev->pos(); 00318 00319 00320 return true; 00321 } 00322 00323 if ( ev->type() == QEvent::MouseMove ) 00324 { 00325 QMouseEvent *mev= (QMouseEvent *)(ev); 00326 00327 //kDebug() << QString("move to %1,%2").arg( mev->x() ).arg( mev->y() ); 00328 00329 if ( d->m_state == Private::Resizing ) 00330 { 00331 setSelectedRegion ( 00332 d->calcSelectionRectangle( d->m_tempFirstClick, mev->pos() ) ); 00333 } 00334 else if (d->m_state == Private::Moving ) 00335 { 00336 int mevx = mev->x(); 00337 int mevy = mev->y(); 00338 bool mouseOutside=false; 00339 if ( mevx < 0 ) 00340 { 00341 d->m_selectedRegion.translate(-d->m_selectedRegion.x(),0); 00342 mouseOutside=true; 00343 } 00344 else if ( mevx > d->m_originalPixmap.width() ) 00345 { 00346 d->m_selectedRegion.translate(d->m_originalPixmap.width()-d->m_selectedRegion.width()-d->m_selectedRegion.x(),0); 00347 mouseOutside=true; 00348 } 00349 if ( mevy < 0 ) 00350 { 00351 d->m_selectedRegion.translate(0,-d->m_selectedRegion.y()); 00352 mouseOutside=true; 00353 } 00354 else if ( mevy > d->m_originalPixmap.height() ) 00355 { 00356 d->m_selectedRegion.translate(0,d->m_originalPixmap.height()-d->m_selectedRegion.height()-d->m_selectedRegion.y()); 00357 mouseOutside=true; 00358 } 00359 if (mouseOutside) { d->updatePixmap(); return true; }; 00360 00361 d->m_selectedRegion.translate( mev->x()-d->m_tempFirstClick.x(), 00362 mev->y()-d->m_tempFirstClick.y() ); 00363 00364 // Check that the region has not fallen outside the image 00365 if (d->m_selectedRegion.x() < 0) 00366 d->m_selectedRegion.translate(-d->m_selectedRegion.x(),0); 00367 else if (d->m_selectedRegion.right() > d->m_originalPixmap.width()) 00368 d->m_selectedRegion.translate(-(d->m_selectedRegion.right()-d->m_originalPixmap.width()),0); 00369 00370 if (d->m_selectedRegion.y() < 0) 00371 d->m_selectedRegion.translate(0,-d->m_selectedRegion.y()); 00372 else if (d->m_selectedRegion.bottom() > d->m_originalPixmap.height()) 00373 d->m_selectedRegion.translate(0,-(d->m_selectedRegion.bottom()-d->m_originalPixmap.height())); 00374 00375 d->m_tempFirstClick=mev->pos(); 00376 d->updatePixmap(); 00377 } 00378 return true; 00379 } 00380 00381 if ( ev->type() == QEvent::MouseButtonRelease ) 00382 { 00383 QMouseEvent *mev= (QMouseEvent *)(ev); 00384 00385 if ( d->m_state == Private::Resizing && mev->pos() == d->m_tempFirstClick) 00386 resetSelection(); 00387 00388 d->m_state=Private::None; 00389 QApplication::restoreOverrideCursor(); 00390 d->m_rubberBand->hide(); 00391 return true; 00392 } 00393 00394 QWidget::eventFilter(obj, ev); 00395 return false; 00396 } 00397 00398 QRect KPixmapRegionSelectorWidget::Private::calcSelectionRectangle( const QPoint & startPoint, const QPoint & _endPoint ) 00399 { 00400 QPoint endPoint = _endPoint; 00401 if ( endPoint.x() < 0 ) endPoint.setX(0); 00402 else if ( endPoint.x() > m_originalPixmap.width() ) endPoint.setX(m_originalPixmap.width()); 00403 if ( endPoint.y() < 0 ) endPoint.setY(0); 00404 else if ( endPoint.y() > m_originalPixmap.height() ) endPoint.setY(m_originalPixmap.height()); 00405 int w=abs(startPoint.x()-endPoint.x()); 00406 int h=abs(startPoint.y()-endPoint.y()); 00407 00408 if (m_forcedAspectRatio>0) 00409 { 00410 double aspectRatio=w/double(h); 00411 00412 if (aspectRatio>m_forcedAspectRatio) 00413 h=(int)(w/m_forcedAspectRatio); 00414 else 00415 w=(int)(h*m_forcedAspectRatio); 00416 } 00417 00418 int x,y; 00419 if ( startPoint.x() < endPoint.x() ) 00420 x=startPoint.x(); 00421 else 00422 x=startPoint.x()-w; 00423 if ( startPoint.y() < endPoint.y() ) 00424 y=startPoint.y(); 00425 else 00426 y=startPoint.y()-h; 00427 00428 if (x<0) 00429 { 00430 w+=x; 00431 x=0; 00432 h=(int)(w/m_forcedAspectRatio); 00433 00434 if ( startPoint.y() > endPoint.y() ) 00435 y=startPoint.y()-h; 00436 } 00437 else if (x+w>m_originalPixmap.width()) 00438 { 00439 w=m_originalPixmap.width()-x; 00440 h=(int)(w/m_forcedAspectRatio); 00441 00442 if ( startPoint.y() > endPoint.y() ) 00443 y=startPoint.y()-h; 00444 } 00445 if (y<0) 00446 { 00447 h+=y; 00448 y=0; 00449 w=(int)(h*m_forcedAspectRatio); 00450 00451 if ( startPoint.x() > endPoint.x() ) 00452 x=startPoint.x()-w; 00453 } 00454 else if (y+h>m_originalPixmap.height()) 00455 { 00456 h=m_originalPixmap.height()-y; 00457 w=(int)(h*m_forcedAspectRatio); 00458 00459 if ( startPoint.x() > endPoint.x() ) 00460 x=startPoint.x()-w; 00461 } 00462 00463 return QRect(x,y,w,h); 00464 } 00465 00466 QRect KPixmapRegionSelectorWidget::unzoomedSelectedRegion() const 00467 { 00468 return QRect((int)(d->m_selectedRegion.x()/d->m_zoomFactor), 00469 (int)(d->m_selectedRegion.y()/d->m_zoomFactor), 00470 (int)(d->m_selectedRegion.width()/d->m_zoomFactor), 00471 (int)(d->m_selectedRegion.height()/d->m_zoomFactor)); 00472 } 00473 00474 QImage KPixmapRegionSelectorWidget::selectedImage() const 00475 { 00476 QImage origImage=d->m_unzoomedPixmap.toImage(); 00477 return origImage.copy(unzoomedSelectedRegion()); 00478 } 00479 00480 void KPixmapRegionSelectorWidget::setSelectionAspectRatio(int width, int height) 00481 { 00482 d->m_forcedAspectRatio=width/double(height); 00483 } 00484 00485 void KPixmapRegionSelectorWidget::setFreeSelectionAspectRatio() 00486 { 00487 d->m_forcedAspectRatio=0; 00488 } 00489 00490 void KPixmapRegionSelectorWidget::setMaximumWidgetSize(int width, int height) 00491 { 00492 d->m_maxWidth=width; 00493 d->m_maxHeight=height; 00494 00495 if (d->m_selectedRegion == d->m_originalPixmap.rect()) d->m_selectedRegion=QRect(); 00496 d->m_originalPixmap=d->m_unzoomedPixmap; 00497 00498 // kDebug() << QString(" original Pixmap :") << d->m_originalPixmap.rect(); 00499 // kDebug() << QString(" unzoomed Pixmap : %1 x %2 ").arg(d->m_unzoomedPixmap.width()).arg(d->m_unzoomedPixmap.height()); 00500 00501 if ( !d->m_originalPixmap.isNull() && 00502 ( d->m_originalPixmap.width() > d->m_maxWidth || 00503 d->m_originalPixmap.height() > d->m_maxHeight ) ) 00504 { 00505 /* We have to resize the pixmap to get it complete on the screen */ 00506 QImage image=d->m_originalPixmap.toImage(); 00507 d->m_originalPixmap=QPixmap::fromImage( image.scaled( width, height, Qt::KeepAspectRatio,Qt::SmoothTransformation ) ); 00508 double oldZoomFactor = d->m_zoomFactor; 00509 d->m_zoomFactor=d->m_originalPixmap.width()/(double)d->m_unzoomedPixmap.width(); 00510 00511 if (d->m_selectedRegion.isValid()) 00512 { 00513 d->m_selectedRegion= 00514 QRect((int)(d->m_selectedRegion.x()*d->m_zoomFactor/oldZoomFactor), 00515 (int)(d->m_selectedRegion.y()*d->m_zoomFactor/oldZoomFactor), 00516 (int)(d->m_selectedRegion.width()*d->m_zoomFactor/oldZoomFactor), 00517 (int)(d->m_selectedRegion.height()*d->m_zoomFactor/oldZoomFactor) ); 00518 } 00519 } 00520 00521 if (!d->m_selectedRegion.isValid()) d->m_selectedRegion = d->m_originalPixmap.rect(); 00522 00523 d->m_linedPixmap=QPixmap(); 00524 d->updatePixmap(); 00525 resize(d->m_label->width(), d->m_label->height()); 00526 } 00527 00528 #include "kpixmapregionselectorwidget.moc"
KDE 4.6 API Reference