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