class ManBanner(ManSectBase): def __init__(self, parent): ManSectBase.__init__(self, parent) self.Panel = BorderedPanel(parent) txt_section = wx.StaticText(self.Panel, label=GT(u'Section')) self.sel_section = Choice(self.Panel, choices=tuple(sections)) self.sel_section.Default = u'1' self.sel_section.SetStringSelection(self.sel_section.Default) # Section description that changes with EVT_CHOICE self.LabelSection = wx.StaticText(self.Panel) self.SetSectionLabel() txt_date = wx.StaticText(self.Panel, label=GT(u'Date')) spin_year = wx.SpinCtrl(self.Panel, min=1900, max=2100, initial=GetYear(string_value=False)) spin_month = wx.SpinCtrl(self.Panel, min=1, max=12, initial=GetMonthInt()) spin_day = wx.SpinCtrl(self.Panel, min=1, max=31, initial=GetDayInt()) # FIXME: What is this for? txt_unknown1 = wx.StaticText(self.Panel, label=GT(u'Unknown')) ti_unknown1 = wx.TextCtrl(self.Panel) # FIXME: What is this for? txt_unknown2 = wx.StaticText(self.Panel, label=GT(u'Unknown')) ti_unknown2 = wx.TextCtrl(self.Panel) # *** Event Handling *** # self.sel_section.Bind(wx.EVT_CHOICE, self.OnSetSection) # *** Layout *** # lyt_section = BoxSizer(wx.HORIZONTAL) lyt_section.Add(txt_section, 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER, 5) lyt_section.Add(self.sel_section, 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER, 5) lyt_section.Add(self.LabelSection, 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER, 5) lyt_date = wx.GridBagSizer() lyt_date.Add(txt_date, (1, 0), flag=wx.ALIGN_CENTER_VERTICAL) lyt_date.Add(wx.StaticText(self.Panel, label=GT(u'Year')), (0, 1)) lyt_date.Add(wx.StaticText(self.Panel, label=GT(u'Month')), (0, 2)) lyt_date.Add(wx.StaticText(self.Panel, label=GT(u'Day')), (0, 3)) lyt_date.Add(spin_year, (1, 1), flag=wx.LEFT, border=5) lyt_date.Add(spin_month, (1, 2)) lyt_date.Add(spin_day, (1, 3)) lyt_uknwn1 = BoxSizer(wx.HORIZONTAL) lyt_uknwn1.Add(txt_unknown1, 0, wx.ALIGN_CENTER_VERTICAL) lyt_uknwn1.Add(ti_unknown1, 0, wx.LEFT, 5) lyt_uknwn2 = BoxSizer(wx.HORIZONTAL) lyt_uknwn2.Add(txt_unknown2, 0, wx.ALIGN_CENTER_VERTICAL) lyt_uknwn2.Add(ti_unknown2, 0, wx.LEFT, 5) # Change orientation of main sizer to vertical self.lyt_main = BoxSizer(wx.VERTICAL) self.lyt_main.Add(lyt_section, 0, wx.TOP, 5) self.lyt_main.Add(lyt_date, 0, wx.TOP, 5) self.lyt_main.Add(lyt_uknwn1, 0, wx.TOP, 5) self.lyt_main.Add(lyt_uknwn2, 0, wx.TOP, 5) self.Panel.SetSizer(self.lyt_main) ## Retrieve main object instance def GetPanel(self): return self.Panel ## TODO: Doxygen def OnSetSection(self, event=None): self.SetSectionLabel(self.sel_section.GetStringSelection()) ## Updates the label for the current section def SetSectionLabel(self, section=None): if section == None: section = self.sel_section.GetStringSelection() if section in sections: Logger.Debug(__name__, u'Setting section to {}'.format(section)) self.LabelSection.SetLabel(sections[section]) return True return False
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.COPYRIGHT) self.custom_licenses = [] ## A list of available license templates self.sel_templates = Choice(self, selid.LICENSE, name=u'list»') # Initialize the template list self.OnRefreshList() btn_template = CreateButton(self, label=GT(u'Full Template'), image=u'full', name=u'full»') self.btn_template_simple = CreateButton(self, label=GT(u'Short Template'), image=u'short', name=u'short»') btn_refresh = CreateButton(self, btnid.REFRESH, GT(u'Refresh Template List'), u'refresh', name=u'btn refresh') btn_open = CreateButton(self, btnid.BROWSE, GT(u'Open Template Directory'), u'browse', name=u'btn opendir', commands=u'xdg-open') if not self.sel_templates.GetCount(): self.sel_templates.Enable(False) btn_template.Enable(False) self.btn_template_simple.Enable(False) ## Area where license text is displayed self.dsp_copyright = TextAreaPanelESS(self, monospace=True, name=u'license') self.dsp_copyright.EnableDropTarget() SetPageToolTips(self) # Initiate tooltip for drop-down selector if self.sel_templates.IsEnabled(): self.OnSelectLicense(self.sel_templates) # *** Event Handling *** # self.sel_templates.Bind(wx.EVT_CHOICE, self.OnSelectLicense) btn_open.Bind(wx.EVT_BUTTON, self.OnOpenPath) btn_refresh.Bind(wx.EVT_BUTTON, self.OnRefreshList) btn_template.Bind(wx.EVT_BUTTON, self.OnTemplateFull) self.btn_template_simple.Bind(wx.EVT_BUTTON, self.OnTemplateShort) # *** Layout *** # lyt_top = BoxSizer(wx.HORIZONTAL) lyt_top.Add(wx.StaticText(self, label=GT(u'Available Templates')), 0, lyt.ALGN_CV) lyt_top.Add(self.sel_templates, 0, lyt.ALGN_CV|wx.LEFT, 5) lyt_top.Add(btn_template, 0, wx.LEFT, 5) lyt_top.Add(self.btn_template_simple) lyt_top.Add(btn_refresh) lyt_top.Add(btn_open) lyt_main = BoxSizer(wx.VERTICAL) lyt_main.AddSpacer(10) lyt_main.Add(lyt_top, 0, lyt.PAD_LR|wx.BOTTOM, 5) lyt_main.Add(self.dsp_copyright, 1, wx.EXPAND|lyt.PAD_LRB, 5) self.SetAutoLayout(True) self.SetSizer(lyt_main) self.Layout() ## Displays a confirmation dialog to clear the text area if it is not empty # # \return # <b><i>True</i></b>, if user confirmed def DestroyLicenseText(self): if not TextIsEmpty(self.dsp_copyright.GetValue()): warn_msg = GT(u'This will destroy all license text.') warn_msg = u'{}\n\n{}'.format(warn_msg, GT(u'Continue?')) if ConfirmationDialog(GetMainWindow(), text=warn_msg).ShowModal() not in (wx.ID_OK, wx.OK): return False return True ## \see wiz.wizard.WizardPage.ExportBuild def ExportBuild(self, stage): stage = u'{}/usr/share/doc/{}'.format(stage, GetPage(pgid.CONTROL).GetPackageName()).replace(u'//', u'/') # FIXME: Should be error check self.Export(stage, u'copyright') return (0, None) ## Retrieves copyright/license text # # \return # <b><i>tuple(str, str)</i></b>: Filename & copyright/license text def Get(self, getModule=False): page = self.dsp_copyright.GetValue() if TextIsEmpty(page): page = None if getModule: page = (__name__, page,) return page ## Retrieves license path # # \param licName # License file basename to search for # If 'None', uses currently selected license # \return # Full path to license file if found def GetLicensePath(self, licName=None): # Default to currently selected template if not licName: licName = self.GetSelectedName() return GetLicenseTemplateFile(licName) ## Retrieves the name of the template currently selected def GetSelectedName(self): return GetField(self, selid.LICENSE).GetStringSelection() ## Sets page's fields from opened file def ImportFromFile(self, filename): if not os.path.isfile(filename): return dbrerrno.ENOENT copyright_data = ReadFile(filename, split=True) # Remove preceding empty lines remove_index = 0 for I in copyright_data: if not TextIsEmpty(I): break remove_index += 1 for I in reversed(range(remove_index)): copyright_data.remove(copyright_data[I]) copyright_data = u'\n'.join(copyright_data) self.dsp_copyright.SetValue(copyright_data) return 0 ## Checks if page can be exported or or added to build def IsOkay(self): return not TextIsEmpty(self.dsp_copyright.GetValue()) ## Opens directory containing currently selected license def OnOpenPath(self, event=None): CMD_open = GetExecutable(u'xdg-open') if CMD_open: path = self.GetLicensePath() if not path: ShowErrorDialog(GT(u'Error retrieving template path: {}').format(self.GetSelectedName())) return False path = os.path.dirname(path) if os.path.isdir(path): ExecuteCommand(CMD_open, (path,)) return True return False ## Repopulates template list def OnRefreshList(self, event=None): # FIXME: Ignore symbolic links??? self.custom_licenses = GetLocalLicenses() licenses = list(self.custom_licenses) # System licenses are not added to "custom" list for LIC in GetSysLicenses(): if LIC not in licenses: licenses.append(LIC) for LIC in GetCustomLicenses(): if LIC not in licenses: licenses.append(LIC) self.custom_licenses.append(LIC) self.custom_licenses.sort(key=GS.lower) sel_templates = GetField(self, selid.LICENSE) selected = None if sel_templates.GetCount(): selected = sel_templates.GetStringSelection() sel_templates.Set(sorted(licenses, key=GS.lower)) if selected: if not sel_templates.SetStringSelection(selected): # Selected template file was not found sel_templates.SetSelection(sel_templates.GetDefaultValue()) # Update short template button enabled state self.OnSelectLicense() else: sel_templates.SetSelection(sel_templates.GetDefaultValue()) ## Enables/Disables simple template button # # Simple template generation is only available # for system licenses. def OnSelectLicense(self, event=None): choice = GetField(self, selid.LICENSE) if choice: template = choice.GetString(choice.GetSelection()) if template in self.custom_licenses: self.btn_template_simple.Disable() else: self.btn_template_simple.Enable() self.SetLicenseTooltip() ## Generates a full license template def OnTemplateFull(self, event=None): selected_template = self.sel_templates.GetStringSelection() template_file = self.GetLicensePath(selected_template) if self.DestroyLicenseText(): if not template_file or not os.path.isfile(template_file): ShowErrorDialog(GT(u'Could not locate license file: {}').format(self.GetSelectedName())) return Logger.Debug(__name__, u'Copying license {}'.format(template_file)) license_text = ReadFile(template_file, noStrip=u' ') # Number defines how many empty lines to add after the copyright header # Boolean/Integer defines whether copyright header should be centered/offset add_header = { u'Artistic': (1, True), u'BSD': (0, False), } template_name = os.path.basename(template_file) if template_name in add_header: license_text = license_text.split(u'\n') empty_lines = add_header[template_name][0] for L in range(empty_lines): license_text.insert(0, wx.EmptyString) header = copyright_header.format(GetYear()) center_header = add_header[template_name][1] if center_header: Logger.Debug(__name__, u'Centering header...') offset = 0 # Don't use isinstance() here because boolean is an instance of integer if type(center_header) == int: offset = center_header else: # Use the longest line found in the text to center the header longest_line = GetLongestLine(license_text) Logger.Debug(__name__, u'Longest line: {}'.format(longest_line)) header_length = len(header) if header_length < longest_line: offset = (longest_line - header_length) / 2 if offset: Logger.Debug(__name__, u'Offset: {}'.format(offset)) header = u'{}{}'.format(u' ' * offset, header) # Special changes for BSD license if template_name == u'BSD': line_index = 0 for LI in license_text: if u'copyright (c)' in LI.lower(): license_text[line_index] = header break line_index += 1 else: license_text.insert(0, header) license_text = u'\n'.join(license_text) if not license_text: ShowErrorDialog(GT(u'License template is empty')) return self.dsp_copyright.SetValue(license_text) self.dsp_copyright.SetInsertionPoint(0) self.dsp_copyright.SetFocus() ## Generates a short reference template for a system license def OnTemplateShort(self, event=None): if self.DestroyLicenseText(): self.dsp_copyright.Clear() license_path = u'{}/{}'.format(sys_licenses_path, self.sel_templates.GetString(self.sel_templates.GetSelection())) self.dsp_copyright.WriteText(u'{}\n\n{}'.format(copyright_header.format(GetYear()), license_path)) self.dsp_copyright.SetInsertionPoint(0) self.dsp_copyright.SetFocus() ## Resets all page fields to default values def Reset(self): self.dsp_copyright.Clear() if self.sel_templates.IsEnabled(): self.sel_templates.Reset() self.OnSelectLicense(self.sel_templates) ## Sets the text of the displayed copyright # # \param data # Text to parse for field values def Set(self, data): self.dsp_copyright.SetValue(data) ## Changes the Choice instance's tooltip for the current license def SetLicenseTooltip(self): license_name = self.sel_templates.GetString(self.sel_templates.GetSelection()) license_path = self.GetLicensePath(license_name) if license_path: self.sel_templates.SetToolTip(wx.ToolTip(license_path)) return self.sel_templates.SetToolTip(None)
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])