]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/ogl/canvas.cpp
aa04b2d01caf76d019063087fe79fb4b3067b6dc
   1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Shape canvas class 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13 #pragma implementation "canvas.h" 
  16 // For compilers that support precompilation, includes "wx.h". 
  17 #include "wx/wxprec.h" 
  27 #include <wx/wxexpr.h> 
  43 #include <wx/ogl/basic.h> 
  44 #include <wx/ogl/basicp.h> 
  45 #include <wx/ogl/canvas.h> 
  46 #include <wx/ogl/ogldiag.h> 
  47 #include <wx/ogl/misc.h> 
  48 #include <wx/ogl/lines.h> 
  49 #include <wx/ogl/composit.h> 
  51 #define CONTROL_POINT_SIZE       6 
  53 // Control point types 
  54 // Rectangle and most other shapes 
  55 #define CONTROL_POINT_VERTICAL   1 
  56 #define CONTROL_POINT_HORIZONTAL 2 
  57 #define CONTROL_POINT_DIAGONAL   3 
  60 #define CONTROL_POINT_ENDPOINT_TO 4 
  61 #define CONTROL_POINT_ENDPOINT_FROM 5 
  62 #define CONTROL_POINT_LINE       6 
  64 IMPLEMENT_DYNAMIC_CLASS(wxShapeCanvas
, wxScrolledWindow
) 
  66 BEGIN_EVENT_TABLE(wxShapeCanvas
, wxScrolledWindow
) 
  67     EVT_PAINT(wxShapeCanvas::OnPaint
) 
  68     EVT_MOUSE_EVENTS(wxShapeCanvas::OnMouseEvent
) 
  72 wxShapeCanvas::wxShapeCanvas(wxWindow 
*parent
, wxWindowID id
, const wxPoint
& pos
, const wxSize
& size
, long style
): 
  73   wxScrolledWindow(parent
, id
, pos
, size
, style
) 
  75   m_shapeDiagram 
= NULL
; 
  76   m_dragState 
= NoDragging
; 
  77   m_draggedShape 
= NULL
; 
  82   m_checkTolerance 
= TRUE
; 
  85 wxShapeCanvas::~wxShapeCanvas() 
  89 void wxShapeCanvas::OnPaint(wxPaintEvent
& event
) 
  98                 GetDiagram()->Redraw(dc
); 
 101 void wxShapeCanvas::OnMouseEvent(wxMouseEvent
& event
) 
 106   wxPoint 
logPos(event
.GetLogicalPosition(dc
)); 
 109   x 
= (double) logPos
.x
; 
 110   y 
= (double) logPos
.y
; 
 113   if (event
.ShiftDown()) 
 114     keys 
= keys 
| KEY_SHIFT
; 
 115   if (event
.ControlDown()) 
 116     keys 
= keys 
| KEY_CTRL
; 
 118   bool dragging 
= event
.Dragging(); 
 120   // Check if we're within the tolerance for mouse movements. 
 121   // If we're very close to the position we started dragging 
 122   // from, this may not be an intentional drag at all. 
 125     int dx 
= abs(dc
.LogicalToDeviceX((long) (x 
- m_firstDragX
))); 
 126     int dy 
= abs(dc
.LogicalToDeviceY((long) (y 
- m_firstDragY
))); 
 127     if (m_checkTolerance 
&& (dx 
<= GetDiagram()->GetMouseTolerance()) && (dy 
<= GetDiagram()->GetMouseTolerance())) 
 132       // If we've ignored the tolerance once, then ALWAYS ignore 
 133       // tolerance in this drag, even if we come back within 
 134       // the tolerance range. 
 135       m_checkTolerance 
= FALSE
; 
 138   // Dragging - note that the effect of dragging is left entirely up 
 139   // to the object, so no movement is done unless explicitly done by 
 141   if (dragging 
&& m_draggedShape 
&& m_dragState 
== StartDraggingLeft
) 
 143     m_dragState 
= ContinueDraggingLeft
; 
 145     // If the object isn't m_draggable, transfer message to canvas 
 146     if (m_draggedShape
->Draggable()) 
 147       m_draggedShape
->GetEventHandler()->OnBeginDragLeft((double)x
, (double)y
, keys
, m_draggedAttachment
); 
 150       m_draggedShape 
= NULL
; 
 151       OnBeginDragLeft((double)x
, (double)y
, keys
); 
 154     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 156   else if (dragging 
&& m_draggedShape 
&& m_dragState 
== ContinueDraggingLeft
) 
 159     m_draggedShape
->GetEventHandler()->OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
); 
 160     m_draggedShape
->GetEventHandler()->OnDragLeft(TRUE
, (double)x
, (double)y
, keys
, m_draggedAttachment
); 
 161     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 163   else if (event
.LeftUp() && m_draggedShape 
&& m_dragState 
== ContinueDraggingLeft
) 
 165     m_dragState 
= NoDragging
; 
 166     m_checkTolerance 
= TRUE
; 
 168     m_draggedShape
->GetEventHandler()->OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
); 
 170     m_draggedShape
->GetEventHandler()->OnEndDragLeft((double)x
, (double)y
, keys
, m_draggedAttachment
); 
 171     m_draggedShape 
= NULL
; 
 173   else if (dragging 
&& m_draggedShape 
&& m_dragState 
== StartDraggingRight
) 
 175     m_dragState 
= ContinueDraggingRight
; 
 177     if (m_draggedShape
->Draggable()) 
 178       m_draggedShape
->GetEventHandler()->OnBeginDragRight((double)x
, (double)y
, keys
, m_draggedAttachment
); 
 181       m_draggedShape 
= NULL
; 
 182       OnBeginDragRight((double)x
, (double)y
, keys
); 
 184     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 186   else if (dragging 
&& m_draggedShape 
&& m_dragState 
== ContinueDraggingRight
) 
 189     m_draggedShape
->GetEventHandler()->OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
); 
 190     m_draggedShape
->GetEventHandler()->OnDragRight(TRUE
, (double)x
, (double)y
, keys
, m_draggedAttachment
); 
 191     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 193   else if (event
.RightUp() && m_draggedShape 
&& m_dragState 
== ContinueDraggingRight
) 
 195     m_dragState 
= NoDragging
; 
 196     m_checkTolerance 
= TRUE
; 
 198     m_draggedShape
->GetEventHandler()->OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
, m_draggedAttachment
); 
 200     m_draggedShape
->GetEventHandler()->OnEndDragRight((double)x
, (double)y
, keys
, m_draggedAttachment
); 
 201     m_draggedShape 
= NULL
; 
 204   // All following events sent to canvas, not object 
 205   else if (dragging 
&& !m_draggedShape 
&& m_dragState 
== StartDraggingLeft
) 
 207     m_dragState 
= ContinueDraggingLeft
; 
 208     OnBeginDragLeft((double)x
, (double)y
, keys
); 
 209     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 211   else if (dragging 
&& !m_draggedShape 
&& m_dragState 
== ContinueDraggingLeft
) 
 214     OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
); 
 215     OnDragLeft(TRUE
, (double)x
, (double)y
, keys
); 
 216     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 218   else if (event
.LeftUp() && !m_draggedShape 
&& m_dragState 
== ContinueDraggingLeft
) 
 220     m_dragState 
= NoDragging
; 
 221     m_checkTolerance 
= TRUE
; 
 223     OnDragLeft(FALSE
, m_oldDragX
, m_oldDragY
, keys
); 
 224     OnEndDragLeft((double)x
, (double)y
, keys
); 
 225     m_draggedShape 
= NULL
; 
 227   else if (dragging 
&& !m_draggedShape 
&& m_dragState 
== StartDraggingRight
) 
 229     m_dragState 
= ContinueDraggingRight
; 
 230     OnBeginDragRight((double)x
, (double)y
, keys
); 
 231     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 233   else if (dragging 
&& !m_draggedShape 
&& m_dragState 
== ContinueDraggingRight
) 
 236     OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
); 
 237     OnDragRight(TRUE
, (double)x
, (double)y
, keys
); 
 238     m_oldDragX 
= x
; m_oldDragY 
= y
; 
 240   else if (event
.RightUp() && !m_draggedShape 
&& m_dragState 
== ContinueDraggingRight
) 
 242     m_dragState 
= NoDragging
; 
 243     m_checkTolerance 
= TRUE
; 
 245     OnDragRight(FALSE
, m_oldDragX
, m_oldDragY
, keys
); 
 246     OnEndDragRight((double)x
, (double)y
, keys
); 
 247     m_draggedShape 
= NULL
; 
 250   // Non-dragging events 
 251   else if (event
.IsButton()) 
 253     m_checkTolerance 
= TRUE
; 
 255     // Find the nearest object 
 257     wxShape 
*nearest_object 
= FindShape(x
, y
, &attachment
); 
 258     if (nearest_object
) // Object event 
 260       if (event
.LeftDown()) 
 262         m_draggedShape 
= nearest_object
; 
 263         m_draggedAttachment 
= attachment
; 
 264         m_dragState 
= StartDraggingLeft
; 
 268       else if (event
.LeftUp()) 
 270         // N.B. Only register a click if the same object was 
 271         // identified for down *and* up. 
 272         if (nearest_object 
== m_draggedShape
) 
 273           nearest_object
->GetEventHandler()->OnLeftClick((double)x
, (double)y
, keys
, attachment
); 
 275         m_draggedShape 
= NULL
; 
 276         m_dragState 
= NoDragging
; 
 278       else if (event
.LeftDClick()) 
 280         nearest_object
->GetEventHandler()->OnLeftDoubleClick((double)x
, (double)y
, keys
, attachment
); 
 282         m_draggedShape 
= NULL
; 
 283         m_dragState 
= NoDragging
; 
 285       else if (event
.RightDown()) 
 287         m_draggedShape 
= nearest_object
; 
 288         m_draggedAttachment 
= attachment
; 
 289         m_dragState 
= StartDraggingRight
; 
 293       else if (event
.RightUp()) 
 295         if (nearest_object 
== m_draggedShape
) 
 296           nearest_object
->GetEventHandler()->OnRightClick((double)x
, (double)y
, keys
, attachment
); 
 298         m_draggedShape 
= NULL
; 
 299         m_dragState 
= NoDragging
; 
 302     else // Canvas event (no nearest object) 
 304       if (event
.LeftDown()) 
 306         m_draggedShape 
= NULL
; 
 307         m_dragState 
= StartDraggingLeft
; 
 311       else if (event
.LeftUp()) 
 313         OnLeftClick((double)x
, (double)y
, keys
); 
 315         m_draggedShape 
= NULL
; 
 316         m_dragState 
= NoDragging
; 
 318       else if (event
.RightDown()) 
 320         m_draggedShape 
= NULL
; 
 321         m_dragState 
= StartDraggingRight
; 
 325       else if (event
.RightUp()) 
 327         OnRightClick((double)x
, (double)y
, keys
); 
 329         m_draggedShape 
= NULL
; 
 330         m_dragState 
= NoDragging
; 
 337  * Try to find a sensitive object, working up the hierarchy of composites. 
 340 wxShape 
*wxShapeCanvas::FindFirstSensitiveShape(double x
, double y
, int *new_attachment
, int op
) 
 342   wxShape 
*image 
= FindShape(x
, y
, new_attachment
); 
 343   if (!image
) return NULL
; 
 345   wxShape 
*actualImage 
= FindFirstSensitiveShape1(image
, op
); 
 349     // Find actual attachment 
 350     actualImage
->HitTest(x
, y
, new_attachment
, &dist
); 
 355 wxShape 
*wxShapeCanvas::FindFirstSensitiveShape1(wxShape 
*image
, int op
) 
 357   if (image
->GetSensitivityFilter() & op
) 
 359   if (image
->GetParent()) 
 360     return FindFirstSensitiveShape1(image
->GetParent(), op
); 
 364 // Helper function: TRUE if 'contains' wholly contains 'contained'. 
 365 static bool WhollyContains(wxShape 
*contains
, wxShape 
*contained
) 
 367   double xp1
, yp1
, xp2
, yp2
; 
 368   double w1
, h1
, w2
, h2
; 
 369   double left1
, top1
, right1
, bottom1
, left2
, top2
, right2
, bottom2
; 
 371   xp1 
= contains
->GetX(); yp1 
= contains
->GetY(); xp2 
= contained
->GetX(); yp2 
= contained
->GetY(); 
 372   contains
->GetBoundingBoxMax(&w1
, &h1
); 
 373   contained
->GetBoundingBoxMax(&w2
, &h2
); 
 375   left1 
= (double)(xp1 
- (w1 
/ 2.0)); 
 376   top1 
= (double)(yp1 
- (h1 
/ 2.0)); 
 377   right1 
= (double)(xp1 
+ (w1 
/ 2.0)); 
 378   bottom1 
= (double)(yp1 
+ (h1 
/ 2.0)); 
 380   left2 
= (double)(xp2 
- (w2 
/ 2.0)); 
 381   top2 
= (double)(yp2 
- (h2 
/ 2.0)); 
 382   right2 
= (double)(xp2 
+ (w2 
/ 2.0)); 
 383   bottom2 
= (double)(yp2 
+ (h2 
/ 2.0)); 
 385   return ((left1 
<= left2
) && (top1 
<= top2
) && (right1 
>= right2
) && (bottom1 
>= bottom2
)); 
 388 wxShape 
*wxShapeCanvas::FindShape(double x
, double y
, int *attachment
, wxClassInfo 
*info
, wxShape 
*notObject
) 
 390   double nearest 
= 100000.0; 
 391   int nearest_attachment 
= 0; 
 392   wxShape 
*nearest_object 
= NULL
; 
 394   // Go backward through the object list, since we want: 
 395   // (a) to have the control points drawn LAST to overlay 
 397   // (b) to find the control points FIRST if they exist 
 399   wxNode 
*current 
= GetDiagram()->GetShapeList()->Last(); 
 402     wxShape 
*object 
= (wxShape 
*)current
->Data(); 
 407     // First pass for lines, which might be inside a container, so we 
 408     // want lines to take priority over containers. This first loop 
 409     // could fail if we clickout side a line, so then we'll 
 411     if (object
->IsShown() && 
 412         object
->IsKindOf(CLASSINFO(wxLineShape
)) && 
 413         object
->HitTest(x
, y
, &temp_attachment
, &dist
) && 
 414         ((info 
== NULL
) || object
->IsKindOf(info
)) && 
 415         (!notObject 
|| !notObject
->HasDescendant(object
))) 
 417       // A line is trickier to spot than a normal object. 
 418       // For a line, since it's the diagonal of the box 
 419       // we use for the hit test, we may have several 
 420       // lines in the box and therefore we need to be able 
 421       // to specify the nearest point to the centre of the line 
 422       // as our hit criterion, to give the user some room for 
 427         nearest_object 
= object
; 
 428         nearest_attachment 
= temp_attachment
; 
 432       current 
= current
->Previous(); 
 435   current 
= GetDiagram()->GetShapeList()->Last(); 
 438     wxShape 
*object 
= (wxShape 
*)current
->Data(); 
 442     // On second pass, only ever consider non-composites or divisions. If children want to pass 
 443     // up control to the composite, that's up to them. 
 444     if (object
->IsShown() && (object
->IsKindOf(CLASSINFO(wxDivisionShape
)) || !object
->IsKindOf(CLASSINFO(wxCompositeShape
))) 
 445         && object
->HitTest(x
, y
, &temp_attachment
, &dist
) && ((info 
== NULL
) || object
->IsKindOf(info
)) && 
 446         (!notObject 
|| !notObject
->HasDescendant(object
))) 
 448       if (!object
->IsKindOf(CLASSINFO(wxLineShape
))) 
 450         // If we've hit a container, and we have already found a line in the 
 451         // first pass, then ignore the container in case the line is in the container. 
 452         // Check for division in case line straddles divisions (i.e. is not wholly contained). 
 453         if (!nearest_object 
|| !(object
->IsKindOf(CLASSINFO(wxDivisionShape
)) || WhollyContains(object
, nearest_object
))) 
 456           nearest_object 
= object
; 
 457           nearest_attachment 
= temp_attachment
; 
 463       current 
= current
->Previous(); 
 466   *attachment 
= nearest_attachment
; 
 467   return nearest_object
; 
 471  * Higher-level events called by OnEvent 
 475 void wxShapeCanvas::OnLeftClick(double x
, double y
, int keys
) 
 479 void wxShapeCanvas::OnRightClick(double x
, double y
, int keys
) 
 483 void wxShapeCanvas::OnDragLeft(bool draw
, double x
, double y
, int keys
) 
 487 void wxShapeCanvas::OnBeginDragLeft(double x
, double y
, int keys
) 
 491 void wxShapeCanvas::OnEndDragLeft(double x
, double y
, int keys
) 
 495 void wxShapeCanvas::OnDragRight(bool draw
, double x
, double y
, int keys
) 
 499 void wxShapeCanvas::OnBeginDragRight(double x
, double y
, int keys
) 
 503 void wxShapeCanvas::OnEndDragRight(double x
, double y
, int keys
) 
 507 void wxShapeCanvas::AddShape(wxShape 
*object
, wxShape 
*addAfter
) 
 508  { GetDiagram()->AddShape(object
, addAfter
); } 
 509 void wxShapeCanvas::InsertShape(wxShape 
*object
) 
 510  { GetDiagram()->InsertShape(object
); } 
 511 void wxShapeCanvas::RemoveShape(wxShape 
*object
) 
 512  { GetDiagram()->RemoveShape(object
); } 
 513 bool wxShapeCanvas::GetQuickEditMode() 
 514  { return GetDiagram()->GetQuickEditMode(); } 
 515 void wxShapeCanvas::Redraw(wxDC
& dc
) 
 516  { GetDiagram()->Redraw(dc
); } 
 517 void wxShapeCanvas::Snap(double *x
, double *y
) 
 518  { GetDiagram()->Snap(x
, y
); }