Changeset 388 for cafu/trunk

Show
Ignore:
Timestamp:
09/20/11 21:18:12 (8 months ago)
Author:
Carsten
Message:

Gui Editor:
Make sure that windows have valid names at all times, so that saving and re-loading the GUI works as expected.
A window name is valid if it is a valid Lua script identifier and unique among all windows in the GUI.

Fixes #87 and #89.
Many thanks to Joe for reports and analysis!

Location:
cafu/trunk/CaWE
Files:
12 modified

Legend:

Unmodified
Added
Removed
  • cafu/trunk/CaWE/DialogInsp-EntityProps.cpp

    r285 r388  
    949949 
    950950        // Check Lua compatibility for entity names. 
    951         if (Key=="name" && !CheckLuaVarCompat(NewValue)) 
    952         { 
    953             wxMessageBox("Entity names must be valid Lua identifiers: They can be any combination of letters, digits and underscores that does not begin with a digit and is not a reserved Lua keyword.", "Error: Entity name is not a Lua identifier.", wxOK | wxICON_ERROR); 
     951        if (Key=="name" && !IsLuaIdentifier(NewValue)) 
     952        { 
     953            wxMessageBox("An entity name must be a string of letters, digits, and underscores that is\n" 
     954                "not beginning with a digit and is not a reserved Lua keyword or global variable.", 
     955                "Entity name is not a valid script identifier.", wxOK | wxICON_ERROR); 
     956 
    954957            NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, ""); // Intentionally update also this dialog to restore previous property value. 
    955958            return; 
  • cafu/trunk/CaWE/GuiEditor/Commands/ModifyWindow.cpp

    r374 r388  
    115115    { 
    116116        m_OldString=m_Window->Name; 
    117  
    118         if (!GuiDocumentT::GetSibling(m_Window)->SetName(m_NewString)) 
    119         { 
    120             m_GuiDocument->UpdateAllObservers_Modified(m_Window, WMD_PROPERTY_CHANGED, m_PropertyName); // This is needed to reset the property grid correctly. 
    121             return false; 
    122         } 
     117        m_Window->Name=m_GuiDocument->CheckWindowName(m_NewString, GuiDocumentT::GetSibling(m_Window)); 
    123118    } 
    124119    else if (m_PropertyName=="BackMatName") 
  • cafu/trunk/CaWE/GuiEditor/Commands/Paste.cpp

    r367 r388  
    8686        m_Windows[WinNr]->Parent=m_NewParent; 
    8787        m_NewParent->Children.PushBack(m_Windows[WinNr]); 
    88  
    89         // If the name of the window is not unique among its siblings, find a new unique name. 
    90         GuiDocumentT::GetSibling(m_Windows[WinNr])->RepairNameUniqueness(); 
    9188    } 
    9289 
  • cafu/trunk/CaWE/GuiEditor/GuiDocument.cpp

    r386 r388  
    2424#include "../GameConfig.hpp" 
    2525#include "../EditorMaterialEngine.hpp" 
     26#include "../LuaAux.hpp" 
    2627 
    2728#include "GuiSys/GuiImpl.hpp" 
     
    7677    else 
    7778    { 
    78         m_Gui=new cf::GuiSys::GuiImplT("Win=gui:new('WindowT'); gui:SetRootWindow(Win); gui:showMouse(false); gui:setFocus(Win); Win:SetName('Window'); Win:set(\"rect\", 0, 0, 640, 480);", true); 
     79        m_Gui=new cf::GuiSys::GuiImplT("Win=gui:new('WindowT'); gui:SetRootWindow(Win); gui:showMouse(false); gui:setFocus(Win); Win:SetName('Root'); Win:set(\"rect\", 0, 0, 640, 480);", true); 
    7980 
    8081        m_GuiProperties=GuiPropertiesT(*m_Gui); 
     
    139140 
    140141 
    141 const ArrayT<cf::GuiSys::WindowT*>& GuiDocumentT::GetSelection() 
    142 { 
    143     return m_Selection; 
    144 } 
    145  
    146  
    147 // Recursively makes sure that the children of each window have unique names. 
    148 // Normally the other GUI editor code should make sure that that is always true, but right now it 
    149 // is possible to create violations of this constraint via drag-and-drop in the window hierarchy tree 
    150 // (can drop a window as a child of another window that already has a child with the same name). 
    151 static void CheckWindowNames(cf::GuiSys::WindowT* Window) 
    152 { 
    153     const std::string OldName=Window->Name; 
    154  
    155     GuiDocumentT::GetSibling(Window)->RepairNameUniqueness(); 
    156  
    157     if (Window->Name!=OldName) 
    158         GuiDocumentT::GetSibling(Window)->GetGuiDoc()->UpdateAllObservers_Modified(Window, WMD_PROPERTY_CHANGED, "Name"); 
    159  
    160     for (unsigned long ChildNr=0; ChildNr<Window->Children.Size(); ChildNr++) 
    161         CheckWindowNames(Window->Children[ChildNr]); 
     142wxString GuiDocumentT::CheckWindowName(const wxString& TestName, EditorWindowT* Win) const 
     143{ 
     144    const wxString               Name_  =CheckLuaIdentifier(TestName); 
     145    const cf::GuiSys::WindowT*   Win_   =Win ? Win->GetDual() : NULL; 
     146    wxString                     NewName=Name_; 
     147    ArrayT<cf::GuiSys::WindowT*> AllChildren; 
     148 
     149    AllChildren.PushBack(m_RootWindow); 
     150    m_RootWindow->GetChildren(AllChildren, true); 
     151 
     152    while (true) 
     153    { 
     154        bool IsUnique=true; 
     155 
     156        for (unsigned long ChildNr=0; ChildNr<AllChildren.Size(); ChildNr++) 
     157        { 
     158            if (AllChildren[ChildNr]->Name==NewName && AllChildren[ChildNr]!=Win_) 
     159            { 
     160                IsUnique=false; 
     161                break; 
     162            } 
     163        } 
     164 
     165        if (IsUnique) break; 
     166 
     167        static unsigned int Count=1; 
     168        NewName=Name_+wxString::Format("_%u", Count); 
     169        Count++; 
     170    } 
     171 
     172    return NewName; 
    162173} 
    163174 
     
    243254bool GuiDocumentT::SaveInit_cgui(std::ostream& OutFile) 
    244255{ 
    245     CheckWindowNames(m_RootWindow); 
    246  
    247256    OutFile << "-- This is a Cafu engine GUI script file, written by CaWE, the Cafu World Editor.\n"; 
    248257    OutFile << "-- You CAN edit this file manually, but note that CaWE may overwrite your changes.\n"; 
  • cafu/trunk/CaWE/GuiEditor/GuiDocument.hpp

    r386 r388  
    6363 
    6464        void SetSelection(const ArrayT<cf::GuiSys::WindowT*>& NewSelection); 
    65         const ArrayT<cf::GuiSys::WindowT*>& GetSelection(); 
     65        const ArrayT<cf::GuiSys::WindowT*>& GetSelection() const { return m_Selection; } 
    6666 
    6767        const ArrayT<EditorMaterialI*>& GetEditorMaterials() const { return m_EditorMaterials; } 
    6868        GameConfigT* GetGameConfig() { return m_GameConfig; } 
     69 
     70        /// Checks if the given string is a valid name for the given window. 
     71        /// A name is valid if it is a valid Lua identifier and unique among all windows in this GUI. 
     72        /// @returns 
     73        ///     If the given string \c TestName is valid, it is returned unchanged. 
     74        ///     Otherwise, a new string is created from \c TestName that is valid. 
     75        wxString CheckWindowName(const wxString& TestName, EditorWindowT* Win) const; 
    6976 
    7077        bool SaveInit_cgui(std::ostream& OutFile); 
  • cafu/trunk/CaWE/GuiEditor/WindowTree.cpp

    r374 r388  
    417417    } 
    418418 
     419    // The command may well have set a name different from TE.GetLabel(). 
     420    TE.Veto(); 
     421    SetItemText(TE.GetItem(), Window->Name); 
     422 
    419423    m_IsRecursiveSelfNotify=false; 
    420424} 
  • cafu/trunk/CaWE/GuiEditor/Windows/EditorWindow.cpp

    r387 r388  
    2525#include "../Commands/ModifyWindow.hpp" 
    2626#include "../../EditorMaterial.hpp" 
    27 #include "../../LuaAux.hpp" 
    2827#include "../../MaterialBrowser/DocAccess.hpp" 
    2928#include "../../MaterialBrowser/MaterialBrowserDialog.hpp" 
     
    4645    : m_Win(Win), 
    4746      m_GuiDoc(GuiDoc), 
    48       m_IsSelected(false), 
    49       m_Counter(1) 
    50 { 
    51     // If window has no name, create default name. 
    52     if (m_Win->Name=="") m_Win->Name="Window"; 
    53  
    54     // Check window name uniqueness and repair it. 
    55     RepairNameUniqueness(); 
    56  
    57     // Note: Since the name of a window comes from an already functional script or is checked when set 
    58     // by the method below, we assert that the name is already Lua compatible here. 
    59     wxASSERT(CheckLuaVarCompat(m_Win->Name)); 
    60 } 
    61  
    62  
    63 bool EditorWindowT::SetName(const wxString& NewName) 
    64 { 
    65     if (!CheckLuaVarCompat(NewName)) 
    66     { 
    67         wxMessageBox("A window name must be a string of letters, digits, and underscores that is\n" 
    68             "not beginning with a digit and is not a reserved Lua keyword or global variable.", 
    69             "Window name is not a valid Lua identifier.", wxOK | wxICON_ERROR); 
    70         return false; 
    71     } 
    72  
    73     if (!CheckNameUniqueness(NewName)) 
    74     { 
    75         wxMessageBox("The window name must be unique in this window hierarchy level. The window can't have a name that is already taken by one of its siblings.", "Error: The given window name is not unique.", wxOK | wxICON_ERROR); 
    76         return false; 
    77     } 
    78  
    79     m_Win->Name=NewName.c_str(); 
    80  
    81     return true; 
    82 } 
    83  
    84  
    85 bool EditorWindowT::CheckNameUniqueness(const wxString& Name) const 
    86 { 
    87     if (m_Win->GetRoot()==m_Win) return true; // Root window can have any name. 
    88  
    89     // Get the siblings of this windows and check name uniqueness against them. 
    90     cf::GuiSys::WindowT* Parent=m_Win->GetParent(); 
    91     ArrayT<cf::GuiSys::WindowT*> Siblings; 
    92     Parent->GetChildren(Siblings); 
    93  
    94     for (unsigned long SibNr=0; SibNr<Siblings.Size(); SibNr++) 
    95     { 
    96         if (Siblings[SibNr]==m_Win) continue; // Don't check against ourselves. 
    97  
    98         if (Siblings[SibNr]->Name==Name) return false; 
    99     } 
    100  
    101     return true; 
    102 } 
    103  
    104  
    105 static wxString StripSuffix(const wxString& Str) 
    106 { 
    107     const size_t Pos=Str.rfind("_"); 
    108  
    109     if (Pos==std::string::npos) return Str; 
    110  
    111     for (size_t i=Pos+1; i<Str.length(); i++) 
    112         if (!wxIsdigit(Str[i])) return Str; 
    113  
    114     if (Pos==0) return "Window"; 
    115  
    116     return Str.Left(Pos); 
    117 } 
    118  
    119  
    120 void EditorWindowT::RepairNameUniqueness() 
    121 { 
    122     const wxString BaseName=StripSuffix(m_Win->Name); 
    123     wxString       NewName =m_Win->Name; 
    124  
    125     while (!CheckNameUniqueness(NewName)) 
    126     { 
    127         NewName=BaseName+"_"+wxString::Format("%u", m_Counter); 
    128         m_Counter++; 
    129     } 
    130  
    131     m_Win->Name=NewName; 
     47      m_IsSelected(false) 
     48{ 
     49    if (m_Win->Name=="") 
     50    { 
     51        m_Win->Name=m_Win->GetType()->ClassName; 
     52 
     53        const size_t len=m_Win->Name.length(); 
     54 
     55        if (len>1 && m_Win->Name[len-1]=='T') 
     56        { 
     57            // Remove the trailing "T" from our class name. 
     58            m_Win->Name=std::string(m_Win->Name, 0, len-1); 
     59        } 
     60    } 
     61 
     62    m_Win->Name=m_GuiDoc->CheckWindowName(m_Win->Name, this); 
    13263} 
    13364 
  • cafu/trunk/CaWE/GuiEditor/Windows/EditorWindow.hpp

    r387 r388  
    5959        bool IsSelected() const { return m_IsSelected; } 
    6060 
    61         /// Sets the name for this window. 
    62         /// This method checks if the name is valid in the sense of Lua compatibility 
    63         /// (window name is used as a Lua variable name for this window) and uniqueness. 
    64         /// This function should always be called instead of setting the name member 
    65         /// of the window directly. 
    66         /// @param NewName   The name to be set. 
    67         /// @return Whether the name has been successfully set. 
    68         bool SetName(const wxString& NewName); 
    69  
    70         /// Checks the name uniqueness of a new name string within the windows siblings. 
    71         /// @param Name    Name to check for uniqueness. 
    72         /// @return Whether this name is unique. 
    73         bool CheckNameUniqueness(const wxString& Name) const; 
    74  
    75         /// Helper method to check and auto-repair the uniqueness of the name of this window. 
    76         void RepairNameUniqueness(); 
    77  
    7861        /// Fills a property grid manager with one property for each class member. 
    7962        /// @param PropMan   The property manager grid to fill. 
     
    10588        cf::GuiSys::WindowT* m_Win;         ///< The GuiSys's "dual" or "sibling" of this window. 
    10689        GuiDocumentT*        m_GuiDoc;      ///< The GUI document that this window lives in. 
    107         bool                 m_IsSelected; 
    108         unsigned int         m_Counter; 
     90        bool                 m_IsSelected;  ///< Is this window selected for editing? 
    10991    }; 
    11092} 
  • cafu/trunk/CaWE/LuaAux.cpp

    r387 r388  
    2626 
    2727 
    28 static const std::string Keywords_Lua[]={"and", "break", "do", "else", "elseif", 
    29                                          "end", "false", "for", "function", "if", 
    30                                          "in", "local", "nil", "not", "or", "repeat", 
    31                                          "return", "then", "true", "until", "while"}; 
     28namespace 
     29{ 
     30    const wxString Keywords[]= 
     31    { 
     32        "and", "break", "do", "else", "elseif", 
     33        "end", "false", "for", "function", "if", 
     34        "in", "local", "nil", "not", "or", "repeat", 
     35        "return", "then", "true", "until", "while" 
     36    }; 
    3237 
    33 static const int NrOfKeywords=sizeof(Keywords_Lua)/sizeof(*Keywords_Lua); 
    34  
    35  
    36 bool CheckLuaVarCompat(const wxString& Varname) 
    37 { 
    38     wxRegEx LuaVarName("^[A-Za-z_][\\w]+$", wxRE_ADVANCED); 
    39     wxASSERT(LuaVarName.IsValid()); 
    40  
    41     if (LuaVarName.Matches(Varname)) 
    42     { 
    43         // Check if variable name is a reserved Lua keyword. 
    44         for (int KeywordNr=0; KeywordNr<NrOfKeywords; KeywordNr++) 
    45             if (Varname.c_str()==Keywords_Lua[KeywordNr]) return false; 
    46  
    47         // Check if variable name matches a Lua global variable. 
    48         wxRegEx LuaGlobals("^[_][A-Z]+$", wxRE_ADVANCED); 
    49         wxASSERT(LuaGlobals.IsValid()); 
    50  
    51         if (LuaGlobals.Matches(Varname)) return false; 
    52  
    53         return true; 
    54     } 
    55  
    56     return false; 
     38    const unsigned int NrOfKeywords=sizeof(Keywords)/sizeof(*Keywords); 
    5739} 
    5840 
    5941 
    60 wxString MakeLuaVarName(const wxString& Varname) 
     42bool IsLuaIdentifier(const wxString& id) 
    6143{ 
    62     // Return the original name if it is already valid. 
    63     if (CheckLuaVarCompat(Varname)) return Varname; 
     44    // Does id fail to meet the Lua identifier rules? 
     45    const wxRegEx LuaIdRegEx("^[A-Za-z_][\\w]+$", wxRE_ADVANCED); 
     46    wxASSERT(LuaIdRegEx.IsValid()); 
    6447 
    65     // Return a default string if passed varname is empty so this functions never fails. 
    66     if (Varname=="") return "Variable"; 
     48    if (!LuaIdRegEx.Matches(id)) return false; 
    6749 
    68     wxRegEx LuaVarName("\\W", wxRE_ADVANCED); 
    69     wxASSERT(LuaVarName.IsValid()); 
     50    // Is id a global reserved Lua identifier? 
     51    const wxRegEx LuaGlobalRegEx("^[_][A-Z]+$", wxRE_ADVANCED); 
     52    wxASSERT(LuaGlobalRegEx.IsValid()); 
    7053 
    71     wxString LuaVar=Varname; 
     54    if (LuaGlobalRegEx.Matches(id)) return false; 
    7255 
    73     LuaVarName.Replace(&LuaVar, "_"); // Replace all non compatible chars with underscore. 
     56    // Is id a reserved Lua keyword? 
     57    for (unsigned int kwNr=0; kwNr<NrOfKeywords; kwNr++) 
     58        if (id==Keywords[kwNr]) return false; 
    7459 
    75     // If variable is now compatible, return it. 
    76     if (CheckLuaVarCompat(LuaVar)) return LuaVar; 
     60    // All tests passed: id is a valid Lua identifier! 
     61    return true; 
     62} 
    7763 
    78     // Add a leading underscore which will make the variable compatible in any case. 
    79     LuaVar="_"+LuaVar; 
    8064 
    81     wxASSERT(CheckLuaVarCompat(LuaVar)); 
     65wxString CheckLuaIdentifier(const wxString& id) 
     66{ 
     67    // If id is valid, return it unchanged. 
     68    if (IsLuaIdentifier(id)) return id; 
    8269 
    83     return LuaVar; 
     70    // If id is empty, just come up with some valid identifier. 
     71    if (id=="") return "id"; 
     72 
     73    // Replace all non-alphanumeric characters with an underscore. 
     74    wxString NewId=id; 
     75 
     76    const wxRegEx NotAlphaNumRegEx("\\W", wxRE_ADVANCED); 
     77    wxASSERT(NotAlphaNumRegEx.IsValid()); 
     78 
     79    NotAlphaNumRegEx.Replace(&NewId, "_"); 
     80 
     81    if (IsLuaIdentifier(NewId)) return NewId; 
     82 
     83    // Still not good? Add a prefix. 
     84    NewId="id_"+NewId; 
     85 
     86    wxASSERT(IsLuaIdentifier(NewId)); 
     87    return NewId; 
    8488} 
  • cafu/trunk/CaWE/LuaAux.hpp

    r387 r388  
    3939 
    4040 
    41 /// Checks if a string is a valid Lua variable name. 
    42 /// @param Varname The variable name to be checked. 
    43 /// @return Whether the string is valid or not. 
    44 bool CheckLuaVarCompat(const wxString& Varname); 
     41/// Determines if the given string is a valid Lua identifier. 
     42/// 
     43/// @param id   The identifier name to check. 
     44/// @return Whether \c id is a valid Lua identifier. 
     45bool IsLuaIdentifier(const wxString& id); 
    4546 
    46 /// Converts a string into a valid Lua variable name by replacing 
    47 /// all special invalid characters with underscores and/or adding 
    48 /// underscores to the begining of the name to make it valid. 
    49 /// @param Varname The variable name to convert. 
    50 /// @return The converted valid Lua variable name. 
    51 wxString MakeLuaVarName(const wxString& Varname); 
     47 
     48/// Determines if the given string is a valid Lua identifier. 
     49/// If valid, \c id is returned unchanged. 
     50/// Otherwise, for the return value a variant of \c id is created that is valid. 
     51/// 
     52/// @param id   The identifier name to check. 
     53/// @return A valid Lua identifier derived from \c id. 
     54wxString CheckLuaIdentifier(const wxString& id); 
    5255 
    5356#endif 
  • cafu/trunk/CaWE/MapEntity.cpp

    r285 r388  
    409409            for (unsigned long Count=1; true; Count++) 
    410410            { 
    411                 const wxString UniqueValue=MakeLuaVarName(m_Class->GetName())+wxString::Format("_%03lu", Count); 
     411                const wxString UniqueValue=CheckLuaIdentifier(m_Class->GetName())+wxString::Format("_%03lu", Count); 
    412412                unsigned long  EntNr; 
    413413 
  • cafu/trunk/CaWE/ModelEditor/ObserverPattern.hpp

    r383 r388  
    8181        /// Notifies the observer that a skin has changed. 
    8282        /// @param Subject   The model document with the model in which the skin has changed. 
    83         /// @param AnimNr    The number of the skin that has changed. 
     83        /// @param SkinNr    The number of the skin that has changed. 
    8484        virtual void Notify_SkinChanged(SubjectT* Subject, unsigned int SkinNr) { } 
    8585