1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // wxFormBuilder - A Visual Dialog Editor for wxWidgets.
4 // Copyright (C) 2005 José Antonio Hurtado
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program 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 this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 //
20 // Written by
21 //   José Antonio Hurtado - joseantonio.hurtado@gmail.com
22 //   Juan Antonio Ortega  - jortegalalmolda@gmail.com
23 // Modified by
24 //   Michal Bliznak
25 //
26 ///////////////////////////////////////////////////////////////////////////////
27 
28 #include "pythonpanel.h"
29 
30 #include "rad/codeeditor/codeeditor.h"
31 #include "rad/wxfbevent.h"
32 #include "rad/bitmaps.h"
33 #include "rad/appdata.h"
34 #include "utils/wxfbdefs.h"
35 
36 #include "utils/typeconv.h"
37 #include "utils/encodingutils.h"
38 #include "utils/wxfbexception.h"
39 
40 #include "model/objectbase.h"
41 
42 #include "codegen/codewriter.h"
43 #include "codegen/pythoncg.h"
44 
45 #include <wx/fdrepdlg.h>
46 #include <wx/config.h>
47 
48 #if wxVERSION_NUMBER < 2900
49     #include <wx/wxScintilla/wxscintilla.h>
50 #else
51     #include <wx/stc/stc.h>
52 #endif
53 
BEGIN_EVENT_TABLE(PythonPanel,wxPanel)54 BEGIN_EVENT_TABLE ( PythonPanel,  wxPanel )
55 	EVT_FB_CODE_GENERATION( PythonPanel::OnCodeGeneration )
56 	EVT_FB_PROJECT_REFRESH( PythonPanel::OnProjectRefresh )
57 	EVT_FB_PROPERTY_MODIFIED( PythonPanel::OnPropertyModified )
58 	EVT_FB_OBJECT_CREATED( PythonPanel::OnObjectChange )
59 	EVT_FB_OBJECT_REMOVED( PythonPanel::OnObjectChange )
60 	EVT_FB_OBJECT_SELECTED( PythonPanel::OnObjectChange )
61 	EVT_FB_EVENT_HANDLER_MODIFIED( PythonPanel::OnEventHandlerModified )
62 
63 	EVT_FIND( wxID_ANY, PythonPanel::OnFind )
64 	EVT_FIND_NEXT( wxID_ANY, PythonPanel::OnFind )
65 END_EVENT_TABLE()
66 
67 PythonPanel::PythonPanel( wxWindow *parent, int id )
68 :
69 wxPanel( parent, id )
70 {
71 	AppData()->AddHandler( this->GetEventHandler() );
72 	wxBoxSizer *top_sizer = new wxBoxSizer( wxVERTICAL );
73 
74 	m_pythonPanel = new CodeEditor( this, -1 );
75 	InitStyledTextCtrl( m_pythonPanel->GetTextCtrl() );
76 
77 	top_sizer->Add( m_pythonPanel, 1, wxEXPAND, 0 );
78 
79 	SetSizer( top_sizer );
80 	SetAutoLayout( true );
81 	//top_sizer->SetSizeHints( this );
82 	top_sizer->Fit( this );
83 	top_sizer->Layout();
84 
85 	m_pythonCW = PTCCodeWriter( new TCCodeWriter( m_pythonPanel->GetTextCtrl() ) );
86 }
87 
~PythonPanel()88 PythonPanel::~PythonPanel()
89 {
90 	//delete m_icons;
91 	AppData()->RemoveHandler( this->GetEventHandler() );
92 }
93 
94 #if wxVERSION_NUMBER < 2900
InitStyledTextCtrl(wxScintilla * stc)95 void PythonPanel::InitStyledTextCtrl( wxScintilla *stc )
96 {
97     stc->SetLexer( wxSCI_LEX_PYTHON );
98 #else
99 void PythonPanel::InitStyledTextCtrl( wxStyledTextCtrl *stc )
100 {
101     stc->SetLexer( wxSTC_LEX_PYTHON );
102 #endif
103 	stc->SetKeyWords( 0, wxT( "and assert break class continue def del elif else \
104 							   except exec finally for from global if import in \
105 							   is lambda not or pass print raise return try while" ) );
106 
107 #ifdef __WXGTK__
108 	wxFont font( 8, wxMODERN, wxNORMAL, wxNORMAL );
109 	font.SetFaceName( wxT( "Monospace" ) );
110 #else
111 	wxFont font( 10, wxMODERN, wxNORMAL, wxNORMAL );
112 #endif
113 
114 #if wxVERSION_NUMBER < 2900
115 	stc->StyleSetFont( wxSCI_STYLE_DEFAULT, font );
116 	stc->StyleClearAll();
117 	stc->StyleSetBold( wxSCI_C_WORD, true );
118 	stc->StyleSetForeground( wxSCI_C_WORD, *wxBLUE );
119 	stc->StyleSetForeground( wxSCI_C_STRING, *wxRED );
120 	stc->StyleSetForeground( wxSCI_C_STRINGEOL, *wxRED );
121 	stc->StyleSetForeground( wxSCI_C_PREPROCESSOR, wxColour( 49, 106, 197 ) );
122 	stc->StyleSetForeground( wxSCI_C_COMMENT, wxColour( 0, 128, 0 ) );
123 	stc->StyleSetForeground( wxSCI_C_COMMENTLINE, wxColour( 0, 128, 0 ) );
124 	stc->StyleSetForeground( wxSCI_C_COMMENTDOC, wxColour( 0, 128, 0 ) );
125 	stc->StyleSetForeground( wxSCI_C_COMMENTLINEDOC, wxColour( 0, 128, 0 ) );
126 	stc->StyleSetForeground( wxSCI_C_NUMBER, *wxBLUE );
127 #else
128     stc->StyleSetFont( wxSTC_STYLE_DEFAULT, font );
129     stc->StyleClearAll();
130     stc->StyleSetBold( wxSTC_C_WORD, true );
131     stc->StyleSetForeground( wxSTC_C_WORD, *wxBLUE );
132     stc->StyleSetForeground( wxSTC_C_STRING, *wxRED );
133     stc->StyleSetForeground( wxSTC_C_STRINGEOL, *wxRED );
134     stc->StyleSetForeground( wxSTC_C_PREPROCESSOR, wxColour( 49, 106, 197 ) );
135     stc->StyleSetForeground( wxSTC_C_COMMENT, wxColour( 0, 128, 0 ) );
136     stc->StyleSetForeground( wxSTC_C_COMMENTLINE, wxColour( 0, 128, 0 ) );
137     stc->StyleSetForeground( wxSTC_C_COMMENTDOC, wxColour( 0, 128, 0 ) );
138     stc->StyleSetForeground( wxSTC_C_COMMENTLINEDOC, wxColour( 0, 128, 0 ) );
139     stc->StyleSetForeground( wxSTC_C_NUMBER, *wxBLUE );
140 #endif
141 	stc->SetUseTabs( true );
142 	stc->SetTabWidth( 4 );
143 	stc->SetTabIndents( true );
144 	stc->SetBackSpaceUnIndents( true );
145 	stc->SetIndent( 4 );
146 	stc->SetSelBackground( true, wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
147 	stc->SetSelForeground( true, wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
148 
149 	stc->SetCaretWidth( 2 );
150 	stc->SetReadOnly( true );
151 }
152 
153 void PythonPanel::OnFind( wxFindDialogEvent& event )
154 {
155 	m_pythonPanel->GetEventHandler()->ProcessEvent( event );
156 }
157 
158 void PythonPanel::OnPropertyModified( wxFBPropertyEvent& event )
159 {
160 	// Generate code to the panel only
161 	event.SetId( 1 );
162 	OnCodeGeneration( event );
163 }
164 
165 void PythonPanel::OnProjectRefresh( wxFBEvent& event )
166 {
167 	// Generate code to the panel only
168 	event.SetId( 1 );
169 	OnCodeGeneration( event );
170 }
171 
172 void PythonPanel::OnObjectChange( wxFBObjectEvent& event )
173 {
174 	// Generate code to the panel only
175 	event.SetId( 1 );
176 	OnCodeGeneration( event );
177 }
178 
179 void PythonPanel::OnEventHandlerModified( wxFBEventHandlerEvent& event )
180 {
181 	// Generate code to the panel only
182 	event.SetId( 1 );
183 	OnCodeGeneration( event );
184 }
185 
186 void PythonPanel::OnCodeGeneration( wxFBEvent& event )
187 {
188     PObjectBase objectToGenerate;
189 
190 	// Generate code in the panel if the panel is active
191 	bool doPanel = IsShown();
192 
193 	// Using the previously unused Id field in the event to carry a boolean
194 	bool panelOnly = ( event.GetId() != 0 );
195 
196 	// Only generate to panel + panel is not shown = do nothing
197 	if ( panelOnly && !doPanel )
198 	{
199 		return;
200 	}
201 
202 	// For code preview generate only code relevant to selected form,
203 	// otherwise generate full project code.
204 
205 	// Create copy of the original project due to possible temporary modifications
206 	PObjectBase project = PObjectBase(new ObjectBase(*AppData()->GetProjectData()));
207 
208 	if(panelOnly)
209 	{
210 	    objectToGenerate = AppData()->GetSelectedForm();
211 	}
212 
213 	if(!panelOnly || !objectToGenerate)
214 	{
215 	    objectToGenerate = project;
216 	}
217 
218 	// If only one project item should be generated then remove the rest items
219 	// from the temporary project
220 	if(doPanel && panelOnly && (objectToGenerate != project))
221 	{
222 	    if( project->GetChildCount() > 0)
223 	    {
224 	        unsigned int i = 0;
225             while( project->GetChildCount() > 1 )
226             {
227                 if(project->GetChild( i ) != objectToGenerate)
228                 {
229                     project->RemoveChild( i );
230                 }
231                 else
232                     i++;
233             }
234 	    }
235 	}
236 
237     if(!project || !objectToGenerate)return;
238 
239     // Get Python properties from the project
240 
241 	// If Python generation is not enabled, do not generate the file
242 	bool doFile = false;
243 	PProperty pCodeGen = project->GetProperty( wxT( "code_generation" ) );
244 	if ( pCodeGen )
245 	{
246 		//doFile = TypeConv::FlagSet( wxT("C++"), pCodeGen->GetValue() ) && !panelOnly;
247 		doFile = TypeConv::FlagSet( wxT("Python"), pCodeGen->GetValue() ) && !panelOnly;
248 	}
249 
250 	if ( !(doPanel || doFile ) )
251 	{
252 		return;
253 	}
254 
255 	// Get First ID from Project File
256 	unsigned int firstID = 1000;
257 	PProperty pFirstID = project->GetProperty( wxT("first_id") );
258 	if ( pFirstID )
259 	{
260 		firstID = pFirstID->GetValueAsInteger();
261 	}
262 
263 	// Get the file name
264 	wxString file;
265 	PProperty pfile = project->GetProperty( wxT( "file" ) );
266 	if ( pfile )
267 	{
268 		file = pfile->GetValue();
269 	}
270 	if ( file.empty() )
271 	{
272 		file = wxT("noname");
273 	}
274 
275 	// Determine if the path is absolute or relative
276 	bool useRelativePath = false;
277 	PProperty pRelPath = project->GetProperty( wxT( "relative_path" ) );
278 	if ( pRelPath )
279 	{
280 		useRelativePath = ( pRelPath->GetValueAsInteger() ? true : false );
281 	}
282 
283 	// Get the output path
284 	wxString path;
285 	try
286 	{
287 		path = AppData()->GetOutputPath();
288 	}
289 	catch ( wxFBException& ex )
290 	{
291 		if ( doFile )
292 		{
293 			path = wxEmptyString;
294 			wxLogWarning( ex.what() );
295 			return;
296 		}
297 	}
298 
299 	// Generate code in the panel
300 	if ( doPanel )
301 	{
302 		PythonCodeGenerator codegen;
303 		codegen.UseRelativePath( useRelativePath, path );
304 
305 		if ( pFirstID )
306 		{
307 			codegen.SetFirstID( firstID );
308 		}
309 
310 		codegen.SetSourceWriter( m_pythonCW );
311 
312 		Freeze();
313 
314 #if wxVERSION_NUMBER < 2900
315 		wxScintilla* pythonEditor = m_pythonPanel->GetTextCtrl();
316 #else
317         wxStyledTextCtrl* pythonEditor = m_pythonPanel->GetTextCtrl();
318 #endif
319 		pythonEditor->SetReadOnly( false );
320 		int pythonLine = pythonEditor->GetFirstVisibleLine() + pythonEditor->LinesOnScreen() - 1;
321 		int pythonXOffset = pythonEditor->GetXOffset();
322 
323 		codegen.GenerateCode( project );
324 
325 		pythonEditor->SetReadOnly( true );
326 		pythonEditor->GotoLine( pythonLine );
327 		pythonEditor->SetXOffset( pythonXOffset );
328 		pythonEditor->SetAnchor( 0 );
329 		pythonEditor->SetCurrentPos( 0 );
330 
331 		Thaw();
332 	}
333 
334 	// Generate code in the file
335 	if ( doFile )
336 	{
337 		try
338 		{
339 			PythonCodeGenerator codegen;
340 			codegen.UseRelativePath( useRelativePath, path );
341 
342 			if ( pFirstID )
343 			{
344 				codegen.SetFirstID( firstID );
345 			}
346 
347 			// Determin if Microsoft BOM should be used
348 			bool useMicrosoftBOM = false;
349 
350 			PProperty pUseMicrosoftBOM = project->GetProperty( wxT( "use_microsoft_bom" ) );
351 
352 			if ( pUseMicrosoftBOM )
353 			{
354 				useMicrosoftBOM = ( pUseMicrosoftBOM->GetValueAsInteger() != 0 );
355 			}
356 
357 			// Determine if Utf8 or Ansi is to be created
358 			bool useUtf8 = false;
359 			PProperty pUseUtf8 = project->GetProperty( _("encoding") );
360 
361 			if ( pUseUtf8 )
362 			{
363 				useUtf8 = ( pUseUtf8->GetValueAsString() != wxT("ANSI") );
364 			}
365 
366 			PCodeWriter python_cw( new FileCodeWriter( path + file + wxT( ".py" ), useMicrosoftBOM, useUtf8 ) );
367 
368 			codegen.SetSourceWriter( python_cw );
369 			codegen.GenerateCode( project );
370 			wxLogStatus( wxT( "Code generated on \'%s\'." ), path.c_str() );
371 
372 			// check if we have to convert to ANSI encoding
373 			if (project->GetPropertyAsString(wxT("encoding")) == wxT("ANSI"))
374 			{
375 				UTF8ToAnsi(path + file + wxT( ".py" ));
376 			}
377 		}
378 		catch ( wxFBException& ex )
379 		{
380 			wxLogError( ex.what() );
381 		}
382 	}
383 }
384