placeable_window_proxy.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2014, Southwest Research Institute® (SwRI®)
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 //     * Redistributions of source code must retain the above copyright
00009 //       notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above copyright
00011 //       notice, this list of conditions and the following disclaimer in the
00012 //       documentation and/or other materials provided with the distribution.
00013 //     * Neither the name of Southwest Research Institute® (SwRI®) nor the
00014 //       names of its contributors may be used to endorse or promote products
00015 //       derived from this software without specific prior written permission.
00016 //
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //
00028 // *****************************************************************************
00029 
00030 #include <mapviz_plugins/placeable_window_proxy.h>
00031 
00032 #include <math.h>
00033 #include <cmath>
00034 
00035 #include <QApplication>
00036 #include <QCursor>
00037 #include <QLine>
00038 #include <QMouseEvent>
00039 #include <QResizeEvent>
00040 #include <QTimerEvent>
00041 #include <QWidget>
00042 
00043 #include <QDebug>
00044 
00045 namespace mapviz_plugins
00046 {
00047 PlaceableWindowProxy::PlaceableWindowProxy()
00048   :
00049   target_(NULL),
00050   visible_(true),
00051   has_cursor_(false),
00052   state_(INACTIVE),
00053   win_resize_timer_(-1)
00054 {
00055 }
00056 
00057 PlaceableWindowProxy::~PlaceableWindowProxy()
00058 {
00059   if (target_)
00060   {
00061     target_->removeEventFilter(this);
00062   }
00063 }
00064 
00065 void PlaceableWindowProxy::setContainer(QWidget *target)
00066 {
00067   if (target_)
00068   {
00069     target_->removeEventFilter(this);
00070   }
00071 
00072   target_ = target;
00073 
00074   if (target_)
00075   {
00076     target_->installEventFilter(this);
00077   }
00078 }
00079 
00080 QRect PlaceableWindowProxy::rect() const
00081 {
00082   return rect_.toRect();
00083 }
00084 
00085 void PlaceableWindowProxy::setRect(const QRect &rect)
00086 {
00087   rect_ = QRectF(rect);
00088   state_ = INACTIVE;
00089 }
00090 
00091 void PlaceableWindowProxy::setVisible(bool visible)
00092 {
00093   if (visible == visible_)
00094   {
00095     return;
00096   }
00097 
00098   visible_ = visible;
00099 
00100   if (!visible_ && state_ != INACTIVE)
00101   {
00102     if (has_cursor_)
00103     {
00104       QApplication::restoreOverrideCursor();
00105       has_cursor_ = false;
00106     }
00107     state_ = INACTIVE;
00108   } 
00109 }
00110 
00111 bool PlaceableWindowProxy::eventFilter(QObject *, QEvent *event)
00112 {
00113   // This should never happen, but doesn't hurt to be defensive.
00114   if (!target_)
00115   {
00116     return false;
00117   }
00118 
00119   if (!visible_)
00120   {
00121     return false;
00122   }
00123 
00124   switch (event->type())
00125   {
00126   case QEvent::MouseButtonPress:
00127     return handleMousePress(static_cast<QMouseEvent*>(event));
00128   case QEvent::MouseButtonRelease:
00129     return handleMouseRelease(static_cast<QMouseEvent*>(event));
00130   case QEvent::MouseMove:
00131     return handleMouseMove(static_cast<QMouseEvent*>(event));
00132   case QEvent::Resize:
00133     return handleResize(static_cast<QResizeEvent*>(event));
00134   default:
00135     return false;
00136   }
00137 }
00138 
00139 bool PlaceableWindowProxy::handleMousePress(QMouseEvent *event)
00140 {
00141   if (!visible_)
00142   {
00143     return false;
00144   }
00145 
00146   if (!rect_.contains(event->pos()))
00147   {
00148     // We don't care about anything outside the rect.
00149      return false;
00150   }
00151 
00152   if (state_ != INACTIVE)
00153   {
00154     // We're already doing something, so we don't want to enter
00155     // another state.  But we also don't want someone else to start
00156     // doing something, so we filter out the press.
00157     return true;
00158   }
00159   
00160   if (event->button() == Qt::LeftButton)
00161   {
00162     start_rect_ = rect_;
00163     start_point_ = event->pos();
00164     state_ = getNextState(event->posF());
00165     qWarning("changing state to %d", state_);
00166     return true;
00167   }
00168 
00169   // Event if we're not doing anything, we want to present a
00170   // consistent interface that says "this region is belongs to me", so
00171   // we filter out events.
00172   return true;
00173 }
00174 
00175 bool PlaceableWindowProxy::handleMouseRelease(QMouseEvent *event)
00176 {
00177   if (!visible_)
00178   {
00179     return false;
00180   }
00181 
00182   if (state_ == INACTIVE)
00183   {
00184     return false;
00185   }
00186 
00187   if (event->button() == Qt::LeftButton)
00188   {
00189     state_ = INACTIVE;
00190     return true;    
00191   }
00192   
00193   return false;
00194 }
00195 
00196 bool PlaceableWindowProxy::handleMouseMove(QMouseEvent *event)
00197 {
00198   if (!visible_)
00199   {
00200     return false;
00201   }
00202 
00203   if (state_ == INACTIVE)
00204   {
00205     if (!rect_.contains(event->posF()))
00206     {
00207       if (has_cursor_)
00208       {
00209         QApplication::restoreOverrideCursor();
00210         has_cursor_ = false;
00211       }
00212       return false;
00213     }    
00214 
00215     // The mouse cursor is over the rect, so we're going to change the
00216     // cursor to indicate the state the user would enter by clicking.
00217 
00218     Qt::CursorShape shape;
00219     switch(getNextState(event->posF()))
00220     {
00221     case MOVE_TOP_LEFT:
00222     case MOVE_BOTTOM_RIGHT:
00223       shape = Qt::SizeFDiagCursor;
00224       break;
00225     case MOVE_TOP_RIGHT:
00226     case MOVE_BOTTOM_LEFT:
00227       shape = Qt::SizeBDiagCursor;
00228       break;
00229     default:
00230       shape = Qt::SizeAllCursor;
00231     }
00232 
00233     if (has_cursor_)
00234     {
00235       QApplication::changeOverrideCursor(QCursor(shape));
00236     }
00237     else
00238     {
00239       QApplication::setOverrideCursor(QCursor(shape));
00240       has_cursor_ = true;
00241     }
00242 
00243     return true;
00244   }
00245 
00246   QPointF dp = event->posF() - start_point_;
00247 
00248   // todo: enforce minimum size & constrain aspect ratio for resizes.  
00249   if (state_ == MOVE_ALL)
00250   {
00251     rect_ = start_rect_.translated(dp);    
00252   }
00253   else if (state_ == MOVE_TOP_LEFT)
00254   {
00255     rect_= resizeHelper(start_rect_,
00256                         start_rect_.bottomRight(),
00257                         start_rect_.topLeft(),
00258                         event->posF());
00259     rect_.moveBottomRight(start_rect_.bottomRight());      
00260   }
00261   else if (state_ == MOVE_BOTTOM_LEFT)
00262   {
00263     rect_= resizeHelper(start_rect_,
00264                         start_rect_.topRight(),
00265                         start_rect_.bottomLeft(),
00266                         event->posF());
00267     rect_.moveTopRight(start_rect_.topRight());
00268   }
00269   else if (state_ == MOVE_BOTTOM_RIGHT)
00270   {
00271     rect_= resizeHelper(start_rect_,
00272                         start_rect_.topLeft(),
00273                         start_rect_.bottomRight(),
00274                         event->posF());
00275     rect_.moveTopLeft(start_rect_.topLeft());      
00276   }
00277   else if (state_ == MOVE_TOP_RIGHT)
00278   {
00279     rect_= resizeHelper(start_rect_,
00280                         start_rect_.bottomLeft(),
00281                         start_rect_.topRight(),
00282                         event->posF());
00283     rect_.moveBottomLeft(start_rect_.bottomLeft());      
00284   }
00285   else
00286   {
00287     qWarning("Unhandled state in PlaceableWindowProxy: %d", state_);
00288   }
00289 
00290   return true;
00291 }
00292 
00293 QRectF PlaceableWindowProxy::resizeHelper(const QRectF &rect,
00294                                           const QPointF &p1,
00295                                           const QPointF &p2,
00296                                           const QPointF &p3) const
00297 {
00298   QPointF v1 = p2 - p1;
00299   QPointF v2 = p3 - p1;
00300 
00301   double d = v1.x()*v2.y() - v1.y()*v2.x();
00302   if (d < 0)
00303   {
00304     double new_width = std::abs(p3.x() - p1.x());
00305     if (new_width < 10)
00306     {
00307       new_width = 10;
00308     }
00309 
00310     double new_height = rect.height() / rect.width() * new_width;
00311     return QRectF(0, 0, new_width, new_height);
00312   }
00313   else
00314   {
00315     double new_height = std::abs(p3.y() - p1.y());
00316     if (new_height < 10)
00317     {
00318       new_height = 10;
00319     }
00320 
00321     double new_width = rect.width() / rect.height() * new_height;
00322     return QRectF(0, 0, new_width, new_height);
00323   }
00324 }
00325 
00326 
00327 bool PlaceableWindowProxy::handleResize(QResizeEvent *event)
00328 {
00329   // We always want to pass the resize event along to other widgets.
00330   return false;
00331 }
00332 
00333 void PlaceableWindowProxy::timerEvent(QTimerEvent *event)
00334 {
00335   if (event->timerId() == win_resize_timer_)
00336   {
00337     killTimer(win_resize_timer_);
00338     win_resize_timer_ = -1;
00339     if (target_)
00340     {
00341       winResize(target_->size());
00342     }
00343   }
00344 }
00345 
00346 void PlaceableWindowProxy::rectResize(int dx, int dy)
00347 {
00348 }
00349 
00350 void PlaceableWindowProxy::winResize(const QSize &size)
00351 {
00352 }
00353 
00354 PlaceableWindowProxy::State PlaceableWindowProxy::getNextState(
00355   const QPointF &pt) const
00356 {
00357   if (!rect_.contains(pt))
00358   {
00359     return INACTIVE;
00360   }
00361 
00362   const double threshold = 10.0;  
00363   double near_left = pt.x() - rect_.left() < threshold;
00364   double near_top = pt.y() - rect_.top() < threshold;
00365   double near_right = rect_.right() - pt.x() < threshold;
00366   double near_bottom = rect_.bottom() - pt.y() < threshold;
00367 
00368   if (near_top && near_left)
00369   {
00370     return MOVE_TOP_LEFT;
00371   }
00372   else if (near_top && near_right)
00373   {
00374     return MOVE_TOP_RIGHT;
00375   }
00376   else if (near_bottom && near_left)
00377   {
00378     return MOVE_BOTTOM_LEFT;
00379   }
00380   else if (near_bottom && near_right)
00381   {
00382     return MOVE_BOTTOM_RIGHT;
00383   }
00384   else
00385   {
00386     return MOVE_ALL;
00387   }
00388 }
00389 }  // namespace mapviz_plugins


mapviz_plugins
Author(s): Marc Alban
autogenerated on Thu Aug 24 2017 02:46:09