class Page(WizardPage): ## Constructor # # \param parent # Parent <b><i>wx.Window</i></b> instance def __init__(self, parent): WizardPage.__init__(self, parent, pgid.CHANGELOG) txt_package = wx.StaticText(self, label=GT(u'Package'), name=u'package') self.ti_package = TextArea(self, inputid.PACKAGE, name=txt_package.Name) txt_version = wx.StaticText(self, label=GT(u'Version'), name=u'version') self.ti_version = TextArea(self, inputid.VERSION, name=txt_version.Name) dist_names = GetOSDistNames() txt_dist = wx.StaticText(self, label=GT(u'Distribution'), name=u'dist') if dist_names: self.ti_dist = ComboBox(self, inputid.DIST, choices=dist_names, name=txt_dist.Name) # Use regular text input if could not retrieve distribution names list else: self.ti_dist = TextArea(self, inputid.DIST, name=txt_dist.Name) opts_urgency = ( u'low', u'medium', u'high', u'emergency', ) txt_urgency = wx.StaticText(self, label=GT(u'Urgency'), name=u'urgency') self.sel_urgency = Choice(self, selid.URGENCY, choices=opts_urgency, name=txt_urgency.Name) txt_maintainer = wx.StaticText(self, label=GT(u'Maintainer'), name=u'maintainer') self.ti_maintainer = TextArea(self, inputid.MAINTAINER, name=txt_maintainer.Name) txt_email = wx.StaticText(self, label=GT(u'Email'), name=u'email') self.ti_email = TextArea(self, inputid.EMAIL, name=txt_email.Name) btn_import = CreateButton(self, btnid.IMPORT, GT(u'Import'), u'import', name=u'btn import') txt_import = wx.StaticText( self, label=GT(u'Import information from Control page')) # Changes input self.ti_changes = TextAreaPanel(self, size=(20, 150), name=u'changes') # *** Target installation directory # FIXME: Should this be set by config or project file??? self.pnl_target = FileOTarget(self, u'/usr/share/doc/<package>', name=u'target default', defaultType=CheckBoxESS, customType=PathCtrlESS, pathIds=( chkid.TARGET, inputid.TARGET, )) self.btn_add = CreateButton(self, btnid.ADD, GT(u'Add'), u'add', name=u'btn add') txt_add = wx.StaticText(self, label=GT(u'Insert new changelog entry')) self.chk_indentation = CheckBox(self, label=GT(u'Preserve indentation'), name=u'indent') self.dsp_changes = TextAreaPanelESS(self, inputid.CHANGES, monospace=True, name=u'log') self.dsp_changes.EnableDropTarget() SetPageToolTips(self) # *** Event Handling *** # btn_import.Bind(wx.EVT_BUTTON, self.OnImportFromControl) self.btn_add.Bind(wx.EVT_BUTTON, self.AddInfo) # *** Layout *** # LEFT_BOTTOM = lyt.ALGN_LB LEFT_CENTER = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL RIGHT_CENTER = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL lyt_info = wx.FlexGridSizer(2, 6) lyt_info.AddGrowableCol(1) lyt_info.AddGrowableCol(3) lyt_info.AddGrowableCol(5) lyt_info.AddMany( ((txt_package, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_package, 1, wx.EXPAND | wx.BOTTOM | wx.RIGHT, 5), (txt_version, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_version, 1, wx.EXPAND | wx.BOTTOM | wx.RIGHT, 5), (txt_dist, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_dist, 1, wx.EXPAND | wx.BOTTOM, 5), (txt_urgency, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.sel_urgency, 1, wx.RIGHT, 5), (txt_maintainer, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_maintainer, 1, wx.EXPAND | wx.RIGHT, 5), (txt_email, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_email, 1, wx.EXPAND))) lyt_details = wx.GridBagSizer() lyt_details.SetCols(3) lyt_details.AddGrowableRow(2) lyt_details.AddGrowableCol(1) lyt_details.Add(btn_import, (0, 0)) lyt_details.Add(txt_import, (0, 1), flag=LEFT_CENTER) lyt_details.Add(wx.StaticText(self, label=GT(u'Changes')), (1, 0), flag=LEFT_BOTTOM) lyt_details.Add(wx.StaticText(self, label=GT(u'Target')), (1, 2), flag=LEFT_BOTTOM) lyt_details.Add(self.ti_changes, (2, 0), (1, 2), wx.EXPAND | wx.RIGHT, 5) lyt_details.Add(self.pnl_target, (2, 2)) lyt_details.Add(self.btn_add, (3, 0), (2, 1)) lyt_details.Add(txt_add, (3, 1), flag=LEFT_BOTTOM | wx.TOP, border=5) lyt_details.Add(self.chk_indentation, (4, 1), flag=LEFT_BOTTOM) lyt_main = BoxSizer(wx.VERTICAL) lyt_main.AddSpacer(10) lyt_main.Add(lyt_info, 0, wx.EXPAND | lyt.PAD_LR, 5) lyt_main.AddSpacer(10) lyt_main.Add(lyt_details, 1, wx.EXPAND | lyt.PAD_LR, 5) lyt_main.Add(wx.StaticText(self, label=u'Changelog Output'), 0, LEFT_BOTTOM | lyt.PAD_LT, 5) lyt_main.Add(self.dsp_changes, 1, wx.EXPAND | lyt.PAD_LR | wx.BOTTOM, 5) self.SetAutoLayout(True) self.SetSizer(lyt_main) self.Layout() ## Formats input text from 'changes' field for new entry in changelog def AddInfo(self, event=None): new_changes = self.ti_changes.GetValue() if TextIsEmpty(new_changes): DetailedMessageDialog( GetMainWindow(), GT(u'Warning'), ICON_WARNING, GT(u'"Changes" section is empty')).ShowModal() self.ti_changes.SetInsertionPointEnd() self.ti_changes.SetFocus() return package = self.ti_package.GetValue() version = self.ti_version.GetValue() dist = self.ti_dist.GetValue() urgency = self.sel_urgency.GetStringSelection() maintainer = self.ti_maintainer.GetValue() email = self.ti_email.GetValue() new_changes = FormatChangelog(new_changes, package, version, dist, urgency, maintainer, email, self.chk_indentation.GetValue()) # Clean up leading & trailing whitespace in old changes old_changes = self.dsp_changes.GetValue().strip(u' \t\n\r') # Only append newlines if log isn't already empty if not TextIsEmpty(old_changes): new_changes = u'{}\n\n\n{}'.format(new_changes, old_changes) # Add empty line to end of log if not new_changes.endswith(u'\n'): new_changes = u'{}\n'.format(new_changes) self.dsp_changes.SetValue(new_changes) # Clear "Changes" text self.ti_changes.Clear() self.ti_changes.SetFocus() ## Exports page's data to file # # \param out_dir # Target directory where file will be written # \out_name # Filename of output file # \compress # If <b><i>True</i></b>, compresses file with gzip def Export(self, out_dir, out_name=wx.EmptyString, compress=False): ret_value = WizardPage.Export(self, out_dir, out_name=out_name) absolute_filename = u'{}/{}'.format(out_dir, out_name).replace(u'//', u'/') CMD_gzip = GetExecutable(u'gzip') if compress and CMD_gzip: commands.getstatusoutput(u'{} -n9 "{}"'.format( CMD_gzip, absolute_filename)) return ret_value ## Export instructions specifically for build phase # # \param stage # Formatted staged directory where file heirarchy is temporarily kept # \return # <b><i>Tuple</i></b> containing a return code & string value of page data def ExportBuild(self, stage): target = self.pnl_target.GetPath() if target == self.pnl_target.GetDefaultPath(): target.replace(u'<package>', GetFieldValue(pgid.CONTROL, inputid.PACKAGE)) stage = ConcatPaths((stage, target)) if not os.path.isdir(stage): os.makedirs(stage) # FIXME: Allow user to set filename self.Export(stage, u'changelog', True) export_summary = GT(u'Changelog export failed') changelog = ConcatPaths((stage, u'changelog.gz')) if os.path.isfile(changelog): export_summary = GT(u'Changelog export to: {}').format(changelog) return (0, export_summary) ## Retrieves changelog text # # The output is a text file that uses sections defined by braces ([, ]) # # \param getModule # If <b><i>True</i></b>, returns a <b><i>tuple</b></i> of the module name # & page data, otherwise return only page data string # \return # <b><i>tuple(str, str)</i></b>: Filename & formatted string of changelog target & body def Get(self, getModule=False): target = self.pnl_target.GetPath() if target == self.pnl_target.GetDefaultPath(): target = u'DEFAULT' body = self.dsp_changes.GetValue() if TextIsEmpty(body): page = None else: page = u'[TARGET={}]\n\n[BODY]\n{}'.format(target, body) if getModule: page = ( __name__, page, ) return page ## Retrieves plain text of the changelog field # # \return # Formatted changelog text def GetChangelog(self): return self.dsp_changes.GetValue() ## Reads & parses page data from a formatted text file # # \param filename # File path to open def ImportFromFile(self, filename): if not os.path.isfile(filename): return dbrerrno.ENOENT clog_data = ReadFile(filename, split=True) sections = {} def parse_section(key, lines): value = u'\n'.join(lines).split(u'\n[')[0] if u'=' in key: key = key.split(u'=') value = (key[-1], value) key = key[0] sections[key] = value # NOTE: This would need to be changed were more sections added to project file for L in clog_data: line_index = clog_data.index(L) if not TextIsEmpty(L) and u'[' in L and u']' in L: L = L.split(u'[')[-1].split(u']')[0] parse_section(L, clog_data[line_index + 1:]) for S in sections: Logger.Debug( __name__, GT(u'Changelog section: "{}", Value:\n{}').format( S, sections[S])) if isinstance(sections[S], (tuple, list)): value_index = 0 for I in sections[S]: Logger.Debug(__name__, GT(u'Value {}: {}').format(value_index, I)) value_index += 1 if S == u'TARGET': Logger.Debug(__name__, u'SECTION TARGET FOUND') if sections[S][0] == u'DEFAULT': Logger.Debug(__name__, u'Using default target') if not self.pnl_target.UsingDefault(): self.pnl_target.Reset() else: Logger.Debug( __name__, GT(u'Using custom target: {}').format(sections[S][0])) self.pnl_target.SetPath(sections[S][0]) continue if S == u'BODY': Logger.Debug(__name__, u'SECTION BODY FOUND') self.dsp_changes.SetValue(sections[S]) continue return 0 ## Checks the page's fields for exporting # # \return # <b><i>False</i></b> if page cannot be exported def IsOkay(self): return not TextIsEmpty(self.dsp_changes.GetValue()) ## Imports select field values from the 'Control' page def OnImportFromControl(self, event=None): fields = ( (self.ti_package, inputid.PACKAGE), (self.ti_version, inputid.VERSION), (self.ti_maintainer, inputid.MAINTAINER), (self.ti_email, inputid.EMAIL), ) for F, FID in fields: field_value = GetFieldValue(pgid.CONTROL, FID) if isinstance(field_value, ErrorTuple): err_msg1 = GT( u'Got error when attempting to retrieve field value') err_msg2 = u'\tError code: {}\n\tError message: {}'.format( field_value.GetCode(), field_value.GetString()) Logger.Error(__name__, u'{}:\n{}'.format(err_msg1, err_msg2)) continue if not TextIsEmpty(field_value): F.SetValue(field_value) ## Sets values of page's fields with given input # # \param data # Text to parse for values def Set(self, data): changelog = data.split(u'\n') target = changelog[0].split(u'<<DEST>>')[1].split(u'<</DEST>>')[0] if target == u'DEFAULT': if not self.pnl_target.UsingDefault(): self.pnl_target.Reset() else: self.pnl_target.SetPath(target) self.dsp_changes.SetValue(u'\n'.join(changelog[1:]))
class Page(WizardPage): ## Constructor # # \param parent # Parent <b><i>wx.Window</i></b> instance def __init__(self, parent): WizardPage.__init__(self, parent, pgid.CHANGELOG) txt_package = wx.StaticText(self, label=GT(u'Package'), name=u'package') self.ti_package = TextArea(self, inputid.PACKAGE, name=txt_package.Name) txt_version = wx.StaticText(self, label=GT(u'Version'), name=u'version') self.ti_version = TextArea(self, inputid.VERSION, name=txt_version.Name) dist_names = GetOSDistNames() txt_dist = wx.StaticText(self, label=GT(u'Distribution'), name=u'dist') if dist_names: self.ti_dist = ComboBox(self, inputid.DIST, choices=dist_names, name=txt_dist.Name) # Use regular text input if could not retrieve distribution names list else: self.ti_dist = TextArea(self, inputid.DIST, name=txt_dist.Name) opts_urgency = ( u'low', u'medium', u'high', u'emergency', ) txt_urgency = wx.StaticText(self, label=GT(u'Urgency'), name=u'urgency') self.sel_urgency = Choice(self, selid.URGENCY, choices=opts_urgency, name=txt_urgency.Name) txt_maintainer = wx.StaticText(self, label=GT(u'Maintainer'), name=u'maintainer') self.ti_maintainer = TextArea(self, inputid.MAINTAINER, name=txt_maintainer.Name) txt_email = wx.StaticText(self, label=GT(u'Email'), name=u'email') self.ti_email = TextArea(self, inputid.EMAIL, name=txt_email.Name) btn_import = CreateButton(self, btnid.IMPORT, GT(u'Import'), u'import', name=u'btn import') txt_import = wx.StaticText( self, label=GT(u'Import information from Control page')) # Changes input self.ti_changes = TextAreaPanel(self, size=(20, 150), name=u'changes') # *** Target installation directory # FIXME: Should this be set by config or project file??? self.pnl_target = FileOTarget(self, u'/usr/share/doc/<package>', name=u'target default', defaultType=CheckBoxESS, customType=PathCtrlESS, pathIds=( chkid.TARGET, inputid.TARGET, )) self.btn_add = CreateButton(self, btnid.ADD, GT(u'Add'), u'add', name=u'btn add') txt_add = wx.StaticText(self, label=GT(u'Insert new changelog entry')) self.chk_indentation = CheckBox(self, label=GT(u'Preserve indentation'), name=u'indent') self.dsp_changes = TextAreaPanelESS(self, inputid.CHANGES, monospace=True, name=u'log') self.dsp_changes.EnableDropTarget() SetPageToolTips(self) # *** Event Handling *** # btn_import.Bind(wx.EVT_BUTTON, self.OnImportFromControl) self.btn_add.Bind(wx.EVT_BUTTON, self.AddInfo) # *** Layout *** # LEFT_BOTTOM = lyt.ALGN_LB LEFT_CENTER = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL RIGHT_CENTER = wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL lyt_info = wx.FlexGridSizer(2, 6) lyt_info.AddGrowableCol(1) lyt_info.AddGrowableCol(3) lyt_info.AddGrowableCol(5) lyt_info.AddMany( ((txt_package, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_package, 1, wx.EXPAND | wx.BOTTOM | wx.RIGHT, 5), (txt_version, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_version, 1, wx.EXPAND | wx.BOTTOM | wx.RIGHT, 5), (txt_dist, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_dist, 1, wx.EXPAND | wx.BOTTOM, 5), (txt_urgency, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.sel_urgency, 1, wx.RIGHT, 5), (txt_maintainer, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_maintainer, 1, wx.EXPAND | wx.RIGHT, 5), (txt_email, 0, RIGHT_CENTER | wx.RIGHT, 5), (self.ti_email, 1, wx.EXPAND))) lyt_details = wx.GridBagSizer() lyt_details.SetCols(3) lyt_details.AddGrowableRow(2) lyt_details.AddGrowableCol(1) lyt_details.Add(btn_import, (0, 0)) lyt_details.Add(txt_import, (0, 1), flag=LEFT_CENTER) lyt_details.Add(wx.StaticText(self, label=GT(u'Changes')), (1, 0), flag=LEFT_BOTTOM) lyt_details.Add(wx.StaticText(self, label=GT(u'Target')), (1, 2), flag=LEFT_BOTTOM) lyt_details.Add(self.ti_changes, (2, 0), (1, 2), wx.EXPAND | wx.RIGHT, 5) lyt_details.Add(self.pnl_target, (2, 2)) lyt_details.Add(self.btn_add, (3, 0), (2, 1)) lyt_details.Add(txt_add, (3, 1), flag=LEFT_BOTTOM | wx.TOP, border=5) lyt_details.Add(self.chk_indentation, (4, 1), flag=LEFT_BOTTOM) lyt_main = BoxSizer(wx.VERTICAL) lyt_main.AddSpacer(10) lyt_main.Add(lyt_info, 0, wx.EXPAND | lyt.PAD_LR, 5) lyt_main.AddSpacer(10) lyt_main.Add(lyt_details, 1, wx.EXPAND | lyt.PAD_LR, 5) lyt_main.Add(wx.StaticText(self, label=u'Changelog Output'), 0, LEFT_BOTTOM | lyt.PAD_LT, 5) lyt_main.Add(self.dsp_changes, 1, wx.EXPAND | lyt.PAD_LR | wx.BOTTOM, 5) self.SetAutoLayout(True) self.SetSizer(lyt_main) self.Layout() ## Formats input text from 'changes' field for new entry in changelog def AddInfo(self, event=None): new_changes = self.ti_changes.GetValue() if TextIsEmpty(new_changes): DetailedMessageDialog( GetMainWindow(), GT(u'Warning'), ICON_WARNING, GT(u'"Changes" section is empty')).ShowModal() self.ti_changes.SetInsertionPointEnd() self.ti_changes.SetFocus() return package = self.ti_package.GetValue() version = self.ti_version.GetValue() dist = self.ti_dist.GetValue() urgency = self.sel_urgency.GetStringSelection() maintainer = self.ti_maintainer.GetValue() email = self.ti_email.GetValue() new_changes = FormatChangelog(new_changes, package, version, dist, urgency, maintainer, email, self.chk_indentation.GetValue()) # Clean up leading & trailing whitespace in old changes old_changes = self.dsp_changes.GetValue().strip(u' \t\n\r') # Only append newlines if log isn't already empty if not TextIsEmpty(old_changes): new_changes = u'{}\n\n\n{}'.format(new_changes, old_changes) # Add empty line to end of log if not new_changes.endswith(u'\n'): new_changes = u'{}\n'.format(new_changes) self.dsp_changes.SetValue(new_changes) # Clear "Changes" text self.ti_changes.Clear() self.ti_changes.SetFocus() ## Retrieves changelog text # # The output is a text file that uses sections defined by braces ([, ]) # # \return # <b><i>tuple(str, str)</i></b>: Filename & formatted string of changelog target & body def Get(self): target = self.pnl_target.GetPath() if target == self.pnl_target.GetDefaultPath(): target = u'STANDARD' return (target, self.GetChangelog()) ## Retrieves plain text of the changelog field # # \return # Formatted changelog text def GetChangelog(self): return self.dsp_changes.GetValue() ## TODO: Doxygen def GetSaveData(self): target = self.pnl_target.GetPath() if target == self.pnl_target.GetDefaultPath(): target = u'<<DEST>>DEFAULT<</DEST>>' else: target = u'<<DEST>>{}<</DEST>>'.format(target) return u'\n'.join((u'<<CHANGELOG>>', target, self.dsp_changes.GetValue(), u'<</CHANGELOG>>')) ## Checks the page's fields for exporting # # \return # <b><i>False</i></b> if page cannot be exported def IsOkay(self): return not TextIsEmpty(self.dsp_changes.GetValue()) ## Imports select field values from the 'Control' page def OnImportFromControl(self, event=None): fields = ( (self.ti_package, inputid.PACKAGE), (self.ti_version, inputid.VERSION), (self.ti_maintainer, inputid.MAINTAINER), (self.ti_email, inputid.EMAIL), ) for F, FID in fields: field_value = GetFieldValue(pgid.CONTROL, FID) if isinstance(field_value, ErrorTuple): err_msg1 = GT( u'Got error when attempting to retrieve field value') err_msg2 = u'\tError code: {}\n\tError message: {}'.format( field_value.GetCode(), field_value.GetString()) Logger.Error(__name__, u'{}:\n{}'.format(err_msg1, err_msg2)) continue if not TextIsEmpty(field_value): F.SetValue(field_value) ## Sets values of page's fields with given input # # \param data # Text to parse for values def Set(self, data): changelog = data.split(u'\n') target = changelog[0].split(u'<<DEST>>')[1].split(u'<</DEST>>')[0] if target == u'DEFAULT': if not self.pnl_target.UsingDefault(): self.pnl_target.Reset() else: self.pnl_target.SetPath(target) self.dsp_changes.SetValue(u'\n'.join(changelog[1:]))
class Page(WizardPage): ## Constructor # # \param parent # Parent <b><i>wx.Window</i></b> instance def __init__(self, parent): WizardPage.__init__(self, parent, pgid.FILES) # *** Left Panel *** # pnl_treeopts = BorderedPanel(self) self.chk_individuals = CheckBoxCFG(pnl_treeopts, label=GT(u'List files individually'), name=u'individually', cfgSect=u'FILES') self.chk_preserve_top = CheckBoxCFG(pnl_treeopts, chkid.TOPLEVEL, GT(u'Preserve top-level directories'), name=u'top-level', cfgSect=u'FILES') self.chk_nofollow_symlink = CheckBoxCFG(pnl_treeopts, chkid.SYMLINK, GT(u'Don\'t follow symbolic links'), defaultValue=True, name=u'nofollow-symlink', cfgSect=u'FILES') self.tree_dirs = DirectoryTreePanel(self, size=(300,20)) # ----- Target path pnl_target = BorderedPanel(self) # choices of destination rb_bin = wx.RadioButton(pnl_target, label=u'/bin', style=wx.RB_GROUP) rb_usrbin = wx.RadioButton(pnl_target, label=u'/usr/bin') rb_usrlib = wx.RadioButton(pnl_target, label=u'/usr/lib') rb_locbin = wx.RadioButton(pnl_target, label=u'/usr/local/bin') rb_loclib = wx.RadioButton(pnl_target, label=u'/usr/local/lib') self.rb_custom = wx.RadioButton(pnl_target, inputid.CUSTOM, GT(u'Custom')) self.rb_custom.Default = True # Start with "Custom" selected self.rb_custom.SetValue(self.rb_custom.Default) # group buttons together # FIXME: Unnecessary??? self.grp_targets = ( rb_bin, rb_usrbin, rb_usrlib, rb_locbin, rb_loclib, self.rb_custom, ) # ----- Add/Remove/Clear buttons btn_add = CreateButton(self, btnid.ADD) btn_remove = CreateButton(self, btnid.REMOVE) btn_clear = CreateButton(self, btnid.CLEAR) self.prev_dest_value = u'/usr/bin' self.ti_target = TextArea(self, defaultValue=self.prev_dest_value, name=u'target') self.btn_browse = CreateButton(self, btnid.BROWSE) btn_refresh = CreateButton(self, btnid.REFRESH) # Display area for files added to list self.lst_files = FileListESS(self, inputid.LIST, name=u'filelist') # *** Event Handling *** # # create an event to enable/disable custom widget for item in self.grp_targets: wx.EVT_RADIOBUTTON(item, wx.ID_ANY, self.OnSetDestination) # Context menu events for directory tree wx.EVT_MENU(self, wx.ID_ADD, self.OnImportFromTree) # Button events btn_add.Bind(wx.EVT_BUTTON, self.OnImportFromTree) btn_remove.Bind(wx.EVT_BUTTON, self.OnRemoveSelected) btn_clear.Bind(wx.EVT_BUTTON, self.OnClearFileList) self.btn_browse.Bind(wx.EVT_BUTTON, self.OnBrowse) btn_refresh.Bind(wx.EVT_BUTTON, self.OnRefreshFileList) # ???: Not sure what these do wx.EVT_KEY_DOWN(self.ti_target, self.GetDestValue) wx.EVT_KEY_UP(self.ti_target, self.CheckDest) # Key events for file list wx.EVT_KEY_DOWN(self.lst_files, self.OnRemoveSelected) self.Bind(wx.EVT_DROP_FILES, self.OnDropFiles) # *** Layout *** # lyt_treeopts = BoxSizer(wx.VERTICAL) lyt_treeopts.AddSpacer(5) lyt_treeopts.Add(self.chk_individuals, 0, lyt.PAD_LR, 5) lyt_treeopts.Add(self.chk_preserve_top, 0, lyt.PAD_LR, 5) lyt_treeopts.Add(self.chk_nofollow_symlink, 0, lyt.PAD_LR, 5) lyt_treeopts.AddSpacer(5) pnl_treeopts.SetSizer(lyt_treeopts) lyt_left = BoxSizer(wx.VERTICAL) lyt_left.AddSpacer(10) lyt_left.Add(wx.StaticText(self, label=GT(u'Directory options')), 0, wx.ALIGN_BOTTOM) lyt_left.Add(pnl_treeopts, 0, wx.EXPAND|wx.ALIGN_LEFT|wx.BOTTOM, 5) lyt_left.Add(self.tree_dirs, 1, wx.EXPAND) lyt_target = wx.GridSizer(3, 2, 5, 5) for item in self.grp_targets: lyt_target.Add(item, 0, lyt.PAD_LR, 5) pnl_target.SetAutoLayout(True) pnl_target.SetSizer(lyt_target) pnl_target.Layout() # Put text input in its own sizer to force expand lyt_input = BoxSizer(wx.HORIZONTAL) lyt_input.Add(self.ti_target, 1, wx.ALIGN_CENTER_VERTICAL) lyt_buttons = BoxSizer(wx.HORIZONTAL) lyt_buttons.Add(btn_add, 0) lyt_buttons.Add(btn_remove, 0) lyt_buttons.Add(btn_clear, 0) lyt_buttons.Add(lyt_input, 1, wx.ALIGN_CENTER_VERTICAL) lyt_buttons.Add(self.btn_browse, 0) lyt_buttons.Add(btn_refresh, 0) lyt_right = BoxSizer(wx.VERTICAL) lyt_right.AddSpacer(10) lyt_right.Add(wx.StaticText(self, label=GT(u'Target'))) lyt_right.Add(pnl_target, 0, wx.TOP, 5) lyt_right.Add(lyt_buttons, 0, wx.EXPAND) lyt_right.Add(self.lst_files, 5, wx.EXPAND|wx.TOP, 5) PROP_LEFT = 0 PROP_RIGHT = 1 lyt_main = wx.FlexGridSizer(1, 2) lyt_main.AddGrowableRow(0) # Directory tree size issues with wx 2.8 if wx.MAJOR_VERSION <= 2: PROP_LEFT = 1 lyt_main.AddGrowableCol(0, 1) lyt_main.AddGrowableCol(1, 2) lyt_main.Add(lyt_left, PROP_LEFT, wx.EXPAND|lyt.PAD_LR|wx.BOTTOM, 5) lyt_main.Add(lyt_right, PROP_RIGHT, wx.EXPAND|lyt.PAD_RB, 5) self.SetAutoLayout(True) self.SetSizer(lyt_main) self.Layout() SetPageToolTips(self) ## Adds files to file list # # \param dirs # <b><i>dict</i></b>: dict[dir] = [file list] # \param fileCount # Number of explicit files being added to list # \param showDialog # If <b><i>True</i></b>, displays a progress dialog def AddPaths(self, dirs, fileCount=None, showDialog=False): target = self.GetTarget() if fileCount == None: fileCount = 0 for D in dirs: for F in dirs[D]: fileCount += 1 progress = None Logger.Debug(__name__, u'Adding {} files ...'.format(fileCount)) if showDialog: progress = ProgressDialog(GetMainWindow(), GT(u'Adding Files'), maximum=fileCount, style=PD_DEFAULT_STYLE|wx.PD_CAN_ABORT) progress.Show() completed = 0 for D in sorted(dirs): for F in sorted(dirs[D]): if progress and progress.WasCancelled(): progress.Destroy() return False if progress: wx.Yield() progress.Update(completed, GT(u'Adding file {}').format(F)) self.lst_files.AddFile(F, D, target) completed += 1 if progress: wx.Yield() progress.Update(completed) progress.Destroy() return True ## TODO: Doxygen def CheckDest(self, event=None): if TextIsEmpty(self.ti_target.GetValue()): self.ti_target.SetValue(self.prev_dest_value) self.ti_target.SetInsertionPoint(-1) elif self.ti_target.GetValue()[0] != u'/': self.ti_target.SetValue(self.prev_dest_value) self.ti_target.SetInsertionPoint(-1) if event: event.Skip() ## Retrieves information on files to be packaged # # \return # A list of files with their targets formatted for text output def Get(self): # Remove section delimeters & first line which is just an integer return self.GetSaveData().split(u'\n')[2:-1] ## Retrieves target destination set by user input # # TODO: Rename to 'GetTarget' or 'GetInputTarget' def GetDestValue(self, event=None): if not TextIsEmpty(self.ti_target.GetValue()): if self.ti_target.GetValue()[0] == u'/': self.prev_dest_value = self.ti_target.GetValue() if event: event.Skip() ## Retrieves the directory tree object used by this page # # Used in input.list.FileList for referencing size # # \return # <b><i>ui.tree.DirectoryTreePanel</i></b> instance def GetDirTreePanel(self): return self.tree_dirs ## Retrieves number of files in list # # \return # <b><i>Integer</i></b> count of items in file list def GetFileCount(self): return self.lst_files.GetItemCount() ## Retrieves the file list object used by this page # # \return # <b><i>input.list.FileList</i></b> instance def GetListInstance(self): return self.lst_files ## Retrieves file list to export to text file # # \return # List formatted text def GetSaveData(self): file_list = [] item_count = self.lst_files.GetItemCount() if item_count > 0: count = 0 while count < item_count: filename = self.lst_files.GetItemText(count) source = self.lst_files.GetItem(count, columns.SOURCE).GetText() target = self.lst_files.GetItem(count, columns.TARGET).GetText() absolute_filename = ConcatPaths((source, filename)) # Populate list with tuples of ('src', 'file', 'dest') if self.lst_files.GetItemTextColour(count) == (255, 0, 0): # Mark file as executable file_list.append((u'{}*'.format(absolute_filename), filename, target)) else: file_list.append((absolute_filename, filename, target)) count += 1 return_list = [] for F in file_list: f0 = u'{}'.encode(u'utf-8').format(F[0]) f1 = u'{}'.encode(u'utf-8').format(F[1]) f2 = u'{}'.encode(u'utf-8').format(F[2]) return_list.append(u'{} -> {} -> {}'.format(f0, f1, f2)) return u'<<FILES>>\n1\n{}\n<</FILES>>'.format(u'\n'.join(return_list)) else: # Place a "0" in FILES field if we are not saving any files return u'<<FILES>>\n0\n<</FILES>>' ## Retrieves the target output directory # # FIXME: Duplicate of wizbin.files.Page.GetDestValue? def GetTarget(self): if FieldEnabled(self.ti_target): return self.ti_target.GetValue() for target in self.grp_targets: if target.GetId() != inputid.CUSTOM and target.GetValue(): return target.GetLabel() ## Accepts a file path to read & parse to fill the page's fields # # \param filename # Absolute path of formatted text file to read def ImportFromFile(self, filename): Logger.Debug(__name__, GT(u'Importing page info from {}').format(filename)) if not os.path.isfile(filename): return dbrerrno.ENOENT files_data = ReadFile(filename, split=True) # Lines beginning with these characters will be ignored ignore_characters = ( u'', u' ', u'#', ) target = None targets_list = [] for L in files_data: if not TextIsEmpty(L) and L[0] not in ignore_characters: if u'[' in L and u']' in L: target = L.split(u'[')[-1].split(u']')[0] continue if target: executable = (len(L) > 1 and L[-2:] == u' *') if executable: L = L[:-2] targets_list.append((target, L, executable)) missing_files = [] for T in targets_list: # FIXME: Create method in FileList class to retrieve all missing files if not os.path.exists(T[1]): missing_files.append(T[1]) source_file = os.path.basename(T[1]) source_dir = os.path.dirname(T[1]) self.lst_files.AddFile(source_file, source_dir, T[0], executable=T[2]) if len(missing_files): main_window = GetMainWindow() err_line1 = GT(u'The following files/folders are missing from the filesystem.') err_line2 = GT(u'They will be highlighted on the Files page.') DetailedMessageDialog(main_window, title=GT(u'Warning'), icon=ICON_ERROR, text=u'\n'.join((err_line1, err_line2)), details=u'\n'.join(missing_files)).ShowModal() return 0 ## Checks if the page is ready for export/build # # \return # <b><i>True</i></b> if the file list (self.lst_files) is not empty def IsOkay(self): return not self.lst_files.IsEmpty() ## Reads files & directories & preps for loading into list # # \param pathsList # <b><i>List/Tuple</i></b> of <b><i>string</i></b> values representing # files & directories to be added # \return # Value of wizbin.files.Page.AddPaths, or <b><i>False</i></b> in case of error def LoadPaths(self, pathsList): if isinstance(pathsList, tuple): pathsList = list(pathsList) if not pathsList or not isinstance(pathsList, list): return False file_list = [] dir_list = {} prep = ProgressDialog(GetMainWindow(), GT(u'Processing Files'), GT(u'Scanning files ...'), style=wx.PD_APP_MODAL|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT) # Only update the gauge every N files (hack until I figure out timer) update_interval = 450 count = 0 prep.Show() if not self.chk_preserve_top.GetValue(): for INDEX in reversed(range(len(pathsList))): path = pathsList[INDEX] if os.path.isdir(path): # Remove top-level directory from list pathsList.pop(INDEX) insert_index = INDEX for P in os.listdir(path): pathsList.insert(insert_index, ConcatPaths((path, P))) insert_index += 1 try: for P in pathsList: if prep.WasCancelled(): prep.Destroy() return False count += 1 if count >= update_interval: wx.Yield() prep.Pulse() count = 0 if not self.chk_individuals.GetValue() or os.path.isfile(P): file_list.append(P) continue if os.path.isdir(P): parent_dir = os.path.dirname(P) if parent_dir not in dir_list: dir_list[parent_dir] = [] for ROOT, DIRS, FILES in os.walk(P): if prep.WasCancelled(): prep.Destroy() return False wx.Yield() prep.SetMessage(GT(u'Scanning directory {} ...').format(ROOT)) count += 1 if count >= update_interval: wx.Yield() prep.Pulse() count = 0 for F in FILES: if prep.WasCancelled(): prep.Destroy() return False count += 1 if count >= update_interval: wx.Yield() prep.Pulse() count = 0 # os.path.dirname preserves top level directory ROOT = ROOT.replace(os.path.dirname(P), u'').strip(u'/') F = u'{}/{}'.format(ROOT, F).strip(u'/') if F not in dir_list[parent_dir]: dir_list[parent_dir].append(F) except: prep.Destroy() ShowErrorDialog(GT(u'Could not retrieve file list'), traceback.format_exc()) return False wx.Yield() prep.Pulse(GT(u'Counting Files')) file_count = len(file_list) count = 0 for D in dir_list: for F in dir_list[D]: file_count += 1 count += 1 if count >= update_interval: wx.Yield() prep.Pulse() count = 0 prep.Destroy() # Add files to directory list for F in file_list: f_name = os.path.basename(F) f_dir = os.path.dirname(F) if f_dir not in dir_list: dir_list[f_dir] = [] dir_list[f_dir].append(f_name) if file_count > warning_threshhold: count_warnmsg = GT(u'Importing {} files'.format(file_count)) count_warnmsg = u'{}. {}.'.format(count_warnmsg, GT(u'This could take a VERY long time')) count_warnmsg = u'{}\n{}'.format(count_warnmsg, GT(u'Are you sure you want to continue?')) if not ConfirmationDialog(GetMainWindow(), text=count_warnmsg).Confirmed(): return False return self.AddPaths(dir_list, file_count, showDialog=file_count >= efficiency_threshold) ## Handles event emitted by 'browse' button # # Opens a directory dialog to select a custom output target def OnBrowse(self, event=None): dia = GetDirDialog(GetMainWindow(), GT(u'Choose Target Directory')) if ShowDialog(dia): self.ti_target.SetValue(dia.GetPath()) ## Handles event emitted by 'clear' button # # Displays confirmation dialog to clear list if not empty # # TODO: Rename to OnClearList? def OnClearFileList(self, event=None): if self.lst_files.GetItemCount(): if ConfirmationDialog(GetMainWindow(), GT(u'Confirm'), GT(u'Clear all files?')).Confirmed(): self.lst_files.DeleteAllItems() ## Adds files to list from file manager drop # # Note that this method should not be renamed as 'OnDropFiles' # is the implicit handler for wx.FileDropTarget (<- correct class???) # # \param fileList # <b><i>List</i></b> of files dropped from file manager # \return # Value of wizbin.files.Page.LoadPaths def OnDropFiles(self, fileList): return self.LoadPaths(fileList) ## Handles files & directories added from ui.tree.DirectoryTreePanel object # (self.tree_dirs) # # Actually bypasses DirectoryTreePanel & directly accesses # ui.tree.DirectoryTree.GetSelectedPaths def OnImportFromTree(self, event=None): return self.LoadPaths(self.DirTree.GetSelectedPaths()) ## Updates files' status in the file list # # Refreshes files' executable & available status # # \return # Value of self.lst_files.RefreshFileList def OnRefreshFileList(self, event=None): return self.lst_files.RefreshFileList() ## Handles event emitted by 'remove' button # # Removes all currently selected/highlighted files in list def OnRemoveSelected(self, event=None): try: modifier = event.GetModifiers() keycode = event.GetKeyCode() except AttributeError: keycode = event.GetEventObject().GetId() if keycode in (wx.ID_REMOVE, wx.WXK_DELETE): self.lst_files.RemoveSelected() elif keycode == 65 and modifier == wx.MOD_CONTROL: self.lst_files.SelectAll() ## Handles enabling/disabling the custom target field if the corresponding # when a target radio button is selected # # TODO: Rename to 'OnSetTarget' or 'OnSelectTarget' def OnSetDestination(self, event=None): enable = self.rb_custom.GetValue() self.ti_target.Enable(enable) self.btn_browse.Enable(enable) ## Resets page's fields to default values # # \return # Value of self.lst_files.Reset def Reset(self): return self.lst_files.Reset() ## Selects all files in the list # # \return # Value of self.lst_files.SelectAll def SelectAll(self): return self.lst_files.SelectAll() ## Sets the page's fields # # \param data # The text information to parse # \return # <b><i>True</i></b> if the data was imported correctly def Set(self, data): # Clear files list self.lst_files.DeleteAllItems() files_data = data.split(u'\n') if int(files_data[0]): # Get file count from list minus first item "1" files_total = len(files_data) # Store missing files here missing_files = [] progress = None if files_total >= efficiency_threshold: progress = ProgressDialog(GetMainWindow(), GT(u'Adding Files'), maximum=files_total, style=PD_DEFAULT_STYLE|wx.PD_CAN_ABORT) wx.Yield() progress.Show() current_file = files_total while current_file > 1: if progress and progress.WasCancelled(): progress.Destroy() # Project continues opening even if file import is cancelled msg = ( GT(u'File import did not complete.'), GT(u'Project files may be missing in file list.'), ) ShowMessageDialog(u'\n'.join(msg), GT(u'Import Cancelled')) return False current_file -= 1 executable = False file_info = files_data[current_file].split(u' -> ') absolute_filename = file_info[0] if absolute_filename[-1] == u'*': # Set executable flag and remove "*" executable = True absolute_filename = absolute_filename[:-1] filename = file_info[1] source_dir = absolute_filename[:len(absolute_filename) - len(filename)] target_dir = file_info[2] if not self.lst_files.AddFile(filename, source_dir, target_dir, executable): Logger.Warn(__name__, GT(u'File not found: {}').format(absolute_filename)) missing_files.append(absolute_filename) if progress: update_value = files_total - current_file wx.Yield() progress.Update(update_value+1, GT(u'Imported file {} of {}').format(update_value, files_total)) if progress: progress.Destroy() Logger.Debug(__name__, u'Missing file count: {}'.format(len(missing_files))) # If files are missing show a message if missing_files: alert = DetailedMessageDialog(GetMainWindow(), GT(u'Missing Files'), ICON_EXCLAMATION, GT(u'Could not locate the following files:'), u'\n'.join(missing_files)) alert.ShowModal() return True
class Page(WizardPage): ## Constructor # # \param parent # Parent <b><i>wx.Window</i></b> instance def __init__(self, parent): WizardPage.__init__(self, parent, pgid.DEPENDS) ## Override default label self.Label = GT(u'Dependencies and Conflicts') # Bypass checking this page for build self.prebuild_check = False # Buttons to open, save, & preview control file self.btn_open = CreateButton(self, btnid.BROWSE, GT(u'Browse'), u'browse', name=u'btn browse') self.btn_save = CreateButton(self, btnid.SAVE, GT(u'Save'), u'save', name=u'btn save') self.btn_preview = CreateButton(self, btnid.PREVIEW, GT(u'Preview'), u'preview', name=u'btn preview') txt_package = wx.StaticText(self, label=GT(u'Dependency/Conflict Package Name'), name=u'package') txt_version = wx.StaticText(self, label=GT(u'Version'), name=u'version') self.ti_package = TextArea(self, size=(300,25), name=u'package') opts_operator = ( u'>=', u'<=', u'=', u'>>', u'<<', ) self.sel_operator = Choice(self, choices=opts_operator, name=u'operator') self.ti_version = TextArea(self, name=u'version') self.ti_package.SetSize((100,50)) pnl_categories = BorderedPanel(self) self.DefaultCategory = u'Depends' rb_dep = wx.RadioButton(pnl_categories, label=GT(u'Depends'), name=self.DefaultCategory, style=wx.RB_GROUP) rb_pre = wx.RadioButton(pnl_categories, label=GT(u'Pre-Depends'), name=u'Pre-Depends') rb_rec = wx.RadioButton(pnl_categories, label=GT(u'Recommends'), name=u'Recommends') rb_sug = wx.RadioButton(pnl_categories, label=GT(u'Suggests'), name=u'Suggests') rb_enh = wx.RadioButton(pnl_categories, label=GT(u'Enhances'), name=u'Enhances') rb_con = wx.RadioButton(pnl_categories, label=GT(u'Conflicts'), name=u'Conflicts') rb_rep = wx.RadioButton(pnl_categories, label=GT(u'Replaces'), name=u'Replaces') rb_break = wx.RadioButton(pnl_categories, label=GT(u'Breaks'), name=u'Breaks') self.categories = ( rb_dep, rb_pre, rb_rec, rb_sug, rb_enh, rb_con, rb_rep, rb_break, ) # Buttons to add and remove dependencies from the list btn_add = CreateButton(self, btnid.ADD) btn_append = CreateButton(self, btnid.APPEND) btn_remove = CreateButton(self, btnid.REMOVE) btn_clear = CreateButton(self, btnid.CLEAR) # ----- List self.lst_deps = ListCtrlESS(self, inputid.LIST, name=u'list') self.lst_deps.SetSingleStyle(wx.LC_REPORT) self.lst_deps.InsertColumn(0, GT(u'Category'), width=150) self.lst_deps.InsertColumn(1, GT(u'Package(s)')) # wx 3.0 compatibility if wx.MAJOR_VERSION < 3: self.lst_deps.SetColumnWidth(100, wx.LIST_AUTOSIZE) SetPageToolTips(self) # *** Event Handling *** # wx.EVT_KEY_DOWN(self.ti_package, self.SetDepends) wx.EVT_KEY_DOWN(self.ti_version, self.SetDepends) btn_add.Bind(wx.EVT_BUTTON, self.SetDepends) btn_append.Bind(wx.EVT_BUTTON, self.SetDepends) btn_remove.Bind(wx.EVT_BUTTON, self.SetDepends) btn_clear.Bind(wx.EVT_BUTTON, self.SetDepends) wx.EVT_KEY_DOWN(self.lst_deps, self.SetDepends) # *** Layout *** # LEFT_BOTTOM = lyt.ALGN_LB lyt_top = wx.GridBagSizer() lyt_top.SetCols(6) lyt_top.AddGrowableCol(3) # Row 1 lyt_top.Add(txt_package, (1, 0), flag=LEFT_BOTTOM) lyt_top.Add(txt_version, (1, 2), flag=LEFT_BOTTOM) lyt_top.Add(self.btn_open, (0, 3), (4, 1), wx.ALIGN_RIGHT) lyt_top.Add(self.btn_save, (0, 4), (4, 1)) lyt_top.Add(self.btn_preview, (0, 5), (4, 1)) # Row 2 lyt_top.Add(self.ti_package, (2, 0), flag=wx.ALIGN_CENTER_VERTICAL) lyt_top.Add(self.sel_operator, (2, 1), flag=wx.ALIGN_CENTER_VERTICAL) lyt_top.Add(self.ti_version, (2, 2), flag=wx.ALIGN_CENTER_VERTICAL) lyt_categories = wx.GridSizer(4, 2, 5, 5) for C in self.categories: lyt_categories.Add(C, 0) pnl_categories.SetAutoLayout(True) pnl_categories.SetSizer(lyt_categories) pnl_categories.Layout() lyt_buttons = BoxSizer(wx.HORIZONTAL) lyt_buttons.AddMany(( (btn_add, 0, wx.ALIGN_CENTER_VERTICAL), (btn_append, 0, wx.ALIGN_CENTER_VERTICAL), (btn_remove, 0, wx.ALIGN_CENTER_VERTICAL), (btn_clear, 0, wx.ALIGN_CENTER_VERTICAL), )) lyt_mid = wx.GridBagSizer() lyt_mid.SetCols(2) lyt_mid.Add(wx.StaticText(self, label=u'Categories'), (0, 0), (1, 1), LEFT_BOTTOM) lyt_mid.Add(pnl_categories, (1, 0), flag=wx.RIGHT, border=5) lyt_mid.Add(lyt_buttons, (1, 1), flag=wx.ALIGN_BOTTOM) lyt_list = BoxSizer(wx.HORIZONTAL) lyt_list.Add(self.lst_deps, 1, wx.EXPAND) lyt_main = BoxSizer(wx.VERTICAL) # Spacer is less on this page because text is aligned to bottom lyt_main.AddSpacer(5) lyt_main.Add(lyt_top, 0, wx.EXPAND|lyt.PAD_LR, 5) lyt_main.Add(lyt_mid, 0, lyt.PAD_LR, 5) lyt_main.Add(lyt_list, 1, wx.EXPAND|wx.ALL, 5) self.SetAutoLayout(True) self.SetSizer(lyt_main) self.Layout() ## Add a category & dependency to end of list # # \param category # Category label # \param value # Dependency value def AppendDependency(self, category, value): self.lst_deps.AppendStringItem((category, value)) ## Retrieves the default category to use def GetDefaultCategory(self): return self.DefaultCategory ## Reads & parses page data from a formatted text file # # \param filename # File path to open # \see wiz.wizard.WizardPage.ImportFromFile def ImportFromFile(self, d_type, d_string): Logger.Debug(__name__, GT(u'Importing {}: {}'.format(d_type, d_string))) values = d_string.split(u', ') for V in values: self.lst_deps.InsertStringItem(0, d_type) self.lst_deps.SetStringItem(0, 1, V) ## \see wiz.wizard.WizardPage.InitPage def InitPage(self): control_page = GetPage(pgid.CONTROL) self.btn_open.Bind(wx.EVT_BUTTON, control_page.OnBrowse) self.btn_save.Bind(wx.EVT_BUTTON, control_page.OnSave) self.btn_preview.Bind(wx.EVT_BUTTON, control_page.OnPreviewControl) return True ## Resets all fields on page to default values def Reset(self): for C in self.categories: if C.GetName() == self.DefaultCategory: C.SetValue(True) break self.ti_package.Clear() self.sel_operator.Reset() self.ti_version.Clear() self.lst_deps.DeleteAllItems() ## Adds/Appends/Removes dependency to list def SetDepends(self, event=None): try: key_id = event.GetKeyCode() except AttributeError: key_id = event.GetEventObject().GetId() addname = self.ti_package.GetValue() oper = self.sel_operator.GetStringSelection() ver = self.ti_version.GetValue() addver = u'({}{})'.format(oper, ver) if key_id in (btnid.ADD, wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): if TextIsEmpty(addname): return category = self.GetDefaultCategory() for C in self.categories: if C.GetValue(): category = C.GetName() break if TextIsEmpty(ver): self.AppendDependency(category, addname) else: self.AppendDependency(category, u'{} {}'.format(addname, addver)) elif key_id == btnid.APPEND: selected_count = self.lst_deps.GetSelectedItemCount() Logger.Debug(__name__, u'Appending to {} items'.format(selected_count)) if not TextIsEmpty(addname) and self.lst_deps.GetItemCount() and selected_count: selected_rows = self.lst_deps.GetSelectedIndexes() if DebugEnabled(): Logger.Debug(__name__, u'Selected rows:') for R in selected_rows: print(u'\t{}'.format(R)) for listrow in selected_rows: Logger.Debug(__name__, u'Setting list row: {}'.format(listrow)) # Get item from second column colitem = self.lst_deps.GetItem(listrow, 1) # Get the text from that item prev_text = colitem.GetText() if not TextIsEmpty(ver): new_text = u'{} | {} {}'.format(prev_text, addname, addver) else: new_text = u'{} | {}'.format(prev_text, addname) Logger.Debug(__name__, u'Appended item: {}'.format(new_text)) self.lst_deps.SetStringItem(listrow, 1, new_text) elif key_id in (btnid.REMOVE, wx.WXK_DELETE): self.lst_deps.RemoveSelected() elif key_id == btnid.CLEAR: if self.lst_deps.GetItemCount(): if ConfirmationDialog(GetMainWindow(), GT(u'Confirm'), GT(u'Clear all dependencies?')).ShowModal() in (wx.ID_OK, wx.OK): self.lst_deps.DeleteAllItems() if event: event.Skip() ## Sets the page's fields data # # \param data # Text to parse for field values def Set(self, data): self.lst_deps.DeleteAllItems() for item in data: item_count = len(item) while item_count > 1: item_count -= 1 self.lst_deps.InsertStringItem(0, item[0]) self.lst_deps.SetStringItem(0, 1, item[item_count])