kivio

tool_select.cpp

00001 /*
00002  * Kivio - Visual Modelling and Flowcharting
00003  * Copyright (C) 2000-2003 theKompany.com & Dave Marotti,
00004  *                         Peter Simonsson
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  */
00020 /**************************************************************************************
00021  *
00022  * The code for dragging and resizing stencils is all contained in this class. KivioCanvas
00023  * is used only for drawing since it's a canvas.
00024  *
00025  */
00026 
00027 #include "tool_select.h"
00028 
00029 #include "kivio_view.h"
00030 #include "kivio_doc.h"
00031 #include "kivio_canvas.h"
00032 #include "kivio_page.h"
00033 
00034 #include "kivio_custom_drag_data.h"
00035 #include "kivio_layer.h"
00036 #include "kivio_stencil.h"
00037 
00038 #include <kactionclasses.h>
00039 #include <kpopupmenu.h>
00040 #include <kdebug.h>
00041 #include <KoZoomHandler.h>
00042 #include <KoPoint.h>
00043 #include <klocale.h>
00044 #include <KoGuides.h>
00045 #include "kivio_command.h"
00046 
00047 #include <qwmatrix.h>
00048 
00049 #include "kivio_pluginmanager.h"
00050 
00051 SelectTool::SelectTool( KivioView* parent ) : Kivio::MouseTool(parent, "Selection Mouse Tool")
00052 {
00053   view()->pluginManager()->setDefaultTool(this);
00054 
00055   KShortcut selectShortCut(Key_Space);
00056   selectShortCut.setSeq(1, QKeySequence(Key_Escape));
00057   m_selectAction = new KRadioAction(i18n("&Select"), "select", selectShortCut, actionCollection(), "select");
00058   connect(m_selectAction, SIGNAL(toggled(bool)), this, SLOT(setActivated(bool)));
00059 
00060   m_textEditAction = new KAction(i18n("&Edit Text..."), "text", Key_F2,
00061                                  this, SLOT(editStencilText()), actionCollection(), "editText");
00062   (void) new KAction(i18n("Format &Stencils && Connectors..."), 0, 0, view(), SLOT(stencilFormat()),
00063                           actionCollection(), "formatStencil");
00064   m_arrowHeadAction = new KAction(i18n("Format &Arrowheads..."), 0, 0, view(), SLOT(arrowHeadFormat()),
00065                                   actionCollection(), "formatConnector");
00066 
00067   m_mode = stmNone;
00068   m_pResizingStencil = NULL;
00069   m_pCustomDraggingStencil = NULL;
00070 
00071   m_lstOldGeometry.setAutoDelete(true);
00072 
00073   m_customDragID = 0;
00074 }
00075 
00076 SelectTool::~SelectTool()
00077 {
00078 }
00079 
00080 
00087 bool SelectTool::processEvent(QEvent* e)
00088 {
00089   KivioCanvas* canvas = view()->canvasWidget();
00090   QMouseEvent *m;
00091 
00092   switch (e->type())
00093   {
00094     case QEvent::MouseButtonDblClick:
00095       m = (QMouseEvent *)e;
00096 
00097       if( m->button() == LeftButton ) {
00098         leftDoubleClick(m->pos());
00099       }
00100 
00101       canvas->setFocus();
00102       return true;
00103       break;
00104 
00105     case QEvent::MouseButtonPress:
00106       m = (QMouseEvent *)e;
00107 
00108       if( m->button() == RightButton ) {
00109         showPopupMenu(m->globalPos());
00110       } else if( m->button() == LeftButton ) {
00111         if(m->state() & ControlButton) {
00112           m_controlKey = true;
00113         } else {
00114           m_controlKey = false;
00115         }
00116 
00117         mousePress( m->pos() );
00118       }
00119 
00120       canvas->setFocus();
00121       return true;
00122       break;
00123 
00124     case QEvent::MouseButtonRelease:
00125       mouseRelease( ((QMouseEvent *)e)->pos() );
00126       canvas->setFocus();
00127       return true;
00128       break;
00129 
00130     case QEvent::MouseMove:
00131       mouseMove( static_cast<QMouseEvent*>(e));
00132       return true;
00133       break;
00134 
00135     case QEvent::KeyPress:
00136       if((static_cast<QKeyEvent*>(e)->key() >= Key_Left) && (static_cast<QKeyEvent*>(e)->key() <= Key_Down)) {
00137         keyPress(static_cast<QKeyEvent*>(e));
00138         return true;
00139       }
00140       break;
00141 
00142     default:
00143       break;
00144   }
00145 
00146   return false;
00147 }
00148 
00149 void SelectTool::setActivated(bool a)
00150 {
00151   if(a) {
00152     m_selectAction->setChecked(true);
00153     view()->canvasWidget()->unsetCursor();
00154     m_mode = stmNone;
00155     emit activated(this);
00156   } else if(m_selectAction->isChecked()) {
00157     m_selectAction->setChecked(false);
00158     view()->canvasWidget()->activePage()->setPaintSelected(true);
00159   }
00160 }
00161 
00165 void SelectTool::select(const QRect &r)
00166 {
00167     // Calculate the start and end clicks in terms of page coordinates
00168     KoPoint startPoint = view()->canvasWidget()->mapFromScreen( QPoint( r.x(), r.y() ) );
00169     KoPoint releasePoint = view()->canvasWidget()->mapFromScreen( QPoint( r.x() + r.width(), r.y() + r.height() ) );
00170 
00171 
00172     double x, y, w, h;
00173 
00174     // Calculate the x,y position of the selection box
00175     x = startPoint.x() < releasePoint.x() ? startPoint.x() : releasePoint.x();
00176     y = startPoint.y() < releasePoint.y() ? startPoint.y() : releasePoint.y();
00177 
00178     // Calculate the w/h of the selection box
00179     w = releasePoint.x() - startPoint.x();
00180 
00181     if( w < 0.0 ) {
00182         w *= -1.0;
00183     }
00184 
00185     h = releasePoint.y() - startPoint.y();
00186 
00187     if( h < 0.0 ) {
00188         h *= -1.0;
00189     }
00190 
00191     // Tell the page to select all stencils in this box
00192     view()->activePage()->selectStencils( x, y, w, h );
00193 }
00194 
00195 void SelectTool::mousePress(const QPoint &pos)
00196 {
00197   // Last point is used for undrawing at the last position and calculating the distance the mouse has moved
00198   m_lastPoint = view()->canvasWidget()->mapFromScreen(pos);
00199   m_origPoint = m_lastPoint;
00200 
00201   // Check if we nailed a custom drag point on a selected stencil
00202   if( startCustomDragging(pos, true) )
00203   {
00204     m_mode = stmCustomDragging;
00205     return;
00206   }
00207 
00208   // Check if we are resizing
00209   if( startResizing(pos) )
00210   {
00211     m_mode = stmResizing;
00212     return;
00213   }
00214 
00215 
00216   // Check if we nailed a custom drag point on any other stencil
00217   if( startCustomDragging(pos, false) )
00218   {
00219     m_mode = stmCustomDragging;
00220     return;
00221   }
00222 
00223   // Check if we can drag a stencil
00224   if( startDragging(pos, false) )
00225   {
00226     m_mode = stmDragging;
00227     return;
00228   }
00229 
00230   // This should always be the last 'start' call since it always returns true
00231   if( startRubberBanding(pos) )
00232   {
00233     m_mode = stmDrawRubber;
00234     return;
00235   }
00236 }
00237 
00238 
00242 bool SelectTool::startRubberBanding(const QPoint &pos)
00243 {
00244   KivioCanvas* canvas = view()->canvasWidget();
00245   // We didn't find a stencil, so unselect everything if we aren't holding the control key down
00246   if( !m_controlKey )
00247     canvas->activePage()->unselectAllStencils();
00248 
00249   canvas->startRectDraw( pos, KivioCanvas::Rubber );
00250   canvas->repaint();
00251 
00252   return true;
00253 }
00254 
00255 
00259 bool SelectTool::startDragging(const QPoint &pos, bool onlySelected)
00260 {
00261   KivioCanvas* canvas = view()->canvasWidget();
00262   KivioPage *pPage = canvas->activePage();
00263   KivioStencil *pStencil;
00264   int colType;
00265 
00266   // Figure out how big 4 pixels is in terms of points
00267   double threshold =  view()->zoomHandler()->unzoomItY(4);
00268 
00269   KoPoint pagePoint = canvas->mapFromScreen( pos );
00270 
00271   pStencil = pPage->checkForStencil( &pagePoint, &colType, threshold, onlySelected );
00272 
00273   if( !pStencil )
00274     return false;
00275 
00276   canvas->setEnabled(false);
00277 
00278   if( pStencil->isSelected() )
00279   {
00280     // If we are clicking an already selected stencil, and the control
00281     // key down, then unselect this stencil
00282     if( m_controlKey==true ) {
00283       pPage->unselectStencil( pStencil );
00284     }
00285 
00286     // Otherwise, it means we are just moving
00287   }
00288   else
00289   {
00290     // Clicking a new stencil, and the control key is not down
00291     if( !m_controlKey )
00292       pPage->unselectAllStencils();
00293 
00294     pPage->selectStencil( pStencil );
00295     // Update the auto guidelines
00296     view()->canvasWidget()->updateAutoGuideLines();
00297   }
00298 
00299   // Create a new painter object
00300   canvas->beginUnclippedSpawnerPainter();
00301 
00302   // Build the list of old geometry
00303   KivioSelectDragData *pData;
00304   m_lstOldGeometry.clear();
00305   pStencil = canvas->activePage()->selectedStencils()->first();
00306 
00307   while( pStencil )
00308   {
00309     pData = new KivioSelectDragData;
00310     pData->rect = pStencil->rect();
00311     m_lstOldGeometry.append(pData);
00312 
00313     pStencil = canvas->activePage()->selectedStencils()->next();
00314   }
00315 
00316   m_selectedRect = view()->activePage()->getRectForAllSelectedStencils();
00317   changeMouseCursor(pos);
00318   // Set the mode
00319   m_mode = stmDragging;
00320   m_firstTime = true;
00321   canvas->setEnabled(true);
00322   return true;
00323 }
00324 
00325 bool SelectTool::startCustomDragging(const QPoint &pos, bool selectedOnly )
00326 {
00327   KivioCanvas* canvas = view()->canvasWidget();
00328   KivioPage *pPage = canvas->activePage();
00329   KivioStencil *pStencil;
00330   int colType;
00331 
00332   KoPoint pagePoint = canvas->mapFromScreen( pos );
00333   
00334   // Figure out how big 4 pixels is in terms of points
00335   double threshold =  view()->zoomHandler()->unzoomItY(4);
00336 
00337   pStencil = pPage->checkForStencil( &pagePoint, &colType, threshold, selectedOnly );
00338 
00339   if( !pStencil || colType < kctCustom ) {
00340     return false;
00341   }
00342 
00343 
00344   if(pStencil->isSelected()) {
00345     // If we are clicking an already selected stencil, and the control
00346     // key down, then unselect this stencil
00347     if(m_controlKey) {
00348       pPage->unselectStencil(pStencil);
00349     }
00350   } else {
00351     // Clicking a new stencil, and the control key is not down
00352     if(!m_controlKey) {
00353       pPage->unselectAllStencils();
00354     }
00355 
00356     pPage->selectStencil( pStencil );
00357   }
00358 
00359   m_pCustomDraggingStencil = pStencil;
00360 
00361   // Set the mode
00362   m_mode = stmCustomDragging;
00363 
00364   m_customDragID = colType;
00365   m_customDragOrigPoint = pStencil->customIDPoint(m_customDragID);
00366 
00367   view()->canvasWidget()->setShowConnectorTargets(true);
00368   view()->canvasWidget()->repaint();
00369 
00370   // Create a new painter object
00371   canvas->beginUnclippedSpawnerPainter();
00372   m_firstTime = true;
00373 
00374   return true;
00375 }
00376 
00380 bool SelectTool::startResizing(const QPoint &pos)
00381 {
00382   KivioCanvas* canvas = view()->canvasWidget();
00383   KoPoint pagePoint = canvas->mapFromScreen(pos);
00384   KivioSelectDragData *pData;
00385 
00386   double x = pagePoint.x();
00387   double y = pagePoint.y();
00388 
00389   // Search selected stencils to see if we have a resizing point
00390   KivioStencil *pStencil = canvas->activePage()->selectedStencils()->first();
00391   while( pStencil )
00392   {
00393     m_resizeHandle = isOverResizeHandle(pStencil, x, y);
00394     if( m_resizeHandle > 0 )
00395     {
00396       switch( m_resizeHandle )
00397       {
00398         case 1: // top left
00399           m_origPoint.setCoords(pStencil->x(), pStencil->y());
00400           break;
00401 
00402         case 2:
00403           m_origPoint.setCoords((pStencil->x() + pStencil->w()) / 2.0, pStencil->y());
00404           break;
00405 
00406         case 3:
00407           m_origPoint.setCoords(pStencil->x() + pStencil->w(), pStencil->y());
00408           break;
00409 
00410         case 4:
00411           m_origPoint.setCoords(pStencil->x() + pStencil->w(), (pStencil->y() + pStencil->h()) / 2.0);
00412           break;
00413 
00414         case 5:
00415           m_origPoint.setCoords(pStencil->x() + pStencil->w(), pStencil->y() + pStencil->h());
00416           break;
00417 
00418         case 6:
00419           m_origPoint.setCoords((pStencil->x() + pStencil->w()) / 2.0, pStencil->y() + pStencil->h());
00420           break;
00421 
00422         case 7:
00423           m_origPoint.setCoords(pStencil->x(), pStencil->y() + pStencil->h());
00424           break;
00425 
00426         case 8:
00427           m_origPoint.setCoords(pStencil->x(), (pStencil->y() + pStencil->h()) / 2.0);
00428           break;
00429       }
00430 
00431       m_lstOldGeometry.clear();
00432       pData = new KivioSelectDragData;
00433       pData->rect = pStencil->rect();
00434       m_lstOldGeometry.append(pData);
00435 
00436       m_pResizingStencil = pStencil;
00437 
00438       // Create a new painter object
00439       canvas->beginUnclippedSpawnerPainter();
00440       m_firstTime = true;
00441 
00442       return true;
00443     }
00444 
00445     pStencil = canvas->activePage()->selectedStencils()->next();
00446   }
00447 
00448   return false;
00449 }
00450 
00451 
00452 
00453 void SelectTool::mouseMove(QMouseEvent* e)
00454 {
00455     QPoint pos = e->pos();
00456     bool ignoreGridGuides = e->state() & ShiftButton;
00457     
00458     switch( m_mode )
00459     {
00460         case stmDrawRubber:
00461             continueRubberBanding(pos);
00462             break;
00463 
00464         case stmDragging:
00465             continueDragging(pos, ignoreGridGuides);
00466             break;
00467 
00468         case stmCustomDragging:
00469             continueCustomDragging(pos);
00470             break;
00471 
00472         case stmResizing:
00473             continueResizing(pos, ignoreGridGuides);
00474             break;
00475 
00476         default:
00477             changeMouseCursor(pos);
00478             break;
00479     }
00480 
00481     m_lastPoint = view()->canvasWidget()->mapFromScreen(pos);
00482 }
00483 
00484 void SelectTool::continueRubberBanding(const QPoint &pos)
00485 {
00486     view()->canvasWidget()->continueRectDraw( pos, KivioCanvas::Rubber );
00487 }
00488 
00489 
00497 void SelectTool::continueDragging(const QPoint &pos, bool ignoreGridGuides)
00498 {
00499   KivioCanvas* canvas = view()->canvasWidget();
00500   KoPoint pagePoint = canvas->mapFromScreen( pos );
00501 
00502   double dx = pagePoint.x() - m_origPoint.x();
00503   double dy = pagePoint.y() - m_origPoint.y();
00504 
00505   bool snappedX;
00506   bool snappedY;
00507 
00508   double newX, newY;
00509 
00510   // Undraw the old stencils
00511   if(!m_firstTime) {
00512     canvas->drawSelectedStencilsXOR();
00513   } else {
00514     canvas->activePage()->setPaintSelected(false);
00515     canvas->repaint();
00516     m_firstTime = false;
00517   }
00518 
00519   // Translate to the new position
00520   KoPoint p;
00521 
00522   newX = m_selectedRect.x() + dx;
00523   newY = m_selectedRect.y() + dy;
00524 
00525   if(!ignoreGridGuides) {
00526     // First attempt a snap-to-grid
00527     p.setCoords(newX, newY);
00528 
00529     p = canvas->snapToGrid(p);
00530 
00531     newX = p.x();
00532     newY = p.y();
00533 
00534     // Now the guides override the grid so we attempt to snap to them
00535     // The bottom
00536     p.setCoords(m_selectedRect.x() + dx + m_selectedRect.width(), m_selectedRect.y() + dy + m_selectedRect.height());
00537     p = canvas->snapToGuides(p, snappedX, snappedY);
00538 
00539     if(snappedX) {
00540       newX = p.x() - m_selectedRect.width();
00541     }
00542 
00543     if(snappedY) {
00544       newY = p.y() - m_selectedRect.height();
00545     }
00546 
00547     // The middle
00548     p.setCoords(m_selectedRect.x() + dx + (m_selectedRect.width() / 2.0),
00549                 m_selectedRect.y() + dy + (m_selectedRect.height() / 2.0));
00550     p = canvas->snapToGuides(p, snappedX, snappedY);
00551 
00552     if(snappedX) {
00553       newX = p.x() - (m_selectedRect.width() / 2.0);
00554     }
00555 
00556     if(snappedY) {
00557       newY = p.y() - (m_selectedRect.height() / 2.0);
00558     }
00559 
00560     // The top
00561     p.setCoords(m_selectedRect.x() + dx, m_selectedRect.y() + dy);
00562     p = canvas->snapToGuides(p, snappedX, snappedY);
00563 
00564     if(snappedX) {
00565       newX = p.x();
00566     }
00567 
00568     if(snappedY) {
00569       newY = p.y();
00570     }
00571   }
00572 
00573   dx = newX - m_selectedRect.x();
00574   dy = newY - m_selectedRect.y();
00575 
00576   KivioSelectDragData *pData;
00577   KivioStencil *pStencil = canvas->activePage()->selectedStencils()->first();
00578   pData = m_lstOldGeometry.first();
00579 
00580   while( pStencil && pData )
00581   {
00582     newX = pData->rect.x() + dx;
00583     newY = pData->rect.y() + dy;
00584 
00585     if( pStencil->protection()->at( kpX ) == false ) {
00586       pStencil->setX(newX);
00587     }
00588     if( pStencil->protection()->at( kpY ) == false ) {
00589       pStencil->setY(newY);
00590     }
00591 
00592     pData = m_lstOldGeometry.next();
00593     pStencil = canvas->activePage()->selectedStencils()->next();
00594   }
00595 
00596   // Draw the stencils
00597   canvas->drawSelectedStencilsXOR();
00598   view()->updateToolBars();
00599 }
00600 
00601 void SelectTool::continueCustomDragging(const QPoint &pos)
00602 {
00603   KivioCanvas* canvas = view()->canvasWidget();
00604   KoPoint pagePoint = canvas->mapFromScreen(pos);
00605   bool hit = false;
00606 
00607   if(m_pCustomDraggingStencil->type() == kstConnector){
00608     pagePoint = canvas->activePage()->snapToTarget(pagePoint, 8.0, hit);
00609   }
00610 
00611   if(!hit) {
00612     pagePoint = canvas->snapToGridAndGuides( pagePoint );
00613   }
00614 
00615   KivioCustomDragData data;
00616   data.page = canvas->activePage();
00617   data.dx = pagePoint.x() - m_lastPoint.x();
00618   data.dy = pagePoint.y() - m_lastPoint.y();
00619   data.x = pagePoint.x();
00620   data.y = pagePoint.y();
00621   data.id = m_customDragID;
00622   data.scale = view()->zoomHandler()->zoomedResolutionY();
00623 
00624 
00625   if(m_pCustomDraggingStencil->type() != kstConnector){
00626     // Undraw the old stencils
00627     if(!m_firstTime) {
00628       canvas->drawStencilXOR(m_pCustomDraggingStencil);
00629     } else {
00630       m_pCustomDraggingStencil->setHidden(true);
00631       canvas->repaint();
00632       m_firstTime = false;
00633     }
00634   }
00635 
00636   // Custom dragging can only occur on one stencil
00637   if( m_pCustomDraggingStencil )
00638     m_pCustomDraggingStencil->customDrag( &data );
00639 
00640   // Draw the stencils
00641   if(m_pCustomDraggingStencil->type() != kstConnector){
00642     canvas->drawStencilXOR(m_pCustomDraggingStencil);
00643   } else {
00644     view()->canvasWidget()->repaint();
00645   }
00646 
00647   view()->updateToolBars();
00648 }
00649 
00650 
00651 void SelectTool::continueResizing(const QPoint &pos, bool ignoreGridGuides)
00652 {
00653   KivioCanvas* canvas = view()->canvasWidget();
00654   KoPoint pagePoint = canvas->mapFromScreen(pos);
00655   
00656   if(!ignoreGridGuides) {
00657     pagePoint = canvas->snapToGridAndGuides( pagePoint );
00658   }
00659   
00660   KivioSelectDragData *pData = m_lstOldGeometry.first();
00661 
00662 /*  QWMatrix m;
00663   double w2 = m_pResizingStencil->w() / 2.0;
00664   double h2 = m_pResizingStencil->h() / 2.0;
00665   m.translate(m_pResizingStencil->x(), m_pResizingStencil->y());
00666   m.translate(m_pResizingStencil->pinPoint().x(), m_pResizingStencil->pinPoint().y());
00667   m.rotate(-m_pResizingStencil->rotation());
00668   m.translate(-m_pResizingStencil->pinPoint().x(), -m_pResizingStencil->pinPoint().y());
00669   m.translate(-m_pResizingStencil->x(), -m_pResizingStencil->y());
00670   m.invert();
00671   
00672   double x = pagePoint.x() * m.m11() + pagePoint.y() * m.m21() + m.dx();
00673   double y = pagePoint.x() * m.m12() + pagePoint.y() * m.m22() + m.dy();*/
00674   
00675   if( !pData )
00676   {
00677       kdDebug(43000) << "SelectTool::continueResizing() - Original geometry not found" << endl;
00678       return;
00679   }
00680 
00681   double dx = pagePoint.x() - m_origPoint.x();
00682   double dy = pagePoint.y() - m_origPoint.y();
00683       
00684   if((dx > 0) || (dy > 0) || (dx < 0) || (dy < 0)) { // Do we really need to redraw?
00685     // Undraw the old outline
00686     if(!m_firstTime) {
00687       canvas->drawStencilXOR( m_pResizingStencil );
00688     } else {
00689       m_pResizingStencil->setHidden(true);
00690       canvas->repaint();
00691       m_firstTime = false;
00692     }
00693   
00694     double sx = pData->rect.x();
00695     double sy = pData->rect.y();
00696     double sw = pData->rect.width();
00697     double sh = pData->rect.height();
00698     double ratio = sw / sh;
00699   
00700     switch( m_resizeHandle )
00701     {
00702       case 1: // top left
00703         if( m_pResizingStencil->protection()->testBit( kpWidth )==false &&
00704           m_pResizingStencil->protection()->testBit( kpHeight )==false )
00705         {
00706           if((dx > dy) && (dx != 0)) {
00707             dy = dx / ratio;
00708           } else {
00709             dx = dy * ratio;
00710           }
00711   
00712           m_pResizingStencil->setX( sx + dx );
00713           m_pResizingStencil->setW( sw - dx );
00714   
00715           m_pResizingStencil->setY( sy + dy );
00716           m_pResizingStencil->setH( sh - dy );
00717         }
00718         break;
00719   
00720       case 2: // top
00721         if( m_pResizingStencil->protection()->testBit( kpHeight )==false )
00722         {
00723           m_pResizingStencil->setY( sy + dy );
00724           m_pResizingStencil->setH( sh - dy );
00725         }
00726         break;
00727   
00728       case 3: // top right
00729         if( m_pResizingStencil->protection()->testBit( kpHeight )==false &&
00730           m_pResizingStencil->protection()->testBit( kpWidth )==false )
00731         {
00732           if((dx > dy) && (dx != 0)) {
00733             dy = -(dx / ratio);
00734           } else {
00735             dx = -(dy * ratio);
00736           }
00737   
00738           m_pResizingStencil->setY( sy + dy );
00739           m_pResizingStencil->setH( sh - dy );
00740   
00741           m_pResizingStencil->setW( sw + dx );
00742         }
00743         break;
00744   
00745       case 4: // right
00746         if( m_pResizingStencil->protection()->testBit( kpWidth )==false )
00747         {
00748           // see old kivio source when snaptogrid gets implemented
00749           //setX( SnapToGrid(sx+sw+dx)-sx )
00750           m_pResizingStencil->setW( sw + dx );
00751         }
00752         break;
00753   
00754       case 5: // bottom right
00755         if( m_pResizingStencil->protection()->testBit( kpWidth )==false &&
00756           m_pResizingStencil->protection()->testBit( kpHeight )==false )
00757         {
00758           if((dx > dy) && (dx != 0)) {
00759             dy = dx / ratio;
00760           } else {
00761             dx = dy * ratio;
00762           }
00763   
00764           m_pResizingStencil->setW( sw + dx );
00765           m_pResizingStencil->setH( sh + dy );
00766         }
00767         break;
00768   
00769       case 6: // bottom
00770         if( m_pResizingStencil->protection()->testBit( kpHeight )==false )
00771         {
00772           m_pResizingStencil->setH( sh + dy );
00773         }
00774         break;
00775   
00776       case 7: // bottom left
00777         if( m_pResizingStencil->protection()->testBit( kpWidth )==false &&
00778           m_pResizingStencil->protection()->testBit( kpHeight )==false )
00779         {
00780           if((dx > dy) && (dx != 0)) {
00781             dy = -(dx / ratio);
00782           } else {
00783             dx = -(dy * ratio);
00784           }
00785   
00786           m_pResizingStencil->setX( sx + dx );
00787           m_pResizingStencil->setW( sw - dx );
00788   
00789           m_pResizingStencil->setH( sh + dy );
00790         }
00791         break;
00792   
00793       case 8: // left
00794         if( m_pResizingStencil->protection()->testBit( kpWidth )==false )
00795         {
00796           KoPoint pinPoint = m_pResizingStencil->pinPoint();
00797           m_pResizingStencil->setPinPoint(KoPoint(pinPoint.x() - (dx / 2.0), pinPoint.y()));
00798           m_pResizingStencil->setX( sx + dx );
00799           m_pResizingStencil->setW( sw - dx );
00800         }
00801         break;
00802   
00803       default:
00804         kdDebug(43000) << "SelectTool::continueResizing() - unknown resize handle: " <<  m_resizeHandle << endl;
00805         break;
00806     }
00807   
00808     canvas->drawStencilXOR( m_pResizingStencil );
00809     view()->updateToolBars();
00810   }
00811 }
00812 
00813 
00817 void SelectTool::changeMouseCursor(const QPoint &pos)
00818 {
00819   KivioCanvas* canvas = view()->canvasWidget();
00820   KoPoint pagePoint = canvas->mapFromScreen(pos);
00821   KivioStencil *pStencil;
00822   double threshold = view()->zoomHandler()->unzoomItY(4);
00823   int cursorType;
00824 
00825   // Iterate through all the selected stencils
00826   pStencil = canvas->activePage()->selectedStencils()->first();
00827   while( pStencil )
00828   {
00829     cursorType = isOverResizeHandle(pStencil, pagePoint.x(), pagePoint.y());
00830     switch( cursorType )
00831     {
00832       case 1: // top left
00833         canvas->setCursor( sizeFDiagCursor );
00834         return;
00835 
00836       case 2: // top
00837         canvas->setCursor( sizeVerCursor );
00838         return;
00839 
00840       case 3: // top right
00841         canvas->setCursor( sizeBDiagCursor );
00842         return;
00843 
00844       case 4: // right
00845         canvas->setCursor( sizeHorCursor );
00846         return;
00847 
00848       case 5: // bottom right
00849         canvas->setCursor( sizeFDiagCursor );
00850         return;
00851 
00852       case 6: // bottom
00853         canvas->setCursor( sizeVerCursor );
00854         return;
00855 
00856       case 7: // bottom left
00857         canvas->setCursor( sizeBDiagCursor );
00858         return;
00859 
00860       case 8: // left
00861         canvas->setCursor( sizeHorCursor );
00862         return;
00863 
00864       default:
00865         if( pStencil->checkForCollision( &pagePoint, threshold )!= kctNone )
00866         {
00867           canvas->setCursor( sizeAllCursor );
00868           return;
00869         }
00870         break;
00871 
00872     }
00873 
00874 
00875     pStencil = canvas->activePage()->selectedStencils()->next();
00876   }
00877 
00878   canvas->unsetCursor();
00879 }
00880 
00881 
00885 #define RESIZE_BOX_TEST( x, y, bx, by ) \
00886 x >= bx-three_pixels && \
00887 x <= bx+three_pixels && \
00888 y >= by-three_pixels && \
00889 y <= by+three_pixels
00890 
00894 int SelectTool::isOverResizeHandle( KivioStencil *pStencil, const double x, const double y )
00895 {
00896   double three_pixels = 4.0;
00897 
00898   int available;
00899 
00900   QWMatrix m;
00901   double w = pStencil->w();
00902   double h = pStencil->h();
00903   double w2 = pStencil->w() / 2.0;
00904   double h2 = pStencil->h() / 2.0;
00905   m.translate(pStencil->x(), pStencil->y());
00906   m.translate(w2, h2);
00907   m.rotate(pStencil->rotation());
00908   m.translate(-w2, -h2);
00909 
00910   // FIXME: this needs to be optimized!!!!
00911   KoPoint tl(0 * m.m11() + 0 * m.m21() + m.dx(), 0 * m.m12() + 0 * m.m22() + m.dy());
00912   KoPoint t(w2 * m.m11() + 0 * m.m21() + m.dx(), w2 * m.m12() + 0 * m.m22() + m.dy());
00913   KoPoint tr(w * m.m11() + 0 * m.m21() + m.dx(), w * m.m12() + 0 * m.m22() + m.dy());
00914   KoPoint r(w * m.m11() + h2 * m.m21() + m.dx(), w * m.m12() + h2 * m.m22() + m.dy());
00915   KoPoint br(w * m.m11() + h * m.m21() + m.dx(), w * m.m12() + h * m.m22() + m.dy());
00916   KoPoint b(w2 * m.m11() + h * m.m21() + m.dx(), w2 * m.m12() + h * m.m22() + m.dy());
00917   KoPoint bl(0 * m.m11() + h * m.m21() + m.dx(), 0 * m.m12() + h * m.m22() + m.dy());
00918   KoPoint l(0 * m.m11() + h2 * m.m21() + m.dx(), 0 * m.m12() + h2 * m.m22() + m.dy());
00919 
00920   available = pStencil->resizeHandlePositions();
00921 
00922   // Quick reject
00923   if( !available )
00924     return 0;
00925 
00926 
00927   // Top left
00928   if( available & krhpNW &&
00929     RESIZE_BOX_TEST( x, y, tl.x(), tl.y() ) )
00930     return 1;
00931 
00932   // Top
00933   if( available & krhpN &&
00934     RESIZE_BOX_TEST( x, y, t.x(), t.y() ) )
00935     return 2;
00936 
00937   // Top right
00938   if( available & krhpNE &&
00939     RESIZE_BOX_TEST( x, y, tr.x(), tr.y()  ) )
00940     return 3;
00941 
00942   // Right
00943   if( available & krhpE &&
00944     RESIZE_BOX_TEST( x, y, r.x(), r.y() ) )
00945     return 4;
00946 
00947   // Bottom right
00948   if( available & krhpSE &&
00949     RESIZE_BOX_TEST( x, y, br.x(), br.y() ) )
00950     return 5;
00951 
00952   // Bottom
00953   if( available & krhpS &&
00954     RESIZE_BOX_TEST( x, y, b.x(), b.y() ) )
00955     return 6;
00956 
00957   // Bottom left
00958   if( available & krhpSW &&
00959     RESIZE_BOX_TEST( x, y, bl.x(), bl.y() ) )
00960     return 7;
00961 
00962   // Left
00963   if( available & krhpW &&
00964     RESIZE_BOX_TEST( x, y, l.x(), l.y() ) )
00965     return 8;
00966 
00967   // Nothing found
00968   return 0;
00969 }
00970 
00971 
00972 void SelectTool::mouseRelease(const QPoint &pos)
00973 {
00974   m_releasePoint = pos;
00975 
00976   switch( m_mode )
00977   {
00978     case stmDrawRubber:
00979       endRubberBanding(pos);
00980       break;
00981 
00982     case stmCustomDragging:
00983       endCustomDragging(pos);
00984       break;
00985 
00986     case stmDragging:
00987       endDragging(pos);
00988       break;
00989 
00990     case stmResizing:
00991       endResizing(pos);
00992       break;
00993   }
00994 
00995   m_mode = stmNone;
00996 
00997   view()->canvasWidget()->guideLines().repaintAfterSnapping();
00998   view()->doc()->updateView(view()->activePage());
00999 }
01000 
01001 void SelectTool::endRubberBanding(const QPoint &pos)
01002 {
01003   KivioCanvas* canvas = view()->canvasWidget();
01004   // End the rubber-band drawing
01005   canvas->endRectDraw();
01006 
01007   KoPoint p = canvas->mapFromScreen(pos);
01008 
01009   // We can't select if the start and end points are the same
01010   if( m_origPoint.x() != p.x() && m_origPoint.y() != p.y() )
01011   {
01012     select(canvas->rect());
01013   }
01014 
01015   view()->updateToolBars();
01016 }
01017 
01018 void SelectTool::endDragging(const QPoint&)
01019 {
01020   KivioCanvas* canvas = view()->canvasWidget();
01021   canvas->activePage()->setPaintSelected(true);
01022   KMacroCommand *macro=new KMacroCommand( i18n("Move Stencil"));
01023   KivioStencil *pStencil = canvas->activePage()->selectedStencils()->first();
01024   KivioSelectDragData *pData = m_lstOldGeometry.first();
01025   bool moved = false;
01026 
01027   while( pStencil && pData )
01028   {
01029     if((pData->rect.x() != pStencil->rect().x()) || (pData->rect.y() != pStencil->rect().y())) {
01030       KivioMoveStencilCommand * cmd = new KivioMoveStencilCommand( i18n("Move Stencil"),
01031         pStencil, pData->rect, pStencil->rect(), canvas->activePage());
01032       macro->addCommand( cmd);
01033 
01034       if(pStencil->type() == kstConnector) {
01035         pStencil->searchForConnections(view()->activePage(), view()->zoomHandler()->unzoomItY(4));
01036       }
01037 
01038       moved = true;
01039     }
01040 
01041     pData = m_lstOldGeometry.next();
01042     pStencil = canvas->activePage()->selectedStencils()->next();
01043   }
01044 
01045   if(moved) {
01046     canvas->doc()->addCommand( macro );
01047   } else {
01048     delete macro;
01049   }
01050 
01051   canvas->drawSelectedStencilsXOR();
01052   canvas->endUnclippedSpawnerPainter();
01053   // Clear the list of old geometry
01054   m_lstOldGeometry.clear();
01055 }
01056 
01057 void SelectTool::endCustomDragging(const QPoint&)
01058 {
01059   KivioCanvas* canvas = view()->canvasWidget();
01060   m_pCustomDraggingStencil->setHidden(false);
01061   KivioCustomDragCommand* cmd = new KivioCustomDragCommand(i18n("Move Connector Point"), view()->activePage(),
01062       m_pCustomDraggingStencil, m_customDragID, m_customDragOrigPoint,
01063       m_pCustomDraggingStencil->customIDPoint(m_customDragID));
01064   view()->doc()->addCommand(cmd);
01065   m_customDragID = 0;
01066   KivioStencil *pStencil = canvas->activePage()->selectedStencils()->first();
01067 
01068   while( pStencil )
01069   {
01070     if(pStencil->type() == kstConnector) {
01071       pStencil->searchForConnections(view()->activePage(), view()->zoomHandler()->unzoomItY(4));
01072     }
01073 
01074     pStencil = canvas->activePage()->selectedStencils()->next();
01075   }
01076 
01077   canvas->endUnclippedSpawnerPainter();
01078 
01079   canvas->setShowConnectorTargets(false);
01080   canvas->repaint();
01081 }
01082 
01083 void SelectTool::endResizing(const QPoint&)
01084 {
01085   KivioCanvas* canvas = view()->canvasWidget();
01086   m_pResizingStencil->setHidden(false);
01087   KivioResizeStencilCommand * cmd = new KivioResizeStencilCommand( i18n("Resize Stencil"),
01088     m_pResizingStencil, m_lstOldGeometry.first()->rect, m_pResizingStencil->rect(), view()->activePage());
01089   canvas->doc()->addCommand( cmd );
01090   // Undraw the last outline
01091   canvas->drawStencilXOR( m_pResizingStencil );
01092 
01093   if(m_pResizingStencil->type() == kstConnector) {
01094     m_pResizingStencil->searchForConnections(view()->activePage(), view()->zoomHandler()->unzoomItY(4));
01095   }
01096 
01097   // Deallocate the painter object
01098   canvas->endUnclippedSpawnerPainter();
01099 
01100   // Set the class vars to nothing
01101   m_pResizingStencil = NULL;
01102   m_resizeHandle = 0;
01103 }
01104 
01108 void SelectTool::showPopupMenu( const QPoint &pos )
01109 {
01110   KPopupMenu* menu = 0;
01111 
01112   if(view()->activePage()->selectedStencils()->count() < 1) {
01113     menu = static_cast<KPopupMenu*>(view()->factory()->container("PagePopup", view()));
01114   } else {
01115     menu = static_cast<KPopupMenu*>(view()->factory()->container("StencilPopup", view()));
01116     m_arrowHeadAction->setEnabled(view()->activePage()->checkForStencilTypeInSelection(kstConnector));
01117 
01118     if(view()->activePage()->checkForTextBoxesInSelection()) {
01119       m_textEditAction->setEnabled(true);
01120     } else {
01121       m_textEditAction->setEnabled(false);
01122     }
01123   }
01124 
01125   if(menu) {
01126     m_lastPoint = view()->canvasWidget()->mapFromScreen(pos);
01127     menu->popup(pos);
01128   } else {
01129     kdDebug(43000) << "What no popup! *ARGH*!" << endl;
01130   }
01131 }
01132 
01133 
01141 void SelectTool::leftDoubleClick(const QPoint& pos)
01142 {
01143   if( view()->activePage()->selectedStencils()->count() <= 0 )
01144     return;
01145   
01146   KoPoint pagePoint = view()->canvasWidget()->mapFromScreen(pos);
01147   // Figure out how big 4 pixels is in terms of points
01148   double threshold =  view()->zoomHandler()->unzoomItY(4);
01149   int colType;
01150   KivioPage *page = view()->activePage();
01151   KivioStencil* stencil = page->checkForStencil( &pagePoint, &colType, threshold, false);
01152   
01153   if(stencil) {
01154     // Locate the text tool.  If not found, bail with an error
01155     Kivio::Plugin *p = view()->pluginManager()->findPlugin("Text Mouse Tool");
01156     
01157     if( !p )
01158     {
01159       kdDebug(43000) << "SelectTool::leftDoubleClick() - unable to locate Text Tool" << endl;
01160       return;
01161     }
01162     
01163     static_cast<Kivio::MouseTool*>(p)->applyToolAction(stencil, pagePoint);
01164   }
01165 }
01166 
01167 void SelectTool::editText(QPtrList<KivioStencil>* stencils)
01168 {
01169   // Locate the text tool.  If not found, bail with an error
01170   Kivio::Plugin *p = view()->pluginManager()->findPlugin("Text Mouse Tool");
01171   if( !p )
01172   {
01173     kdDebug(43000) << "SelectTool::leftDoubleClick() - unable to locate Text Tool" << endl;
01174     return;
01175   }
01176   
01177   // Select the text tool (which makes the text dialog pop up)
01178   static_cast<Kivio::MouseTool*>(p)->applyToolAction(stencils);
01179 }
01180 
01181 void SelectTool::showProperties()
01182 {
01183   //FIXME: This needs to be implemented ;)
01184   if(view()->activePage()->selectedStencils()->count() == 0) {
01185     view()->paperLayoutDlg();
01186   }
01187 }
01188 
01189 void SelectTool::editStencilText()
01190 {
01191   editText(view()->activePage()->selectedStencils());
01192 }
01193 
01194 void SelectTool::keyPress(QKeyEvent* e)
01195 {
01196   KivioCanvas* canvas = view()->canvasWidget();
01197   
01198   canvas->setEnabled(false);
01199 
01200   // Create a new painter object
01201   canvas->beginUnclippedSpawnerPainter();
01202 
01203   // Build the list of old geometry
01204   KivioSelectDragData *pData;
01205   m_lstOldGeometry.clear();
01206   KivioStencil* pStencil = canvas->activePage()->selectedStencils()->first();
01207 
01208   while( pStencil )
01209   {
01210     pData = new KivioSelectDragData;
01211     pData->rect = pStencil->rect();
01212     m_lstOldGeometry.append(pData);
01213 
01214 
01215     pStencil = canvas->activePage()->selectedStencils()->next();
01216   }
01217 
01218   m_selectedRect = view()->activePage()->getRectForAllSelectedStencils();
01219   // Set the mode
01220   m_mode = stmDragging;
01221   canvas->setEnabled(true);
01222   m_origPoint = m_selectedRect.topLeft();
01223   KivioGridData gd = view()->doc()->grid();
01224   bool ignoreGridGuides = e->state() & ShiftButton;
01225   double distX, distY;
01226   
01227   if(ignoreGridGuides || !view()->doc()->grid().isSnap) {
01228     distX = view()->zoomHandler()->unzoomItX(1);
01229     distY = view()->zoomHandler()->unzoomItY(1);
01230   } else {
01231     distX = gd.freq.width();
01232     distY = gd.freq.height();
01233   }
01234   
01235   switch(e->key()) {
01236     case Key_Left:
01237       continueDragging(canvas->mapToScreen(KoPoint(m_selectedRect.x() - distX,
01238         m_selectedRect.y())), ignoreGridGuides);
01239       break;
01240     case Key_Up:
01241       continueDragging(canvas->mapToScreen(KoPoint(m_selectedRect.x(),
01242         m_selectedRect.y() - distY)), ignoreGridGuides);
01243       break;
01244     case Key_Right:
01245       continueDragging(canvas->mapToScreen(KoPoint(m_selectedRect.x() + distX,
01246         m_selectedRect.y())), ignoreGridGuides);
01247       break;
01248     case Key_Down:
01249       continueDragging(canvas->mapToScreen(KoPoint(m_selectedRect.x(),
01250         m_selectedRect.y() + distY)), ignoreGridGuides);
01251       break;
01252     default:
01253       break;
01254   }
01255   
01256   endDragging(QPoint());
01257   canvas->guideLines().repaintAfterSnapping();
01258   canvas->setFocus();
01259 }
01260 
01261 #include "tool_select.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys