[pygccxml-commit] SF.net SVN: pygccxml: [1029] pyplusplus_dev/ide
Brought to you by:
mbaas,
roman_yakovenko
From: <ale...@us...> - 2007-05-02 14:59:12
|
Revision: 1029 http://svn.sourceforge.net/pygccxml/?rev=1029&view=rev Author: alex_eisen Date: 2007-05-02 07:58:58 -0700 (Wed, 02 May 2007) Log Message: ----------- Working ide without code generation Modified Paths: -------------- pyplusplus_dev/ide/controllers/__init__.py pyplusplus_dev/ide/controllers/controller_main.py pyplusplus_dev/ide/ide.py pyplusplus_dev/ide/model/__init__.py pyplusplus_dev/ide/views/frame_main.py Added Paths: ----------- pyplusplus_dev/ide/IdeTemplate.xml pyplusplus_dev/ide/ProjectTemplate.xml pyplusplus_dev/ide/doc/ pyplusplus_dev/ide/doc/EnterpriseArchitectModel.eap pyplusplus_dev/ide/doc/IdeInBoa.png pyplusplus_dev/ide/doc/PackageOverview.png pyplusplus_dev/ide/doc/ReadmeForDevelopment.odt pyplusplus_dev/ide/model/code_generator.py pyplusplus_dev/ide/model/etree_extension.py pyplusplus_dev/ide/model/settings.py pyplusplus_dev/ide/test/ pyplusplus_dev/ide/test/test_attrib_finder.py Removed Paths: ------------- pyplusplus_dev/ide/model/ProjectFileTemplatate.xml Added: pyplusplus_dev/ide/IdeTemplate.xml =================================================================== --- pyplusplus_dev/ide/IdeTemplate.xml (rev 0) +++ pyplusplus_dev/ide/IdeTemplate.xml 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,9 @@ +<!-- Template file for pyplusplus IDE --> +<pyPPIde> + <project + maxNumInMenue="6" + recentProjects="[]" + lastPrjPath="." + lastIncPath="." + /> +</pyPPIde> \ No newline at end of file Added: pyplusplus_dev/ide/ProjectTemplate.xml =================================================================== --- pyplusplus_dev/ide/ProjectTemplate.xml (rev 0) +++ pyplusplus_dev/ide/ProjectTemplate.xml 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,11 @@ +<pyPPProject> + <gccXmlSettings + gccXmlPath="" + headerPath="" + includePathList="[]" + macroList="[]" /> + <guiGeometry + horizontalSplitter="350" + size="(0, 0, 800, 600)" + verticalSplitter="300" /> +</pyPPProject> \ No newline at end of file Modified: pyplusplus_dev/ide/controllers/__init__.py =================================================================== --- pyplusplus_dev/ide/controllers/__init__.py 2007-05-01 18:06:08 UTC (rev 1028) +++ pyplusplus_dev/ide/controllers/__init__.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2004 Roman Yakovenko. +# 2007 Alexander Eisenhuth +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) Modified: pyplusplus_dev/ide/controllers/controller_main.py =================================================================== --- pyplusplus_dev/ide/controllers/controller_main.py 2007-05-01 18:06:08 UTC (rev 1028) +++ pyplusplus_dev/ide/controllers/controller_main.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -4,63 +4,338 @@ # Distributed under the Boost Software License, Version 1.0. (See # accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) - +from threading import Timer +from Queue import Queue, Empty +import os import wx from views import dialog_macro +from model.settings import ProjectSettings, IdeSettings, ParameterContainer +import model.code_generator as code_generator -""" Contoller class. Part of MVC -Responsibility: Glue view and model code: -- Handle all events from view (p.e. button) """ +class MainParameter(object): + ''' + Abstract main parameters + ''' + def __init__(self): + + object.__init__(self) + + # Following attributes are parameters of the ide (realized as properties) + self.max_num_recent_projects = ParameterContainer(int, 0) + self.recent_prj_list = ParameterContainer(list, []) + self.last_prj_path = ParameterContainer(unicode, "") + self.last_inc_path = ParameterContainer(unicode, "") + +class AsyncExecHandler(wx.EvtHandler): + ''' + This class handles async execution. It exec the callable given in the + constructor. There are some callbacs for misc. issues + ''' + + def __init__(self, async_callable, args): + wx.EvtHandler.__init__(self) + self._async_callable = async_callable + self._args = args + self._cur_timer_calls = 0 + self._ui_eventhanler = None + self._timer_res = 100 # 100 msec + + def GetRunning(self): + ''' + Return boolean if async execution is running + ''' + return not self._thread.finished.isSet() + + def Start(self): + ''' + Start async execution + ''' + self._start_in_thread() + + def SetErrorOutput(self, err_cb): + ''' + Set callback for error output + @param err_cb: callback + ''' + self._err_cb = err_cb + + def SetResultOutput(self, result_cb): + ''' + Set callback for result + @param result_cb: callback + ''' + self._result_cb = result_cb + + def SetProgressCb(self, progress_cb, progress_tm_100msec): + ''' + Set callback for progress. Param progress_tm_100msec defines the time + to call the callback while async execution is running + @param progress_cb: callback + @param progress_tm_100msec: time in 100 msec + ''' + assert(isinstance(progress_tm_100msec, int)) + self._progress_cb = progress_cb + self._num_timer_calls = progress_tm_100msec + + def SetFinishedCb(self, finished_cb): + ''' + Set callback, when async execution has finished + @param finished_cb: callback + ''' + self.finished_cb = finished_cb + + def _notify(self, event): + # Read from result queue and error queue + for que_obj, cb_obj in zip( \ + [self._q_err, self._q_result], + [self._err_cb, self._result_cb]): + + try: + txt_element = que_obj.get(False) + cb_obj(txt_element) + except Empty: + pass + + if self._thread.finished.isSet(): + self._wxtimer.Stop() + self.finished_cb() + else: + pass + + self._cur_timer_calls += 1 + + # self._num_timer_calls is set in SetProgressCb + if self._cur_timer_calls % self._num_timer_calls == 0: + self._progress_cb() + + event.Skip() + + def _start_in_thread(self): + # Start python thread and wx timer + self._q_result = Queue() + self._q_err = Queue() + + self._thread = Timer(0, self._async_callable, [self._args, + self._q_result, self._q_err]) + self._thread.start() + + self._wxtimer = wx.Timer(self) + self._wxtimer.SetOwner(self) + self.Bind(wx.EVT_TIMER, self._notify) + self._wxtimer.Start(self._timer_res) + + class MainController: + """ Contoller class. Part of MVC + Responsibility: Glue view and model code: + - Handle all events from view (p.e. button) + """ def __init__(self, view): self._view = view # Give controller object to the view self._view.set_controller(self) - + + # Template file for new project + self._prjTemplateFile = "./ProjectTemplate.xml" + + # Tag to appera in title of main window + self._changedTag = " [changed]" + + # Dict with id's of recent projects + self._recentPrjDict = {} + + # Parameters + self.param = MainParameter() + + # To access prj settings (settings are related to view) + self._prj_settings = ProjectSettings(self._view, None) + + # To access ide settings (settings are related to MainParameter) + self._ide_settings = IdeSettings(None, self.param) + + # To count code generation durance + self._tm_code_gen = 0 + + # Object to control async execution + self._async_runner = None + + self._setup_ide_settings() + + def ExitIde(self): + ''' + Exit IDE. Exit can be canceled. + @return: False if exit is canceled else True + ''' + if not self._check_and_save_project(): + return False + + if self._async_runner != None: + if self._async_runner.GetRunning(): + self.OutputWarning("Cannot exit. "\ + "Ide is doing async execution ...") + return False + + new_list = [] + + # build list with max max_num_recent_projects elements + last_idxs = self._recentPrjDict.keys() + last_idxs.reverse() # Last added should be saved + for idx in last_idxs: + new_list.append(self._recentPrjDict[idx]) + if len(new_list) >= self.param.max_num_recent_projects.get(): + break + + self.param.recent_prj_list.set(new_list) + + self._ide_settings.save() + + self._view.Destroy() + + return True + + def OnRecentPrjLoad(self, event): + ''' + Callback from the file menue. (Recent projects) + @param event: wx event. + ''' + project_file_name = self._recentPrjDict[event.GetId()] + self._load_project(project_file_name) + def DoRemoveCurInclude(self): - """Remove current selected Include item""" + ''' + Remove current selected Include item + ''' + cur_num = self._view.currentItemInclude if None == cur_num: return self._view.listIncludes.DeleteItem(cur_num) + self._set_settings_changed() def DoRemoveCurMacro(self): - """Remove current selected Macro item""" + ''' + Remove current selected Macro item + ''' + cur_num = self._view.currentItemMacro if None == cur_num: return self._view.listMacros.DeleteItem(cur_num) + self._set_settings_changed() + + + def CountCodeGenSec(self, reset=False): + ''' + Count the time of code generation. Must be called once per second. + Update UI + @param reset: Boolean to reset the counter + ''' + if reset: + self._tm_code_gen = 0 + else: + self._tm_code_gen += 1 + self._view.statusBar.SetStatusText(number=1, text=u'Time: %d sec' % \ + self._tm_code_gen) - def GenXmlCode(self): - """ Generate XML code""" - self._appendOutText("Generation of XML code staretd") + def OutputError(self, err_txt): + ''' + Print error text in output window + @param err_txt: Text to display + ''' + self._append_out_text(err_txt, self._text_error) - for i in range(0,5): - self._view.listIncludes.InsertStringItem(i, "First Element - this is a long") - + def OutputWarning(self, err_txt): + ''' + Print warning text in output window + @param err_txt: Text to display + ''' + self._append_out_text(err_txt, self._text_warn) + + def OutputInfo(self, inf_txt): + ''' + Print info text in output window + @param inf_txt: + ''' + self._append_out_text(inf_txt, self._text_info) + + def OutputCode(self, code_txt): + ''' + Append text in the code window + @param code_txt: Text to append + ''' + self._view.textCode.AppendText(code_txt) + + def GenCodeFinished(self): + ''' + Inform that code generation has finished + ''' + self._enable_generation_widgets(True) + + def GenCppCode(self): - """ Generate Boost.Python code""" - self._appendOutText("Generation of C++ code for Boost.Python started") + ''' + Generate Boost.Python code + ''' + self._enable_generation_widgets(False) + self._view.textCode.SetValue("") + self._append_out_text("Generation of C++ code for Boost.Python started") + self.CountCodeGenSec(reset=True) + params = self._get_gccxml_params(False) + + gen_xml_obj = AsyncExecHandler(code_generator.gen_cpp, params) + self._start_async_exec(gen_xml_obj) + def GenPyPPCode(self): - """ Generate Py++ code""" - self._appendOutText("Generation of Py++ code started") + ''' + Generate Py++ code + ''' + self._enable_generation_widgets(False) + self._view.textCode.SetValue("") + self._append_out_text("Generation of Py++ code started") + self.CountCodeGenSec(reset=True) + params = self._get_gccxml_params(False) + + gen_xml_obj = AsyncExecHandler(code_generator.gen_pypp, params) + self._start_async_exec(gen_xml_obj) + + def GenXmlCode(self): + ''' + Generate XML code + ''' + self._enable_generation_widgets(False) + self._append_out_text("Generation of XML code started") + self._view.textCode.SetValue("") + self.CountCodeGenSec(reset=True) + + params = self._get_gccxml_params(False) + + gen_xml_obj = AsyncExecHandler(code_generator.gen_xml, params) + self._start_async_exec(gen_xml_obj) + def OpenDlgHeader(self): - """Open dialog to get header file""" - self._openFileDlgWithRelatedWxText( self._view.textHeader, + ''' + Open dialog to get header file + ''' + self._open_file_dlg_text_ctrl( self._view.textHeader, "Choose a Header file", "Header (*.h)|*.h|All Files(*)|*") def OpenDlgGccXml(self): - """Open dialog to get GccXml executable""" - self._openFileDlgWithRelatedWxText( self._view.textGccXml, + ''' + Open dialog to get GccXml executable + ''' + + self._open_file_dlg_text_ctrl( self._view.textGccXml, "Choose GccXml executable", "All Files(*)|*") def OpenDlgEditCurInclude(self): - """ """ + ''' + Open dialog to edit current include + ''' cur_num = self._view.currentItemInclude if None == cur_num: return @@ -71,11 +346,14 @@ self._view.listIncludes.DeleteItem(cur_num) self._view.listIncludes.InsertStringItem( cur_num, dialog.GetPath()) + self._set_settings_changed() finally: dialog.Destroy() def OpenDlgEditCurMacro(self): - """ """ + ''' + Open dialog to edit current macro + ''' cur_num = self._view.currentItemMacro if None == cur_num: return @@ -87,12 +365,16 @@ new_macro = dialog.textMacro.GetLineText(0) self._view.listMacros.InsertStringItem(cur_num, new_macro) + self._set_settings_changed() finally: dialog.Destroy() def OpenDlgAddInclude(self): - """ """ - dialog = wx.DirDialog(self._view, "Choose include directory", ".") + ''' + Open Dialog to add a include path + ''' + dialog = wx.DirDialog(self._view, "Choose include directory", + self.param.last_inc_path.get()) try: if dialog.ShowModal() == wx.ID_OK: @@ -100,16 +382,20 @@ dir_path = dialog.GetPath() # Check weather path is already in list - if not self._checkItemIsInList(dir_path, + if not self._check_item_in_list(dir_path, self._view.listIncludes): self._view.listIncludes.InsertStringItem( cur_num, dir_path) + self.param.last_inc_path.set(str(dir_path)) + self._set_settings_changed() finally: dialog.Destroy() def OpenDlgAddMacro(self): - """ """ + ''' + Open dialog to add a macro + ''' dialog = dialog_macro.MacroDialog(self._view) if dialog.ShowModal() == wx.OK: @@ -118,12 +404,192 @@ new_macro = dialog.textMacro.GetLineText(0) # Check weather macro is already in list - if not self._checkItemIsInList(new_macro, self._view.listMacros): + if not self._check_item_in_list(new_macro, self._view.listMacros): self._view.listMacros.InsertStringItem(cur_num, new_macro) + self._set_settings_changed() + + def OpenDlgLoadProject(self): + ''' + Open dialog to load a project + ''' + from xml.parsers.expat import ExpatError + + dialog = wx.FileDialog(self._view, "Load existing Py++ project", + self.param.last_prj_path.get(), + "", "Project files (*.xml)|*.xml|All Files(*)|*", wx.OPEN) + + try: + if dialog.ShowModal() == wx.ID_OK: + self.ClearUi() + project_file_name = dialog.GetPath() + self._load_project(project_file_name) + + except ExpatError: + self._append_out_text("XML parser error in file:%s" % \ + dialog.GetPath(), self._text_error) + except Exception: + self._append_out_text("Error loading file:%s" % \ + dialog.GetPath(), self._text_error) + raise + + finally: + dialog.Destroy() + + def OpenDlgSaveProject(self, new_file=False): + ''' + Open dialog to save a project + @param new_file: Boolean weather to use a new file + @return: False if project save was canceled + ''' + # Is current file prj template? + if self._prj_settings.get_file_name() == self._prjTemplateFile: + project_file_name = self._open_new_prj_file() + else: + if new_file: + project_file_name = self._open_new_prj_file() + else: + project_file_name = self._prj_settings.get_file_name() + + try: + if project_file_name == None: + return False # prior dialog skipped + + if not self._save_project(project_file_name): + return False + except Exception: + self._append_out_text("Error saving file:%s" % \ + project_file_name, self._text_error) + raise + + return True + + def DoProjectNew(self): + ''' + Open dialog for new Project + ''' + self._load_project(self._prjTemplateFile) + + def ClearUi(self): + ''' + Clear all controls of UI + ''' + + # Clear text ctrls + for textCtrl in [self._view.textHeader, self._view.textGccXml, + self._view.textOutput, self._view.textCode]: + textCtrl.Clear() + + # clear list ctrls + for listCtrl in [self._view.listMacros, self._view.listIncludes]: + tot_num = listCtrl.GetItemCount() + for cur_num in range(tot_num, 0, -1): + listCtrl.DeleteItem(cur_num-1) + def _cancel_if_file_exist(self, project_file_name, parent_dlg=None): + # check for overwriting of file + if os.path.exists(project_file_name): + dialog = wx.MessageDialog(parent_dlg, + "%s exists. Should it be overwritten?" % \ + (project_file_name), pos=self._view.GetPosition()) + + if dialog.ShowModal() == wx.ID_CANCEL: + return True + else: + return False - def _openFileDlgWithRelatedWxText(self, + # Return True if successfull + def _check_and_save_project(self): + if self._prj_settings.get_changed(): + + # Anyhow pos is working + dialog = wx.MessageDialog(self._view, "Current project changed. "\ + "Should project be saved?", style = (wx.YES_NO), + pos=self._view.GetPosition()) + + user_input = dialog.ShowModal() + dialog.Destroy() + + if user_input == wx.ID_NO: + return True + elif user_input == wx.ID_YES: + return self.OpenDlgSaveProject() + else: + return True + + def _load_project(self, project_file_name): + self._check_and_save_project() + self.ClearUi() + self._prj_settings.load(project_file_name) + self._set_prj_filename_in_title(project_file_name) + + if project_file_name != self._prjTemplateFile: + self.param.last_prj_path.set(unicode(os.path.dirname(project_file_name))) + + self._add_to_prj_history(project_file_name) + + def _save_project(self, project_file_name): + + self._prj_settings.save(project_file_name) + self._reset_settings_changed() + self.param.last_prj_path.set(os.path.dirname(project_file_name)) + self._set_prj_filename_in_title(project_file_name) + self._add_to_prj_history(project_file_name) + + return True + + + def _open_new_prj_file(self): + + project_file_name = None + + dialog = wx.FileDialog(self._view, + "Save Py++ project to new file", self.param.last_prj_path.get(), "", + "Project files (*.xml)|*.xml|All Files(*)|*", wx.FD_SAVE) + + if dialog.ShowModal() == wx.ID_OK: + + project_file_name = dialog.GetPath() + + if self._cancel_if_file_exist(project_file_name, dialog): + project_file_name = None + + dialog.Destroy() + + return project_file_name + + def _add_to_prj_history(self, prj_filename): + +# This don't work, see below +# max_num_history = self.param.max_num_recent_projects.get() + + + if prj_filename in self._recentPrjDict.values(): + return + + if prj_filename == self._prjTemplateFile: + return + + file_name = os.path.split(prj_filename)[1] + + menue = self._view.menueRecentPrj + new_item_id = id=wx.NewId() + new_item = wx.MenuItem(menue, help=prj_filename, + id=new_item_id, + kind=wx.ITEM_NORMAL, text=file_name) + + menue.PrependItem(new_item) + + self._view.Bind(wx.EVT_MENU, self.OnRecentPrjLoad, + id=new_item_id) + + self._recentPrjDict[new_item_id] = prj_filename + +# Doesn't work. Dont know why +# if menue.GetMenuItemCount() > max_num_history: +# menue.Remove(max_num_history) + + def _open_file_dlg_text_ctrl(self, related_wx_text, caption_txt="", file_filter="All Files(*)|*", @@ -135,10 +601,67 @@ if dialog.ShowModal() == wx.ID_OK: related_wx_text.Clear() related_wx_text.AppendText(dialog.GetPath()) + self._set_settings_changed() finally: dialog.Destroy() - def _checkItemIsInList(self, item, wx_list): + def _set_settings_changed(self): + self._prj_settings.set_changed() + title_str = self._view.GetTitle() + if not self._changedTag in title_str: + title_str += self._changedTag + self._view.SetTitle(title_str) + + def _enable_generation_widgets(self, state): + ctrls = [self._view.butGenCpp, self._view.butGenXml, \ + self._view.butGenPyPP] + + for ctrl in ctrls: + ctrl.Enable(state) + + def _get_gccxml_params(self, verbose): + + gcc_xml = self._prj_settings.get_param('gccXmlSettings.gccXmlPath') + inc_path_list = eval(self._prj_settings.get_param( + 'gccXmlSettings.includePathList')) + macro_list = eval(self._prj_settings.get_param( + 'gccXmlSettings.macroList')) + + if verbose: + self._append_out_text(" "+ gcc_xml) + self._append_out_text(" "+ str(inc_path_list)) + self._append_out_text(" "+ str(macro_list)) + + + return (gcc_xml, inc_path_list, macro_list) + + def _setup_ide_settings(self): + + # load ide settings + self._ide_settings.load() + prj_list = self.param.recent_prj_list.get() + prj_list.reverse() + for prj in prj_list: + self._add_to_prj_history(prj) + + def _set_prj_filename_in_title(self, filename): + + if filename == self._prjTemplateFile: + filename = "New project" + + title_str = self._view.GetTitle() + start_idx = title_str.find("(") + end_idx = title_str.find(")") + fnamstr = title_str[start_idx:end_idx+1] + title_str = title_str.replace(fnamstr, "(" + filename + ")") + self._view.SetTitle(title_str) + + def _reset_settings_changed(self): + title_str = self._view.GetTitle() + title_str = title_str.replace(self._changedTag, "") + self._view.SetTitle(title_str) + + def _check_item_in_list(self, item, wx_list): idx = wx_list.FindItem(0, item) if idx == -1: return False @@ -146,7 +669,7 @@ return True - def _appendOutText(self, text, type_of_text = 0): + def _append_out_text(self, text, type_of_text = 0): """ append text with different error level""" text_ctrl = self._view.textOutput type_txt = "INFO" @@ -164,7 +687,16 @@ text_ctrl.SetDefaultStyle(wx.TextAttr(wx.BLACK)) text_ctrl.AppendText(type_txt + ": " + text + "\n") + # Start async execution + def _start_async_exec(self, async_runner): + self._async_runner = async_runner + self._async_runner.SetErrorOutput(self.OutputError) + self._async_runner.SetResultOutput(self.OutputCode) + self._async_runner.SetFinishedCb(self.GenCodeFinished) + self._async_runner.SetProgressCb(self.CountCodeGenSec, 10) + self._async_runner.Start() + # levels _text_info = 0 # Text has informational character _text_warn = 1 # Text has warning character _text_error = 2 # Text has error character Added: pyplusplus_dev/ide/doc/EnterpriseArchitectModel.eap =================================================================== (Binary files differ) Property changes on: pyplusplus_dev/ide/doc/EnterpriseArchitectModel.eap ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: pyplusplus_dev/ide/doc/IdeInBoa.png =================================================================== (Binary files differ) Property changes on: pyplusplus_dev/ide/doc/IdeInBoa.png ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: pyplusplus_dev/ide/doc/PackageOverview.png =================================================================== (Binary files differ) Property changes on: pyplusplus_dev/ide/doc/PackageOverview.png ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: pyplusplus_dev/ide/doc/ReadmeForDevelopment.odt =================================================================== (Binary files differ) Property changes on: pyplusplus_dev/ide/doc/ReadmeForDevelopment.odt ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: pyplusplus_dev/ide/ide.py =================================================================== --- pyplusplus_dev/ide/ide.py 2007-05-01 18:06:08 UTC (rev 1028) +++ pyplusplus_dev/ide/ide.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -28,6 +28,9 @@ self.main.Show() self.SetTopWindow(self.main) + + controller.DoProjectNew() + return True def main(): Deleted: pyplusplus_dev/ide/model/ProjectFileTemplatate.xml =================================================================== --- pyplusplus_dev/ide/model/ProjectFileTemplatate.xml 2007-05-01 18:06:08 UTC (rev 1028) +++ pyplusplus_dev/ide/model/ProjectFileTemplatate.xml 2007-05-02 14:58:58 UTC (rev 1029) @@ -1,10 +0,0 @@ -<!-- Template file for pyplusplus IDE --> -<pyPPProject> - <gccXmlSettings - headerPath="" - includePathList="[]" - gccXmlPath="" - macroList="[]" - /> - </gccXmlSettings> -</pyPPProject> \ No newline at end of file Modified: pyplusplus_dev/ide/model/__init__.py =================================================================== --- pyplusplus_dev/ide/model/__init__.py 2007-05-01 18:06:08 UTC (rev 1028) +++ pyplusplus_dev/ide/model/__init__.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2004 Roman Yakovenko. +# 2007 Alexander Eisenhuth +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) Added: pyplusplus_dev/ide/model/code_generator.py =================================================================== --- pyplusplus_dev/ide/model/code_generator.py (rev 0) +++ pyplusplus_dev/ide/model/code_generator.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright 2004 Roman Yakovenko. +# 2007 Alexander Eisenhuth +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +''' +Code generation is started in a own thread. To exchange data, queues +are used. +''' + +import time + +def gen_xml(params, q_result, q_error): + ''' + Generate XML code + @param params: List of parameters [gccxml,incPath,macros] + @param q_result: python queue to put result in + @param q_error: python queue to put error in + @return None (isn't evaluated) + ''' + + gcc_xml = params[0] + inc_path_list = params[1] + macro_list = params[2] + + time.sleep(1) + q_result.put("This is dummy data of gen_xml\n") + q_error.put("This is dummy error gen_xml") + time.sleep(4) + q_result.put("This is dummy data of gen_xml") + q_error.put("This is dummy error of gen_xml") + +def gen_cpp(params, q_result, q_error): + ''' + Generate cpp (Boost.Python) code + @param params: List of parameters [gccxml,incPath,macros] + @param q_result: python queue to put result in + @param q_error: python queue to put error in + @return None (isn't evaluated) + ''' + pass + +def gen_pypp(params, q_result, q_error): + ''' + Generate Python (Py++) code + @param params: List of parameters [gccxml,incPath,macros] + @param q_result: python queue to put result in + @param q_error: python queue to put error in + @return None (isn't evaluated) + ''' + pass + + \ No newline at end of file Added: pyplusplus_dev/ide/model/etree_extension.py =================================================================== --- pyplusplus_dev/ide/model/etree_extension.py (rev 0) +++ pyplusplus_dev/ide/model/etree_extension.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# Copyright 2004 Roman Yakovenko. +# 2007 Alexander Eisenhuth +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +from xml.etree.ElementTree import ElementTree + +class ElNotFound(Exception): + ''' + Exception element not found + ''' + pass + +class AttribNotFound(Exception): + ''' + Execption attribute not found + ''' + pass + +class XmlElFinder: + ''' + Find a element in a Tree (xml.etree.ElementTree) + ''' + def __init__(self, root_el): + self._root = root_el # Must be of typ xml.etree.ElementTree + self._finder = None + + def get_obj(self, element_text): + ''' + Find and return XML element object (Element) + @param element_text: Textual description of element to find. Hirarchie + is seperated by '.' Ex.: 'rootNode.childNode.element' + @return: Element object + ''' + + child_el = None + self._finder = self._root + + for el_name in element_text.split("."): + child_el = self._finder.find(el_name) + + # Child not found + if child_el == None: + raise ElNotFound, "Cannot find Element %s in %s" % \ + (el_name, element_text) + + self._finder = ElementTree(child_el) + + return child_el + +class XmlAttribFinder: + '''Find a attribute in a Tree (xml.etree.ElementTree)''' + def __init__(self, root_el): + self._el_finder = XmlElFinder(root_el) + + def get_obj(self, attrib_text): + ''' + Find and return attribute as tuple + @param attrib_text: Textual description of attribute to find. Hirarchie + is seperated by '.' Ex.: 'rootNode.childNode.element.attrib1' + @return: (<allAttribDict>, <relatedAttribName>) This means a tuple + cintaining all attributes of parent element and name of attribute. + ''' + el_text = attrib_text[0 : attrib_text.rfind(".")] + attrib_name = attrib_text[attrib_text.rfind(".") + 1 : ] + element = self._el_finder.get_obj(el_text) + return (element.attrib, attrib_name) + + def get_val(self, attrib_text): + ''' + Find and return attribute value + @param attrib_text: Textual description of attribute to find. Hirarchie + is seperated by '.' Ex.: 'rootNode.childNode.element.attrib1' + @return Attribute value + ''' + attrib_value = None + + el_text = attrib_text[0 : attrib_text.rfind(".")] + attrib_name = attrib_text[attrib_text.rfind(".") + 1 : ] + element = self._el_finder.get_obj(el_text) + + try: + attrib_value = element.attrib[attrib_name] + + except KeyError: + raise AttribNotFound, "Cannot find attribute '%s' in element '%s'" \ + % (attrib_name, el_text) + + return attrib_value + + + \ No newline at end of file Added: pyplusplus_dev/ide/model/settings.py =================================================================== --- pyplusplus_dev/ide/model/settings.py (rev 0) +++ pyplusplus_dev/ide/model/settings.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,383 @@ +# -*- coding: utf-8 -*- +# Copyright 2004 Roman Yakovenko. +# 2007 Alexander Eisenhuth +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +''' +This module contains classes for settings and parameters +''' +from model.etree_extension import XmlAttribFinder +from shutil import copyfile +# Python 2.5 +from xml.etree.ElementTree import ElementTree +import os +import wx + +class BaseSettings: + ''' + Base class for settings as a collection of parameters + ''' + def __init__(self, ide_ui, param_obj): + self._file_name = None # filename of settings + self._ide_ui = ide_ui + self._param_obj = param_obj + + def set_changed(self): + ''' + Inform, that any parameter has changed + ''' + self._changed = True + + def get_changed(self): + ''' + Access changed information + ''' + return self._changed + + def get_file_name(self): + ''' + Return file path of settings + ''' + return self._file_name + + def get_param(self, xml_attrib_name): + ''' + Return parameter as string + @param xml_attrib_name: Name of parameter + ''' + + ide_ui = self._ide_ui #@UnusedVariable + param_obj = self._param_obj #@UnusedVariable + + for cur_name, rel_obj_str in self._param_list: + if cur_name == xml_attrib_name: + param = ParameterAccess(eval(rel_obj_str)) + return param.get_related_object_value() + + raise RuntimeError, "Parameter:%s not defined" % xml_attrib_name + + def _load(self, file_name):#@UnusedVariable + ''' + Load settings from given file + @param file_name: path of file. Format is XML. + @param ide_ui: wxPython ctrl of ide + @param param_obj: object holding parameters + ''' + self._file_name = file_name + + ide_ui = self._ide_ui #@UnusedVariable + param_obj = self._param_obj #@UnusedVariable + + root_element = ElementTree(file=file_name).getroot() + attrib_finder = XmlAttribFinder(root_element) + + # Loop through our parameter list + for xml_attrib_name, rel_obj_str in self._param_list: + # In eval we need ide_ui and param_obj + # print "XML:%s obj_str:%s" % (xml_attrib_name, rel_obj_str) + param = ParameterAccess(eval(rel_obj_str)) + attrib_val = attrib_finder.get_val(xml_attrib_name) + param.update_related_object(attrib_val) + + def _save(self, file_name): + ''' + Save settings into the given file + @param file_name: path of file. Format is XML. + @param ide_ui: wxPython ctrl of ide + @param param_obj: object holding parameters + ''' + + self._file_name = file_name + + ide_ui = self._ide_ui #@UnusedVariable + param_obj = self._param_obj #@UnusedVariable + + etree = ElementTree(file=file_name) + root_element = etree.getroot() + + attrib_finder = XmlAttribFinder(root_element) + + for xml_attrib_name, rel_obj_str in self._param_list: + # In eval we need ide_ui and param_obj + param = ParameterAccess(eval(rel_obj_str)) + attrib, attrib_key = attrib_finder.get_obj(xml_attrib_name) + attrib[attrib_key] = param.get_related_object_value() + + # Write XML-File + etree.write(file_name) + + self._changed = False + + +class IdeSettings(BaseSettings): + ''' + Abstracts the settings of the ide + ''' + def __init__(self, ide_ui, param_obj): + + self._param_list = [ + ('project.maxNumInMenue', + 'param_obj.max_num_recent_projects'), + + ('project.recentProjects', + 'param_obj.recent_prj_list'), + + ('project.lastPrjPath', + 'param_obj.last_prj_path'), + + ('project.lastIncPath', + 'param_obj.last_inc_path') + + ] + + BaseSettings.__init__(self, ide_ui, param_obj) + + def load(self): + ''' + Load ide settings + ''' + ide_file_name = self._get_ide_file_name() + self._load(ide_file_name) + + def save(self): + ''' + Save settings + + ''' + ide_file_name = self._get_ide_file_name() + self._save(ide_file_name) + + def _get_ide_file_name(self): + ''' + Get the file name of the ide settings filE. If the file doesen't exist + we copy a template fiel into the location + @param file_path: File path to check + ''' + try: + home_dir = os.environ['HOME'] + except KeyError: + home_dir = os.getcwd() + + file_path = os.path.join(home_dir, self._ide_file_name ) + + if not os.path.exists(file_path): + print "File copied !!" + copyfile(self._ide_template_path, file_path) + + return file_path + + # Project template file + _ide_template_path = "./IdeTemplate.xml" + _ide_file_name = ".pyplusplus_ide" + +class ProjectSettings(BaseSettings): + ''' + Abstracts the settings of a project + ''' + def __init__(self, ide_ui, param_obj): + + BaseSettings.__init__(self, ide_ui, param_obj) + + # This list contains tuples of ui elements and the related + # attribute of the project file + self._param_list = [ + ('gccXmlSettings.headerPath', + 'ide_ui.textHeader'), + + ('gccXmlSettings.gccXmlPath', + 'ide_ui.textGccXml'), + + ('gccXmlSettings.includePathList', + 'ide_ui.listIncludes'), + + ('gccXmlSettings.macroList', + 'ide_ui.listMacros'), + + ('guiGeometry.size', + 'ide_ui'), + + ('guiGeometry.horizontalSplitter', + 'ide_ui.splitterHorizontal'), + + ('guiGeometry.verticalSplitter', + 'ide_ui.splitterVertical') + ] + + self._changed = False + + def load(self, file_name): + ''' + Load settings from given file + @param file_name: path of file. Format is XML. + ''' + self._load(file_name) + + def save(self, file_name): + ''' + Save settings into the given file + @param file_name: path of file. Format is XML. + ''' + # Assert new prj file + self._assert_prj_file(file_name) + self._save(file_name) + + def _assert_prj_file(self, file_path): + ''' + Assert that the given file exists. If not we copy the template + @param file_path: File path to check + ''' + if not os.path.exists(file_path): + copyfile(self._prj_template_path, file_path) + + # Project template file + _prj_template_path = "./ProjectTemplate.xml" + +class ParameterContainer: + ''' + Abstracts one parameter with getter and setter + ''' + def __init__(self, p_type, init_val=None): + self._type = p_type + self._val = init_val + + def set(self, val): + ''' + Set the related parameter + @param val: new value + ''' + self._val = self._type(val) + + def get(self): + ''' + Get the related parameter + ''' + + return self._val + + def get_type(self): + ''' + Get the (python) type of the parameter + ''' + return self._type + + +class ParameterAccess: + ''' + Abstracts the access (setting and getting) of a parameter. This class + has knowledge of wxPython controls (and know how to handle them). + Currently we support: + - wxTextCtrl + - wxListCtrl + - wxFrame + - wxSplitterWindow + - ParameterContainer + ''' + def __init__(self, related_object): + self._rel_obj = related_object + + def update_related_object(self, param_value): + ''' + Set the value of the related object with given param_value + @param param_value: parameter value to use + ''' + + #print ">>> rel obj:", self._rel_obj, "val:", param_value + + # Handle wxTextCtrl + if isinstance(self._rel_obj, wx.TextCtrl): + # We need a string + str_param_value = str(param_value) + self._rel_obj.SetValue(str_param_value) + + # Handle wxListCtrl + elif isinstance(self._rel_obj, wx.ListCtrl): + list_param_value = eval(param_value) + assert(isinstance(list_param_value, list)) + cur_num = 0 + for list_el in list_param_value: + self._rel_obj.InsertStringItem(cur_num, list_el) + cur_num += 1 + + # Handle wxFrame + elif isinstance(self._rel_obj, wx.Frame): + tup_size = eval(param_value) + assert(isinstance(tup_size, tuple)) + x = tup_size[0] + y = tup_size[1] + wid = tup_size[2] + high = tup_size[3] + self._rel_obj.SetDimensions(x, y, wid, high) + + # SplitterWindow + elif isinstance(self._rel_obj, wx.SplitterWindow): + int_sash_pos = int(param_value) + self._rel_obj.SetSashPosition(int_sash_pos, True) + + # ParameterContainer + elif isinstance(self._rel_obj, ParameterContainer): + if self._rel_obj.get_type() == list: + eval_param_value = eval(param_value) + elif self._rel_obj.get_type() == int: + eval_param_value = eval(param_value) + elif self._rel_obj.get_type() == str: + eval_param_value = param_value + elif self._rel_obj.get_type() == unicode: + eval_param_value = unicode(param_value) + else: + raise RuntimeError, "Unupported typ found", \ + self._rel_obj.get_type() + + cnv_param_val = self._rel_obj.get_type()(eval_param_value) + self._rel_obj.set(cnv_param_val) + + else: + raise RuntimeError, "Unsupported type %s" % self._rel_obj.__class__ + + def get_related_object_value(self): + ''' + Get value of the related object + @return value as string + ''' + + #print ">>>", self._rel_obj + + str_param_value = "Value not specified" + + # Handle wxTextCtrl + if isinstance(self._rel_obj, wx.TextCtrl): + # We need a string + str_param_value = self._rel_obj.GetValue() + + # Handle wxListCtrl + elif isinstance(self._rel_obj, wx.ListCtrl): + + list_of_ctrl = [] + + for cur_num in range(self._rel_obj.GetItemCount()): + list_el = self._rel_obj.GetItemText(cur_num) + list_of_ctrl.append(list_el) + cur_num += 1 + str_param_value = str(list_of_ctrl) + + # Handle wxFrame + elif isinstance(self._rel_obj, wx.Frame): + x,y = self._rel_obj.GetPositionTuple() + wid, high = self._rel_obj.GetSizeTuple() + dim = (x,y,wid,high) + str_param_value = str(dim) + + # SplitterWindow + elif isinstance(self._rel_obj, wx.SplitterWindow): + sash_pos = self._rel_obj.GetSashPosition() + str_param_value = str(sash_pos) + + # Handle ParameterContainer + elif isinstance(self._rel_obj, ParameterContainer): + str_param_value = str(self._rel_obj.get()) + + else: + raise RuntimeError, "Unsupported type %s" % self._rel_obj.__class__ + + return str_param_value + \ No newline at end of file Added: pyplusplus_dev/ide/test/test_attrib_finder.py =================================================================== --- pyplusplus_dev/ide/test/test_attrib_finder.py (rev 0) +++ pyplusplus_dev/ide/test/test_attrib_finder.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -0,0 +1,41 @@ +import sys +import unittest +# Python 2.5 +from xml.etree.ElementTree import ElementTree + +if ".." not in sys.path: + sys.path.append("..") + +from model.etree_extension import XmlAttribFinder, AttribNotFound +prj_template_file = "../model/ProjectTemplate.xml" + + +class TestAttribFinder(unittest.TestCase): + + def setUp(self): + ''' Executed before each test ''' + self._root_el_prj_file = ElementTree(file=prj_template_file).getroot() + + def test_find(self): + '''Test get_val methode with project template''' + af_obj = XmlAttribFinder(self._root_el_prj_file) + + header_path = af_obj.get_val('gccXmlSettings.headerPath') + self.assertEqual(header_path, "") + + inc_path_list = af_obj.get_val('gccXmlSettings.includePathList') + self.assertEqual(eval(inc_path_list), []) + + inc_path_list_obj = af_obj.get_obj('gccXmlSettings.includePathList') + + self.assertTrue(inc_path_list_obj[1], []) + + self.assertRaises(AttribNotFound, af_obj.get_val, + ('gccXmlSettings.xxx')) + + def tearDown(self): + ''' Executed after each test ''' + del self._root_el_prj_file + +if __name__ == "__main__": + unittest.main() Modified: pyplusplus_dev/ide/views/frame_main.py =================================================================== --- pyplusplus_dev/ide/views/frame_main.py 2007-05-01 18:06:08 UTC (rev 1028) +++ pyplusplus_dev/ide/views/frame_main.py 2007-05-02 14:58:58 UTC (rev 1029) @@ -15,10 +15,10 @@ def create(parent): return MainFrame(parent) -[wxID_MAINFRAMEMENUEFILEEXIT, wxID_MAINFRAMEMENUEFILENEW, - wxID_MAINFRAMEMENUEFILEOPEN, wxID_MAINFRAMEMENUEFILERECENT, - wxID_MAINFRAMEMENUEFILESAVE, -] = [wx.NewId() for _init_coll_menueFile_Items in range(5)] +[wxID_MAINFRAMEMENUEFILEEXIT, wxID_MAINFRAMEMENUEFILEITEMS7, + wxID_MAINFRAMEMENUEFILENEW, wxID_MAINFRAMEMENUEFILEOPEN, + wxID_MAINFRAMEMENUEFILERECENT, wxID_MAINFRAMEMENUEFILESAVE, +] = [wx.NewId() for _init_coll_menueFile_Items in range(6)] [wxID_MAINFRAMEMENUINCLUDESADDINC, wxID_MAINFRAMEMENUINCLUDESITEMS1, ] = [wx.NewId() for _init_coll_menuIncludes_Items in range(2)] @@ -154,20 +154,24 @@ def _init_coll_menueFile_Items(self, parent): # generated method, don't edit - parent.Append(help=u'Create new Project', id=wxID_MAINFRAMEMENUEFILENEW, - kind=wx.ITEM_NORMAL, text=u'&New Project') - parent.Append(help=u'Open existing Project', + parent.Append(help=u'Create new project with default settings', + id=wxID_MAINFRAMEMENUEFILENEW, kind=wx.ITEM_NORMAL, + text=u'&New project') + parent.Append(help=u'Open existing project', id=wxID_MAINFRAMEMENUEFILEOPEN, kind=wx.ITEM_NORMAL, - text=u'&Open Project') - parent.Append(help=u'Save current Project', + text=u'&Open project') + parent.Append(help=u'Save current project', id=wxID_MAINFRAMEMENUEFILESAVE, kind=wx.ITEM_NORMAL, - text=u'&Save Project') + text=u'&Save project') + parent.Append(help=u'Save current project under a different filename', + id=wxID_MAINFRAMEMENUEFILEITEMS7, kind=wx.ITEM_NORMAL, + text=u'S&ave as ...') parent.AppendSeparator() - parent.AppendMenu(help=u'Open recently used Project', - id=wxID_MAINFRAMEMENUEFILERECENT, submenu=wx.Menu(), - text=u'Recent Projects') + parent.AppendMenu(help=u'Open recently used project', + id=wxID_MAINFRAMEMENUEFILERECENT, submenu=self.menueRecentPrj, + text=u'&Recent projects') parent.AppendSeparator() - parent.Append(help='', id=wxID_MAINFRAMEMENUEFILEEXIT, + parent.Append(help=u'Exit IDE', id=wxID_MAINFRAMEMENUEFILEEXIT, kind=wx.ITEM_NORMAL, text=u'&Exit') self.Bind(wx.EVT_MENU, self.OnMenueFileNewMenu, id=wxID_MAINFRAMEMENUEFILENEW) @@ -177,6 +181,10 @@ id=wxID_MAINFRAMEMENUEFILESAVE) self.Bind(wx.EVT_MENU, self.OnMenueFileExitMenu, id=wxID_MAINFRAMEMENUEFILEEXIT) + self.Bind(wx.EVT_MENU, self.OnMenueFileSaveAsMenu, + id=wxID_MAINFRAMEMENUEFILEITEMS7) + self.Bind(wx.EVT_MENU, self.OnMenueFileRecentMenu, + id=wxID_MAINFRAMEMENUEFILERECENT) def _init_coll_menuMacros_Items(self, parent): # generated method, don't edit @@ -212,13 +220,12 @@ def _init_coll_statusBar_Fields(self, parent): # generated method, don't edit - parent.SetFieldsCount(3) + parent.SetFieldsCount(2) - parent.SetStatusText(number=0, text=u'<helptextOrStatus>') - parent.SetStatusText(number=1, text=u'<parseTime>') - parent.SetStatusText(number=2, text=u'<compileTime>') + parent.SetStatusText(number=0, text=u'') + parent.SetStatusText(number=1, text=u'') - parent.SetStatusWidths([-1, -1, -1]) + parent.SetStatusWidths([-1, -1]) def _init_sizers(self): # generated method, don't edit @@ -278,6 +285,8 @@ self.menuMacros = wx.Menu(title='') + self.menueRecentPrj = wx.Menu(title=u'') + self._init_coll_menueFile_Items(self.menueFile) self._init_coll_menuBar_Menus(self.menuBar) self._init_coll_menuIncludes_Items(self.menuIncludes) @@ -286,11 +295,12 @@ def _init_ctrls(self, prnt): # generated method, don't edit wx.Frame.__init__(self, id=wxID_MAINFRAME, name=u'MainFrame', - parent=prnt, pos=wx.Point(-3, -3), size=wx.Size(800, 600), - style=wx.DEFAULT_FRAME_STYLE, title=u'Py++ IDE') + parent=prnt, pos=wx.Point(0, 0), size=wx.Size(800, 600), + style=wx.DEFAULT_FRAME_STYLE, title=u'Py++ IDE ()') self._init_utils() self.SetClientSize(wx.Size(792, 566)) self.SetMenuBar(self.menuBar) + self.Bind(wx.EVT_CLOSE, self.OnMainFrameClose) self.statusBar = wx.StatusBar(id=wxID_MAINFRAMESTATUSBAR, name=u'statusBar', parent=self, style=0) @@ -366,7 +376,7 @@ self.textCode = wx.TextCtrl(id=wxID_MAINFRAMETEXTCODE, name=u'textCode', parent=self.panelNbCode, pos=wx.Point(0, 0), size=wx.Size(730, 0), style=wx.TE_READONLY | wx.TE_MULTILINE, value=u'') - self.textCode.SetToolTipString(u'textCtrl2') + self.textCode.SetHelpText(u'') self.panelButtons = wx.Panel(id=wxID_MAINFRAMEPANELBUTTONS, name=u'panelButtons', parent=self.panelNbCode, pos=wx.Point(156, @@ -376,6 +386,8 @@ label=u'Generate XML code', name=u'butGenXml', parent=self.panelButtons, pos=wx.Point(10, 0), size=wx.Size(120, 23), style=0) + self.butGenXml.SetToolTipString(u'') + self.butGenXml.SetHelpText(u'Help for button') self.butGenXml.Bind(wx.EVT_BUTTON, self.OnButGenXmlButton, id=wxID_MAINFRAMEBUTGENXML) @@ -465,28 +477,34 @@ self._init_sizers() def __init__(self, parent): - + self.currentItemInclude = None self.currentItemMacro = None self._init_ctrls(parent) - self._setup_ide_ctrls() - self.splitterVertical.SetSashPosition(300, True) - self.SetSize((self.GetSize()[0]+1,self.GetSize()[1]+1)) + self._setup_ide_ctrls() def OnMenueFileNewMenu(self, event): + self._controller.DoProjectNew() event.Skip() def OnMenueFileOpenMenu(self, event): + self._controller.OpenDlgLoadProject() event.Skip() def OnMenueFileSaveMenu(self, event): + self._controller.OpenDlgSaveProject() event.Skip() + + def OnMenueFileSaveAsMenu(self, event): + self._controller.OpenDlgSaveProject(new_file=True) + event.Skip() def OnMenueFileRecentMenu(self, event): event.Skip() def OnMenueFileExitMenu(self, event): + self._controller.ExitIde() event.Skip() def OnTextGenCodeRightDown(self, event): @@ -582,6 +600,17 @@ self.currentItemMacro = None event.Skip() + def OnMenueRecentPrjItems0Menu(self, event): + print "OnMenueRecentPrjItems0Menu" + event.Skip() + + def OnMainFrameClose(self, event): + if not self._controller.ExitIde(): + return() # Don't close + else: + event.Skip() + + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |