"""\ C++ code generator How the code is generated: every time the end of an object is reached during the parsing of the xml tree, either the function 'add_object' or the function 'add_class' is called: the latter when the object is a toplevel one, the former when it is not. In the last case, 'add_object' calls the appropriate ``writer'' function for the specific object, found in the 'obj_builders' dict. Such function accepts one argument, the CodeObject representing the object for which the code has to be written, and returns 3 lists of strings, representing the lines to add to the '__init__', '__set_properties' and '__do_layout' methods of the parent object. @copyright: 2002-2007 Alberto Griggio @copyright: 2012-2016 Carsten Grohmann @license: MIT (see LICENSE.txt) - THIS PROGRAM COMES WITH NO WARRANTY """ import StringIO import os import os.path import re from codegen import BaseLangCodeWriter, \ BaseSourceFileContent, \ BaseWidgetHandler import config import misc import wcodegen class SourceFileContent(BaseSourceFileContent): """\ Keeps info about an existing file that has to be updated, to replace only the lines inside a wxGlade block, an to keep the rest of the file as it was. @ivar event_handlers: List of event handlers for each class @type event_handlers: dict @ivar header_content: Content of the header file @type header_content: str @ivar source_content: Content of the source file @type source_content: str """ rec_block_start = re.compile( r'^(?P\s*)' # leading spaces r'//\s*' # comment sign r'begin\s+wxGlade:\s*' # "begin wxGlade:" statement and tailing spaces r'(?P\w*)' # class or function name r'::' # separator between class and function / block (non-greedy) r'(?P\w+)' # function / block name r'\s*$' # tailing spaces ) rec_block_end = re.compile( r'^\s*' # leading spaces r'//\s*' # comment sign r'end\s+wxGlade' # "end exGlade" statement r'\s*$' # tailing spaces ) rec_class_end = re.compile( r'^\s*};\s*' # closing curly brackets r'//\s*' # comment sign r'wxGlade:\s+end\s+class' # "wxGlade: end class" statement r'\s*$' # tailing spaces ) """\ Regexp to match last line of a class statement """ rec_class_decl = re.compile( r'^\s*' # leading spaces r'class\s+([a-zA-Z_]\w*)' # "class " statement r'\s*' # tailing spaces ) """\ Regexp to match class declarations This isn't very accurate - doesn't match template classes, nor virtual inheritance, but should be enough for most cases """ rec_decl_event_table = re.compile( r'^\s*' # leading spaces r'DECLARE_EVENT_TABLE\s*\(\s*\)\s*;?' # declaration of the event table r'\s*$' # tailing spaces ) """\ Regexp to match declaration of event table """ rec_def_event_table = re.compile( r'^\s*' # leading spaces r'BEGIN_EVENT_TABLE\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)' r'\s*$' # tailing spaces ) """\ Regexp to match event table """ rec_event_handler = re.compile( r'^\s*' # leading spaces r'(?:virtual\s+)?' r'void\s+(?P[A-Za-z_]+\w*)' # event handler name r'\s*' # optional spaces r'\([A-Za-z_:0-9]+\s*&\s*\w*\)\s*;' r'\s*' # optional spaces r'//\s*wxGlade:\s*' # wxGlade event handler statement r'\s*$' # tailing spaces ) rec_event_handlers_marker = re.compile( r'^\s*' # leading spaces r'//\s*wxGlade:\s*add\s+' r'((?:\w|:)+)\s+event handlers' r'\s*$' # tailing spaces ) """\ Regexp to match wxGlade comment of event handlers """ def __init__(self, name, code_writer): # initialise new variables first self.header_content = None self.source_content = None self.event_table_decl = {} self.event_table_def = {} self.header_extension = code_writer.header_extension self.source_extension = code_writer.source_extension # call inherited constructor BaseSourceFileContent.__init__(self, name, code_writer) def build_untouched_content(self): BaseSourceFileContent.build_untouched_content(self) self._build_untouched(self.name + self.header_extension, True) BaseSourceFileContent.build_untouched_content(self) self._build_untouched(self.name + self.source_extension, False) def _build_untouched(self, filename, is_header): prev_was_handler = False events_tag_added = False inside_block = False inside_comment = False tmp_in = self._load_file(filename) out_lines = [] for line in tmp_in: comment_index = line.find('/*') if not inside_comment and comment_index != -1 \ and comment_index > line.find('//'): inside_comment = True if inside_comment: end_index = line.find('*/') if end_index > comment_index: inside_comment = False if not is_header: result = None else: result = self.rec_class_decl.match(line) if not inside_comment and not inside_block and result: ## self._logger.debug(">> class %r", result.group(1)) if not self.class_name: # this is the first class declared in the file: insert the # new ones before this out_lines.append('<%swxGlade insert new_classes>' % self.nonce) self.new_classes_inserted = True self.class_name = result.group(1) self.class_name = self.format_classname(self.class_name) self.classes[self.class_name] = 1 # add the found class to the list # of classes of this module out_lines.append(line) elif not inside_block: result = self.rec_block_start.match(line) if not inside_comment and result: ## self._logger.debug(">> block %r %r %r", ## result.group('spaces'), ## result.group('classname'), ## result.group('block'), ## ) # replace the lines inside a wxGlade block with a tag that # will be used later by add_class spaces = result.group('spaces') which_class = result.group('classname') which_block = result.group('block') if not which_class: which_class = self.class_name else: which_class = self.format_classname(which_class) self.spaces[which_class] = spaces inside_block = True out_lines.append('<%swxGlade replace %s %s>' % ( self.nonce, result.group('classname'), result.group('block') )) else: dont_append = False # ALB 2004-12-08 event handling support... if is_header and not inside_comment: result = self.rec_event_handler.match(line) if result: prev_was_handler = True which_handler = result.group('handler') which_class = self.class_name self.event_handlers.setdefault( which_class, {})[which_handler] = 1 else: if prev_was_handler: # add extra event handlers here... out_lines.append('<%swxGlade event_handlers %s>' % (self.nonce, self.class_name) ) prev_was_handler = False events_tag_added = True elif not events_tag_added and \ self.is_end_of_class(line): out_lines.append( '<%swxGlade event_handlers %s>' % \ (self.nonce, self.class_name) ) # now try to see if we already have a # DECLARE_EVENT_TABLE result = self.rec_decl_event_table.match(line) if result: self.event_table_decl[self.class_name] = True elif not inside_comment: result = self.rec_event_handlers_marker.match(line) if result: out_lines.append('<%swxGlade add %s event ' 'handlers>' % \ (self.nonce, result.group(1))) dont_append = True result = self.rec_def_event_table.match(line) if result: which_class = result.group(1) self.event_table_def[which_class] = True # ---------------------------------------- if not dont_append: out_lines.append(line) else: # ignore all the lines inside a wxGlade block if self.rec_block_end.match(line): inside_block = False if is_header and not self.new_classes_inserted: # if we are here, the previous ``version'' of the file did not # contain any class, so we must add the new_classes tag at the # end of the file out_lines.append('<%swxGlade insert new_classes>' % self.nonce) # set the ``persistent'' content of the file if is_header: self.header_content = "".join(out_lines) else: self.source_content = "".join(out_lines) def is_end_of_class(self, line): """\ Return True if the line is the last line of a class Not really, but for wxglade-generated code it should work... """ return self.rec_class_end.match(line) # end of class SourceFileContent class WidgetHandler(BaseWidgetHandler): """\ Interface the various code generators for the widgets must implement """ constructor = [] """\ ``signature'' of the widget's constructor """ import_modules = [] """\ If not None, list of extra header file, in the form or "header.h" """ def __init__(self): BaseWidgetHandler.__init__(self) self.constructor = [] def get_ids_code(self, obj): """\ Handler for the code of the ids enum of toplevel objects. Returns a list of strings containing the code to generate. Usually the default implementation is ok (i.e. there are no extra lines to add) """ return [] # end of class WidgetHandler class CPPCodeWriter(BaseLangCodeWriter, wcodegen.CppMixin): """\ Code writer class for writing C++ code out of the designed GUI elements @ivar source_extension: Extension of the source file @type source_extension: str @ivar header_extension: Extension of the header file @type header_extension: str @ivar last_generated_id: Last generated Id number (wxNewId() is not used yet) @type last_generated_id: int @cvar tmpl_init_gettext: Template for inclusion of i18n headers and defining APP_CATALOG constant @type tmpl_init_gettext: None | str @see: L{BaseLangCodeWriter} """ _code_statements = { 'backgroundcolour': "%(objname)sSetBackgroundColour(%(value)s);\n", 'disabled': "%(objname)sEnable(0);\n", 'extraproperties': "%(objname)sSet%(propname_cap)s(%(value)s);\n", 'focused': "%(objname)sSetFocus();\n", 'foregroundcolour': "%(objname)sSetForegroundColour(%(value)s);\n", 'hidden': "%(objname)sHide();\n", 'setfont': "%(objname)sSetFont(wxFont(%(size)s, %(family)s, " "%(style)s, %(weight)s, %(underlined)s, wxT(%(face)s)));\n", 'tooltip': "%(objname)sSetToolTip(%(tooltip)s);\n", 'wxcolour': "wxColour(%(value)s)", 'wxsystemcolour': "wxSystemSettings::GetColour(%(value)s)", } class_separator = '::' global_property_writers = { 'font': BaseLangCodeWriter.FontPropertyHandler, 'events': BaseLangCodeWriter.EventsPropertyHandler, 'extraproperties': BaseLangCodeWriter.ExtraPropertiesPropertyHandler, } language_note = \ '// Example for compiling a single file project under Linux using g++:\n' \ '// g++ MyApp.cpp $(wx-config --libs) $(wx-config --cxxflags) -o MyApp\n' \ '//\n' \ '// Example for compiling a multi file project under Linux using g++:\n' \ '// g++ main.cpp $(wx-config --libs) $(wx-config --cxxflags) -o MyApp Dialog1.cpp Frame1.cpp\n' \ '//\n' last_generated_id = 1000 output_name = None """\ If not None, name (without extension) of the file to write into @type: str """ output_header = None """\ Temporary storage of header file for writing into @type: StringIO """ output_file = None """\ Temporary storage of source file for writing into @type: StringIO """ shebang = '// -*- C++ -*-\n//\n' tmpl_cfunc_end = '}\n\n' tmpl_name_do_layout = 'do_layout' tmpl_name_set_properties = 'set_properties' tmpl_sizeritem = '%s->Add(%s, %s, %s, %s);\n' tmpl_ctor_call_layout = '\n' \ '%(tab)sset_properties();\n' \ '%(tab)sdo_layout();\n' tmpl_func_do_layout = '\n' \ 'void %(klass)s::do_layout()\n{\n' \ '%(content)s' \ '}\n\n' tmpl_func_set_properties = '\n' \ 'void %(klass)s::set_properties()\n{\n' \ '%(content)s' \ '}\n\n' tmpl_appfile = """\ %(overwrite)s\ %(header_lines)s\ #include "%(filename_top_win_class)s" """ tmpl_init_gettext = """\ #include #ifndef APP_CATALOG #define APP_CATALOG "%(textdomain)s" // replace with the appropriate catalog name #endif """ tmpl_detailed = """\ class %(klass)s: public wxApp { public: %(tab)sbool OnInit(); }; IMPLEMENT_APP(%(klass)s) bool %(klass)s::OnInit() { %(tab)swxInitAllImageHandlers(); %(tab)s%(top_win_class)s* %(top_win)s = new %(top_win_class)s(NULL, wxID_ANY, wxEmptyString); %(tab)sSetTopWindow(%(top_win)s); %(tab)s%(top_win)s->Show(); %(tab)sreturn true; }""" tmpl_gettext_detailed = """\ class %(klass)s: public wxApp { public: %(tab)sbool OnInit(); protected: %(tab)swxLocale m_locale; // locale we'll be using }; IMPLEMENT_APP(%(klass)s) bool %(klass)s::OnInit() { %(tab)sm_locale.Init(); #ifdef APP_LOCALE_DIR %(tab)sm_locale.AddCatalogLookupPathPrefix(wxT(APP_LOCALE_DIR)); #endif %(tab)sm_locale.AddCatalog(wxT(APP_CATALOG)); %(tab)swxInitAllImageHandlers(); %(tab)s%(top_win_class)s* %(top_win)s = new %(top_win_class)s(NULL, wxID_ANY, wxEmptyString); %(tab)sSetTopWindow(%(top_win)s); %(tab)s%(top_win)s->Show(); %(tab)sreturn true; }""" tmpl_simple = """\ class MyApp: public wxApp { public: %(tab)sbool OnInit(); }; IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { %(tab)swxInitAllImageHandlers(); %(tab)s%(top_win_class)s* %(top_win)s = new %(top_win_class)s(NULL, wxID_ANY, wxEmptyString); %(tab)sSetTopWindow(%(top_win)s); %(tab)s%(top_win)s->Show(); %(tab)sreturn true; }""" tmpl_gettext_simple = """\ class MyApp: public wxApp { public: %(tab)sbool OnInit(); protected: %(tab)swxLocale m_locale; // locale we'll be using }; IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { %(tab)sm_locale.Init(); #ifdef APP_LOCALE_DIR %(tab)sm_locale.AddCatalogLookupPathPrefix(wxT(APP_LOCALE_DIR)); #endif %(tab)sm_locale.AddCatalog(wxT(APP_CATALOG)); %(tab)swxInitAllImageHandlers(); %(tab)s%(top_win_class)s* %(top_win)s = new %(top_win_class)s(NULL, wxID_ANY, wxEmptyString); %(tab)sSetTopWindow(%(top_win)s); %(tab)s%(top_win)s->Show(); %(tab)sreturn true; }""" tmpl_empty_string = 'wxEmptyString' class ClassLines(BaseLangCodeWriter.ClassLines): """\ Stores the lines of C++ code for a custom class @ivar ids: Ids declared in the source (to use for Event handling): these are grouped together into a public enum in the custom class @ivar sub_objs: List of 2-tuples (type, name) of the sub-objects which are attributes of the toplevel object @ivar extra_code_h: Extra header code to output @ivar extra_code_cpp: Extra source code to output """ def __init__(self): BaseLangCodeWriter.ClassLines.__init__(self) self.ids = [] self.sub_objs = [] self.extra_code_h = [] self.extra_code_cpp = [] self.dependencies = [] # List not dictionary # end of class ClassLines def init_lang(self, app_attrs): self.last_generated_id = 1000 # Extensions and main filename based on Project options when set self.source_extension = app_attrs.get( 'source_extension', config.default_source_extension) self.header_extension = app_attrs.get( 'header_extension', config.default_header_extension) self.app_filename = self._generate_app_filename() self.header_lines = [ '#include \n', '#include \n', ] # include i18n / gettext if self._use_gettext and self._textdomain: self.header_lines.append( self.tmpl_init_gettext % {'textdomain': self._textdomain} ) # extra lines to generate (see the 'extracode' property of top-level # widgets) self._current_extra_code_h = [] self._current_extra_code_cpp = [] def init_files(self, out_path): if self.multiple_files: self.previous_source = None self.out_dir = out_path else: name = os.path.splitext(out_path)[0] self.output_name = name if not self._overwrite and \ self._file_exists(name + self.header_extension): # the file exists, we must keep all the lines not inside a # wxGlade block. # NOTE: this may cause troubles if out_path is not a valid # source file, so be careful! self.previous_source = SourceFileContent(name, self) else: # if the file doesn't exist, create it and write the ``intro'' self.previous_source = None self.output_header = misc.AsciiStringIO() self.output_file = misc.AsciiStringIO() # isolation directives oh = os.path.basename(name + self.header_extension).upper().replace( '.', '_') self.output_header.write('#ifndef %s\n#define %s\n' % (oh, oh)) self.output_header.write('\n') for line in self.header_lines: self.output_header.write(line) self.output_header.write('\n') # now, write the tags to store dependencies and extra code self.output_header.write('<%swxGlade replace dependencies>' % self.nonce) self.output_header.write('\n<%swxGlade replace extracode>' % self.nonce) self.output_header.write('\n') self.output_file.write('#include "%s%s"\n\n' % \ (os.path.basename(name), self.header_extension)) self.output_file.write('<%swxGlade replace extracode>\n\n' % self.nonce) def finalize(self): if self.previous_source: # insert all the new custom classes inside the old file tag = '<%swxGlade insert new_classes>' % self.nonce if self.previous_source.new_classes: code = "".join([c[0] for c in self.previous_source.new_classes]) else: code = "" header_content = self.previous_source.header_content.replace(tag, code) extra_source = "".join([c[1] for c in self.previous_source.new_classes]) source_content = self.previous_source.source_content # extra code (see the 'extracode' property of top-level widgets) tag = '<%swxGlade replace extracode>' % self.nonce code = self._tagcontent( '::extracode', self._current_extra_code_h ) header_content = header_content.replace(tag, code) code = self._tagcontent( '::extracode', self._current_extra_code_cpp ) source_content = source_content.replace(tag, code) # -------------------------------------------------------------- # now remove all the remaining <123415wxGlade ...> tags from the # source: this may happen if we're not generating multiple files, # and one of the container class names is changed tags = re.findall( r'(<%swxGlade replace ([a-zA-Z_]*\w*) (\w+)>)' % self.nonce, header_content ) for tag in tags: if tag[2] == 'dependencies': #self._logger.debug('writing dependencies') deps = [] for code in self.classes.itervalues(): deps.extend(code.dependencies) lines = self._format_dependencies(deps) elif tag[2] == 'methods': lines = '%svoid set_properties();\n%svoid do_layout();\n' \ % (self.tabs(1), self.tabs(1)) else: lines = '// content of this block (%s) not found: ' \ 'did you rename this class?\n' % tag[2] header_content = header_content.replace(tag[0], lines) # remove all the remaining <123415wxGlade ...> tags in source file source_content = self._content_notfound( source_content, ) # ALB 2004-12-08 tags = re.findall(r'<%swxGlade event_handlers \w+>' % self.nonce, header_content) for tag in tags: header_content = header_content.replace(tag, "") tags = re.findall(r'<%swxGlade add \w+ event_handlers>' % self.nonce, source_content) for tag in tags: source_content = source_content.replace(tag, "") # write the new file contents to disk self.save_file( self.previous_source.name + self.header_extension, header_content, content_only=True ) if extra_source: extra_source = '\n\n' + extra_source self.save_file( self.previous_source.name + self.source_extension, source_content + extra_source, content_only=True ) elif not self.multiple_files: oh = os.path.basename(self.output_name).upper() + '_H' self.output_header.write('\n#endif // %s\n' % oh) # write the list of include files header_content = self.output_header.getvalue() source_content = self.output_file.getvalue() tags = re.findall('<%swxGlade replace dependencies>' % self.nonce, header_content) deps = [] for code in self.classes.itervalues(): deps.extend(code.dependencies) code = self._format_dependencies(deps) header_content = header_content.replace( '<%swxGlade replace dependencies>' % self.nonce, code) # extra code (see the 'extracode' property of top-level widgets) tag = '<%swxGlade replace extracode>' % self.nonce code = self._tagcontent('::extracode', self._current_extra_code_h) header_content = header_content.replace(tag, code) code = self._tagcontent('::extracode', self._current_extra_code_cpp) source_content = source_content.replace(tag, code) # -------------------------------------------------------------- self.save_file( self.output_name + self.header_extension, header_content, self._app_added ) self.save_file( self.output_name + self.source_extension, source_content, self._app_added ) def add_app(self, app_attrs, top_win_class): # add language specific mappings self.lang_mapping['filename_top_win_class'] = \ '%s%s' % (top_win_class, self.header_extension) BaseLangCodeWriter.add_app(self, app_attrs, top_win_class) def add_class(self, code_obj): if self.classes.has_key(code_obj.klass) and \ self.classes[code_obj.klass].done: return # the code has already been generated if self.multiple_files: # let's see if the file to generate exists, and in this case # create a SourceFileContent instance filename = os.path.join(self.out_dir, code_obj.klass.replace('::', '_') + self.header_extension) if self._overwrite or not self._file_exists(filename): prev_src = None else: prev_src = SourceFileContent( os.path.join(self.out_dir, code_obj.klass), self, ) else: # in this case, previous_source is the SourceFileContent instance # that keeps info about the single file to generate prev_src = self.previous_source try: builder = self.obj_builders[code_obj.base] mycn = getattr(builder, 'cn', self.cn) mycn_f = getattr(builder, 'cn_f', self.cn_f) except KeyError: self._logger.error('%s', code_obj) # this is an error, let the exception be raised # the details are logged by the global exception handler raise if prev_src and prev_src.classes.has_key(code_obj.klass): is_new = False else: # this class wasn't in the previous version of the source (if any) is_new = True header_buffer = [] source_buffer = [] hwrite = header_buffer.append swrite = source_buffer.append if not self.classes.has_key(code_obj.klass): # if the class body was empty, create an empty ClassLines self.classes[code_obj.klass] = self.ClassLines() # collect all event handlers event_handlers = self.classes[code_obj.klass].event_handlers for win_id, evt, handler, evt_type in builder.get_event_handlers(code_obj): event_handlers.append((win_id, mycn(evt), handler, evt_type)) # try to see if there's some extra code to add to this class extra_code = getattr(builder, 'extracode', code_obj.properties.get('extracode', "")) if extra_code: extra_code = re.sub(r'\\n', '\n', extra_code) extra_code = re.split(re.compile(r'^###\s*$', re.M), extra_code, 1) self.classes[code_obj.klass].extra_code_h.append(extra_code[0]) if len(extra_code) > 1: self.classes[code_obj.klass].extra_code_cpp.append(extra_code[1]) if not is_new: self.warning( '%s has extra code, but you are not overwriting ' 'existing sources: please check that the resulting ' 'code is correct!' % code_obj.name ) if not self.multiple_files and extra_code: if self.classes[code_obj.klass].extra_code_h: self._current_extra_code_h.append("".join( self.classes[code_obj.klass].extra_code_h[::-1])) if self.classes[code_obj.klass].extra_code_cpp: self._current_extra_code_cpp.append("".join( self.classes[code_obj.klass].extra_code_cpp[::-1])) default_sign = [('wxWindow*', 'parent'), ('wxWindowID', 'id')] sign = getattr(builder, 'constructor', default_sign) defaults = [] for t in sign: if len(t) == 3: defaults.append(t[2]) else: defaults.append(None) tmp_sign = [t[0] + ' ' + t[1] for t in sign] sign_decl2 = ', '.join(tmp_sign) for i in range(len(tmp_sign)): if defaults[i]: tmp_sign[i] += '=%s' % defaults[i] sign_decl1 = ', '.join(tmp_sign) sign_inst = ', '.join([t[1] for t in sign]) # custom base classes support custom_base = getattr(code_obj, 'custom_base', code_obj.properties.get('custom_base', None)) if custom_base and not custom_base.strip(): custom_base = None # generate constructor code if is_new: pass elif custom_base: # custom base classes set, but "overwrite existing sources" not # set. Issue a warning about this self.warning( '%s has custom base classes, but you are not overwriting ' 'existing sources: please check that the resulting code is ' 'correct!' % code_obj.name ) if is_new: # header file base = code_obj.base if custom_base: base = ", public ".join([b.strip() for b in custom_base.split(',')]) hwrite('\nclass %s: public %s {\n' % (code_obj.klass, base)) hwrite('public:\n') # the first thing to add it the enum of the various ids hwrite(self.tabs(1) + '// begin wxGlade: %s::ids\n' % code_obj.klass) ids = self.classes[code_obj.klass].ids # let's try to see if there are extra ids to add to the enum if hasattr(builder, 'get_ids_code'): ids.extend(builder.get_ids_code(code_obj)) if ids: hwrite(self.tabs(1) + 'enum {\n') ids = (',\n' + self.tabs(2)).join(ids) hwrite(self.tabs(2) + ids) hwrite('\n' + self.tabs(1) + '};\n') hwrite(self.tabs(1) + '// end wxGlade\n\n') # constructor prototype hwrite(self.tabs(1) + '%s(%s);\n' % (code_obj.klass, sign_decl1)) hwrite('\nprivate:\n') # set_properties and do_layout prototypes hwrite(self.tabs(1) + '// begin wxGlade: %s::methods\n' % code_obj.klass) hwrite(self.tabs(1) + 'void set_properties();\n') hwrite(self.tabs(1) + 'void do_layout();\n') hwrite(self.tabs(1) + '// end wxGlade\n') # declarations of the attributes hwrite('\n') hwrite('protected:\n') hwrite(self.tabs(1) + '// begin wxGlade: %s::attributes\n' % code_obj.klass) for o_type, o_name in self.classes[code_obj.klass].sub_objs: hwrite(self.tabs(1) + '%s* %s;\n' % (o_type, o_name)) hwrite(self.tabs(1) + '// end wxGlade\n') if event_handlers: t = self.tabs(1) hwrite('\n' + t + 'DECLARE_EVENT_TABLE();\n') hwrite('\npublic:\n') already_there = {} for win_id, evt, handler, evt_type in event_handlers: if handler not in already_there: hwrite('%svoid %s(%s &event); ' '// wxGlade: \n' % (t, handler, evt_type)) already_there[handler] = 1 hwrite('}; // wxGlade: end class\n\n') elif prev_src: hwrite(self.tabs(1) + '// begin wxGlade: %s::ids\n' % code_obj.klass) ids = self.classes[code_obj.klass].ids # let's try to see if there are extra ids to add to the enum if hasattr(builder, 'get_ids_code'): ids.extend(builder.get_ids_code(code_obj)) if ids: hwrite(self.tabs(1) + 'enum {\n') ids = (',\n' + self.tabs(2)).join(ids) hwrite(self.tabs(2) + ids) hwrite('\n' + self.tabs(1) + '};\n') hwrite(self.tabs(1) + '// end wxGlade\n') tag = '<%swxGlade replace %s ids>' % (self.nonce, code_obj.klass) if prev_src.header_content.find(tag) < 0: # no ids tag found, issue a warning and do nothing self.warning( "wxGlade ids block not found for %s, ids declarations " "code NOT generated" % code_obj.name ) else: prev_src.header_content = prev_src.header_content.\ replace(tag, "".join(header_buffer)) header_buffer = [ self.tabs(1) + '// begin wxGlade: %s::methods\n' % \ code_obj.klass, self.tabs(1) + 'void set_properties();\n', self.tabs(1) + 'void do_layout();\n', self.tabs(1) + '// end wxGlade\n', ] tag = '<%swxGlade replace %s methods>' % (self.nonce, code_obj.klass) if prev_src.header_content.find(tag) < 0: # no methods tag found, issue a warning and do nothing self.warning( "wxGlade methods block not found for %s, methods " "declarations code NOT generated" % code_obj.name ) else: prev_src.header_content = prev_src.header_content.\ replace(tag, "".join(header_buffer)) header_buffer = [] hwrite = header_buffer.append hwrite(self.tabs(1) + '// begin wxGlade: %s::attributes\n' % code_obj.klass) for o_type, o_name in self.classes[code_obj.klass].sub_objs: hwrite(self.tabs(1) + '%s* %s;\n' % (o_type, o_name)) hwrite(self.tabs(1) + '// end wxGlade\n') tag = '<%swxGlade replace %s attributes>' % (self.nonce, code_obj.klass) if prev_src.header_content.find(tag) < 0: # no attributes tag found, issue a warning and do nothing self.warning( "wxGlade attributes block not found for %s, attributes " "declarations code NOT generated" % code_obj.name ) else: prev_src.header_content = prev_src.header_content.\ replace(tag, "".join(header_buffer)) header_buffer = [] hwrite = header_buffer.append if event_handlers: already_there = prev_src.event_handlers.get(code_obj.klass, {}) t = self.tabs(1) for win_id, evt, handler, evt_type in event_handlers: if handler not in already_there: hwrite('%svoid %s(%s &event); ' '// wxGlade: \n' % (t, handler, evt_type)) already_there[handler] = 1 if code_obj.klass not in prev_src.event_table_def: hwrite('\nprotected:\n') hwrite(self.tabs(1) + 'DECLARE_EVENT_TABLE()\n') tag = '<%swxGlade event_handlers %s>' % (self.nonce, code_obj.klass) if prev_src.header_content.find(tag) < 0: # no attributes tag found, issue a warning and do nothing self.warning( "wxGlade events block not found for %s, event table code " "NOT generated" % code_obj.name ) else: prev_src.header_content = \ prev_src.header_content.replace( tag, "".join(header_buffer)) # source file # set the window's style prop = code_obj.properties style = prop.get("style", None) if style: style = mycn_f(style) sign_inst = sign_inst.replace('style', '%s' % style) # constructor if is_new: base = "%s(%s)" % (code_obj.base, sign_inst) if custom_base: bases = [b.strip() for b in custom_base.split(',')] if bases: base = "%s(%s)" % (bases[0], sign_inst) rest = ", ".join([b + "()" for b in bases[1:]]) if rest: base += ", " + rest swrite('\n%s::%s(%s):\n%s%s\n{\n' % (code_obj.klass, code_obj.klass, sign_decl2, self.tabs(1), base)) swrite(self.tabs(1) + '// begin wxGlade: %s::%s\n' % (code_obj.klass, code_obj.klass)) tab = self.tabs(1) init_lines = self.classes[code_obj.klass].init parents_init = self.classes[code_obj.klass].parents_init parents_init.reverse() for l in parents_init: swrite(tab + l) for l in init_lines: swrite(tab + l) # now check if there are extra lines to add to the constructor if hasattr(builder, 'get_init_code'): for l in builder.get_init_code(code_obj): swrite(tab + l) swrite(self.tmpl_ctor_call_layout % { 'tab': tab, }) # end tag swrite('%s%s end wxGlade\n' % (tab, self.comment_sign)) # write class function end statement if self.tmpl_cfunc_end and is_new: swrite(self.tmpl_cfunc_end % { 'tab': tab, }) # replace code inside existing constructor block if prev_src and not is_new: # replace the lines inside the ctor wxGlade block # with the new ones tag = '<%swxGlade replace %s %s>' % (self.nonce, code_obj.klass, code_obj.klass) if prev_src.source_content.find(tag) < 0: # no constructor tag found, issue a warning and do nothing self.warning( "wxGlade %s::%s block not found, relative code NOT " "generated" % (code_obj.klass, code_obj.klass) ) else: prev_src.source_content = prev_src.source_content.\ replace(tag, "".join(source_buffer)) source_buffer = [] swrite = source_buffer.append # generate code for __set_properties() code_lines = self.generate_code_set_properties( builder, code_obj, is_new, tab ) source_buffer.extend(code_lines) # replace code inside existing __set_properties() function if prev_src and not is_new: # replace the lines inside the set_properties wxGlade block # with the new ones tag = '<%swxGlade replace %s set_properties>' % (self.nonce, code_obj.klass) if prev_src.source_content.find(tag) < 0: # no set_properties tag found, issue a warning and do nothing self.warning( "wxGlade %s::set_properties block not found, relative " "code NOT generated" % code_obj.klass ) else: prev_src.source_content = prev_src.source_content.\ replace(tag, "".join(source_buffer)) source_buffer = [] swrite = source_buffer.append # generate code for __do_layout() code_lines = self.generate_code_do_layout( builder, code_obj, is_new, tab ) source_buffer.extend(code_lines) # replace code inside existing do_layout() function if prev_src and not is_new: # replace the lines inside the do_layout wxGlade block # with the new ones tag = '<%swxGlade replace %s %s>' % (self.nonce, code_obj.klass, 'do_layout') if prev_src.source_content.find(tag) < 0: # no do_layout tag found, issue a warning and do nothing self.warning( "wxGlade do_layout block not found for %s, do_layout " "code NOT generated" % code_obj.name ) else: prev_src.source_content = prev_src.source_content.\ replace(tag, "".join(source_buffer)) source_buffer = [] swrite = source_buffer.append # generate code for event table code_lines = self.generate_code_event_table( code_obj, is_new, tab, prev_src, event_handlers, ) if prev_src and not is_new: tag = '<%swxGlade replace %s event_table>' % (self.nonce, code_obj.klass) if prev_src.source_content.find(tag) < 0: # no constructor tag found, issue a warning and do nothing self.warning( "wxGlade %s::event_table block not found, relative " "code NOT generated" % code_obj.klass ) else: prev_src.source_content = prev_src.source_content.replace( tag, "".join(code_lines), ) else: source_buffer.extend(code_lines) # generate code for event handler stubs code_lines = self.generate_code_event_handler( code_obj, is_new, tab, prev_src, event_handlers, ) # replace code inside existing event handlers if prev_src and not is_new: tag = '<%swxGlade add %s event handlers>' % \ (self.nonce, code_obj.klass) if prev_src.source_content.find(tag) < 0: # no constructor tag found, issue a warning and do nothing self.warning( "wxGlade %s event handlers marker not found, relative " "code NOT generated" % code_obj.klass ) else: prev_src.source_content = prev_src.source_content.replace( tag, "".join(code_lines), ) else: source_buffer.extend(code_lines) # the code has been generated self.classes[code_obj.klass].done = True if not self.multiple_files and prev_src: # if this is a new class, add its code to the new_classes list of the # SourceFileContent instance if is_new: prev_src.new_classes.append( ("".join(header_buffer), "".join(source_buffer)) ) return if self.multiple_files: if code_obj.base in self.obj_builders: self.classes[code_obj.klass].dependencies.extend( getattr(self.obj_builders[code_obj.base], 'import_modules', [])) if prev_src: tag = '<%swxGlade insert new_classes>' % self.nonce prev_src.header_content = prev_src.header_content.replace(tag, "") # insert the module dependencies of this class extra_modules = self.classes[code_obj.klass].dependencies # self._logger.debug( # 'extra_modules: %s, %s', # extra_modules, # code_obj.base, # ) # WARNING: there's a double space ' ' between 'replace' and # 'dependencies' in the tag below, because there is no class name # (see SourceFileContent, line ~147) tag = '<%swxGlade replace dependencies>' % self.nonce code = self._format_dependencies(extra_modules) prev_src.header_content = prev_src.header_content.\ replace(tag, code) # insert the extra code of this class extra_code_h = "".join(self.classes[code_obj.klass].extra_code_h[::-1]) extra_code_cpp = \ "".join(self.classes[code_obj.klass].extra_code_cpp[::-1]) # if there's extra code but we are not overwriting existing # sources, warn the user if extra_code_h or extra_code_cpp: self.warning( '%s (or one of its children) has extra ' 'code classes, but you are not overwriting ' 'existing sources: please check that the resulting ' 'code is correct!' % code_obj.name ) extra_code_h = self._tagcontent("::extracode", extra_code_h) extra_code_cpp = self._tagcontent("::extracode", extra_code_cpp) tag = '<%swxGlade replace extracode>' % self.nonce prev_src.header_content = prev_src.header_content.replace( tag, extra_code_h) prev_src.source_content = prev_src.source_content.replace( tag, extra_code_cpp) # store the new file contents to disk name = os.path.join(self.out_dir, code_obj.klass) self.save_file( name + self.header_extension, prev_src.header_content, content_only=True ) self.save_file( name + self.source_extension, prev_src.source_content, content_only=True ) return # create the new source file header_file = os.path.join(self.out_dir, code_obj.klass + self.header_extension) source_file = os.path.join(self.out_dir, code_obj.klass + self.source_extension) hout = StringIO.StringIO() sout = StringIO.StringIO() # header file hwrite = hout.write # isolation directives hn = os.path.basename(header_file).upper().replace('.', '_') hwrite('#ifndef %s\n#define %s\n' % (hn, hn)) hwrite('\n') # write the common lines for line in self.header_lines: hwrite(line) hwrite('\n') # write the module dependencies for this class extra_modules = self.classes[code_obj.klass].dependencies code = self._format_dependencies(extra_modules) hwrite(code) hwrite('\n') # insert the extra code of this class extra_code_h = "".join(self.classes[code_obj.klass].extra_code_h[::-1]) extra_code_h = self._tagcontent('::extracode', extra_code_h) hwrite(extra_code_h) hwrite('\n') # write the class body for line in header_buffer: hwrite(line) hwrite('\n#endif // %s\n' % hn) # source file swrite = sout.write # write the common lines #for line in self.header_lines: # swrite(line) swrite(self.header_lines[0]) swrite('#include "%s"\n\n' % os.path.basename(header_file)) # insert the extra code of this class extra_code_cpp = "".join(self.classes[code_obj.klass].extra_code_cpp[::-1]) extra_code_cpp = self._tagcontent('::extracode', extra_code_cpp) swrite(extra_code_cpp) swrite('\n') # write the class implementation for line in source_buffer: swrite(line) # store source to disk self.save_file(header_file, hout.getvalue()) self.save_file(source_file, sout.getvalue()) hout.close() sout.close() else: # not self.multiple_files # write the class body onto the single source file hwrite = self.output_header.write for line in header_buffer: hwrite(line) swrite = self.output_file.write for line in source_buffer: swrite(line) def add_object(self, top_obj, sub_obj): # get top level source code object and the widget builder instance klass, builder = self._add_object_init(top_obj, sub_obj) if not klass or not builder: return try: init, ids, props, layout = builder.get_code(sub_obj) except: print sub_obj raise # this shouldn't happen if sub_obj.in_windows: # the object is a wxWindow instance if sub_obj.is_container and not sub_obj.is_toplevel: init.reverse() klass.parents_init.extend(init) else: klass.init.extend(init) mycn = getattr(builder, 'cn', self.cn) for win_id, evt, handler, evt_type in builder.get_event_handlers(sub_obj): klass.event_handlers.append( (win_id, mycn(evt), handler, evt_type)) # try to see if there's some extra code to add to this class extra_code = getattr(builder, 'extracode', sub_obj.properties.get('extracode', "")) if extra_code: extra_code = re.sub(r'\\n', '\n', extra_code) extra_code = re.split(re.compile(r'^###\s*$', re.M), extra_code, 1) klass.extra_code_h.append(extra_code[0]) if len(extra_code) > 1: klass.extra_code_cpp.append(extra_code[1]) # if we are not overwriting existing source, warn the user # about the presence of extra code if not self.multiple_files and self.previous_source: self.warning( '%s has extra code, but you are not ' 'overwriting existing sources: please check ' 'that the resulting code is correct!' % \ sub_obj.name ) klass.ids.extend(ids) if sub_obj.klass != 'spacer': # attribute is a special property which control whether # sub_obj must be accessible as an attribute of top_obj, # or as a local variable in the do_layout method if self.test_attribute(sub_obj): klass.sub_objs.append((sub_obj.klass, sub_obj.name)) else: # the object is a sizer if self.test_attribute(sub_obj): klass.sub_objs.append((sub_obj.klass, sub_obj.name)) klass.sizers_init.extend(init) klass.props.extend(props) klass.layout.extend(layout) if self.multiple_files and \ (sub_obj.is_toplevel and sub_obj.base != sub_obj.klass): #print top_obj.name, sub_obj.name klass.dependencies.append(sub_obj.klass) else: if sub_obj.base in self.obj_builders: headers = getattr(self.obj_builders[sub_obj.base], 'import_modules', []) klass.dependencies.extend(headers) def generate_code_event_handler(self, code_obj, is_new, tab, prev_src, event_handlers): """\ Generate the event handler stubs @param code_obj: Object to generate code for @type code_obj: CodeObject @param is_new: Indicates if previous source code exists @type is_new: bool @param tab: Indentation of function body @type tab: str @param prev_src: Previous source code @type prev_src: SourceFileContent @param event_handlers: List of event handlers @rtype: list[str] @see: L{tmpl_func_event_stub} """ code_lines = [] swrite = code_lines.append if not event_handlers: return [] tmpl_handler = """ void %(klass)s::%(handler)s(%(evt_type)s &event) { %(tab)sevent.Skip(); %(tab)s// notify the user that he hasn't implemented the event handler yet %(tab)swxLogDebug(wxT("Event handler (%(klass)s::%(handler)s) not implemented yet")); } """ if prev_src: already_there = prev_src.event_handlers.get(code_obj.klass, {}) else: already_there = {} for win_id, event, handler, evt_type in event_handlers: if handler not in already_there: swrite(tmpl_handler % { 'evt_type': evt_type, 'handler': handler, 'klass': code_obj.klass, 'tab': tab, }) already_there[handler] = 1 if is_new or not prev_src: swrite('\n\n') swrite('// wxGlade: add %s event handlers\n' % code_obj.klass) if is_new or not prev_src: swrite('\n') return code_lines def generate_code_event_table(self, code_obj, is_new, tab, prev_src, event_handlers): """\ Generate code for event table declaration. @param code_obj: Object to generate code for @type code_obj: CodeObject @param is_new: Indicates if previous source code exists @type is_new: bool @param tab: Indentation of function body @type tab: str @param prev_src: Previous source code @type prev_src: SourceFileContent @param event_handlers: List of event handlers @type event_handlers: list[(str, str, str)] @rtype: list[str] """ code_lines = [] write = code_lines.append if not event_handlers: return code_lines if prev_src and code_obj.klass in prev_src.event_table_decl: has_event_table = True else: has_event_table = False if is_new or not has_event_table: write('\nBEGIN_EVENT_TABLE(%s, %s)\n' % \ (code_obj.klass, code_obj.base)) write(tab + '// begin wxGlade: %s::event_table\n' % code_obj.klass) for win_id, event, handler, evt_type in event_handlers: if 'EVT_NAVIGATION_KEY' in event: tmpl = '%(tab)s%(event)s(%(klass)s::%(handler)s)\n' else: tmpl = '%(tab)s%(event)s(%(win_id)s, ' \ '%(klass)s::%(handler)s)\n' details = { 'tab': tab, 'event': event, 'win_id': win_id, 'klass': code_obj.klass, 'handler': handler, } write(tmpl % details) write(tab + '// end wxGlade\n') if is_new or not has_event_table: write('END_EVENT_TABLE();\n\n') return code_lines def generate_code_id(self, obj, id=None): if id is None: id = obj.properties.get('id') if not id: return '', 'wxID_ANY' tokens = id.split('=', 1) if len(tokens) == 2: name, val = tokens else: return '', tokens[0] # we assume name is declared elsewhere if not name: return '', val name = name.strip() val = val.strip() if val == '?': val = 'wxID_HIGHEST + %d' % self.last_generated_id self.last_generated_id += 1 else: val = val return '%s = %s' % (name, val), name def generate_code_size(self, obj): objname = self.format_generic_access(obj) if obj.is_toplevel: name2 = 'this' else: name2 = obj.name size = obj.properties.get('size', '').strip() use_dialog_units = (size[-1] == 'd') if not obj.parent: method = 'SetSize' else: method = 'SetMinSize' if use_dialog_units: return '%s%s(wxDLG_UNIT(%s, wxSize(%s)));\n' % \ (objname, method, name2, size[:-1]) else: return '%s%s(wxSize(%s));\n' % (objname, method, size) def quote_path(self, s): path = super(CPPCodeWriter, self).quote_path(s) # path starts and ends with double quotes already return 'wxT(%s)' % path def _quote_str(self, s): if self._use_gettext: return '_("%s")' % s else: return 'wxT("%s")' % s def format_generic_access(self, obj): if obj.is_toplevel: return '' else: return '%s->' % obj.name def _unique(self, sequence): """\ Strips all duplicates from sequence. Works only if items of sequence are hashable """ tmp = {} for item in sequence: tmp[item] = 1 return tmp.keys() def _format_dependencies(self, dependencies): """\ Format the dependencies output @param dependencies: List if header files @type dependencies: list[str] @return: Changed content @rtype: str @see: L{_tagcontent()} """ dep_list = [] for dependency in self._unique(dependencies): if dependency and ('"' != dependency[0] != '<'): dep_list.append('#include "%s.h"\n' % dependency) else: dep_list.append('#include %s\n' % dependency) code = self._tagcontent( '::dependencies', dep_list, ) return code def _generate_app_filename(self): """\ Return the filename of C++ main file @rtype: str @see: L{config.default_cpp_app_name} @see: L{source_extension} """ base = os.path.splitext(config.default_cpp_app_name)[0] app_filename = '%s%s' % (base, self.source_extension) return app_filename # end of class CPPCodeWriter writer = CPPCodeWriter() """\ The code writer is an instance of L{CPPCodeWriter}. """ language = writer.language """\ Language generated by this code generator """