1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        contrib/samples/ogl/studio/doc.cpp
3 // Purpose:     Implements document functionality
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     12/07/98
7 // RCS-ID:      $Id: doc.cpp 37440 2006-02-10 11:59:52Z ABX $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22 
23 #include "studio.h"
24 #include "doc.h"
25 #include "view.h"
26 #include "wx/ogl/basicp.h"
27 
IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument,wxDocument)28 IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument, wxDocument)
29 
30 #ifdef __VISUALC__
31 #pragma warning(disable:4355)
32 #endif
33 
34 csDiagramDocument::csDiagramDocument():m_diagram(this)
35 {
36 }
37 
38 #ifdef __VISUALC__
39 #pragma warning(default:4355)
40 #endif
41 
~csDiagramDocument()42 csDiagramDocument::~csDiagramDocument()
43 {
44 }
45 
OnCloseDocument()46 bool csDiagramDocument::OnCloseDocument()
47 {
48   m_diagram.DeleteAllShapes();
49   return true;
50 }
51 
52 #if wxUSE_PROLOGIO
OnSaveDocument(const wxString & file)53 bool csDiagramDocument::OnSaveDocument(const wxString& file)
54 {
55   if (file == wxEmptyString)
56     return false;
57 
58   if (!m_diagram.SaveFile(file))
59   {
60     wxString msgTitle;
61     if (wxTheApp->GetAppName() != wxEmptyString)
62         msgTitle = wxTheApp->GetAppName();
63     else
64         msgTitle = wxString(_T("File error"));
65 
66     (void)wxMessageBox(_T("Sorry, could not open this file for saving."), msgTitle, wxOK | wxICON_EXCLAMATION,
67       GetDocumentWindow());
68     return false;
69   }
70 
71   Modify(false);
72   SetFilename(file);
73   return true;
74 }
75 
OnOpenDocument(const wxString & file)76 bool csDiagramDocument::OnOpenDocument(const wxString& file)
77 {
78   if (!OnSaveModified())
79     return false;
80 
81   wxString msgTitle;
82   if (wxTheApp->GetAppName() != wxEmptyString)
83     msgTitle = wxTheApp->GetAppName();
84   else
85     msgTitle = wxString(_T("File error"));
86 
87   m_diagram.DeleteAllShapes();
88   if (!m_diagram.LoadFile(file))
89   {
90     (void)wxMessageBox(_T("Sorry, could not open this file."), msgTitle, wxOK|wxICON_EXCLAMATION,
91      GetDocumentWindow());
92     return false;
93   }
94   SetFilename(file, true);
95   Modify(false);
96   UpdateAllViews();
97 
98   return true;
99 }
100 #endif // wxUSE_PROLOGIO
101 
102 
103 /*
104  * Implementation of drawing command
105  */
106 
csDiagramCommand(const wxString & name,csDiagramDocument * doc,csCommandState * onlyState)107 csDiagramCommand::csDiagramCommand(const wxString& name, csDiagramDocument *doc,
108     csCommandState* onlyState):
109   wxCommand(true, name)
110 {
111   m_doc = doc;
112 
113   if (onlyState)
114   {
115     AddState(onlyState);
116   }
117 }
118 
~csDiagramCommand()119 csDiagramCommand::~csDiagramCommand()
120 {
121     wxObjectList::compatibility_iterator node = m_states.GetFirst();
122     while (node)
123     {
124         csCommandState* state = (csCommandState*) node->GetData();
125         delete state;
126         node = node->GetNext();
127     }
128 }
129 
AddState(csCommandState * state)130 void csDiagramCommand::AddState(csCommandState* state)
131 {
132     state->m_doc = m_doc;
133 //    state->m_cmd = m_cmd;
134     m_states.Append(state);
135 }
136 
137 // Insert a state at the beginning of the list
InsertState(csCommandState * state)138 void csDiagramCommand::InsertState(csCommandState* state)
139 {
140     state->m_doc = m_doc;
141 //    state->m_cmd = m_cmd;
142     m_states.Insert(state);
143 }
144 
145 // Schedule all lines connected to the states to be cut.
RemoveLines()146 void csDiagramCommand::RemoveLines()
147 {
148     wxObjectList::compatibility_iterator node = m_states.GetFirst();
149     while (node)
150     {
151         csCommandState* state = (csCommandState*) node->GetData();
152         wxShape* shape = state->GetShapeOnCanvas();
153         wxASSERT( (shape != NULL) );
154 
155         wxObjectList::compatibility_iterator node1 = shape->GetLines().GetFirst();
156         while (node1)
157         {
158             wxLineShape *line = (wxLineShape *)node1->GetData();
159             if (!FindStateByShape(line))
160             {
161                 csCommandState* newState = new csCommandState(ID_CS_CUT, NULL, line);
162                 InsertState(newState);
163             }
164 
165             node1 = node1->GetNext();
166         }
167         node = node->GetNext();
168     }
169 }
170 
FindStateByShape(wxShape * shape)171 csCommandState* csDiagramCommand::FindStateByShape(wxShape* shape)
172 {
173     wxObjectList::compatibility_iterator node = m_states.GetFirst();
174     while (node)
175     {
176         csCommandState* state = (csCommandState*) node->GetData();
177         if (shape == state->GetShapeOnCanvas() || shape == state->GetSavedState())
178             return state;
179         node = node->GetNext();
180     }
181     return NULL;
182 }
183 
Do()184 bool csDiagramCommand::Do()
185 {
186     wxObjectList::compatibility_iterator node = m_states.GetFirst();
187     while (node)
188     {
189         csCommandState* state = (csCommandState*) node->GetData();
190         if (!state->Do())
191             return false;
192         node = node->GetNext();
193     }
194     return true;
195 }
196 
Undo()197 bool csDiagramCommand::Undo()
198 {
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();
202     while (node)
203     {
204         csCommandState* state = (csCommandState*) node->GetData();
205         if (!state->Undo())
206             return false;
207         node = node->GetPrevious();
208     }
209     return true;
210 }
211 
csCommandState(int cmd,wxShape * savedState,wxShape * shapeOnCanvas)212 csCommandState::csCommandState(int cmd, wxShape* savedState, wxShape* shapeOnCanvas)
213 {
214     m_cmd = cmd;
215     m_doc = NULL;
216     m_savedState = savedState;
217     m_shapeOnCanvas = shapeOnCanvas;
218     m_linePositionFrom = 0;
219     m_linePositionTo = 0;
220 }
221 
~csCommandState()222 csCommandState::~csCommandState()
223 {
224     if (m_savedState)
225     {
226         m_savedState->SetCanvas(NULL);
227         delete m_savedState;
228     }
229 }
230 
Do()231 bool csCommandState::Do()
232 {
233   switch (m_cmd)
234   {
235     case ID_CS_CUT:
236     {
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)
240 
241         wxASSERT( (m_shapeOnCanvas != NULL) );
242         wxASSERT( (m_savedState == NULL) ); // new state will be 'nothing'
243         wxASSERT( (m_doc != NULL) );
244 
245         wxShapeCanvas* canvas = m_shapeOnCanvas->GetCanvas();
246 
247         // In case this is a line
248         wxShape* lineFrom = NULL;
249         wxShape* lineTo = NULL;
250         int attachmentFrom = 0, attachmentTo = 0;
251 
252         if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
253         {
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();
260 
261             m_linePositionFrom = lineFrom->GetLinePosition(lineShape);
262             m_linePositionTo = lineTo->GetLinePosition(lineShape);
263         }
264 
265         m_shapeOnCanvas->Select(false);
266         ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, false);
267 
268         m_shapeOnCanvas->Unlink();
269 
270         m_doc->GetDiagram()->RemoveShape(m_shapeOnCanvas);
271 
272         m_savedState = m_shapeOnCanvas;
273 
274         if (m_savedState->IsKindOf(CLASSINFO(wxLineShape)))
275         {
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);
281 
282             wxClientDC dc(canvas);
283             canvas->PrepareDC(dc);
284 
285             lineFrom->MoveLinks(dc);
286             lineTo->MoveLinks(dc);
287         }
288 
289         m_doc->Modify(true);
290         m_doc->UpdateAllViews();
291         break;
292     }
293     case ID_CS_ADD_SHAPE:
294     case ID_CS_ADD_SHAPE_SELECT:
295     {
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.
301 
302         wxASSERT( (m_shapeOnCanvas == NULL) );
303         wxASSERT( (m_savedState != NULL) );
304         wxASSERT( (m_doc != NULL) );
305 
306         m_shapeOnCanvas = m_savedState;
307         m_savedState = NULL;
308 
309         m_doc->GetDiagram()->AddShape(m_shapeOnCanvas);
310         m_shapeOnCanvas->Show(true);
311 
312         wxClientDC dc(m_shapeOnCanvas->GetCanvas());
313         m_shapeOnCanvas->GetCanvas()->PrepareDC(dc);
314 
315         csEvtHandler *handler = (csEvtHandler *)m_shapeOnCanvas->GetEventHandler();
316         m_shapeOnCanvas->FormatText(dc, handler->m_label);
317 
318         m_shapeOnCanvas->Move(dc, m_shapeOnCanvas->GetX(), m_shapeOnCanvas->GetY());
319 
320         if (m_cmd == ID_CS_ADD_SHAPE_SELECT)
321         {
322             m_shapeOnCanvas->Select(true, &dc);
323             ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, true);
324         }
325 
326         m_doc->Modify(true);
327         m_doc->UpdateAllViews();
328         break;
329     }
330     case ID_CS_ADD_LINE:
331     case ID_CS_ADD_LINE_SELECT:
332     {
333         wxASSERT( (m_shapeOnCanvas == NULL) );
334         wxASSERT( (m_savedState != NULL) );
335         wxASSERT( (m_doc != NULL) );
336 
337         wxLineShape *lineShape = (wxLineShape *)m_savedState;
338         wxASSERT( (lineShape->GetFrom() != NULL) );
339         wxASSERT( (lineShape->GetTo() != NULL) );
340 
341         m_shapeOnCanvas = m_savedState;
342         m_savedState = NULL;
343 
344         m_doc->GetDiagram()->AddShape(lineShape);
345 
346         lineShape->GetFrom()->AddLine(lineShape, lineShape->GetTo(),
347             lineShape->GetAttachmentFrom(), lineShape->GetAttachmentTo());
348 
349         lineShape->Show(true);
350 
351         wxClientDC dc(lineShape->GetCanvas());
352         lineShape->GetCanvas()->PrepareDC(dc);
353 
354         // It won't get drawn properly unless you move both
355         // connected images
356         lineShape->GetFrom()->Move(dc, lineShape->GetFrom()->GetX(), lineShape->GetFrom()->GetY());
357         lineShape->GetTo()->Move(dc, lineShape->GetTo()->GetX(), lineShape->GetTo()->GetY());
358 
359         if (m_cmd == ID_CS_ADD_LINE_SELECT)
360         {
361             lineShape->Select(true, &dc);
362             ((csDiagramView*) m_doc->GetFirstView())->SelectShape(m_shapeOnCanvas, true);
363         }
364 
365         m_doc->Modify(true);
366         m_doc->UpdateAllViews();
367         break;
368     }
369     case ID_CS_CHANGE_BACKGROUND_COLOUR:
370     case ID_CS_MOVE:
371     case ID_CS_SIZE:
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:
379     case ID_CS_ALIGN:
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:
385     {
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
391         // changes!
392 
393         wxASSERT( (m_shapeOnCanvas != NULL) );
394         wxASSERT( (m_savedState != NULL) ); // This is the new shape with changed colour
395         wxASSERT( (m_doc != NULL) );
396 
397         wxClientDC dc(m_shapeOnCanvas->GetCanvas());
398         m_shapeOnCanvas->GetCanvas()->PrepareDC(dc);
399 
400         bool isSelected = m_shapeOnCanvas->Selected();
401         if (isSelected)
402             m_shapeOnCanvas->Select(false, & dc);
403 
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)
406         {
407             m_shapeOnCanvas->Erase(dc);
408         }
409 
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();
413 
414         // Apply the saved state to the shape on the canvas, by copying.
415         m_savedState->CopyWithHandler(*m_shapeOnCanvas);
416 
417         // Delete this state now it's been used (m_shapeOnCanvas currently holds this state)
418         delete m_savedState;
419 
420         // Remember the previous state
421         m_savedState = tempShape;
422 
423         // Redraw the shape
424 
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)
427         {
428             m_shapeOnCanvas->Move(dc, m_shapeOnCanvas->GetX(), m_shapeOnCanvas->GetY());
429 
430             csEvtHandler *handler = (csEvtHandler *)m_shapeOnCanvas->GetEventHandler();
431             m_shapeOnCanvas->FormatText(dc, handler->m_label);
432             m_shapeOnCanvas->Draw(dc);
433         }
434         else if (m_cmd == ID_CS_CHANGE_LINE_ORDERING)
435         {
436             m_shapeOnCanvas->MoveLinks(dc);
437         }
438         else if (m_cmd == ID_CS_CHANGE_LINE_ATTACHMENT)
439         {
440             wxLineShape *lineShape = (wxLineShape *)m_shapeOnCanvas;
441 
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);
446         }
447         else if (m_cmd == ID_CS_SIZE)
448         {
449             double width, height;
450             m_shapeOnCanvas->GetBoundingBoxMax(&width, &height);
451 
452             m_shapeOnCanvas->SetSize(width, height);
453             m_shapeOnCanvas->Move(dc, m_shapeOnCanvas->GetX(), m_shapeOnCanvas->GetY());
454 
455             m_shapeOnCanvas->Show(true);
456 
457             // Recursively redraw links if we have a composite.
458             if (m_shapeOnCanvas->GetChildren().GetCount() > 0)
459                 m_shapeOnCanvas->DrawLinks(dc, -1, true);
460 
461             m_shapeOnCanvas->GetEventHandler()->OnEndSize(width, height);
462         }
463         else if (m_cmd == ID_CS_EDIT_PROPERTIES || m_cmd == ID_CS_FONT_CHANGE)
464         {
465             csEvtHandler *handler = (csEvtHandler *)m_shapeOnCanvas->GetEventHandler();
466             m_shapeOnCanvas->FormatText(dc, handler->m_label);
467             m_shapeOnCanvas->Draw(dc);
468         }
469         else
470         {
471             m_shapeOnCanvas->Draw(dc);
472         }
473 
474         if (isSelected)
475             m_shapeOnCanvas->Select(true, & dc);
476 
477         m_doc->Modify(true);
478         m_doc->UpdateAllViews();
479 
480         break;
481     }
482   }
483   return true;
484 }
485 
Undo()486 bool csCommandState::Undo()
487 {
488   switch (m_cmd)
489   {
490     case ID_CS_CUT:
491     {
492         wxASSERT( (m_savedState != NULL) );
493         wxASSERT( (m_doc != NULL) );
494 
495         m_doc->GetDiagram()->AddShape(m_savedState);
496         m_shapeOnCanvas = m_savedState;
497         m_savedState = NULL;
498 
499         if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
500         {
501             wxLineShape* lineShape = (wxLineShape*) m_shapeOnCanvas;
502             lineShape->GetFrom()->AddLine(lineShape, lineShape->GetTo(),
503                 lineShape->GetAttachmentFrom(), lineShape->GetAttachmentTo(),
504                 m_linePositionFrom, m_linePositionTo);
505 
506             wxShapeCanvas* canvas = lineShape->GetFrom()->GetCanvas();
507 
508             wxClientDC dc(canvas);
509             canvas->PrepareDC(dc);
510 
511             lineShape->GetFrom()->MoveLinks(dc);
512             lineShape->GetTo()->MoveLinks(dc);
513 
514         }
515         m_shapeOnCanvas->Show(true);
516 
517         m_doc->Modify(true);
518         m_doc->UpdateAllViews();
519         break;
520     }
521     case ID_CS_ADD_SHAPE:
522     case ID_CS_ADD_LINE:
523     case ID_CS_ADD_SHAPE_SELECT:
524     case ID_CS_ADD_LINE_SELECT:
525     {
526         wxASSERT( (m_shapeOnCanvas != NULL) );
527         wxASSERT( (m_savedState == NULL) );
528         wxASSERT( (m_doc != NULL) );
529 
530         // In case this is a line
531         wxShape* lineFrom = NULL;
532         wxShape* lineTo = NULL;
533         int attachmentFrom = 0, attachmentTo = 0;
534 
535         if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
536         {
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();
543         }
544 
545         wxClientDC dc(m_shapeOnCanvas->GetCanvas());
546         m_shapeOnCanvas->GetCanvas()->PrepareDC(dc);
547 
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
552 
553         if (m_shapeOnCanvas->IsKindOf(CLASSINFO(wxLineShape)))
554         {
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);
560         }
561 
562         m_savedState = m_shapeOnCanvas;
563         m_shapeOnCanvas = NULL;
564 
565         m_doc->Modify(true);
566         m_doc->UpdateAllViews();
567         break;
568     }
569     case ID_CS_CHANGE_BACKGROUND_COLOUR:
570     case ID_CS_MOVE:
571     case ID_CS_SIZE:
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:
579     case ID_CS_ALIGN:
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:
585     {
586         // Exactly like the Do case; we're just swapping states.
587         Do();
588         break;
589     }
590   }
591 
592     return true;
593 }
594