1 /*
2 * This file is part of wxSmith plugin for Code::Blocks Studio
3 * Copyright (C) 2006-2007 Bartlomiej Swiecki
4 *
5 * wxSmith is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * wxSmith is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with wxSmith. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * $Revision: 10874 $
19 * $Id: wxsversionconverter.cpp 10874 2016-07-16 20:00:28Z jenslody $
20 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/contrib/wxSmith/wxsversionconverter.cpp $
21 */
22
23 #include "wxsversionconverter.h"
24 #include "wxsproject.h"
25 #include "wxscoder.h"
26
27 #include <globals.h>
28 #include <wx/string.h>
29 #include <tinywxuni.h>
30
31 // TODO: This is same as in wxsproject.h, make one set instead of two
32 namespace
33 {
34 const int CurrentVersion = 1;
35 const char* CurrentVersionStr = "1";
36 }
37
Get()38 const wxsVersionConverter& wxsVersionConverter::Get()
39 {
40 static wxsVersionConverter Singleton;
41 return Singleton;
42 }
43
DetectOldConfig(TiXmlElement * Node,cb_unused wxsProject * Project) const44 bool wxsVersionConverter::DetectOldConfig(TiXmlElement* Node,cb_unused wxsProject* Project) const
45 {
46 // New wxSmith style has resources put into <resources> node
47 // and configuration to <gui> node
48 if ( Node->FirstChildElement("dialog") ) return true;
49 if ( Node->FirstChildElement("frame") ) return true;
50 if ( Node->FirstChildElement("panel") ) return true;
51 if ( Node->FirstChildElement("configuration") ) return true;
52
53 return false;
54 }
55
ConvertFromOldConfig(TiXmlElement * ConfigNode,TiXmlDocument * Doc,wxsProject * Project) const56 TiXmlElement* wxsVersionConverter::ConvertFromOldConfig(TiXmlElement* ConfigNode,TiXmlDocument* Doc,wxsProject* Project) const
57 {
58 if ( cbMessageBox(_("This project uses old wxSmith configuration format\n"
59 "Would you like me to convert to new one?\n"),
60 _("wxSmith: Converting from old format"),
61 wxYES_NO) != wxID_YES ) return 0;
62
63 TiXmlElement* NewConfig = Doc->InsertEndChild(TiXmlElement("wxSmith"))->ToElement();
64 TiXmlElement* Resources = NewConfig->InsertEndChild(TiXmlElement("resources"))->ToElement();
65 NewConfig->SetAttribute("version",CurrentVersionStr);
66 for ( TiXmlElement* Node = ConfigNode->FirstChildElement(); Node; Node = Node->NextSiblingElement() )
67 {
68 wxString NodeName = cbC2U(Node->Value());
69 if ( NodeName == _T("configuration") )
70 {
71 const char* AppSrc = Node->Attribute("app_src_file");
72 const char* Main = Node->Attribute("main_resource");
73 const char* InitAll = Node->Attribute("init_all_handlers");
74
75 if ( AppSrc )
76 {
77 TiXmlElement* GUINode = NewConfig->InsertEndChild(TiXmlElement("gui"))->ToElement();
78 GUINode->SetAttribute("name","wxWidgets");
79 GUINode->SetAttribute("src",AppSrc);
80 GUINode->SetAttribute("main",Main?Main:"");
81 GUINode->SetAttribute("init_handlers",InitAll?InitAll:"necessary");
82 GUINode->SetAttribute("language","CPP");
83 }
84 }
85 else
86 {
87 if ( NodeName == _T("dialog") ||
88 NodeName == _T("frame") ||
89 NodeName == _T("panel") )
90 {
91 const char* Wxs = Node->Attribute("wxs_file");
92 const char* Class = Node->Attribute("class");
93 const char* Src = Node->Attribute("src_file");
94 const char* Hdr = Node->Attribute("header_file");
95 const char* Xrc = Node->Attribute("xrc_file");
96 const char* Mode = Node->Attribute("edit_mode");
97
98 if ( Wxs && Class && Src && Hdr && Mode )
99 {
100 if ( cbC2U(Mode) == _T("Source") ) Xrc = 0;
101 TiXmlElement* Res = Resources->InsertEndChild(TiXmlElement(
102 NodeName == _T("dialog") ? "wxDialog" :
103 NodeName == _T("frame") ? "wxFrame" :
104 "wxPanel" ))->ToElement();
105
106 Res->SetAttribute("wxs",cbU2C(_T("wxsmith/")+cbC2U(Wxs)));
107 Res->SetAttribute("src",Src);
108 Res->SetAttribute("hdr",Hdr);
109 if ( Xrc ) Res->SetAttribute("xrc",Xrc);
110 Res->SetAttribute("name",Class);
111 Res->SetAttribute("language","CPP");
112
113 ConvertOldWxsFile(Project->GetProjectPath()+_T("wxsmith/")+cbC2U(Wxs),Xrc!=0);
114 AdoptOldSourceFile(Project->GetProjectPath()+cbC2U(Src),cbC2U(Class));
115 }
116 }
117 }
118 }
119 return NewConfig;
120 }
121
ConvertOldWxsFile(const wxString & FileName,bool UsingXrc) const122 void wxsVersionConverter::ConvertOldWxsFile(const wxString& FileName,bool UsingXrc) const
123 {
124 TiXmlDocument Doc;
125 if ( !TinyXML::LoadDocument(FileName,&Doc) ) return;
126
127 TiXmlElement* Smith = Doc.FirstChildElement("resource");
128 if ( Smith )
129 {
130 Smith->SetValue("wxsmith");
131 }
132
133 if ( UsingXrc && Smith )
134 {
135 // Need to extract extra data from any resource's item and put into <resource_extra> node
136 TiXmlElement* Resource = Smith->FirstChildElement("object");
137 TiXmlElement* Extra = Smith->InsertEndChild(TiXmlElement("resource_extra"))->ToElement();
138 GatherExtraFromOldResourceReq(Resource,Extra,true);
139 }
140
141 TinyXML::SaveDocument(FileName,&Doc);
142 }
143
GatherExtraFromOldResourceReq(TiXmlElement * Object,TiXmlElement * Extra,bool Root) const144 void wxsVersionConverter::GatherExtraFromOldResourceReq(TiXmlElement* Object,TiXmlElement* Extra,bool Root) const
145 {
146 // The only extra information in old wxSmith was:
147 // * variable / member attributes of <object> node
148 // * event handlers enteries
149 // These fields are extracted and put into wxs file
150 if ( !strcmp(Object->Value(),"object") )
151 {
152 if ( Object->Attribute("class") && (Root || Object->Attribute("name")) )
153 {
154 TiXmlElement* ThisExtra = 0;
155
156 // Checking if we got variable name
157 if ( Object->Attribute("variable") && Object->Attribute("member") )
158 {
159 ThisExtra = Extra->InsertEndChild(TiXmlElement("object"))->ToElement();
160 ThisExtra->SetAttribute("variable",Object->Attribute("variable"));
161 ThisExtra->SetAttribute("member",Object->Attribute("member"));
162 }
163
164 // Checking for event handlers
165
166 for ( TiXmlElement* Handler = Object->FirstChildElement("handler"); Handler; Handler = Handler->NextSiblingElement("handler") )
167 {
168 if ( !ThisExtra )
169 {
170 ThisExtra = Extra->InsertEndChild(TiXmlElement("object"))->ToElement();
171 }
172 ThisExtra->InsertEndChild(*Handler);
173 }
174
175 if ( ThisExtra )
176 {
177 if ( Root )
178 {
179 ThisExtra->SetAttribute("root","1");
180 }
181 else
182 {
183 ThisExtra->SetAttribute("name",Object->Attribute("name"));
184 ThisExtra->SetAttribute("class",Object->Attribute("class"));
185 }
186 }
187 }
188 }
189
190 for ( TiXmlElement* Child = Object->FirstChildElement(); Child; Child = Child->NextSiblingElement() )
191 {
192 GatherExtraFromOldResourceReq(Child,Extra,false);
193 }
194 }
195
AdoptOldSourceFile(const wxString & FileName,const wxString & Class) const196 void wxsVersionConverter::AdoptOldSourceFile(const wxString& FileName,const wxString& Class) const
197 {
198 // Need to add two new sections: //(*InternalHeaders and //(*IdInit
199 // to do this //(*InternalHeaders will be added before any source code
200 // but after all #xxx directives
201 // //(*IdInit will be added just before //(*EventTable nearrest line
202 // before that section containing BEGIN_EVENT_TABLE
203 //
204 // This may not be tricky enough but I hope that not much people mess
205 // with code generated by wxSmith from templates ;)
206 //
207 // BTW we do not use wxsCodeMarks::Beg and wxsCodeMarks::End because that could
208 // cause some problems with future conversion of these marks
209 // (upgrade of old-wxSmith project will be done in two steps then,
210 // first - convertion to version 1 of new wxsmith and then upgrading to
211 // higher version so if convention of code marks will change, it will
212 // break the conversion chain)
213
214 bool IsInternalHeaders = wxsCoder::Get()->GetCode(
215 FileName,
216 _T("//(*InternalHeaders(") + Class + _T(")\n"),
217 _T("//*)"),
218 true,true).Length() != 0;
219
220 bool IsIdInit = wxsCoder::Get()->GetCode(
221 FileName,
222 _T("//(*IdInit(") + Class + _T(")\n"),
223 _T("//*)"),
224 true,true).Length() != 0;
225
226 if ( !IsInternalHeaders || !IsIdInit )
227 {
228 wxFontEncoding Encoding;
229 bool UseBOM;
230 wxString Content = wxsCoder::Get()->GetFullCode(FileName,Encoding,UseBOM);
231
232 int Pos = 0;
233 if ( !IsInternalHeaders )
234 {
235 while ( Pos<(int)Content.Len() && LineContainDirectivesOnly(Content,Pos) );
236
237 wxString AddInternalHeaders =
238 _T("//(*InternalHeaders(") + Class + _T(")\n")
239 _T("//*)\n")
240 _T("\n");
241
242 Content = Content.Mid(0,Pos) + AddInternalHeaders + Content.Mid(Pos);
243 }
244 else
245 {
246 Pos = Content.Find(_T("//(*InternalHeaders(") + Class + _T(")\n"));
247 int Shift = Content.Mid(Pos).Find(_T("//*)"));
248 if ( Shift != wxNOT_FOUND )
249 {
250 Pos += Shift;
251 }
252 }
253
254 if ( !IsIdInit )
255 {
256 int NewPos = Content.Find(_T("BEGIN_EVENT_TABLE(")+Class);
257 if ( NewPos!=wxNOT_FOUND )
258 {
259 Pos = NewPos;
260 }
261
262 // Switching to first character in this line
263 wxString Indent;
264 while ( Pos>0 &&
265 Content.GetChar(Pos-1)!=_T('\n') &&
266 Content.GetChar(Pos-1)!=_T('\r') )
267 {
268 wxChar Ch = Content.GetChar(Pos--);
269 Indent.Append((Ch==_T('\t'))?_T('\t'):_T(' '));
270 }
271
272 wxString AddIdInit =
273 Indent + _T("//(*IdInit(") + Class + _T(")\n") +
274 Indent + _T("//*)\n") +
275 Indent + _T("\n");
276
277 Content = Content.Mid(0,Pos) + AddIdInit + Content.Mid(Pos);
278 }
279 wxsCoder::Get()->PutFullCode(FileName,Content,Encoding,UseBOM);
280 }
281 }
282
LineContainDirectivesOnly(const wxString & Code,int & BeginPos) const283 bool wxsVersionConverter::LineContainDirectivesOnly(const wxString& Code,int& BeginPos) const
284 {
285 int Pos = BeginPos;
286
287 wxChar PreviousChar = _T('\0');
288
289 // Processing characters in this line
290 while ( Pos < (int)Code.Len() )
291 {
292 wxChar Ch = Code.GetChar(Pos);
293 if ( Ch==_T('/') && PreviousChar==_T('/') )
294 {
295 // We got // comment, skipping till the end of line
296 while ( ++Pos < (int)Code.Len() )
297 {
298 PreviousChar = Ch;
299 wxChar _Ch = Code.GetChar(Pos);
300 if ( _Ch==_T('\n') || _Ch==_T('\r') )
301 {
302 if ( PreviousChar == _T('\\') )
303 {
304 // Backslash removes EOL
305 if ( ++Pos < (int)Code.Len() )
306 {
307 PreviousChar = _Ch;
308 _Ch = Code.GetChar(Pos);
309 if ( (_Ch!=_T('\n') && _Ch==_T('\r')) || (_Ch==PreviousChar) )
310 {
311 // One-char EOL
312 --Pos;
313 }
314 }
315 }
316 else
317 {
318 // End-Of-Line and End-Of-Comment
319 while ( Pos<(int)Code.Len() )
320 {
321 wxChar __Ch = Code.GetChar(Pos);
322 if ( __Ch!=_T('\n') && __Ch!=_T('\r') ) break;
323 Pos++;
324 }
325
326 BeginPos = Pos;
327 return true;
328 }
329 }
330 }
331 // End of file approached
332 BeginPos = Pos;
333 return false;
334 }
335 else if ( Ch==_T('*') && PreviousChar==_T('/') )
336 {
337 // Starting multiline comment, we skip everything till */ sequence
338 while ( ++Pos < (int)Code.Len() )
339 {
340 PreviousChar = Ch;
341 wxChar _Ch = Code.GetChar(Pos);
342
343 if ( _Ch==_T('/') && PreviousChar==_T('*') )
344 {
345 // End of comment, breaking
346 break;
347 }
348 }
349
350 if ( Pos >= (int)Code.Len() )
351 {
352 // Jumping out to skip another Pos++
353 break;
354 }
355 }
356 else if ( PreviousChar==_T('/') )
357 {
358 // Previous char was not comment beginning
359 // need to rewind to it and jump out
360 Pos--;
361 break;
362 }
363 else if ( Ch!=_T(' ') && Ch!=_T('\t') )
364 {
365 if ( Ch!=_T('/') )
366 {
367 // No white char, jumping out to find out what's this
368 break;
369 }
370 // If it's '/', it may be a start
371 // of comment, we give it a try
372 }
373 PreviousChar = Ch;
374 Pos++;
375 }
376
377 if ( Pos<(int)Code.Len() )
378 {
379 wxChar Ch = Code.GetChar(Pos);
380 if ( (Ch!=_T('\n')) && (Ch!=_T('\r')) )
381 {
382 // This is no directive, jumping out of function
383 if ( Ch != _T('#') ) return false;
384 }
385 }
386
387 // Searching for EOL
388 bool BlockMultilineComment = false;
389 while ( Pos<(int)Code.Len() )
390 {
391 wxChar Ch = Code.GetChar(Pos);
392
393 if ( Ch==_T('\n') || Ch==_T('\r') )
394 {
395 if ( PreviousChar == _T('\\') )
396 {
397 // Backslash removes EOL
398 if ( ++Pos >= (int)Code.Len() )
399 {
400 // EOF reached
401 BeginPos = Pos;
402 return false;
403 }
404 PreviousChar = Ch;
405 Ch = Code.GetChar(Pos);
406 if ( (Ch!=_T('\n') && Ch!=_T('\r')) || (Ch==PreviousChar) )
407 {
408 // One-character EOL
409 Pos--;
410 }
411 }
412 else
413 {
414 break;
415 }
416 }
417 else if ( Ch==_T('/') && PreviousChar==_T('/') )
418 {
419 // Have to block any multiline comment in this line
420 BlockMultilineComment = true;
421 }
422 else if ( Ch==_T('*') && PreviousChar==_T('/') && !BlockMultilineComment )
423 {
424 // Searching for end of comment and return there
425 bool WasAnyNL = false;
426 while ( ++Pos<(int)Code.Len() )
427 {
428 PreviousChar = Ch;
429 Ch = Code.GetChar(Pos);
430 if ( Ch==_T('/') && PreviousChar==_T('*') )
431 {
432 // Comment has been finished
433 if ( WasAnyNL )
434 {
435 // If there was any NL in comment, this mean that
436 // we can start scanning new line here so we return
437 // with true
438 BeginPos = ++Pos;
439 return true;
440 }
441 else
442 {
443 // There was no NL in comment, we're still inside directive
444 break;
445 }
446 }
447 else if ( Ch==_T('\n') || Ch==_T('\r') )
448 {
449 WasAnyNL = true;
450 }
451 }
452 }
453 PreviousChar = Ch;
454 Pos++;
455 }
456
457 // Skipping all NL chars left
458 while ( Pos<(int)Code.Len() )
459 {
460 wxChar Ch = Code.GetChar(Pos);
461 if ( Ch!=_T('\n') && Ch!=_T('\r') ) break;
462 Pos++;
463 }
464
465 BeginPos = Pos;
466 return true;
467 }
468
469
Convert(cb_unused TiXmlElement * ConfigNode,cb_unused TiXmlDocument * Doc,cb_unused wxsProject * Project) const470 TiXmlElement* wxsVersionConverter::Convert(cb_unused TiXmlElement* ConfigNode,cb_unused TiXmlDocument* Doc,cb_unused wxsProject* Project) const
471 {
472 // Currently there's only one version of wxSmith, no need to convert
473 return 0;
474 }
475