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"
27 #include <wx/wxexpr.h>
32 #include <wx/ogl/basicp.h>
34 IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument
, wxDocument
)
37 #pragma warning(disable:4355)
40 csDiagramDocument::csDiagramDocument():m_diagram(this)
45 #pragma warning(default:4355)
48 csDiagramDocument::~csDiagramDocument()
52 bool csDiagramDocument::OnCloseDocument()
54 m_diagram
.DeleteAllShapes();
58 bool csDiagramDocument::OnSaveDocument(const wxString
& file
)
63 if (!m_diagram
.SaveFile(file
))
66 if (wxTheApp
->GetAppName() != "")
67 msgTitle
= wxTheApp
->GetAppName();
69 msgTitle
= wxString("File error");
71 (void)wxMessageBox("Sorry, could not open this file for saving.", msgTitle
, wxOK
| wxICON_EXCLAMATION
,
81 bool csDiagramDocument::OnOpenDocument(const wxString
& file
)
83 if (!OnSaveModified())
87 if (wxTheApp
->GetAppName() != "")
88 msgTitle
= wxTheApp
->GetAppName();
90 msgTitle
= wxString("File error");
92 m_diagram
.DeleteAllShapes();
93 if (!m_diagram
.LoadFile(file
))
95 (void)wxMessageBox("Sorry, could not open this file.", msgTitle
, wxOK
|wxICON_EXCLAMATION
,
99 SetFilename(file
, TRUE
);
108 * Implementation of drawing command
111 csDiagramCommand::csDiagramCommand(const wxString
& name
, csDiagramDocument
*doc
,
112 csCommandState
* onlyState
):
113 wxCommand(TRUE
, name
)
123 csDiagramCommand::~csDiagramCommand()
125 wxNode
* node
= m_states
.First();
128 csCommandState
* state
= (csCommandState
*) node
->Data();
134 void csDiagramCommand::AddState(csCommandState
* state
)
136 state
->m_doc
= m_doc
;
137 // state->m_cmd = m_cmd;
138 m_states
.Append(state
);
141 // Insert a state at the beginning of the list
142 void csDiagramCommand::InsertState(csCommandState
* state
)
144 state
->m_doc
= m_doc
;
145 // state->m_cmd = m_cmd;
146 m_states
.Insert(state
);
149 // Schedule all lines connected to the states to be cut.
150 void csDiagramCommand::RemoveLines()
152 wxNode
* node
= m_states
.First();
155 csCommandState
* state
= (csCommandState
*) node
->Data();
156 wxShape
* shape
= state
->GetShapeOnCanvas();
157 wxASSERT( (shape
!= NULL
) );
159 wxNode
*node1
= shape
->GetLines().First();
162 wxLineShape
*line
= (wxLineShape
*)node1
->Data();
163 if (!FindStateByShape(line
))
165 csCommandState
* newState
= new csCommandState(ID_CS_CUT
, NULL
, line
);
166 InsertState(newState
);
169 node1
= node1
->Next();
175 csCommandState
* csDiagramCommand::FindStateByShape(wxShape
* shape
)
177 wxNode
* node
= m_states
.First();
180 csCommandState
* state
= (csCommandState
*) node
->Data();
181 if (shape
== state
->GetShapeOnCanvas() || shape
== state
->GetSavedState())
188 bool csDiagramCommand::Do()
190 wxNode
* node
= m_states
.First();
193 csCommandState
* state
= (csCommandState
*) node
->Data();
201 bool csDiagramCommand::Undo()
203 // Undo in reverse order, so e.g. shapes get added
204 // back before the lines do.
205 wxNode
* node
= m_states
.Last();
208 csCommandState
* state
= (csCommandState
*) node
->Data();
211 node
= node
->Previous();
216 csCommandState::csCommandState(int cmd
, wxShape
* savedState
, wxShape
* shapeOnCanvas
)
220 m_savedState
= savedState
;
221 m_shapeOnCanvas
= shapeOnCanvas
;
222 m_linePositionFrom
= 0;
223 m_linePositionTo
= 0;
226 csCommandState::~csCommandState()
230 m_savedState
->SetCanvas(NULL
);
235 bool csCommandState::Do()
241 // New state is 'nothing' - maybe pass shape ID to state so we know what
242 // we're talking about.
243 // Then save old shape in m_savedState (actually swap pointers)
245 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
246 wxASSERT( (m_savedState
== NULL
) ); // new state will be 'nothing'
247 wxASSERT( (m_doc
!= NULL
) );
249 wxShapeCanvas
* canvas
= m_shapeOnCanvas
->GetCanvas();
251 // In case this is a line
252 wxShape
* lineFrom
= NULL
;
253 wxShape
* lineTo
= NULL
;
254 int attachmentFrom
= 0, attachmentTo
= 0;
256 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
258 // Store the from/to info to save in the line shape
259 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
260 lineFrom
= lineShape
->GetFrom();
261 lineTo
= lineShape
->GetTo();
262 attachmentFrom
= lineShape
->GetAttachmentFrom();
263 attachmentTo
= lineShape
->GetAttachmentTo();
265 m_linePositionFrom
= lineFrom
->GetLinePosition(lineShape
);
266 m_linePositionTo
= lineTo
->GetLinePosition(lineShape
);
269 m_shapeOnCanvas
->Select(FALSE
);
270 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, FALSE
);
272 m_shapeOnCanvas
->Unlink();
274 m_doc
->GetDiagram()->RemoveShape(m_shapeOnCanvas
);
276 m_savedState
= m_shapeOnCanvas
;
278 if (m_savedState
->IsKindOf(CLASSINFO(wxLineShape
)))
280 // Restore the from/to info for future reference
281 wxLineShape
* lineShape
= (wxLineShape
*) m_savedState
;
282 lineShape
->SetFrom(lineFrom
);
283 lineShape
->SetTo(lineTo
);
284 lineShape
->SetAttachments(attachmentFrom
, attachmentTo
);
286 wxClientDC
dc(canvas
);
287 canvas
->PrepareDC(dc
);
289 lineFrom
->MoveLinks(dc
);
290 lineTo
->MoveLinks(dc
);
294 m_doc
->UpdateAllViews();
297 case ID_CS_ADD_SHAPE
:
298 case ID_CS_ADD_SHAPE_SELECT
:
300 // The app has given the command state a new m_savedState
301 // shape, which is the new shape to add to the canvas (but
302 // not actually added until this point).
303 // The new 'saved state' is therefore 'nothing' since there
304 // was nothing there before.
306 wxASSERT( (m_shapeOnCanvas
== NULL
) );
307 wxASSERT( (m_savedState
!= NULL
) );
308 wxASSERT( (m_doc
!= NULL
) );
310 m_shapeOnCanvas
= m_savedState
;
313 m_doc
->GetDiagram()->AddShape(m_shapeOnCanvas
);
314 m_shapeOnCanvas
->Show(TRUE
);
316 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
317 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
319 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
320 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
322 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
324 if (m_cmd
== ID_CS_ADD_SHAPE_SELECT
)
326 m_shapeOnCanvas
->Select(TRUE
, &dc
);
327 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, TRUE
);
331 m_doc
->UpdateAllViews();
335 case ID_CS_ADD_LINE_SELECT
:
337 wxASSERT( (m_shapeOnCanvas
== NULL
) );
338 wxASSERT( (m_savedState
!= NULL
) );
339 wxASSERT( (m_doc
!= NULL
) );
341 wxLineShape
*lineShape
= (wxLineShape
*)m_savedState
;
342 wxASSERT( (lineShape
->GetFrom() != NULL
) );
343 wxASSERT( (lineShape
->GetTo() != NULL
) );
345 m_shapeOnCanvas
= m_savedState
;
348 m_doc
->GetDiagram()->AddShape(lineShape
);
350 lineShape
->GetFrom()->AddLine(lineShape
, lineShape
->GetTo(),
351 lineShape
->GetAttachmentFrom(), lineShape
->GetAttachmentTo());
353 lineShape
->Show(TRUE
);
355 wxClientDC
dc(lineShape
->GetCanvas());
356 lineShape
->GetCanvas()->PrepareDC(dc
);
358 // It won't get drawn properly unless you move both
360 lineShape
->GetFrom()->Move(dc
, lineShape
->GetFrom()->GetX(), lineShape
->GetFrom()->GetY());
361 lineShape
->GetTo()->Move(dc
, lineShape
->GetTo()->GetX(), lineShape
->GetTo()->GetY());
363 if (m_cmd
== ID_CS_ADD_LINE_SELECT
)
365 lineShape
->Select(TRUE
, &dc
);
366 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, TRUE
);
370 m_doc
->UpdateAllViews();
373 case ID_CS_CHANGE_BACKGROUND_COLOUR
:
376 case ID_CS_EDIT_PROPERTIES
:
377 case ID_CS_FONT_CHANGE
:
378 case ID_CS_ARROW_CHANGE
:
379 case ID_CS_ROTATE_CLOCKWISE
:
380 case ID_CS_ROTATE_ANTICLOCKWISE
:
381 case ID_CS_CHANGE_LINE_ORDERING
:
382 case ID_CS_CHANGE_LINE_ATTACHMENT
:
384 case ID_CS_NEW_POINT
:
385 case ID_CS_CUT_POINT
:
386 case ID_CS_MOVE_LINE_POINT
:
387 case ID_CS_STRAIGHTEN
:
388 case ID_CS_MOVE_LABEL
:
390 // At this point we have been given a new shape
391 // just like the old one but with a changed colour.
392 // It's now time to apply that change to the
393 // shape on the canvas, saving the old state.
394 // NOTE: this is general enough to work with MOST attribute
397 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
398 wxASSERT( (m_savedState
!= NULL
) ); // This is the new shape with changed colour
399 wxASSERT( (m_doc
!= NULL
) );
401 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
402 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
404 bool isSelected
= m_shapeOnCanvas
->Selected();
406 m_shapeOnCanvas
->Select(FALSE
, & dc
);
408 if (m_cmd
== ID_CS_SIZE
|| m_cmd
== ID_CS_ROTATE_CLOCKWISE
|| m_cmd
== ID_CS_ROTATE_ANTICLOCKWISE
||
409 m_cmd
== ID_CS_CHANGE_LINE_ORDERING
|| m_cmd
== ID_CS_CHANGE_LINE_ATTACHMENT
)
411 m_shapeOnCanvas
->Erase(dc
);
414 // TODO: make sure the ID is the same. Or, when applying the new state,
415 // don't change the original ID.
416 wxShape
* tempShape
= m_shapeOnCanvas
->CreateNewCopy();
418 // Apply the saved state to the shape on the canvas, by copying.
419 m_savedState
->CopyWithHandler(*m_shapeOnCanvas
);
421 // Delete this state now it's been used (m_shapeOnCanvas currently holds this state)
424 // Remember the previous state
425 m_savedState
= tempShape
;
429 if (m_cmd
== ID_CS_MOVE
|| m_cmd
== ID_CS_ROTATE_CLOCKWISE
|| m_cmd
== ID_CS_ROTATE_ANTICLOCKWISE
||
430 m_cmd
== ID_CS_ALIGN
)
432 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
434 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
435 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
436 m_shapeOnCanvas
->Draw(dc
);
438 else if (m_cmd
== ID_CS_CHANGE_LINE_ORDERING
)
440 m_shapeOnCanvas
->MoveLinks(dc
);
442 else if (m_cmd
== ID_CS_CHANGE_LINE_ATTACHMENT
)
444 wxLineShape
*lineShape
= (wxLineShape
*)m_shapeOnCanvas
;
446 // Have to move both sets of links since we don't know which links
447 // have been affected (unless we compared before and after states).
448 lineShape
->GetFrom()->MoveLinks(dc
);
449 lineShape
->GetTo()->MoveLinks(dc
);
451 else if (m_cmd
== ID_CS_SIZE
)
453 double width
, height
;
454 m_shapeOnCanvas
->GetBoundingBoxMax(&width
, &height
);
456 m_shapeOnCanvas
->SetSize(width
, height
);
457 m_shapeOnCanvas
->Move(dc
, m_shapeOnCanvas
->GetX(), m_shapeOnCanvas
->GetY());
459 m_shapeOnCanvas
->Show(TRUE
);
461 // Recursively redraw links if we have a composite.
462 if (m_shapeOnCanvas
->GetChildren().Number() > 0)
463 m_shapeOnCanvas
->DrawLinks(dc
, -1, TRUE
);
465 m_shapeOnCanvas
->GetEventHandler()->OnEndSize(width
, height
);
467 else if (m_cmd
== ID_CS_EDIT_PROPERTIES
|| m_cmd
== ID_CS_FONT_CHANGE
)
469 csEvtHandler
*handler
= (csEvtHandler
*)m_shapeOnCanvas
->GetEventHandler();
470 m_shapeOnCanvas
->FormatText(dc
, handler
->m_label
);
471 m_shapeOnCanvas
->Draw(dc
);
475 m_shapeOnCanvas
->Draw(dc
);
479 m_shapeOnCanvas
->Select(TRUE
, & dc
);
482 m_doc
->UpdateAllViews();
490 bool csCommandState::Undo()
496 wxASSERT( (m_savedState
!= NULL
) );
497 wxASSERT( (m_doc
!= NULL
) );
499 m_doc
->GetDiagram()->AddShape(m_savedState
);
500 m_shapeOnCanvas
= m_savedState
;
503 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
505 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
506 lineShape
->GetFrom()->AddLine(lineShape
, lineShape
->GetTo(),
507 lineShape
->GetAttachmentFrom(), lineShape
->GetAttachmentTo(),
508 m_linePositionFrom
, m_linePositionTo
);
510 wxShapeCanvas
* canvas
= lineShape
->GetFrom()->GetCanvas();
512 wxClientDC
dc(canvas
);
513 canvas
->PrepareDC(dc
);
515 lineShape
->GetFrom()->MoveLinks(dc
);
516 lineShape
->GetTo()->MoveLinks(dc
);
519 m_shapeOnCanvas
->Show(TRUE
);
522 m_doc
->UpdateAllViews();
525 case ID_CS_ADD_SHAPE
:
527 case ID_CS_ADD_SHAPE_SELECT
:
528 case ID_CS_ADD_LINE_SELECT
:
530 wxASSERT( (m_shapeOnCanvas
!= NULL
) );
531 wxASSERT( (m_savedState
== NULL
) );
532 wxASSERT( (m_doc
!= NULL
) );
534 // In case this is a line
535 wxShape
* lineFrom
= NULL
;
536 wxShape
* lineTo
= NULL
;
537 int attachmentFrom
= 0, attachmentTo
= 0;
539 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
541 // Store the from/to info to save in the line shape
542 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
543 lineFrom
= lineShape
->GetFrom();
544 lineTo
= lineShape
->GetTo();
545 attachmentFrom
= lineShape
->GetAttachmentFrom();
546 attachmentTo
= lineShape
->GetAttachmentTo();
549 wxClientDC
dc(m_shapeOnCanvas
->GetCanvas());
550 m_shapeOnCanvas
->GetCanvas()->PrepareDC(dc
);
552 m_shapeOnCanvas
->Select(FALSE
, &dc
);
553 ((csDiagramView
*) m_doc
->GetFirstView())->SelectShape(m_shapeOnCanvas
, FALSE
);
554 m_doc
->GetDiagram()->RemoveShape(m_shapeOnCanvas
);
555 m_shapeOnCanvas
->Unlink(); // Unlinks the line, if it is a line
557 if (m_shapeOnCanvas
->IsKindOf(CLASSINFO(wxLineShape
)))
559 // Restore the from/to info for future reference
560 wxLineShape
* lineShape
= (wxLineShape
*) m_shapeOnCanvas
;
561 lineShape
->SetFrom(lineFrom
);
562 lineShape
->SetTo(lineTo
);
563 lineShape
->SetAttachments(attachmentFrom
, attachmentTo
);
566 m_savedState
= m_shapeOnCanvas
;
567 m_shapeOnCanvas
= NULL
;
570 m_doc
->UpdateAllViews();
573 case ID_CS_CHANGE_BACKGROUND_COLOUR
:
576 case ID_CS_EDIT_PROPERTIES
:
577 case ID_CS_FONT_CHANGE
:
578 case ID_CS_ARROW_CHANGE
:
579 case ID_CS_ROTATE_CLOCKWISE
:
580 case ID_CS_ROTATE_ANTICLOCKWISE
:
581 case ID_CS_CHANGE_LINE_ORDERING
:
582 case ID_CS_CHANGE_LINE_ATTACHMENT
:
584 case ID_CS_NEW_POINT
:
585 case ID_CS_CUT_POINT
:
586 case ID_CS_MOVE_LINE_POINT
:
587 case ID_CS_STRAIGHTEN
:
588 case ID_CS_MOVE_LABEL
:
590 // Exactly like the Do case; we're just swapping states.