Example #1
0
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:]))
Example #2
0
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)
Example #3
0
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:]))
Example #4
0
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)
Example #5
0
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)