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 DebianScript(wx.Panel): ## Constructor # # \param parent # The <b><i>wx.Window</i></b> parent instance # \param scriptId # Unique <b><i>integer</i></b> identifier for script def __init__(self, parent, scriptId): wx.Panel.__init__(self, parent, scriptId) ## Filename used for exporting script self.FileName = id_definitions[scriptId].lower() ## String name used for display in the application self.ScriptName = None self.SetScriptName() shell_options = [] shell_options.append(u'/bin/sh') # Place /bin/sh as first item for P in u'/bin/', u'/usr/bin/', u'/usr/bin/env ': for S in sorted(shell_descriptions, key=GS.lower): if S == u'sh': pass else: shell_options.append(P + S) self.Shell = ComboBoxESS(self, self.GetId(), choices=shell_options, monospace=True, defaultValue=u'/bin/bash') self.ScriptBody = TextAreaPanelESS(self, self.GetId(), monospace=True) self.ScriptBody.EnableDropTarget() # *** Layout *** # lyt_shell = BoxSizer(wx.HORIZONTAL) lyt_shell.Add(wx.StaticText(self, label=u'#!'), 0, lyt.ALGN_CV|wx.RIGHT, 5) lyt_shell.Add(self.Shell, 1) lyt_main = BoxSizer(wx.VERTICAL) lyt_main.Add(lyt_shell, 0) lyt_main.Add(self.ScriptBody, 1, wx.EXPAND|wx.TOP, 5) self.SetSizer(lyt_main) self.SetAutoLayout(True) self.Layout() # Scripts are hidden by default self.Hide() ## Exports the script to a text file # # \param out_dir # Target directory of output file # \param executable # If <b><i>True</i></b>, sets the files executable bit # \param build # If <b><i>True</i></b>, format output for final build def Export(self, out_dir, executable=True, build=False): if not os.path.isdir(out_dir): Logger.Error(__name__, GT(u'Directory not available: {}'.format(out_dir))) return (ERR_DIR_NOT_AVAILABLE, __name__) if build: absolute_filename = ConcatPaths((out_dir, self.FileName)) else: filename = u'{}-{}'.format(page_ids[self.Parent.GetId()].upper(), self.FileName) absolute_filename = ConcatPaths((out_dir, filename)) script_text = u'{}\n\n{}'.format(self.GetShebang(), self.ScriptBody.GetValue()) WriteFile(absolute_filename, script_text) if not os.path.isfile(absolute_filename): Logger.Error(__name__, GT(u'Could not write to file: {}'.format(absolute_filename))) return (ERR_FILE_WRITE, __name__) if executable: os.chmod(absolute_filename, 0755) return (0, None) ## Retrieves the filename to use for exporting # # \return # Script filename def GetFilename(self): return self.FileName ## Retrieves the script's name for display # # \return # <b><i>String</i></b> representation of script's name def GetName(self): return self.ScriptName ## Retrieves the description of a shell for display # # \return # Description or <b><i>None</i></b> if using custom shell def GetSelectedShellDescription(self): selected_shell = self.Shell.GetValue() if selected_shell in shell_descriptions: return shell_descriptions[selected_shell] return None ## Retrieves the shebang/shell line def GetShebang(self): shell = self.Shell.GetValue() if shell.startswith(u'/usr/bin/env '): shell = u'#!{}\nset -e'.format(shell) else: shell = u'#!{} -e'.format(shell) return shell ## Retrieves the text body of the script def GetValue(self): return self.ScriptBody.GetValue() ## Checks if the script is used & can be exported # # The text area is checked &, if not empty, signifies that # the user want to export the script. # # \return # <b><i>True</i></b> if text area is not empty, <b><i>False</i></b> otherwise def IsOkay(self): return not TextIsEmpty(self.ScriptBody.GetValue()) ## Resets all members to default values def Reset(self): self.Shell.SetStringSelection(self.Shell.GetDefaultValue()) self.ScriptBody.Clear() ## Sets the name of the script to be displayed # # Sets the displayed script name to a value of either 'Pre Install', # 'Pre Uninstall', 'Post Install', or 'Post Uninstall'. 'self.FileName' # is used to determine the displayed name. # TODO: Add strings to GetText translations def SetScriptName(self): prefix = None suffix = None if u'pre' in self.FileName: prefix = u'Pre' suffix = self.FileName.split(u'pre')[1] elif u'post' in self.FileName: prefix = u'Post' suffix = self.FileName.split(u'post')[1] if suffix.lower() == u'inst': suffix = u'Install' elif suffix.lower() == u'rm': suffix = u'Uninstall' if (prefix != None) and (suffix != None): self.ScriptName = GT(u'{}-{}'.format(prefix, suffix)) ## Sets the shell/shebang line to use for script # # \param shell # Path to desired shell # \param forced # ??? def SetShell(self, shell, forced=False): if forced: self.Shell.SetValue(shell) return self.Shell.SetStringSelection(shell) ## Fills the script # # \param value # Text to be entered into the script body def SetValue(self, value): self.ScriptBody.SetValue(value)
class DebianScript(wx.Panel): ## Constructor # # \param parent # The <b><i>wx.Window</i></b> parent instance # \param scriptId # Unique <b><i>integer</i></b> identifier for script def __init__(self, parent, scriptId): wx.Panel.__init__(self, parent, scriptId) ## Filename used for exporting script self.FileName = id_definitions[scriptId].lower() ## String name used for display in the application self.ScriptName = None self.SetScriptName() self.ScriptBody = TextAreaPanelESS(self, self.GetId(), monospace=True) self.ScriptBody.EnableDropTarget() self.Check = None # *** Layout *** # lyt_main = BoxSizer(wx.VERTICAL) lyt_main.Add(self.ScriptBody, 1, wx.EXPAND | wx.TOP, 5) self.SetSizer(lyt_main) self.SetAutoLayout(True) self.Layout() # Scripts are hidden by default self.Hide() ## TODO: Doxygen def Disable(self): return self.Enable(False) ## TODO: Doxygen def Enable(self, enable=True): return self.ScriptBody.Enable(enable) ## Retrieves the filename to use for exporting # # \return # Script filename def GetFilename(self): return self.FileName ## Retrieves the script's name for display # # \return # <b><i>String</i></b> representation of script's name def GetName(self): return self.ScriptName ## Retrieves the text body of the script def GetValue(self): return self.ScriptBody.GetValue() ## TODO: Doxygen def Hide(self): if self.Check: self.Check.Hide() return wx.Panel.Hide(self) ## TODO: Doxygen def IsChecked(self): # FIXME: Should check if field is wx.CheckBox if self.Check: return self.Check.IsChecked() return False ## TODO: Doxygen def IsEnabled(self): return FieldEnabled(self.ScriptBody) ## Checks if the script is used & can be exported # # The text area is checked &, if not empty, signifies that # the user want to export the script. # # \return # <b><i>True</i></b> if text area is not empty, <b><i>False</i></b> otherwise def IsOkay(self): return not TextIsEmpty(self.ScriptBody.GetValue()) ## Resets all members to default values def Reset(self): self.ScriptBody.Clear() if self.Check: self.Check.Reset() ## TODO: Doxygen def SetCheckBox(self, check_box): self.Check = check_box ## TODO: Doxygen def SetChecked(self, value=True): self.Check.SetValue(value) ## Sets the name of the script to be displayed # # Sets the displayed script name to a value of either 'Pre Install', # 'Pre Uninstall', 'Post Install', or 'Post Uninstall'. 'self.FileName' # is used to determine the displayed name. # TODO: Add strings to GetText translations def SetScriptName(self): prefix = None suffix = None if u'pre' in self.FileName: prefix = u'Pre' suffix = self.FileName.split(u'pre')[1] elif u'post' in self.FileName: prefix = u'Post' suffix = self.FileName.split(u'post')[1] if suffix.lower() == u'inst': suffix = u'Install' elif suffix.lower() == u'rm': suffix = u'Uninstall' if (prefix != None) and (suffix != None): self.ScriptName = GT(u'{}-{}'.format(prefix, suffix)) ## Fills the script # # \param value # Text to be entered into the script body def SetValue(self, value): self.ScriptBody.SetValue(value) ## TODO: Doxygen def Show(self): if self.Check: self.Check.Show() return wx.Panel.Show(self)