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 28IMPLEMENT_DYNAMIC_CLASS(csDiagramDocument, wxDocument) 29 30#ifdef __VISUALC__ 31#pragma warning(disable:4355) 32#endif 33 34csDiagramDocument::csDiagramDocument():m_diagram(this) 35{ 36} 37 38#ifdef __VISUALC__ 39#pragma warning(default:4355) 40#endif 41 42csDiagramDocument::~csDiagramDocument() 43{ 44} 45 46bool csDiagramDocument::OnCloseDocument() 47{ 48 m_diagram.DeleteAllShapes(); 49 return true; 50} 51 52#if wxUSE_PROLOGIO 53bool 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 76bool 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 107csDiagramCommand::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 119csDiagramCommand::~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 130void 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 138void 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. 146void 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 171csCommandState* 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 184bool 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 197bool 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 212csCommandState::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 222csCommandState::~csCommandState() 223{ 224 if (m_savedState) 225 { 226 m_savedState->SetCanvas(NULL); 227 delete m_savedState; 228 } 229} 230 231bool 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 486bool 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