1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implements document functionality
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
26 #include <wx/ogl/basicp.h>
28 IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument
, wxDocument
)
31 #pragma warning(disable:4355)
34 csDiagramDocument::csDiagramDocument():m_diagram(this)
39 #pragma warning(default:4355)
42 csDiagramDocument::~csDiagramDocument()
46 bool csDiagramDocument::OnCloseDocument()
48 m_diagram
.DeleteAllShapes();
53 bool csDiagramDocument::OnSaveDocument(const wxString
& file
)
55 if (file
== wxEmptyString
)
58 if (!m_diagram
.SaveFile(file
))
61 if (wxTheApp
->GetAppName() != wxEmptyString
)
62 msgTitle
= wxTheApp
->GetAppName();
64 msgTitle
= wxString(_T("File error"));
66 (void)wxMessageBox(_T("Sorry, could not open this file for saving."), msgTitle
, wxOK
| wxICON_EXCLAMATION
,
76 bool csDiagramDocument::OnOpenDocument(const wxString
& file
)
78 if (!OnSaveModified())
82 if (wxTheApp
->GetAppName() != wxEmptyString
)
83 msgTitle
= wxTheApp
->GetAppName();
85 msgTitle
= wxString(_T("File error"));
87 m_diagram
.DeleteAllShapes();
88 if (!m_diagram
.LoadFile(file
))
90 (void)wxMessageBox(_T("Sorry, could not open this file."), msgTitle
, wxOK
|wxICON_EXCLAMATION
,
94 SetFilename(file
, true);
100 #endif // wxUSE_PROLOGIO
104 * Implementation of drawing command
107 csDiagramCommand::csDiagramCommand(const wxString
& name
, csDiagramDocument
*doc
,
108 csCommandState
* onlyState
):
109 wxCommand(true, name
)
119 csDiagramCommand::~csDiagramCommand()
121 wxObjectList::compatibility_iterator node
= m_states
.GetFirst();
124 csCommandState
* state
= (csCommandState
*) node
->GetData();
126 node
= node
->GetNext();
130 void csDiagramCommand::AddState(csCommandState
* state
)
132 state
->m_doc
= m_doc
;
133 // state->m_cmd = m_cmd;
134 m_states
.Append(state
);
137 // Insert a state at the beginning of the list
138 void csDiagramCommand::InsertState(csCommandState
* state
)
140 state
->m_doc
= m_doc
;
141 // state->m_cmd = m_cmd;
142 m_states
.Insert(state
);
145 // Schedule all lines connected to the states to be cut.
146 void csDiagramCommand::RemoveLines()
148 wxObjectList::compatibility_iterator node
= m_states
.GetFirst();
151 csCommandState
* state
= (csCommandState
*) node
->GetData();
152 wxShape
* shape
= state
->GetShapeOnCanvas();
153 wxASSERT( (shape
!= NULL
) );
155 wxObjectList::compatibility_iterator node1
= shape
->GetLines().GetFirst();
158 wxLineShape
*line
= (wxLineShape
*)node1
->GetData();
159 if (!FindStateByShape(line
))
161 csCommandState
* newState
= new csCommandState(ID_CS_CUT
, NULL
, line
);
162 InsertState(newState
);
165 node1
= node1
->GetNext();
167 node
= node
->GetNext();
171 csCommandState
* csDiagramCommand::FindStateByShape(wxShape
* shape
)
173 wxObjectList::compatibility_iterator node
= m_states
.GetFirst();
176 csCommandState
* state
= (csCommandState
*) node
->GetData();
177 if (shape
== state
->GetShapeOnCanvas() || shape
== state
->GetSavedState())
179 node
= node
->GetNext();
184 bool csDiagramCommand::Do()
186 wxObjectList::compatibility_iterator node
= m_states
.GetFirst();
189 csCommandState
* state
= (csCommandState
*) node
->GetData();
192 node
= node
->GetNext();
197 bool csDiagramCommand::Undo()
199 // Undo in reverse order, so e.g. shapes get added
200 // back before the lines do.
201 wxObjectList::compatibility_iterator node
= m_states
.GetLast();
204 csCommandState
* state
= (csCommandState
*) node
->GetData();
207 node
= node
->GetPrevious();
212 csCommandState::csCommandState(int cmd
, wxShape
* savedState
, wxShape
* shapeOnCanvas
)
216 m_savedState
= savedState
;
217 m_shapeOnCanvas
= shapeOnCanvas
;
218 m_linePositionFrom
= 0;
219 m_linePositionTo
= 0;
222 csCommandState::~csCommandState()
226 m_savedState
->SetCanvas(NULL
);
231 bool csCommandState::Do()
237 // New state is 'nothing' - maybe pass shape ID to state so we know what
238 // we're talking about.
239 // Then save old shape in m_savedState (actually swap pointers)
241 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
242 wxASSERT( (m_savedState
== NULL
) ); // new state will be 'nothing'
243 wxASSERT( (m_doc
!= NULL
) );
245 wxShapeCanvas
* canvas
= m_shapeOnCanvas
->GetCanvas();
247 // In case this is a line
248 wxShape
* lineFrom
= NULL
;
249 wxShape
* lineTo
= NULL
;
250 int attachmentFrom
= 0, attachmentTo
= 0;
252 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
254 // Store the from/to info to save in the line shape
255 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
256 lineFrom
= lineShape
->GetFrom();
257 lineTo
= lineShape
->GetTo();
258 attachmentFrom
= lineShape
->GetAttachmentFrom();
259 attachmentTo
= lineShape
->GetAttachmentTo();
261 m_linePositionFrom
= lineFrom
->GetLinePosition(lineShape
);
262 m_linePositionTo
= lineTo
->GetLinePosition(lineShape
);
265 m_shapeOnCanvas
->Select(false);
266 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, false);
268 m_shapeOnCanvas
->Unlink();
270 m_doc
->GetDiagram()->RemoveShape(m_shapeOnCanvas
);
272 m_savedState
= m_shapeOnCanvas
;
274 if (m_savedState
->IsKindOf(CLASSINFO(wxLineShape
)))
276 // Restore the from/to info for future reference
277 wxLineShape
* lineShape
= (wxLineShape
*) m_savedState
;
278 lineShape
->SetFrom(lineFrom
);
279 lineShape
->SetTo(lineTo
);
280 lineShape
->SetAttachments(attachmentFrom
, attachmentTo
);
282 wxClientDC
dc(canvas
);
283 canvas
->PrepareDC(dc
);
285 lineFrom
->MoveLinks(dc
);
286 lineTo
->MoveLinks(dc
);
290 m_doc
->UpdateAllViews();
293 case ID_CS_ADD_SHAPE
:
294 case ID_CS_ADD_SHAPE_SELECT
:
296 // The app has given the command state a new m_savedState
297 // shape, which is the new shape to add to the canvas (but
298 // not actually added until this point).
299 // The new 'saved state' is therefore 'nothing' since there
300 // was nothing there before.
302 wxASSERT( (m_shapeOnCanvas
== NULL
) );
303 wxASSERT( (m_savedState
!= NULL
) );
304 wxASSERT( (m_doc
!= NULL
) );
306 m_shapeOnCanvas
= m_savedState
;
309 m_doc
->GetDiagram()->AddShape(m_shapeOnCanvas
);
310 m_shapeOnCanvas
->Show(true);
312 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
313 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
315 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
316 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
318 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
320 if (m_cmd
== ID_CS_ADD_SHAPE_SELECT
)
322 m_shapeOnCanvas
->Select(true, &dc
);
323 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, true);
327 m_doc
->UpdateAllViews();
331 case ID_CS_ADD_LINE_SELECT
:
333 wxASSERT( (m_shapeOnCanvas
== NULL
) );
334 wxASSERT( (m_savedState
!= NULL
) );
335 wxASSERT( (m_doc
!= NULL
) );
337 wxLineShape
*lineShape
= (wxLineShape
*)m_savedState
;
338 wxASSERT( (lineShape
->GetFrom() != NULL
) );
339 wxASSERT( (lineShape
->GetTo() != NULL
) );
341 m_shapeOnCanvas
= m_savedState
;
344 m_doc
->GetDiagram()->AddShape(lineShape
);
346 lineShape
->GetFrom()->AddLine(lineShape
, lineShape
->GetTo(),
347 lineShape
->GetAttachmentFrom(), lineShape
->GetAttachmentTo());
349 lineShape
->Show(true);
351 wxClientDC
dc(lineShape
->GetCanvas());
352 lineShape
->GetCanvas()->PrepareDC(dc
);
354 // It won't get drawn properly unless you move both
356 lineShape
->GetFrom()->Move(dc
, lineShape
->GetFrom()->GetX(), lineShape
->GetFrom()->GetY());
357 lineShape
->GetTo()->Move(dc
, lineShape
->GetTo()->GetX(), lineShape
->GetTo()->GetY());
359 if (m_cmd
== ID_CS_ADD_LINE_SELECT
)
361 lineShape
->Select(true, &dc
);
362 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, true);
366 m_doc
->UpdateAllViews();
369 case ID_CS_CHANGE_BACKGROUND_COLOUR
:
372 case ID_CS_EDIT_PROPERTIES
:
373 case ID_CS_FONT_CHANGE
:
374 case ID_CS_ARROW_CHANGE
:
375 case ID_CS_ROTATE_CLOCKWISE
:
376 case ID_CS_ROTATE_ANTICLOCKWISE
:
377 case ID_CS_CHANGE_LINE_ORDERING
:
378 case ID_CS_CHANGE_LINE_ATTACHMENT
:
380 case ID_CS_NEW_POINT
:
381 case ID_CS_CUT_POINT
:
382 case ID_CS_MOVE_LINE_POINT
:
383 case ID_CS_STRAIGHTEN
:
384 case ID_CS_MOVE_LABEL
:
386 // At this point we have been given a new shape
387 // just like the old one but with a changed colour.
388 // It's now time to apply that change to the
389 // shape on the canvas, saving the old state.
390 // NOTE: this is general enough to work with MOST attribute
393 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
394 wxASSERT( (m_savedState
!= NULL
) ); // This is the new shape with changed colour
395 wxASSERT( (m_doc
!= NULL
) );
397 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
398 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
400 bool isSelected
= m_shapeOnCanvas
->Selected();
402 m_shapeOnCanvas
->Select(false, & dc
);
404 if (m_cmd
== ID_CS_SIZE
|| m_cmd
== ID_CS_ROTATE_CLOCKWISE
|| m_cmd
== ID_CS_ROTATE_ANTICLOCKWISE
||
405 m_cmd
== ID_CS_CHANGE_LINE_ORDERING
|| m_cmd
== ID_CS_CHANGE_LINE_ATTACHMENT
)
407 m_shapeOnCanvas
->Erase(dc
);
410 // TODO: make sure the ID is the same. Or, when applying the new state,
411 // don't change the original ID.
412 wxShape
* tempShape
= m_shapeOnCanvas
->CreateNewCopy();
414 // Apply the saved state to the shape on the canvas, by copying.
415 m_savedState
->CopyWithHandler(*m_shapeOnCanvas
);
417 // Delete this state now it's been used (m_shapeOnCanvas currently holds this state)
420 // Remember the previous state
421 m_savedState
= tempShape
;
425 if (m_cmd
== ID_CS_MOVE
|| m_cmd
== ID_CS_ROTATE_CLOCKWISE
|| m_cmd
== ID_CS_ROTATE_ANTICLOCKWISE
||
426 m_cmd
== ID_CS_ALIGN
)
428 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
430 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
431 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
432 m_shapeOnCanvas
->Draw(dc
);
434 else if (m_cmd
== ID_CS_CHANGE_LINE_ORDERING
)
436 m_shapeOnCanvas
->MoveLinks(dc
);
438 else if (m_cmd
== ID_CS_CHANGE_LINE_ATTACHMENT
)
440 wxLineShape
*lineShape
= (wxLineShape
*)m_shapeOnCanvas
;
442 // Have to move both sets of links since we don't know which links
443 // have been affected (unless we compared before and after states).
444 lineShape
->GetFrom()->MoveLinks(dc
);
445 lineShape
->GetTo()->MoveLinks(dc
);
447 else if (m_cmd
== ID_CS_SIZE
)
449 double width
, height
;
450 m_shapeOnCanvas
->GetBoundingBoxMax(&width
, &height
);
452 m_shapeOnCanvas
->SetSize(width
, height
);
453 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
455 m_shapeOnCanvas
->Show(true);
457 // Recursively redraw links if we have a composite.
458 if (m_shapeOnCanvas
->GetChildren().GetCount() > 0)
459 m_shapeOnCanvas
->DrawLinks(dc
, -1, true);
461 m_shapeOnCanvas
->GetEventHandler()->OnEndSize(width
, height
);
463 else if (m_cmd
== ID_CS_EDIT_PROPERTIES
|| m_cmd
== ID_CS_FONT_CHANGE
)
465 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
466 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
467 m_shapeOnCanvas
->Draw(dc
);
471 m_shapeOnCanvas
->Draw(dc
);
475 m_shapeOnCanvas
->Select(true, & dc
);
478 m_doc
->UpdateAllViews();
486 bool csCommandState::Undo()
492 wxASSERT( (m_savedState
!= NULL
) );
493 wxASSERT( (m_doc
!= NULL
) );
495 m_doc
->GetDiagram()->AddShape(m_savedState
);
496 m_shapeOnCanvas
= m_savedState
;
499 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
501 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
502 lineShape
->GetFrom()->AddLine(lineShape
, lineShape
->GetTo(),
503 lineShape
->GetAttachmentFrom(), lineShape
->GetAttachmentTo(),
504 m_linePositionFrom
, m_linePositionTo
);
506 wxShapeCanvas
* canvas
= lineShape
->GetFrom()->GetCanvas();
508 wxClientDC
dc(canvas
);
509 canvas
->PrepareDC(dc
);
511 lineShape
->GetFrom()->MoveLinks(dc
);
512 lineShape
->GetTo()->MoveLinks(dc
);
515 m_shapeOnCanvas
->Show(true);
518 m_doc
->UpdateAllViews();
521 case ID_CS_ADD_SHAPE
:
523 case ID_CS_ADD_SHAPE_SELECT
:
524 case ID_CS_ADD_LINE_SELECT
:
526 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
527 wxASSERT( (m_savedState
== NULL
) );
528 wxASSERT( (m_doc
!= NULL
) );
530 // In case this is a line
531 wxShape
* lineFrom
= NULL
;
532 wxShape
* lineTo
= NULL
;
533 int attachmentFrom
= 0, attachmentTo
= 0;
535 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
537 // Store the from/to info to save in the line shape
538 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
539 lineFrom
= lineShape
->GetFrom();
540 lineTo
= lineShape
->GetTo();
541 attachmentFrom
= lineShape
->GetAttachmentFrom();
542 attachmentTo
= lineShape
->GetAttachmentTo();
545 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
546 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
548 m_shapeOnCanvas
->Select(false, &dc
);
549 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, false);
550 m_doc
->GetDiagram()->RemoveShape(m_shapeOnCanvas
);
551 m_shapeOnCanvas
->Unlink(); // Unlinks the line, if it is a line
553 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
555 // Restore the from/to info for future reference
556 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
557 lineShape
->SetFrom(lineFrom
);
558 lineShape
->SetTo(lineTo
);
559 lineShape
->SetAttachments(attachmentFrom
, attachmentTo
);
562 m_savedState
= m_shapeOnCanvas
;
563 m_shapeOnCanvas
= NULL
;
566 m_doc
->UpdateAllViews();
569 case ID_CS_CHANGE_BACKGROUND_COLOUR
:
572 case ID_CS_EDIT_PROPERTIES
:
573 case ID_CS_FONT_CHANGE
:
574 case ID_CS_ARROW_CHANGE
:
575 case ID_CS_ROTATE_CLOCKWISE
:
576 case ID_CS_ROTATE_ANTICLOCKWISE
:
577 case ID_CS_CHANGE_LINE_ORDERING
:
578 case ID_CS_CHANGE_LINE_ATTACHMENT
:
580 case ID_CS_NEW_POINT
:
581 case ID_CS_CUT_POINT
:
582 case ID_CS_MOVE_LINE_POINT
:
583 case ID_CS_STRAIGHTEN
:
584 case ID_CS_MOVE_LABEL
:
586 // Exactly like the Do case; we're just swapping states.