Changeset 448 for cafu/trunk

Show
Ignore:
Timestamp:
12/22/11 11:46:09 (5 months ago)
Author:
Carsten
Message:

CaWE: Revised the semantics of the Morph tool to finally integrate properly with the Undo/Redo history.

Location:
cafu/trunk/CaWE
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • cafu/trunk/CaWE/MapCommands/AddPrim.cpp

    r285 r448  
    2626 
    2727 
    28 CommandAddPrimT::CommandAddPrimT(MapDocumentT& MapDoc, MapPrimitiveT* AddPrim, MapEntityBaseT* Parent, wxString Name) 
     28CommandAddPrimT::CommandAddPrimT(MapDocumentT& MapDoc, MapPrimitiveT* AddPrim, MapEntityBaseT* Parent, wxString Name, bool SetSel) 
    2929    : m_MapDoc(MapDoc), 
    3030      m_AddPrims(), 
     
    3737    m_AddElems.PushBack(AddPrim); 
    3838 
    39     m_CommandSelect=CommandSelectT::Set(&m_MapDoc, m_AddElems); 
     39    if (SetSel) 
     40        m_CommandSelect=CommandSelectT::Set(&m_MapDoc, m_AddElems); 
    4041} 
    4142 
    4243 
    43 CommandAddPrimT::CommandAddPrimT(MapDocumentT& MapDoc, const ArrayT<MapPrimitiveT*>& AddPrims, MapEntityBaseT* Parent, wxString Name) 
     44CommandAddPrimT::CommandAddPrimT(MapDocumentT& MapDoc, const ArrayT<MapPrimitiveT*>& AddPrims, MapEntityBaseT* Parent, wxString Name, bool SetSel) 
    4445    : m_MapDoc(MapDoc), 
    4546      m_AddPrims(AddPrims), 
     
    5253        m_AddElems.PushBack(m_AddPrims[PrimNr]); 
    5354 
    54     m_CommandSelect=CommandSelectT::Set(&m_MapDoc, m_AddElems); 
     55    if (SetSel) 
     56        m_CommandSelect=CommandSelectT::Set(&m_MapDoc, m_AddElems); 
    5557} 
    5658 
     
    8183    m_MapDoc.UpdateAllObservers_Created(m_AddElems); 
    8284 
    83     m_CommandSelect->Do(); 
     85    if (m_CommandSelect) 
     86        m_CommandSelect->Do(); 
    8487 
    8588    m_Done=true; 
     
    9396    if (!m_Done) return; 
    9497 
    95     m_CommandSelect->Undo(); 
     98    if (m_CommandSelect) 
     99        m_CommandSelect->Undo(); 
    96100 
    97101    for (unsigned long PrimNr=0; PrimNr<m_AddPrims.Size(); PrimNr++) 
  • cafu/trunk/CaWE/MapCommands/AddPrim.hpp

    r285 r448  
    4444    /// @param Parent    The parent entity that the primitive becomes a part of and which it is added to. Can be the world or a custom entity. 
    4545    /// @param Name      The name of this command for the undo history. 
    46     CommandAddPrimT(MapDocumentT& MapDoc, MapPrimitiveT* AddPrim, MapEntityBaseT* Parent, wxString Name="new primitive"); 
     46    /// @param SetSel    Whether the selection should be set to the newly added map primitive(s). 
     47    CommandAddPrimT(MapDocumentT& MapDoc, MapPrimitiveT* AddPrim, MapEntityBaseT* Parent, wxString Name="new primitive", bool SetSel=true); 
    4748 
    4849    /// The constructor for creating a command for adding multiple primitives into the map. 
    4950    /// Analogous to the constructor for adding a single primitive above. 
    50     CommandAddPrimT(MapDocumentT& MapDoc, const ArrayT<MapPrimitiveT*>& AddPrims, MapEntityBaseT* Parent, wxString Name="new primitives"); 
     51    CommandAddPrimT(MapDocumentT& MapDoc, const ArrayT<MapPrimitiveT*>& AddPrims, MapEntityBaseT* Parent, wxString Name="new primitives", bool SetSel=true); 
    5152 
    5253    /// The destructor. 
  • cafu/trunk/CaWE/ToolMorph.cpp

    r447 r448  
    3939#include "MapCommands/AddPrim.hpp" 
    4040#include "MapCommands/Delete.hpp" 
     41#include "MapCommands/Select.hpp" 
    4142 
    4243 
     
    140141    else 
    141142    { 
    142         MorphPrims_CommitAndClear(); 
    143  
    144         // For each brush or bezier patch in the documents selection, create a related instance here. 
    145         for (unsigned long SelNr=0; SelNr<m_MapDoc.GetSelection().Size(); SelNr++) 
    146         { 
    147             MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(m_MapDoc.GetSelection()[SelNr]); 
    148  
    149             if (MapPrim) 
    150                 MorphPrims_TogglePrim(MapPrim); 
    151         } 
     143        MorphPrims_SyncTo(m_MapDoc.GetSelection()); 
     144        m_DragState=DragNothing; 
    152145    } 
    153146} 
     
    162155void ToolMorphT::OnDeactivate(ToolT* NewTool) 
    163156{ 
    164     MorphPrims_CommitAndClear(); 
     157    const ArrayT<MapElementT*> Empty; 
     158 
     159    // Clear/reset the morph tool. 
     160    MorphPrims_SyncTo(Empty); 
     161    m_DragState=DragNothing; 
    165162} 
    166163 
     
    176173 
    177174 
    178 void ToolMorphT::MorphPrims_CommitAndClear() 
    179 { 
    180     // Set the map elements of all morph primitives back to "visible" 
    181     // and remove all unmodified morph primitives from our list. 
    182  
    183     // TODO: Doing it like this is not particularly efficient, but this is going to be refactored anyway. 
    184     while (m_MorphPrims.Size()>0) 
    185     { 
    186         MorphPrims_TogglePrim(m_MorphPrims[0]->GetMapPrim()); 
    187     } 
    188 } 
    189  
    190  
    191 void ToolMorphT::MorphPrims_TogglePrim(const MapPrimitiveT* MapPrim) 
    192 { 
    193     // Only needed for observer message. 
    194     ArrayT<MapElementT*> MapElements; 
    195     MapElements.PushBack(const_cast<MapPrimitiveT*>(MapPrim)); 
    196  
    197     const int MP_Index=MorphPrims_Find(MapPrim); 
    198  
    199     if (MP_Index>=0) 
    200     { 
    201         MorphPrimT* MorphPrim=m_MorphPrims[MP_Index]; 
    202  
    203         m_MorphPrims.RemoveAtAndKeepOrder(MP_Index); 
    204         wxASSERT(MapPrim == MorphPrim->GetMapPrim()); 
    205  
    206         if (MorphPrim->IsModified()) 
    207         { 
    208             MapPrimitiveT* MorphedMapPrim=MorphPrim->GetMorphedMapPrim(); 
    209  
    210             if (MorphedMapPrim) 
    211             { 
    212                 m_IsRecursiveSelfNotify=true; 
    213  
    214                 ArrayT<CommandT*> Commands; 
    215                 Commands.PushBack(new CommandDeleteT(m_MapDoc, const_cast<MapPrimitiveT*>(MorphPrim->GetMapPrim()))); 
    216                 Commands.PushBack(new CommandAddPrimT(m_MapDoc, MorphedMapPrim, MorphPrim->GetMapPrim()->GetParent())); 
    217  
    218                 m_MapDoc.GetHistory().SubmitCommand(new CommandMacroT(Commands, "Edit Vertices")); 
    219  
    220                 m_IsRecursiveSelfNotify=false; 
    221             } 
    222         } 
    223  
    224         delete MorphPrim; 
    225  
    226         // Elem is now no longer mentioned in the m_MorphPrims list, and thus no longer affected by IsHiddenByTool(). 
     175void ToolMorphT::MorphPrims_SyncTo(const ArrayT<MapElementT*>& Elems) 
     176{ 
     177    ArrayT<MapElementT*> VisChanged; 
     178 
     179    // Remove all entries from m_MorphPrims that are not in NewSelection. 
     180    for (unsigned long MPNr=0; MPNr<m_MorphPrims.Size(); MPNr++) 
     181    { 
     182        MapPrimitiveT* MapPrim=const_cast<MapPrimitiveT*>(m_MorphPrims[MPNr]->GetMapPrim()); 
     183 
     184        if (Elems.Find(MapPrim) < 0) 
     185        { 
     186            delete m_MorphPrims[MPNr]; 
     187            m_MorphPrims.RemoveAt(MPNr); 
     188            MPNr--; 
     189 
     190            VisChanged.PushBack(MapPrim); 
     191        } 
     192    } 
     193 
     194    // Add all entries that are in Elems but not yet in m_MorphPrims to m_MorphPrims. 
     195    for (unsigned long ElemNr=0; ElemNr<Elems.Size(); ElemNr++) 
     196    { 
     197        MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(Elems[ElemNr]); 
     198 
     199        if (!MapPrim) continue; 
     200        if (MapPrim->GetType()!=&MapBrushT::TypeInfo && MapPrim->GetType()!=&MapBezierPatchT::TypeInfo) continue; 
     201        if (MorphPrims_Find(MapPrim) >= 0) continue; 
     202 
     203        m_MorphPrims.PushBack(new MorphPrimT(MapPrim)); 
     204        VisChanged.PushBack(MapPrim); 
     205    } 
     206 
     207    // If any changes were made, notify the observers. 
     208    if (VisChanged.Size() > 0) 
     209    { 
    227210        m_IsRecursiveSelfNotify=true; 
    228         m_MapDoc.UpdateAllObservers_Modified(MapElements, MEMD_VISIBILITY);     // TODO: Is this still needed? The element was *deleted* above, after all (but only if modified, mind'ya). 
     211        m_MapDoc.UpdateAllObservers_Modified(VisChanged, MEMD_VISIBILITY); 
    229212        m_IsRecursiveSelfNotify=false; 
    230         return; 
    231     } 
    232  
    233     if (dynamic_cast<const MapBrushT*>(MapPrim)==NULL && dynamic_cast<const MapBezierPatchT*>(MapPrim)==NULL) return; 
    234  
    235     MorphPrimT* MorphPrim=new MorphPrimT(MapPrim); 
    236     m_MorphPrims.PushBack(MorphPrim); 
    237  
    238     // Elem is now mentioned in the m_MorphPrims list, and thus affected by IsHiddenByTool(). 
    239     m_IsRecursiveSelfNotify=true; 
    240     m_MapDoc.UpdateAllObservers_Modified(MapElements, MEMD_VISIBILITY); 
    241     m_IsRecursiveSelfNotify=false; 
    242 } 
    243  
    244  
    245 ArrayT<MorphHandleT> ToolMorphT::GetMorphHandlesAt(ViewWindow2DT& ViewWindow, const wxPoint& Point) 
     213 
     214        m_ToolMan.UpdateAllObservers(this, UPDATE_SOON); 
     215    } 
     216} 
     217 
     218 
     219ArrayT<MorphHandleT> ToolMorphT::GetMorphHandlesAt(ViewWindow2DT& ViewWindow, const wxPoint& Point) const 
    246220{ 
    247221    ArrayT<MorphHandleT> MorphHandles; 
     
    294268 
    295269 
    296 bool ToolMorphT::GetMorphHandleAt(ViewWindow3DT& ViewWindow, const wxPoint& Point, MorphHandleT& FoundMH) 
     270bool ToolMorphT::GetMorphHandleAt(ViewWindow3DT& ViewWindow, const wxPoint& Point, MorphHandleT& FoundMH) const 
    297271{ 
    298272    float BestDist=1000000.0f; 
     
    428402 
    429403 
     404void ToolMorphT::FinishDragMorphHandles() 
     405{ 
     406    ArrayT<CommandT*> Commands; 
     407 
     408    for (unsigned long MPNr=0; MPNr<m_MorphPrims.Size(); MPNr++) 
     409    { 
     410        if (!m_MorphPrims[MPNr]->IsModified()) continue; 
     411 
     412        MapPrimitiveT*  MapPrim       =const_cast<MapPrimitiveT*>(m_MorphPrims[MPNr]->GetMapPrim()); 
     413        MapEntityBaseT* ParentEntity  =MapPrim->GetParent(); 
     414        MapPrimitiveT*  MorphedMapPrim=m_MorphPrims[MPNr]->GetMorphedMapPrim(); 
     415 
     416        if (!MorphedMapPrim) continue; 
     417        wxASSERT(MapPrim->IsSelected()); 
     418 
     419        // The delete command also unselects the MapPrim, calling NotifySubjectChanged_Selection(), 
     420        // followed by NotifySubjectChanged_Deleted(). 
     421        // Thus our m_MorphPrims[MPNr] will be deleted and a new one created for MorphedMapPrim by 
     422        // the implicit calls to the NotifySubjectChanged_*() functions. 
     423        CommandDeleteT* DelCmd=new CommandDeleteT(m_MapDoc, MapPrim); 
     424        DelCmd->Do(); 
     425        Commands.PushBack(DelCmd); 
     426 
     427        CommandAddPrimT* AddPrimCmd=new CommandAddPrimT(m_MapDoc, MorphedMapPrim, ParentEntity, "new prim", false /*don't set the selection*/); 
     428        AddPrimCmd->Do(); 
     429        Commands.PushBack(AddPrimCmd); 
     430 
     431        CommandSelectT* SelCmd=CommandSelectT::Add(&m_MapDoc, MorphedMapPrim); 
     432        SelCmd->Do(); 
     433        Commands.PushBack(SelCmd); 
     434    } 
     435 
     436    m_MapDoc.GetHistory().SubmitCommand(new CommandMacroT(Commands, "Edit Vertices")); 
     437 
     438    // This is somewhat redundant, but have it anyway here for clarity. 
     439    m_DragState=DragNothing; 
     440} 
     441 
     442 
    430443void ToolMorphT::NoteEditModeChanged() 
    431444{ 
    432     // TODO: Clear the selection!!?! 
    433  
    434445    m_ToolMan.UpdateAllObservers(this, UPDATE_SOON); 
    435446} 
     
    469480    m_MorphPrims[0]->m_Vertices[m_MorphPrims[0]->m_Vertices.Size()-1]->pos=Center; 
    470481 
    471     // I *don't* bother to update the m_MorphPrims[0] geometry e.g. by a dummy-call to MoveSelectedHandles(), 
     482    // *Don't* bother to update the m_MorphPrims[0] geometry e.g. by a dummy-call to MoveSelectedHandles(), 
    472483    // because firstly the convex hull should not have been affected anyway, and secondly, the user is supposed 
    473484    // to trigger the update himself by dragging the new vertex. 
     
    608619        if (HitPrim && (HitPrim->GetType()==&MapBrushT::TypeInfo || HitPrim->GetType()==&MapBezierPatchT::TypeInfo)) 
    609620        { 
    610             if (!ME.ControlDown()) MorphPrims_CommitAndClear(); 
    611             MorphPrims_TogglePrim(HitPrim); 
    612  
    613             m_ToolMan.UpdateAllObservers(this, UPDATE_NOW); 
     621            // The handling of the Control key is not ideal: it would possibly be better to 
     622            // not account for it at all, or to route this through the selection tool somehow. 
     623            // 
     624            // The implied call to NotifySubjectChanged_Selection() will update our tool state. 
     625            m_MapDoc.GetHistory().SubmitCommand(ME.ControlDown() 
     626                ? CommandSelectT::Add(&m_MapDoc, HitPrim) 
     627                : CommandSelectT::Set(&m_MapDoc, HitPrim)); 
     628 
    614629            return true; 
    615630        } 
     
    666681 
    667682        case DragMorphHandles: 
     683            FinishDragMorphHandles(); 
    668684            break; 
    669685 
     
    878894        if (HitPrim && (HitPrim->GetType()==&MapBrushT::TypeInfo || HitPrim->GetType()==&MapBezierPatchT::TypeInfo)) 
    879895        { 
    880             if (!ME.ControlDown()) MorphPrims_CommitAndClear(); 
    881             MorphPrims_TogglePrim(HitPrim); 
    882  
    883             m_ToolMan.UpdateAllObservers(this, UPDATE_NOW); 
     896            // The handling of the Control key is not ideal: it would possibly be better to 
     897            // not account for it at all, or to route this through the selection tool somehow. 
     898            // 
     899            // The implied call to NotifySubjectChanged_Selection() will update our tool state. 
     900            m_MapDoc.GetHistory().SubmitCommand(ME.ControlDown() 
     901                ? CommandSelectT::Add(&m_MapDoc, HitPrim) 
     902                : CommandSelectT::Set(&m_MapDoc, HitPrim)); 
     903 
    884904            return true; 
    885905        } 
     
    975995 
    976996        case DragMorphHandles: 
    977             m_ToolMan.UpdateAllObservers(this, UPDATE_SOON); 
     997            FinishDragMorphHandles(); 
    978998            break; 
    979999 
     
    9951015    if (!IsActiveTool() || m_IsRecursiveSelfNotify) return; 
    9961016 
    997     // An external event caused a selection change, such as the user clicking "Undo". 
    998     // 
    999     //   - What we can *not* do is calling MorphPrims_CommitAndClear(), because that 
    1000     //     would attempt to submit another command to the command history while the 
    1001     //     command history is attempting to run the "Undo". 
    1002     // 
    1003     //   - Technically, it would be possible to do nothing: A change in selection 
    1004     //     does not require any alterations of our tool state, the user can continue 
    1005     //     to morph the objects that he previously begun to morph. 
    1006     // 
    1007     //   - Although the user might lose some morph work, probably the least confusion 
    1008     //     action is to just discard and clear the tool state. 
    1009     // 
    1010     for (unsigned long MPNr=0; MPNr<m_MorphPrims.Size(); MPNr++) 
     1017    MorphPrims_SyncTo(NewSelection); 
     1018} 
     1019 
     1020 
     1021void ToolMorphT::NotifySubjectChanged_Deleted(SubjectT* Subject, const ArrayT<MapElementT*>& MapElements) 
     1022{ 
     1023    if (!IsActiveTool() || m_IsRecursiveSelfNotify) return; 
     1024 
     1025    // Remove all entries that are in MapElements and m_MorphPrims from m_MorphPrims. 
     1026    for (unsigned long ElemNr=0; ElemNr<MapElements.Size(); ElemNr++) 
     1027    { 
     1028        MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(MapElements[ElemNr]); 
     1029        if (!MapPrim) continue; 
     1030 
     1031        const int MPNr=MorphPrims_Find(MapPrim); 
     1032        if (MPNr < 0) continue; 
     1033 
    10111034        delete m_MorphPrims[MPNr]; 
    1012     m_MorphPrims.Overwrite(); 
     1035        m_MorphPrims.RemoveAt(MPNr); 
     1036    } 
     1037 
     1038    // No need for this, the elements have been deleted after all. 
     1039    // m_IsRecursiveSelfNotify=true; 
     1040    // m_MapDoc.UpdateAllObservers_Modified(VisChanged, MEMD_VISIBILITY); 
     1041    // m_IsRecursiveSelfNotify=false; 
    10131042 
    10141043    m_ToolMan.UpdateAllObservers(this, UPDATE_SOON); 
    1015     m_DragState=DragNothing; 
    1016 } 
    1017  
    1018  
    1019 void ToolMorphT::NotifySubjectChanged_Deleted(SubjectT* Subject, const ArrayT<MapElementT*>& MapElements) 
     1044} 
     1045 
     1046 
     1047void ToolMorphT::NotifySubjectChanged_Modified(SubjectT* Subject, const ArrayT<MapElementT*>& MapElements, MapElemModDetailE Detail) 
    10201048{ 
    10211049    if (!IsActiveTool() || m_IsRecursiveSelfNotify) return; 
    10221050 
    1023     for (unsigned long i=0; i<MapElements.Size(); i++) 
    1024     { 
    1025         MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(MapElements[i]); 
    1026  
     1051    // If only the visibility changed, do nothing. In all other cases, 
     1052    // don't further examine Detail, but just do the update. 
     1053    if (Detail==MEMD_VISIBILITY) return; 
     1054 
     1055    // Update all entries that are in MapElements and m_MorphPrims. 
     1056    for (unsigned long ElemNr=0; ElemNr<MapElements.Size(); ElemNr++) 
     1057    { 
     1058        MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(MapElements[ElemNr]); 
    10271059        if (!MapPrim) continue; 
    10281060 
    1029         for (unsigned long j=0; j<m_MorphPrims.Size(); j++) 
    1030         { 
    1031             if (MapPrim==m_MorphPrims[j]->GetMapPrim()) 
    1032             { 
    1033                 // Remove this morph primitive from our list. 
    1034                 delete m_MorphPrims[j]; 
    1035                 m_MorphPrims.RemoveAtAndKeepOrder(j); 
    1036  
    1037                 // The element has been found so we can safely break the inner loop and check the next. 
    1038                 break; 
    1039             } 
    1040         } 
     1061        const int MPNr=MorphPrims_Find(MapPrim); 
     1062        if (MPNr < 0) continue; 
     1063 
     1064        delete m_MorphPrims[MPNr]; 
     1065        m_MorphPrims[MPNr]=new MorphPrimT(MapPrim); 
    10411066    } 
    10421067 
     
    10451070 
    10461071 
    1047 void ToolMorphT::NotifySubjectChanged_Modified(SubjectT* Subject, const ArrayT<MapElementT*>& MapElements, MapElemModDetailE Detail) 
    1048 { 
    1049     if (!IsActiveTool() || m_IsRecursiveSelfNotify) return; 
    1050     if (Detail!=MEMD_GENERIC && Detail!=MEMD_VISIBILITY) return; 
    1051  
    1052     for (unsigned long i=0; i<MapElements.Size(); i++) 
    1053     { 
    1054         MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(MapElements[i]); 
    1055  
    1056         if (!MapPrim) continue; 
    1057  
    1058         for (unsigned long j=0; j<m_MorphPrims.Size(); j++) 
    1059         { 
    1060             if (MapPrim==m_MorphPrims[j]->GetMapPrim()) 
    1061             { 
    1062                 // Update the morph primitive of this map element, discarding all prior changes, if any. 
    1063                 delete m_MorphPrims[j]; 
    1064                 m_MorphPrims[j]=new MorphPrimT(MapPrim); 
    1065  
    1066                 // Object has been found so we can safely break the inner loop and check the next object. 
    1067                 break; 
    1068             } 
    1069         } 
    1070     } 
    1071  
    1072     m_ToolMan.UpdateAllObservers(this, UPDATE_SOON); 
    1073 } 
    1074  
    1075  
    10761072void ToolMorphT::NotifySubjectChanged_Modified(SubjectT* Subject, const ArrayT<MapElementT*>& MapElements, MapElemModDetailE Detail, const ArrayT<BoundingBox3fT>& OldBounds) 
    10771073{ 
    1078     if (!IsActiveTool() || m_IsRecursiveSelfNotify) return; 
    1079     if (Detail!=MEMD_GENERIC && Detail!=MEMD_TRANSFORM && Detail!=MEMD_PRIMITIVE_PROPS_CHANGED && Detail!=MEMD_MORPH) return; 
    1080  
    1081     for (unsigned long i=0; i<MapElements.Size(); i++) 
    1082     { 
    1083         MapPrimitiveT* MapPrim=dynamic_cast<MapPrimitiveT*>(MapElements[i]); 
    1084  
    1085         if (!MapPrim) continue; 
    1086  
    1087         for (unsigned long j=0; j<m_MorphPrims.Size(); j++) 
    1088         { 
    1089             if (MapPrim==m_MorphPrims[j]->GetMapPrim()) 
    1090             { 
    1091                 // Update the morph primitive of this map element, discarding all prior changes, if any. 
    1092                 delete m_MorphPrims[j]; 
    1093                 m_MorphPrims[j]=new MorphPrimT(MapPrim); 
    1094  
    1095                 // Object has been found so we can safely break the inner loop and check the next object. 
    1096                 break; 
    1097             } 
    1098         } 
    1099     } 
    1100  
    1101     m_ToolMan.UpdateAllObservers(this, UPDATE_SOON); 
     1074    NotifySubjectChanged_Modified(Subject, MapElements, Detail); 
    11021075} 
    11031076 
  • cafu/trunk/CaWE/ToolMorph.hpp

    r447 r448  
    105105    /// @param Point        The coordinate of interest in ViewWindow, given in Tool(!) Space. 
    106106    /// @returns the array of all morph handles "under" Point in ViewWindow. 
    107     ArrayT<MorphHandleT> GetMorphHandlesAt(ViewWindow2DT& ViewWindow, const wxPoint& Point); 
     107    ArrayT<MorphHandleT> GetMorphHandlesAt(ViewWindow2DT& ViewWindow, const wxPoint& Point) const; 
    108108 
    109109    /// Finds the morph handle that is in ViewWindow along the ray through Point. 
     
    113113    ///    If there is more than one morph handle along the ray through Point, the morph handle that is closest to the viewer is returned. 
    114114    /// @returns true if a morph handle was found in ViewWindow along the ray through Point, false if no such handle exists. 
    115     bool GetMorphHandleAt(ViewWindow3DT& ViewWindow, const wxPoint& Point, MorphHandleT& FoundMH); 
     115    bool GetMorphHandleAt(ViewWindow3DT& ViewWindow, const wxPoint& Point, MorphHandleT& FoundMH) const; 
    116116 
    117117    int  MorphPrims_Find(const MapElementT* Elem) const;        ///< Returns the array index number of the MorphPrimT for the given Elem, -1 if there is none. 
    118     void MorphPrims_CommitAndClear();                           ///< Commits all morphs in m_MorphPrims to their true map elements and resets the morph tool back to empty. 
    119     void MorphPrims_TogglePrim(const MapPrimitiveT* MapPrim);   ///< Toggles the membership of MapPrim in the m_MorphPrims array. 
     118    void MorphPrims_SyncTo(const ArrayT<MapElementT*>& Elems);  ///< Sync's our m_MorphPrims to the given Elems, so that they correspond 1:1 to each other. 
    120119    void MoveSelectedHandles(const Vector3fT& Delta);           ///< WARNING: This method *DESTROYS* all handle pointers into any of the m_MorphPrims!! 
    121120    void NudgeSelectedHandles(const AxesInfoT& AxesInfo, const wxKeyEvent& KE); 
     121    void FinishDragMorphHandles();                              ///< Called from the 2D or 3D views, for morph-modified items, this function replaces the original primitives with the morphed ones in the map. 
    122122    void OnEscape(ViewWindowT& ViewWindow); 
    123123