1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "ViewUtils.h"
21 
22 #include "Exceptions.h"
23 #include "Logger.h"
24 #include "Assets/EntityModelManager.h"
25 #include "Assets/EntityDefinitionFileSpec.h"
26 #include "Assets/ModelDefinition.h"
27 #include "Model/Game.h"
28 #include "Model/GameFactory.h"
29 #include "View/ChoosePathTypeDialog.h"
30 #include "View/MapDocument.h"
31 
32 namespace TrenchBroom {
33     namespace View {
safeGetModel(Assets::EntityModelManager & manager,const Assets::ModelSpecification & spec,Logger & logger)34         Assets::EntityModel* safeGetModel(Assets::EntityModelManager& manager, const Assets::ModelSpecification& spec, Logger& logger) {
35             try {
36                 return manager.model(spec.path);
37             } catch (const GameException& e) {
38                 logger.error(String(e.what()));
39                 return NULL;
40             }
41         }
42 
combineFlags(const size_t numFlags,const int newFlagValue,int & setFlags,int & mixedFlags)43         void combineFlags(const size_t numFlags, const int newFlagValue, int& setFlags, int& mixedFlags) {
44             for (size_t i = 0; i < numFlags; ++i) {
45                 const bool alreadySet = (newFlagValue & (1 << i)) != 0;
46                 const bool willBeSet = (setFlags & (1 << i)) != 0;
47                 if (alreadySet == willBeSet)
48                     continue;
49 
50                 setFlags &= ~(1 << i);
51                 mixedFlags |= (1 << i);
52             }
53         }
54 
loadDroppedFiles(MapDocumentWPtr document,wxWindow * parent,const wxArrayString & wxPaths)55         size_t loadDroppedFiles(MapDocumentWPtr document, wxWindow* parent, const wxArrayString& wxPaths) {
56             size_t count = 0;
57             count += loadTextureCollections(document, parent, wxPaths);
58             if (loadEntityDefinitionFile(document, parent, wxPaths) < wxPaths.size())
59                 ++count;
60             return count > 0;
61         }
62 
loadTextureCollection(MapDocumentWPtr document,wxWindow * parent,const wxString & wxPath)63         bool loadTextureCollection(MapDocumentWPtr document, wxWindow* parent, const wxString& wxPath) {
64             wxArrayString wxPaths;
65             wxPaths.Add(wxPath);
66             return loadTextureCollections(document, parent, wxPaths) == 1;
67         }
68 
containsLoadableTextureCollections(MapDocumentWPtr i_document,const wxArrayString & wxPaths)69         bool containsLoadableTextureCollections(MapDocumentWPtr i_document, const wxArrayString& wxPaths) {
70             MapDocumentSPtr document = lock(i_document);
71             Model::GamePtr game = document->game();
72             const Model::GameFactory& gameFactory = Model::GameFactory::instance();
73             const IO::Path gamePath = gameFactory.gamePath(game->gameName());
74             const IO::Path docPath = document->path();
75 
76             for (size_t i = 0; i < wxPaths.size(); ++i) {
77                 const wxString& wxPath = wxPaths[i];
78                 const IO::Path absPath(wxPath.ToStdString());
79                 if (!game->isTextureCollection(absPath))
80                     return true;
81             }
82 
83             return false;
84         }
85 
loadTextureCollections(MapDocumentWPtr i_document,wxWindow * parent,const wxArrayString & wxPaths)86         size_t loadTextureCollections(MapDocumentWPtr i_document, wxWindow* parent, const wxArrayString& wxPaths) {
87             if (wxPaths.empty())
88                 return 0;
89 
90             size_t count = 0;
91 
92             MapDocumentSPtr document = lock(i_document);
93             Model::GamePtr game = document->game();
94             const Model::GameFactory& gameFactory = Model::GameFactory::instance();
95             const IO::Path gamePath = gameFactory.gamePath(game->gameName());
96             const IO::Path docPath = document->path();
97 
98             Transaction transaction(document);
99             try {
100                 for (size_t i = 0; i < wxPaths.size(); ++i) {
101                     const wxString& wxPath = wxPaths[i];
102                     const IO::Path absPath(wxPath.ToStdString());
103                     if (game->isTextureCollection(absPath)) {
104                         ChoosePathTypeDialog pathDialog(wxGetTopLevelParent(parent), absPath, docPath, gamePath);
105                         if (pathDialog.ShowModal() == wxID_OK) {
106                             const IO::Path collectionPath = pathDialog.path();
107                             document->addTextureCollection(collectionPath.asString());
108                             ++count;
109                         }
110                     }
111                 }
112             } catch (...) {
113                 transaction.rollback();
114                 throw;
115             }
116 
117             return count;
118         }
119 
loadEntityDefinitionFile(MapDocumentWPtr document,wxWindow * parent,const wxString & wxPath)120         bool loadEntityDefinitionFile(MapDocumentWPtr document, wxWindow* parent, const wxString& wxPath) {
121             wxArrayString wxPaths;
122             wxPaths.Add(wxPath);
123             return loadEntityDefinitionFile(document, parent, wxPaths) == 0;
124         }
125 
containsLoadableEntityDefinitionFile(MapDocumentWPtr i_document,const wxArrayString & wxPaths)126         bool containsLoadableEntityDefinitionFile(MapDocumentWPtr i_document, const wxArrayString& wxPaths) {
127             MapDocumentSPtr document = lock(i_document);
128             Model::GamePtr game = document->game();
129             const Model::GameFactory& gameFactory = Model::GameFactory::instance();
130             const IO::Path gamePath = gameFactory.gamePath(game->gameName());
131             const IO::Path docPath = document->path();
132 
133             for (size_t i = 0; i < wxPaths.size(); ++i) {
134                 const wxString& wxPath = wxPaths[i];
135                 const IO::Path absPath(wxPath.ToStdString());
136                 if (game->isEntityDefinitionFile(absPath))
137                     return true;
138             }
139 
140             return false;
141         }
142 
loadEntityDefinitionFile(MapDocumentWPtr i_document,wxWindow * parent,const wxArrayString & wxPaths)143         size_t loadEntityDefinitionFile(MapDocumentWPtr i_document, wxWindow* parent, const wxArrayString& wxPaths) {
144             if (wxPaths.empty())
145                 return 0;
146 
147             MapDocumentSPtr document = lock(i_document);
148             Model::GamePtr game = document->game();
149             const Model::GameFactory& gameFactory = Model::GameFactory::instance();
150             const IO::Path gamePath = gameFactory.gamePath(game->gameName());
151             const IO::Path docPath = document->path();
152 
153             try {
154                 for (size_t i = 0; i < wxPaths.size(); ++i) {
155                     const wxString& wxPath = wxPaths[i];
156                     const IO::Path absPath(wxPath.ToStdString());
157                     if (game->isEntityDefinitionFile(absPath)) {
158                         ChoosePathTypeDialog pathDialog(wxGetTopLevelParent(parent), absPath, docPath, gamePath);
159                         if (pathDialog.ShowModal() == wxID_OK) {
160                             const Assets::EntityDefinitionFileSpec spec = Assets::EntityDefinitionFileSpec::external(pathDialog.path());
161                             document->setEntityDefinitionFile(spec);
162                             return i;
163                         }
164                     }
165                 }
166             } catch (...) {
167                 throw;
168             }
169 
170             return wxPaths.size();
171         }
172 
queryGroupName(wxWindow * parent)173         String queryGroupName(wxWindow* parent) {
174             while (true) {
175                 wxTextEntryDialog dialog(parent, "Enter a name", "Group Name", "Unnamed");
176                 dialog.CentreOnParent();
177                 if (dialog.ShowModal() != wxID_OK)
178                     return "";
179 
180                 const String name = dialog.GetValue().ToStdString();
181                 if (StringUtils::isBlank(name)) {
182                     if (wxMessageBox("Group names cannot be blank.", "Error", wxOK | wxCANCEL | wxCENTRE, parent) != wxOK)
183                         return "";
184                 } else if (StringUtils::containsCaseInsensitive(name, "\"")) {
185                     if (wxMessageBox("Group names cannot contain double quotes.", "Error", wxOK | wxCANCEL | wxCENTRE, parent) != wxOK)
186                         return "";
187                 } else {
188                     return name;
189                 }
190             }
191         }
192     }
193 }
194