1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implements document functionality
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 // #pragma implementation
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
30 #include <wx/ogl/basicp.h>
32 IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument
, wxDocument
)
35 #pragma warning(disable:4355)
38 csDiagramDocument::csDiagramDocument():m_diagram(this)
43 #pragma warning(default:4355)
46 csDiagramDocument::~csDiagramDocument()
50 bool csDiagramDocument::OnCloseDocument()
52 m_diagram
.DeleteAllShapes();
56 bool csDiagramDocument::OnSaveDocument(const wxString
& file
)
61 if (!m_diagram
.SaveFile(file
))
64 if (wxTheApp
->GetAppName() != "")
65 msgTitle
= wxTheApp
->GetAppName();
67 msgTitle
= wxString("File error");
69 (void)wxMessageBox("Sorry, could not open this file for saving.", msgTitle
, wxOK
| wxICON_EXCLAMATION
,
79 bool csDiagramDocument::OnOpenDocument(const wxString
& file
)
81 if (!OnSaveModified())
85 if (wxTheApp
->GetAppName() != "")
86 msgTitle
= wxTheApp
->GetAppName();
88 msgTitle
= wxString("File error");
90 m_diagram
.DeleteAllShapes();
91 if (!m_diagram
.LoadFile(file
))
93 (void)wxMessageBox("Sorry, could not open this file.", msgTitle
, wxOK
|wxICON_EXCLAMATION
,
97 SetFilename(file
, TRUE
);
106 * Implementation of drawing command
109 csDiagramCommand::csDiagramCommand(const wxString
& name
, csDiagramDocument
*doc
,
110 csCommandState
* onlyState
):
111 wxCommand(TRUE
, name
)
121 csDiagramCommand::~csDiagramCommand()
123 wxNode
* node
= m_states
.First();
126 csCommandState
* state
= (csCommandState
*) node
->Data();
132 void csDiagramCommand::AddState(csCommandState
* state
)
134 state
->m_doc
= m_doc
;
135 // state->m_cmd = m_cmd;
136 m_states
.Append(state
);
139 // Insert a state at the beginning of the list
140 void csDiagramCommand::InsertState(csCommandState
* state
)
142 state
->m_doc
= m_doc
;
143 // state->m_cmd = m_cmd;
144 m_states
.Insert(state
);
147 // Schedule all lines connected to the states to be cut.
148 void csDiagramCommand::RemoveLines()
150 wxNode
* node
= m_states
.First();
153 csCommandState
* state
= (csCommandState
*) node
->Data();
154 wxShape
* shape
= state
->GetShapeOnCanvas();
155 wxASSERT( (shape
!= NULL
) );
157 wxNode
*node1
= shape
->GetLines().First();
160 wxLineShape
*line
= (wxLineShape
*)node1
->Data();
161 if (!FindStateByShape(line
))
163 csCommandState
* newState
= new csCommandState(ID_CS_CUT
, NULL
, line
);
164 InsertState(newState
);
167 node1
= node1
->Next();
173 csCommandState
* csDiagramCommand::FindStateByShape(wxShape
* shape
)
175 wxNode
* node
= m_states
.First();
178 csCommandState
* state
= (csCommandState
*) node
->Data();
179 if (shape
== state
->GetShapeOnCanvas() || shape
== state
->GetSavedState())
186 bool csDiagramCommand::Do()
188 wxNode
* node
= m_states
.First();
191 csCommandState
* state
= (csCommandState
*) node
->Data();
199 bool csDiagramCommand::Undo()
201 // Undo in reverse order, so e.g. shapes get added
202 // back before the lines do.
203 wxNode
* node
= m_states
.Last();
206 csCommandState
* state
= (csCommandState
*) node
->Data();
209 node
= node
->Previous();
214 csCommandState::csCommandState(int cmd
, wxShape
* savedState
, wxShape
* shapeOnCanvas
)
218 m_savedState
= savedState
;
219 m_shapeOnCanvas
= shapeOnCanvas
;
220 m_linePositionFrom
= 0;
221 m_linePositionTo
= 0;
224 csCommandState::~csCommandState()
228 m_savedState
->SetCanvas(NULL
);
233 bool csCommandState::Do()
239 // New state is 'nothing' - maybe pass shape ID to state so we know what
240 // we're talking about.
241 // Then save old shape in m_savedState (actually swap pointers)
243 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
244 wxASSERT( (m_savedState
== NULL
) ); // new state will be 'nothing'
245 wxASSERT( (m_doc
!= NULL
) );
247 wxShapeCanvas
* canvas
= m_shapeOnCanvas
->GetCanvas();
249 // In case this is a line
250 wxShape
* lineFrom
= NULL
;
251 wxShape
* lineTo
= NULL
;
252 int attachmentFrom
= 0, attachmentTo
= 0;
254 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
256 // Store the from/to info to save in the line shape
257 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
258 lineFrom
= lineShape
->GetFrom();
259 lineTo
= lineShape
->GetTo();
260 attachmentFrom
= lineShape
->GetAttachmentFrom();
261 attachmentTo
= lineShape
->GetAttachmentTo();
263 m_linePositionFrom
= lineFrom
->GetLinePosition(lineShape
);
264 m_linePositionTo
= lineTo
->GetLinePosition(lineShape
);
267 m_shapeOnCanvas
->Select(FALSE
);
268 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, FALSE
);
270 m_shapeOnCanvas
->Unlink();
272 m_doc
->GetDiagram()->RemoveShape(m_shapeOnCanvas
);
274 m_savedState
= m_shapeOnCanvas
;
276 if (m_savedState
->IsKindOf(CLASSINFO(wxLineShape
)))
278 // Restore the from/to info for future reference
279 wxLineShape
* lineShape
= (wxLineShape
*) m_savedState
;
280 lineShape
->SetFrom(lineFrom
);
281 lineShape
->SetTo(lineTo
);
282 lineShape
->SetAttachments(attachmentFrom
, attachmentTo
);
284 wxClientDC
dc(canvas
);
285 canvas
->PrepareDC(dc
);
287 lineFrom
->MoveLinks(dc
);
288 lineTo
->MoveLinks(dc
);
292 m_doc
->UpdateAllViews();
295 case ID_CS_ADD_SHAPE
:
296 case ID_CS_ADD_SHAPE_SELECT
:
298 // The app has given the command state a new m_savedState
299 // shape, which is the new shape to add to the canvas (but
300 // not actually added until this point).
301 // The new 'saved state' is therefore 'nothing' since there
302 // was nothing there before.
304 wxASSERT( (m_shapeOnCanvas
== NULL
) );
305 wxASSERT( (m_savedState
!= NULL
) );
306 wxASSERT( (m_doc
!= NULL
) );
308 m_shapeOnCanvas
= m_savedState
;
311 m_doc
->GetDiagram()->AddShape(m_shapeOnCanvas
);
312 m_shapeOnCanvas
->Show(TRUE
);
314 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
315 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
317 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
318 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
320 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
322 if (m_cmd
== ID_CS_ADD_SHAPE_SELECT
)
324 m_shapeOnCanvas
->Select(TRUE
, &dc
);
325 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, TRUE
);
329 m_doc
->UpdateAllViews();
333 case ID_CS_ADD_LINE_SELECT
:
335 wxASSERT( (m_shapeOnCanvas
== NULL
) );
336 wxASSERT( (m_savedState
!= NULL
) );
337 wxASSERT( (m_doc
!= NULL
) );
339 wxLineShape
*lineShape
= (wxLineShape
*)m_savedState
;
340 wxASSERT( (lineShape
->GetFrom() != NULL
) );
341 wxASSERT( (lineShape
->GetTo() != NULL
) );
343 m_shapeOnCanvas
= m_savedState
;
346 m_doc
->GetDiagram()->AddShape(lineShape
);
348 lineShape
->GetFrom()->AddLine(lineShape
, lineShape
->GetTo(),
349 lineShape
->GetAttachmentFrom(), lineShape
->GetAttachmentTo());
351 lineShape
->Show(TRUE
);
353 wxClientDC
dc(lineShape
->GetCanvas());
354 lineShape
->GetCanvas()->PrepareDC(dc
);
356 // It won't get drawn properly unless you move both
358 lineShape
->GetFrom()->Move(dc
, lineShape
->GetFrom()->GetX(), lineShape
->GetFrom()->GetY());
359 lineShape
->GetTo()->Move(dc
, lineShape
->GetTo()->GetX(), lineShape
->GetTo()->GetY());
361 if (m_cmd
== ID_CS_ADD_LINE_SELECT
)
363 lineShape
->Select(TRUE
, &dc
);
364 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, TRUE
);
368 m_doc
->UpdateAllViews();
371 case ID_CS_CHANGE_BACKGROUND_COLOUR
:
374 case ID_CS_EDIT_PROPERTIES
:
375 case ID_CS_FONT_CHANGE
:
376 case ID_CS_ARROW_CHANGE
:
377 case ID_CS_ROTATE_CLOCKWISE
:
378 case ID_CS_ROTATE_ANTICLOCKWISE
:
379 case ID_CS_CHANGE_LINE_ORDERING
:
380 case ID_CS_CHANGE_LINE_ATTACHMENT
:
382 case ID_CS_NEW_POINT
:
383 case ID_CS_CUT_POINT
:
384 case ID_CS_MOVE_LINE_POINT
:
385 case ID_CS_STRAIGHTEN
:
386 case ID_CS_MOVE_LABEL
:
388 // At this point we have been given a new shape
389 // just like the old one but with a changed colour.
390 // It's now time to apply that change to the
391 // shape on the canvas, saving the old state.
392 // NOTE: this is general enough to work with MOST attribute
395 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
396 wxASSERT( (m_savedState
!= NULL
) ); // This is the new shape with changed colour
397 wxASSERT( (m_doc
!= NULL
) );
399 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
400 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
402 bool isSelected
= m_shapeOnCanvas
->Selected();
404 m_shapeOnCanvas
->Select(FALSE
, & dc
);
406 if (m_cmd
== ID_CS_SIZE
|| m_cmd
== ID_CS_ROTATE_CLOCKWISE
|| m_cmd
== ID_CS_ROTATE_ANTICLOCKWISE
||
407 m_cmd
== ID_CS_CHANGE_LINE_ORDERING
|| m_cmd
== ID_CS_CHANGE_LINE_ATTACHMENT
)
409 m_shapeOnCanvas
->Erase(dc
);
412 // TODO: make sure the ID is the same. Or, when applying the new state,
413 // don't change the original ID.
414 wxShape
* tempShape
= m_shapeOnCanvas
->CreateNewCopy();
416 // Apply the saved state to the shape on the canvas, by copying.
417 m_savedState
->CopyWithHandler(*m_shapeOnCanvas
);
419 // Delete this state now it's been used (m_shapeOnCanvas currently holds this state)
422 // Remember the previous state
423 m_savedState
= tempShape
;
427 if (m_cmd
== ID_CS_MOVE
|| m_cmd
== ID_CS_ROTATE_CLOCKWISE
|| m_cmd
== ID_CS_ROTATE_ANTICLOCKWISE
||
428 m_cmd
== ID_CS_ALIGN
)
430 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
432 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
433 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
434 m_shapeOnCanvas
->Draw(dc
);
436 else if (m_cmd
== ID_CS_CHANGE_LINE_ORDERING
)
438 m_shapeOnCanvas
->MoveLinks(dc
);
440 else if (m_cmd
== ID_CS_CHANGE_LINE_ATTACHMENT
)
442 wxLineShape
*lineShape
= (wxLineShape
*)m_shapeOnCanvas
;
444 // Have to move both sets of links since we don't know which links
445 // have been affected (unless we compared before and after states).
446 lineShape
->GetFrom()->MoveLinks(dc
);
447 lineShape
->GetTo()->MoveLinks(dc
);
449 else if (m_cmd
== ID_CS_SIZE
)
451 double width
, height
;
452 m_shapeOnCanvas
->GetBoundingBoxMax(&width
, &height
);
454 m_shapeOnCanvas
->SetSize(width
, height
);
455 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
457 m_shapeOnCanvas
->Show(TRUE
);
459 // Recursively redraw links if we have a composite.
460 if (m_shapeOnCanvas
->GetChildren().Number() > 0)
461 m_shapeOnCanvas
->DrawLinks(dc
, -1, TRUE
);
463 m_shapeOnCanvas
->GetEventHandler()->OnEndSize(width
, height
);
465 else if (m_cmd
== ID_CS_EDIT_PROPERTIES
|| m_cmd
== ID_CS_FONT_CHANGE
)
467 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
468 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
469 m_shapeOnCanvas
->Draw(dc
);
473 m_shapeOnCanvas
->Draw(dc
);
477 m_shapeOnCanvas
->Select(TRUE
, & dc
);
480 m_doc
->UpdateAllViews();
488 bool csCommandState::Undo()
494 wxASSERT( (m_savedState
!= NULL
) );
495 wxASSERT( (m_doc
!= NULL
) );
497 m_doc
->GetDiagram()->AddShape(m_savedState
);
498 m_shapeOnCanvas
= m_savedState
;
501 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
503 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
504 lineShape
->GetFrom()->AddLine(lineShape
, lineShape
->GetTo(),
505 lineShape
->GetAttachmentFrom(), lineShape
->GetAttachmentTo(),
506 m_linePositionFrom
, m_linePositionTo
);
508 wxShapeCanvas
* canvas
= lineShape
->GetFrom()->GetCanvas();
510 wxClientDC
dc(canvas
);
511 canvas
->PrepareDC(dc
);
513 lineShape
->GetFrom()->MoveLinks(dc
);
514 lineShape
->GetTo()->MoveLinks(dc
);
517 m_shapeOnCanvas
->Show(TRUE
);
520 m_doc
->UpdateAllViews();
523 case ID_CS_ADD_SHAPE
:
525 case ID_CS_ADD_SHAPE_SELECT
:
526 case ID_CS_ADD_LINE_SELECT
:
528 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
529 wxASSERT( (m_savedState
== NULL
) );
530 wxASSERT( (m_doc
!= NULL
) );
532 // In case this is a line
533 wxShape
* lineFrom
= NULL
;
534 wxShape
* lineTo
= NULL
;
535 int attachmentFrom
= 0, attachmentTo
= 0;
537 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
539 // Store the from/to info to save in the line shape
540 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
541 lineFrom
= lineShape
->GetFrom();
542 lineTo
= lineShape
->GetTo();
543 attachmentFrom
= lineShape
->GetAttachmentFrom();
544 attachmentTo
= lineShape
->GetAttachmentTo();
547 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
548 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
550 m_shapeOnCanvas
->Select(FALSE
, &dc
);
551 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, FALSE
);
552 m_doc
->GetDiagram()->RemoveShape(m_shapeOnCanvas
);
553 m_shapeOnCanvas
->Unlink(); // Unlinks the line, if it is a line
555 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
557 // Restore the from/to info for future reference
558 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
559 lineShape
->SetFrom(lineFrom
);
560 lineShape
->SetTo(lineTo
);
561 lineShape
->SetAttachments(attachmentFrom
, attachmentTo
);
564 m_savedState
= m_shapeOnCanvas
;
565 m_shapeOnCanvas
= NULL
;
568 m_doc
->UpdateAllViews();
571 case ID_CS_CHANGE_BACKGROUND_COLOUR
:
574 case ID_CS_EDIT_PROPERTIES
:
575 case ID_CS_FONT_CHANGE
:
576 case ID_CS_ARROW_CHANGE
:
577 case ID_CS_ROTATE_CLOCKWISE
:
578 case ID_CS_ROTATE_ANTICLOCKWISE
:
579 case ID_CS_CHANGE_LINE_ORDERING
:
580 case ID_CS_CHANGE_LINE_ATTACHMENT
:
582 case ID_CS_NEW_POINT
:
583 case ID_CS_CUT_POINT
:
584 case ID_CS_MOVE_LINE_POINT
:
585 case ID_CS_STRAIGHTEN
:
586 case ID_CS_MOVE_LABEL
:
588 // Exactly like the Do case; we're just swapping states.