1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3 * http://www.gnu.org/licenses/lgpl-3.0.html
4 *
5 * $Revision: 11706 $
6 * $Id: workspaceloader.cpp 11706 2019-05-25 21:39:45Z bluehazzard $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/workspaceloader.cpp $
8 */
9
10 #include "sdk_precomp.h"
11
12 #ifndef CB_PRECOMP
13 #include <wx/confbase.h>
14 #include <wx/fileconf.h>
15 #include <wx/intl.h>
16 #include <wx/string.h>
17
18 #include "workspaceloader.h"
19
20 #include "manager.h"
21 #include "configmanager.h"
22 #include "projectmanager.h"
23 #include "logmanager.h"
24 #include "cbproject.h"
25 #include "globals.h"
26 #include "cbworkspace.h"
27 #include "editormanager.h"
28 #include "cbauibook.h"
29 #endif
30
31
32
33 #include "annoyingdialog.h"
34 #include <tinyxml.h>
35 #include "tinywxuni.h"
36
WorkspaceLoader()37 WorkspaceLoader::WorkspaceLoader()
38 {
39 //ctor
40 }
41
~WorkspaceLoader()42 WorkspaceLoader::~WorkspaceLoader()
43 {
44 //dtor
45 }
46
GetpMan()47 inline ProjectManager* GetpMan() { return Manager::Get()->GetProjectManager(); }
GetpMsg()48 inline LogManager* GetpMsg() { return Manager::Get()->GetLogManager(); }
49
50 #include <wx/intl.h>
51
Open(const wxString & filename,wxString & Title)52 bool WorkspaceLoader::Open(const wxString& filename, wxString& Title)
53 {
54 TiXmlDocument doc;
55 if (!TinyXML::LoadDocument(filename, &doc))
56 return false;
57
58 // ProjectManager* pMan = Manager::Get()->GetProjectManager();
59 // LogManager* pMsg = Manager::Get()->GetLogManager();
60
61 if (!GetpMan() || !GetpMsg())
62 return false;
63
64 // BUG: Race condition. to be fixed by Rick.
65 // If I click close AFTER pMan and pMsg are calculated,
66 // I get a segfault.
67 // I modified classes projectmanager and logmanager,
68 // so that when self==NULL, they do nothing
69 // (constructors, destructors and static functions excempted from this)
70 // This way, we'll use the *manager::Get() functions to check for nulls.
71
72 TiXmlElement* root = doc.FirstChildElement("CodeBlocks_workspace_file");
73 if (!root)
74 {
75 // old tag
76 root = doc.FirstChildElement("Code::Blocks_workspace_file");
77 if (!root)
78 {
79 GetpMsg()->DebugLog(_T("Not a valid Code::Blocks workspace file..."));
80 return false;
81 }
82 }
83 TiXmlElement* wksp = root->FirstChildElement("Workspace");
84 if (!wksp)
85 {
86 GetpMsg()->DebugLog(_T("No 'Workspace' element in file..."));
87 return false;
88 }
89
90 Title = cbC2U(wksp->Attribute("title")); // Conversion to unicode is automatic (see wxString::operator= )
91
92 TiXmlElement* proj = wksp->FirstChildElement("Project");
93 if (!proj)
94 {
95 GetpMsg()->DebugLog(_T("Workspace file contains no projects..."));
96 return false;
97 }
98
99 int failedProjects = 0;
100 // first loop to load projects
101 while (proj)
102 {
103 if (Manager::IsAppShuttingDown() || !GetpMan() || !GetpMsg())
104 return false;
105 wxString projectFilename = UnixFilename(cbC2U(proj->Attribute("filename")));
106 if (projectFilename.IsEmpty())
107 {
108 GetpMsg()->DebugLog(_T("'Project' node exists, but no filename?!?"));
109 }
110 else
111 {
112 wxFileName fname(projectFilename);
113 wxFileName wfname(filename);
114 fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
115 cbProject* pProject = GetpMan()->LoadProject(fname.GetFullPath(), false); // don't activate it
116 if (!pProject)
117 {
118 GetpMsg()->LogError(wxString::Format(_("Unable to open \"%s\" during opening workspace \"%s\" "),
119 projectFilename.c_str(),
120 filename.c_str()));
121 failedProjects++;
122 }
123 }
124 proj = proj->NextSiblingElement("Project");
125 }
126
127 // second loop to setup dependencies
128 proj = wksp->FirstChildElement("Project");
129 while (proj)
130 {
131 cbProject* thisprj = nullptr;
132 wxString projectFilename = UnixFilename(cbC2U(proj->Attribute("filename")));
133 if (projectFilename.IsEmpty())
134 {
135 GetpMsg()->DebugLog(_T("'Project' node exists, but no filename?!?"));
136 thisprj = nullptr;
137 }
138 else
139 {
140 wxFileName fname(projectFilename);
141 wxFileName wfname(filename);
142 fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
143 thisprj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
144 }
145
146 if (thisprj)
147 {
148 TiXmlElement* dep = proj->FirstChildElement("Depends");
149 while (dep)
150 {
151 wxFileName fname( UnixFilename(cbC2U(dep->Attribute("filename"))) );
152 wxFileName wfname(filename);
153 fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
154 cbProject* depprj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
155 if (depprj)
156 Manager::Get()->GetProjectManager()->AddProjectDependency(thisprj, depprj);
157 dep = dep->NextSiblingElement("Depends");
158 }
159 }
160 proj = proj->NextSiblingElement("Project");
161 }
162
163 if (failedProjects > 0)
164 {
165 cbMessageBox(wxString::Format(_("%d projects could not be loaded.\nPlease see the Log window for details"), failedProjects),
166 _("Opening WorkSpace"), wxICON_WARNING);
167 }
168
169 return true;
170 }
171
Save(const wxString & title,const wxString & filename)172 bool WorkspaceLoader::Save(const wxString& title, const wxString& filename)
173 {
174 const char* ROOT_TAG = "CodeBlocks_workspace_file";
175
176 TiXmlDocument doc;
177 doc.SetCondenseWhiteSpace(false);
178 doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
179 TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
180 if (!rootnode)
181 return false;
182
183 TiXmlElement* wksp = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("Workspace")));
184 wksp->SetAttribute("title", cbU2C(title));
185
186 ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
187 for (unsigned int i = 0; i < arr->GetCount(); ++i)
188 {
189 cbProject* prj = arr->Item(i);
190
191 wxFileName wfname(filename);
192 wxFileName fname(prj->GetFilename());
193 fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
194
195 TiXmlElement* node = static_cast<TiXmlElement*>(wksp->InsertEndChild(TiXmlElement("Project")));
196 node->SetAttribute("filename", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
197
198 const ProjectsArray* deps = Manager::Get()->GetProjectManager()->GetDependenciesForProject(prj);
199 if (deps && deps->GetCount())
200 {
201 for (size_t j = 0; j < deps->GetCount(); ++j)
202 {
203 prj = deps->Item(j);
204 fname.Assign(prj->GetFilename());
205 fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
206 TiXmlElement* dnode = static_cast<TiXmlElement*>(node->InsertEndChild(TiXmlElement("Depends")));
207 dnode->SetAttribute("filename", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
208 }
209 }
210 }
211 return cbSaveTinyXMLDocument(&doc, filename);
212 }
213
SaveLayout(const wxString & filename)214 bool WorkspaceLoader::SaveLayout(const wxString& filename)
215 {
216 const char* ROOT_TAG = "CodeBlocks_workspace_layout_file";
217
218 TiXmlDocument doc;
219 doc.SetCondenseWhiteSpace(false);
220 doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
221 TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
222 if (!rootnode)
223 return false; // Failed creating the root node of the workspace layout XML file?!
224
225 rootnode->InsertEndChild(TiXmlElement("FileVersion"));
226 rootnode->FirstChildElement("FileVersion")->SetAttribute("major", WORKSPACE_LAYOUT_FILE_VERSION_MAJOR);
227 rootnode->FirstChildElement("FileVersion")->SetAttribute("minor", WORKSPACE_LAYOUT_FILE_VERSION_MINOR);
228
229 // active project
230 ProjectManager *pm = Manager::Get()->GetProjectManager();
231 if (!pm)
232 return false; // Could not access ProjectManager?!
233
234 if (const cbProject *project = pm->GetActiveProject())
235 {
236 TiXmlElement *el =
237 static_cast<TiXmlElement*>(
238 rootnode->InsertEndChild( TiXmlElement("ActiveProject") ) );
239 wxFileName wfname(filename);
240 wxFileName fname( project->GetFilename() );
241 fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
242 el->SetAttribute("path", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
243 }
244 // else Workspace has no active project?!
245
246 // preferred build target
247 if (const cbWorkspace* wsp = pm->GetWorkspace() )
248 {
249 const wxString preferredTarget = wsp->GetPreferredTarget();
250 if ( ! preferredTarget.IsEmpty() )
251 {
252 TiXmlElement* el =
253 static_cast<TiXmlElement*>(
254 rootnode->InsertEndChild( TiXmlElement("PreferredTarget") ) );
255 el->SetAttribute("name", cbU2C(preferredTarget) );
256 }
257 // else Project has not preferred target.
258 }
259 // else No workspace present to save.
260
261 if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/enable_editor_layout"), false))
262 {
263 TiXmlElement *el =
264 static_cast<TiXmlElement*>(
265 rootnode->InsertEndChild( TiXmlElement("EditorTabsLayout") ) );
266 el->SetAttribute("layout", cbU2C( Manager::Get()->GetEditorManager()->GetNotebook()->SavePerspective() ));
267 }
268 // else ?!
269
270 return cbSaveTinyXMLDocument(&doc, filename);
271 }
272
LoadLayout(const wxString & filename)273 bool WorkspaceLoader::LoadLayout(const wxString& filename)
274 {
275 TiXmlDocument doc;
276 if ( ! TinyXML::LoadDocument(filename, &doc) )
277 return false; // Can't load XML file?!
278
279 if ( ! GetpMan() || ! GetpMsg() )
280 return false; // GetpMan or GetpMsg returns NULL?!
281
282 TiXmlElement* root = doc.FirstChildElement("CodeBlocks_workspace_layout_file");
283 if (!root)
284 {
285 GetpMsg()->DebugLog(_T("Unable to load Code::Blocks workspace layout file: File is invalid."));
286 return false;
287 }
288
289 int major = 0;
290 int minor = 0;
291
292 TiXmlElement* version = root->FirstChildElement("FileVersion");
293
294 // don't show messages if we 're running a batch build (i.e. no gui)
295 if (!Manager::IsBatchBuild() && version)
296 {
297 version->QueryIntAttribute("major", &major);
298 version->QueryIntAttribute("minor", &minor);
299
300 if (major >= WORKSPACE_LAYOUT_FILE_VERSION_MAJOR && minor > WORKSPACE_LAYOUT_FILE_VERSION_MINOR)
301 {
302 GetpMsg()->DebugLog(F(_T("Workspace layout file version is > %d.%d. Trying to load..."), WORKSPACE_LAYOUT_FILE_VERSION_MAJOR, WORKSPACE_LAYOUT_FILE_VERSION_MINOR));
303 AnnoyingDialog dlg(_("Workspace layout file format is newer/unknown"),
304 F(_("This workspace layout file was saved with a newer version of Code::Blocks.\n"
305 "Will try to load, but you might see unexpected results.\n"
306 "In this case close the workspace, delete %s and reopen the workspace."),filename.wx_str()),
307 wxART_WARNING,
308 AnnoyingDialog::OK);
309 dlg.ShowModal();
310 }
311 else
312 {
313 // use one message for all changes
314 wxString msg;
315 wxString warn_msg;
316
317 if (major == 0 && minor == 0)
318 {
319 msg << _("0.0 (unversioned) to 1.0:\n");
320 msg << _(" * save editor-pane layout and order.\n");
321 msg << _("\n");
322 }
323
324 if (!msg.IsEmpty())
325 {
326 msg.Prepend(wxString::Format(_("Workspace layout file format is older (%d.%d) than the current format (%d.%d).\n"
327 "The file will automatically be upgraded on close.\n"
328 "But please read the following list of changes, as some of them\n"
329 "might not automatically convert existing (old) settings.\n"
330 "If you don't understand what a change means, you probably don't\n"
331 "use that feature so you don't have to worry about it.\n\n"
332 "List of changes:\n"),
333 major,
334 minor,
335 WORKSPACE_LAYOUT_FILE_VERSION_MAJOR,
336 WORKSPACE_LAYOUT_FILE_VERSION_MINOR));
337 AnnoyingDialog dlg(_("Workspace layout file format changed"),
338 msg,
339 wxART_INFORMATION,
340 AnnoyingDialog::OK);
341 dlg.ShowModal();
342 }
343
344 if (!warn_msg.IsEmpty())
345 {
346 warn_msg.Prepend(_("!!! WARNING !!!\n\n"));
347 AnnoyingDialog dlg(_("Workspace layout file upgrade warning"),
348 warn_msg,
349 wxART_WARNING,
350 AnnoyingDialog::OK);
351 dlg.ShowModal();
352 }
353 }
354 }
355
356 // active project
357 if (TiXmlElement* el = root->FirstChildElement("ActiveProject"))
358 {
359 wxFileName fname = cbC2U( el->Attribute("path") );
360 wxFileName wfname(filename);
361 fname.MakeAbsolute( wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) );
362 cbProject *project = GetpMan()->IsOpen( fname.GetFullPath() );
363 if (project)
364 {
365 GetpMan()->SetProject(project);
366 Manager::Get()->GetLogManager()->DebugLog(F(_T("Project %s has been activated."), fname.GetFullPath().wx_str()));
367 }
368 else
369 Manager::Get()->GetLogManager()->DebugLog(F(_T("Could not activate project: %s"), fname.GetFullPath().wx_str()));
370 }
371 // else XML element 'ActiveProject' not found?!
372
373 // preferred build target
374 if (TiXmlElement* el = root->FirstChildElement("PreferredTarget"))
375 {
376 const wxString name = cbC2U(el->Attribute("name"));
377 cbWorkspace *wsp = GetpMan()->GetWorkspace();
378 if (wsp)
379 wsp->SetPreferredTarget(name);
380 }
381 // else XML element 'PreferredTarget' not found?!
382
383 if ( (major >= 1)
384 && (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/enable_editor_layout"), false)) )
385 {
386 if (TiXmlElement* el = root->FirstChildElement("EditorTabsLayout"))
387 {
388 if (el->Attribute("layout"))
389 Manager::Get()->GetEditorManager()->GetNotebook()->LoadPerspective(cbC2U(el->Attribute("layout")));
390 }
391 }
392
393 return true;
394 }
395