Code::Blocks  SVN r11506
workspaceloader.cpp
Go to the documentation of this file.
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: 10874 $
6  * $Id: workspaceloader.cpp 10874 2016-07-16 20:00:28Z jenslody $
7  * $HeadURL: https://svn.code.sf.net/p/codeblocks/code/trunk/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 
38 {
39  //ctor
40 }
41 
43 {
44  //dtor
45 }
46 
48 inline LogManager* GetpMsg() { return Manager::Get()->GetLogManager(); }
49 
50 #include <wx/intl.h>
51 
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  // first loop to load projects
100  while (proj)
101  {
102  if (Manager::IsAppShuttingDown() || !GetpMan() || !GetpMsg())
103  return false;
104  wxString projectFilename = UnixFilename(cbC2U(proj->Attribute("filename")));
105  if (projectFilename.IsEmpty())
106  {
107  GetpMsg()->DebugLog(_T("'Project' node exists, but no filename?!?"));
108  }
109  else
110  {
111  wxFileName fname(projectFilename);
112  wxFileName wfname(filename);
113  fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
114  cbProject* pProject = GetpMan()->LoadProject(fname.GetFullPath(), false); // don't activate it
115  if (!pProject)
116  {
117  cbMessageBox(_("Unable to open ") + projectFilename,
118  _("Opening WorkSpace") + filename, wxICON_WARNING);
119  }
120  }
121  proj = proj->NextSiblingElement("Project");
122  }
123 
124  // second loop to setup dependencies
125  proj = wksp->FirstChildElement("Project");
126  while (proj)
127  {
128  cbProject* thisprj = nullptr;
129  wxString projectFilename = UnixFilename(cbC2U(proj->Attribute("filename")));
130  if (projectFilename.IsEmpty())
131  {
132  GetpMsg()->DebugLog(_T("'Project' node exists, but no filename?!?"));
133  thisprj = nullptr;
134  }
135  else
136  {
137  wxFileName fname(projectFilename);
138  wxFileName wfname(filename);
139  fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
140  thisprj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
141  }
142 
143  if (thisprj)
144  {
145  TiXmlElement* dep = proj->FirstChildElement("Depends");
146  while (dep)
147  {
148  wxFileName fname( UnixFilename(cbC2U(dep->Attribute("filename"))) );
149  wxFileName wfname(filename);
150  fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
151  cbProject* depprj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
152  if (depprj)
153  Manager::Get()->GetProjectManager()->AddProjectDependency(thisprj, depprj);
154  dep = dep->NextSiblingElement("Depends");
155  }
156  }
157  proj = proj->NextSiblingElement("Project");
158  }
159 
160  return true;
161 }
162 
163 bool WorkspaceLoader::Save(const wxString& title, const wxString& filename)
164 {
165  const char* ROOT_TAG = "CodeBlocks_workspace_file";
166 
167  TiXmlDocument doc;
168  doc.SetCondenseWhiteSpace(false);
169  doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
170  TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
171  if (!rootnode)
172  return false;
173 
174  TiXmlElement* wksp = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("Workspace")));
175  wksp->SetAttribute("title", cbU2C(title));
176 
177  ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
178  for (unsigned int i = 0; i < arr->GetCount(); ++i)
179  {
180  cbProject* prj = arr->Item(i);
181 
182  wxFileName wfname(filename);
183  wxFileName fname(prj->GetFilename());
184  fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
185 
186  TiXmlElement* node = static_cast<TiXmlElement*>(wksp->InsertEndChild(TiXmlElement("Project")));
187  node->SetAttribute("filename", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
188 
189  const ProjectsArray* deps = Manager::Get()->GetProjectManager()->GetDependenciesForProject(prj);
190  if (deps && deps->GetCount())
191  {
192  for (size_t j = 0; j < deps->GetCount(); ++j)
193  {
194  prj = deps->Item(j);
195  fname.Assign(prj->GetFilename());
196  fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
197  TiXmlElement* dnode = static_cast<TiXmlElement*>(node->InsertEndChild(TiXmlElement("Depends")));
198  dnode->SetAttribute("filename", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
199  }
200  }
201  }
202  return cbSaveTinyXMLDocument(&doc, filename);
203 }
204 
206 {
207  const char* ROOT_TAG = "CodeBlocks_workspace_layout_file";
208 
209  TiXmlDocument doc;
210  doc.SetCondenseWhiteSpace(false);
211  doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
212  TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
213  if (!rootnode)
214  return false; // Failed creating the root node of the workspace layout XML file?!
215 
216  rootnode->InsertEndChild(TiXmlElement("FileVersion"));
217  rootnode->FirstChildElement("FileVersion")->SetAttribute("major", WORKSPACE_LAYOUT_FILE_VERSION_MAJOR);
218  rootnode->FirstChildElement("FileVersion")->SetAttribute("minor", WORKSPACE_LAYOUT_FILE_VERSION_MINOR);
219 
220  // active project
222  if (!pm)
223  return false; // Could not access ProjectManager?!
224 
225  if (const cbProject *project = pm->GetActiveProject())
226  {
227  TiXmlElement *el =
228  static_cast<TiXmlElement*>(
229  rootnode->InsertEndChild( TiXmlElement("ActiveProject") ) );
230  wxFileName wfname(filename);
231  wxFileName fname( project->GetFilename() );
232  fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
233  el->SetAttribute("path", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
234  }
235  // else Workspace has no active project?!
236 
237  // preferred build target
238  if (const cbWorkspace* wsp = pm->GetWorkspace() )
239  {
240  const wxString preferredTarget = wsp->GetPreferredTarget();
241  if ( ! preferredTarget.IsEmpty() )
242  {
243  TiXmlElement* el =
244  static_cast<TiXmlElement*>(
245  rootnode->InsertEndChild( TiXmlElement("PreferredTarget") ) );
246  el->SetAttribute("name", cbU2C(preferredTarget) );
247  }
248  // else Project has not preferred target.
249  }
250  // else No workspace present to save.
251 
252  if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/enable_editor_layout"), false))
253  {
254  TiXmlElement *el =
255  static_cast<TiXmlElement*>(
256  rootnode->InsertEndChild( TiXmlElement("EditorTabsLayout") ) );
257  el->SetAttribute("layout", cbU2C( Manager::Get()->GetEditorManager()->GetNotebook()->SavePerspective() ));
258  }
259  // else ?!
260 
261  return cbSaveTinyXMLDocument(&doc, filename);
262 }
263 
265 {
266  TiXmlDocument doc;
267  if ( ! TinyXML::LoadDocument(filename, &doc) )
268  return false; // Can't load XML file?!
269 
270  if ( ! GetpMan() || ! GetpMsg() )
271  return false; // GetpMan or GetpMsg returns NULL?!
272 
273  TiXmlElement* root = doc.FirstChildElement("CodeBlocks_workspace_layout_file");
274  if (!root)
275  {
276  GetpMsg()->DebugLog(_T("Unable to load Code::Blocks workspace layout file: File is invalid."));
277  return false;
278  }
279 
280  int major = 0;
281  int minor = 0;
282 
283  TiXmlElement* version = root->FirstChildElement("FileVersion");
284 
285  // don't show messages if we 're running a batch build (i.e. no gui)
286  if (!Manager::IsBatchBuild() && version)
287  {
288  version->QueryIntAttribute("major", &major);
289  version->QueryIntAttribute("minor", &minor);
290 
292  {
293  GetpMsg()->DebugLog(F(_T("Workspace layout file version is > %d.%d. Trying to load..."), WORKSPACE_LAYOUT_FILE_VERSION_MAJOR, WORKSPACE_LAYOUT_FILE_VERSION_MINOR));
294  AnnoyingDialog dlg(_("Workspace layout file format is newer/unknown"),
295  F(_("This workspace layout file was saved with a newer version of Code::Blocks.\n"
296  "Will try to load, but you might see unexpected results.\n"
297  "In this case close the workspace, delete %s and reopen the workspace."),filename.wx_str()),
300  dlg.ShowModal();
301  }
302  else
303  {
304  // use one message for all changes
305  wxString msg;
306  wxString warn_msg;
307 
308  if (major == 0 && minor == 0)
309  {
310  msg << _("0.0 (unversioned) to 1.0:\n");
311  msg << _(" * save editor-pane layout and order.\n");
312  msg << _("\n");
313  }
314 
315  if (!msg.IsEmpty())
316  {
317  msg.Prepend(wxString::Format(_("Workspace layout file format is older (%d.%d) than the current format (%d.%d).\n"
318  "The file will automatically be upgraded on close.\n"
319  "But please read the following list of changes, as some of them\n"
320  "might not automatically convert existing (old) settings.\n"
321  "If you don't understand what a change means, you probably don't\n"
322  "use that feature so you don't have to worry about it.\n\n"
323  "List of changes:\n"),
324  major,
325  minor,
328  AnnoyingDialog dlg(_("Workspace layout file format changed"),
329  msg,
332  dlg.ShowModal();
333  }
334 
335  if (!warn_msg.IsEmpty())
336  {
337  warn_msg.Prepend(_("!!! WARNING !!!\n\n"));
338  AnnoyingDialog dlg(_("Workspace layout file upgrade warning"),
339  warn_msg,
342  dlg.ShowModal();
343  }
344  }
345  }
346 
347  // active project
348  if (TiXmlElement* el = root->FirstChildElement("ActiveProject"))
349  {
350  wxFileName fname = cbC2U( el->Attribute("path") );
351  wxFileName wfname(filename);
352  fname.MakeAbsolute( wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) );
353  cbProject *project = GetpMan()->IsOpen( fname.GetFullPath() );
354  if (project)
355  {
356  GetpMan()->SetProject(project);
357  Manager::Get()->GetLogManager()->DebugLog(F(_T("Project %s has been activated."), fname.GetFullPath().wx_str()));
358  }
359  else
360  Manager::Get()->GetLogManager()->DebugLog(F(_T("Could not activate project: %s"), fname.GetFullPath().wx_str()));
361  }
362  // else XML element 'ActiveProject' not found?!
363 
364  // preferred build target
365  if (TiXmlElement* el = root->FirstChildElement("PreferredTarget"))
366  {
367  const wxString name = cbC2U(el->Attribute("name"));
368  cbWorkspace *wsp = GetpMan()->GetWorkspace();
369  if (wsp)
370  wsp->SetPreferredTarget(name);
371  }
372  // else XML element 'PreferredTarget' not found?!
373 
374  if ( (major >= 1)
375  && (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/enable_editor_layout"), false)) )
376  {
377  if (TiXmlElement* el = root->FirstChildElement("EditorTabsLayout"))
378  {
379  if (el->Attribute("layout"))
380  Manager::Get()->GetEditorManager()->GetNotebook()->LoadPerspective(cbC2U(el->Attribute("layout")));
381  }
382  }
383 
384  return true;
385 }
wxString F(const wxChar *msg,...)
sprintf-like function
Definition: logmanager.h:20
bool LoadPerspective(const wxString &layout, bool mergeLayouts=false)
Loads serialized notebook layout.
Definition: cbauibook.cpp:803
#define wxICON_WARNING
cbProject * IsOpen(const wxString &filename)
Check if a project is open based on the project&#39;s filename.
const int version
static Manager * Get()
Use Manager::Get() to get a pointer to its instance Manager::Get() is guaranteed to never return an i...
Definition: manager.cpp:182
static bool IsAppShuttingDown()
Definition: manager.cpp:333
#define WORKSPACE_LAYOUT_FILE_VERSION_MAJOR
bool SaveLayout(const wxString &filename)
#define _T(string)
bool LoadDocument(const wxString &filename, TiXmlDocument *doc)
Definition: tinywxuni.cpp:13
~WorkspaceLoader() override
ProjectManager * GetpMan()
EditorManager * GetEditorManager() const
Definition: manager.cpp:434
DLLIMPORT const wxWX2MBbuf cbU2C(const wxString &str)
Return multibyte (C string) representation of the string.
Definition: globals.cpp:743
ProjectManager * GetProjectManager() const
Functions returning pointers to the respective sub-manager instances.
Definition: manager.cpp:429
bool MakeRelativeTo(const wxString &pathBase=wxEmptyString, wxPathFormat format=wxPATH_NATIVE)
DLLIMPORT wxString UnixFilename(const wxString &filename, wxPathFormat format=wxPATH_NATIVE)
Definition: globals.cpp:228
Represents a Code::Blocks project.
Definition: cbproject.h:96
virtual const wxString & GetFilename() const
cbProject * LoadProject(const wxString &filename, bool activateIt=true)
Load a project from disk.
DLLIMPORT wxString cbC2U(const char *str)
Return str as a proper unicode-compatible string.
Definition: globals.cpp:733
A workspace class.
Definition: cbworkspace.h:26
static bool IsBatchBuild()
Definition: manager.h:66
LogManager * GetLogManager() const
Definition: manager.cpp:439
cbProject * GetActiveProject()
Retrieve the active project.
void SetPreferredTarget(const wxString &target)
Set the preferred target for this workspace.
const wxStringCharType * wx_str() const
bool AddProjectDependency(cbProject *base, cbProject *dependsOn)
Adds a project as a dependency of another project.
bool Save(const wxString &title, const wxString &filename) override
const wxString & _(const wxString &string)
bool Open(const wxString &filename, wxString &Title) override
wxArtID wxART_WARNING
cbWorkspace * GetWorkspace()
Get the current workspace filename.
void SetProject(cbProject *project, bool refresh=true)
Set the active project.
bool IsEmpty() const
wxString GetPath(int flags=wxPATH_GET_VOLUME, wxPathFormat format=wxPATH_NATIVE) const
The entry point singleton for working with projects.
cbAuiNotebook * GetNotebook()
Definition: editormanager.h:73
void DebugLog(const wxString &msg, Logger::level lv=Logger::info)
Definition: logmanager.h:146
int ShowModal() override
wxString & Prepend(const wxString &str)
LogManager * GetpMsg()
ProjectsArray * GetProjects()
Retrieve an array of all the opened projects.
bool MakeAbsolute(const wxString &cwd=wxEmptyString, wxPathFormat format=wxPATH_NATIVE)
bool LoadLayout(const wxString &filename)
wxString GetFullPath(wxPathFormat format=wxPATH_NATIVE) const
DLLIMPORT bool cbSaveTinyXMLDocument(TiXmlDocument *doc, const wxString &filename)
Saves a TinyXML document correctly, even if the path contains unicode characters. ...
Definition: globals.cpp:727
wxArtID wxART_INFORMATION
static wxString Format(const wxString &format,...)
Dialog that contains a "Don&#39;t annoy me" checkbox.
#define WORKSPACE_LAYOUT_FILE_VERSION_MINOR
const ProjectsArray * GetDependenciesForProject(cbProject *base)
Get the array of projects base depends on.
DLLIMPORT int cbMessageBox(const wxString &message, const wxString &caption=wxEmptyString, int style=wxOK, wxWindow *parent=NULL, int x=-1, int y=-1)
wxMessageBox wrapper.
Definition: globals.cpp:1395