예제 #1
0
    def SetTabName(self, index=-1, rename=False, checkBox=None, checked=False):
        getname = TextEntryDialog(GetMainWindow(), GT(u'Name for new page'))
        new_name = None

        if not rename and checkBox:
            check_box = CheckBox(getname, label=checkBox)
            check_box.SetValue(checked)

            sizer = getname.GetSizer()
            insert_point = len(sizer.GetChildren()) - 1

            sizer.InsertSpacer(insert_point, 5)
            sizer.Insert(insert_point + 1, check_box, 0, wx.LEFT, 16)

            getname.SetSize(sizer.GetMinSize())
            getname.Fit()
            getname.CenterOnParent()

        valid_name = False

        while not valid_name:
            if new_name and TextIsEmpty(new_name):
                getname.Clear()

            # User cancelled
            if not ShowDialog(getname):
                return False

            else:
                new_name = getname.GetValue()

            valid_name = self._title_is_ok(new_name)

            if valid_name:
                break

            ShowErrorDialog(GT(u'Page name cannot contain whitespace'),
                            warn=True)

        if rename:
            if index < 0:
                return False

            return self.Tabs.SetPageText(index, new_name)

        if checkBox:
            return self.AddPage(new_name, checkBox=check_box)

        return self.AddPage(new_name)
예제 #2
0
    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()
예제 #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()

    ## 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:]))
예제 #4
0
    def __init__(self, parent):
        WizardPage.__init__(self, parent, pgid.BUILD)

        # Bypass build prep check
        self.prebuild_check = False

        # Add checkable items to this list
        # FIXME: Use a different method
        self.build_options = []

        # ----- Extra Options

        pnl_options = BorderedPanel(self)

        self.chk_md5 = CheckBoxESS(pnl_options,
                                   chkid.MD5,
                                   GT(u'Create md5sums file'),
                                   name=u'MD5',
                                   defaultValue=True,
                                   commands=u'md5sum')
        # The » character denotes that an alternate tooltip should be shown if the control is disabled
        self.chk_md5.tt_name = u'md5»'
        self.chk_md5.col = 0

        if UsingTest(u'alpha'):
            # Brings up control file preview for editing
            self.chk_editctrl = CheckBoxCFG(
                pnl_options,
                chkid.EDIT,
                GT(u'Preview control file for editing'),
                name=u'editctrl')
            self.chk_editctrl.col = 1

        # TODO: Use CheckBoxCFG instead of CheckBoxESS:
        #		   Fields will be set from config instead of project file

        # Option to strip binaries
        self.chk_strip = CheckBoxESS(pnl_options,
                                     chkid.STRIP,
                                     GT(u'Strip binaries'),
                                     name=u'strip»',
                                     defaultValue=True,
                                     commands=u'strip')
        self.chk_strip.col = 0

        # Deletes the temporary build tree
        self.chk_rmstage = CheckBoxESS(pnl_options,
                                       chkid.DELETE,
                                       GT(u'Delete staged directory'),
                                       name=u'RMSTAGE',
                                       defaultValue=True)
        self.chk_rmstage.col = 0

        # Checks the output .deb for errors
        self.chk_lint = CheckBoxESS(
            pnl_options,
            chkid.LINT,
            GT(u'Check package for errors with lintian'),
            name=u'LINTIAN',
            defaultValue=True,
            commands=u'lintian')
        self.chk_lint.tt_name = u'lintian»'
        self.chk_lint.col = 0

        # Installs the deb on the system
        self.chk_install = CheckBox(pnl_options,
                                    chkid.INSTALL,
                                    GT(u'Install package after build'),
                                    name=u'INSTALL',
                                    commands=(
                                        u'gdebi-gtk',
                                        u'gdebi-kde',
                                    ))
        self.chk_install.tt_name = u'install»'
        self.chk_install.col = 0

        # *** Lintian Overrides *** #

        if UsingTest(u'alpha'):
            # FIXME: Move next to lintian check box
            self.lint_overrides = []
            btn_lint_overrides = CreateButton(self,
                                              label=GT(u'Lintian overrides'))
            btn_lint_overrides.Bind(wx.EVT_BUTTON, self.OnSetLintOverrides)

        btn_build = CreateButton(self, btnid.BUILD, GT(u'Build'), u'build', 64)

        # Display log
        dsp_log = OutputLog(self)

        SetPageToolTips(self)

        # *** Event Handling *** #

        btn_build.Bind(wx.EVT_BUTTON, self.OnBuild)

        # *** Layout *** #

        lyt_options = wx.GridBagSizer()

        next_row = 0
        prev_row = next_row
        for CHK in pnl_options.Children:
            row = next_row
            FLAGS = lyt.PAD_LR

            if CHK.col:
                row = prev_row
                FLAGS = wx.RIGHT

            lyt_options.Add(CHK, (row, CHK.col), flag=FLAGS, border=5)

            if not CHK.col:
                prev_row = next_row
                next_row += 1

        pnl_options.SetSizer(lyt_options)
        pnl_options.SetAutoLayout(True)
        pnl_options.Layout()

        lyt_buttons = BoxSizer(wx.HORIZONTAL)
        lyt_buttons.Add(btn_build, 1)

        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.AddSpacer(10)
        lyt_main.Add(wx.StaticText(self, label=GT(u'Extra Options')), 0,
                     lyt.ALGN_LB | wx.LEFT, 5)
        lyt_main.Add(pnl_options, 0, wx.LEFT, 5)
        lyt_main.AddSpacer(5)

        if UsingTest(u'alpha'):
            #lyt_main.Add(wx.StaticText(self, label=GT(u'Lintian overrides')), 0, wx.LEFT, 5)
            lyt_main.Add(btn_lint_overrides, 0, wx.LEFT, 5)

        lyt_main.AddSpacer(5)
        lyt_main.Add(lyt_buttons, 0, lyt.ALGN_C)
        lyt_main.Add(dsp_log, 2, wx.EXPAND | lyt.PAD_LRB, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()

        # *** Post-layout functions *** #

        self.InitDefaultSettings()
예제 #5
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:]))
예제 #6
0
class Page(WizardPage):
    ## Constructor
    #
    #  \param parent
    #	Parent <b><i>wx.Window</i></b> instance
    def __init__(self, parent):
        WizardPage.__init__(self, parent, pgid.BUILD)

        # ----- Extra Options

        pnl_options = BorderedPanel(self)

        self.chk_md5 = CheckBoxESS(pnl_options,
                                   chkid.MD5,
                                   GT(u'Create md5sums file'),
                                   name=u'MD5',
                                   defaultValue=True,
                                   commands=u'md5sum')
        # The » character denotes that an alternate tooltip should be shown if the control is disabled
        self.chk_md5.tt_name = u'md5»'
        self.chk_md5.col = 0

        # Option to strip binaries
        self.chk_strip = CheckBoxESS(pnl_options,
                                     chkid.STRIP,
                                     GT(u'Strip binaries'),
                                     name=u'strip»',
                                     defaultValue=True,
                                     commands=u'strip')
        self.chk_strip.col = 0

        # Deletes the temporary build tree
        self.chk_rmstage = CheckBoxESS(pnl_options,
                                       chkid.DELETE,
                                       GT(u'Delete staged directory'),
                                       name=u'RMSTAGE',
                                       defaultValue=True)
        self.chk_rmstage.col = 0

        # Checks the output .deb for errors
        self.chk_lint = CheckBoxESS(
            pnl_options,
            chkid.LINT,
            GT(u'Check package for errors with lintian'),
            name=u'LINTIAN',
            defaultValue=True,
            commands=u'lintian')
        self.chk_lint.tt_name = u'lintian»'
        self.chk_lint.col = 0

        # Installs the deb on the system
        self.chk_install = CheckBox(pnl_options,
                                    chkid.INSTALL,
                                    GT(u'Install package after build'),
                                    name=u'INSTALL',
                                    commands=(
                                        u'gdebi-gtk',
                                        u'gdebi-kde',
                                    ))
        self.chk_install.tt_name = u'install»'
        self.chk_install.col = 0

        # *** Lintian Overrides *** #

        if UsingTest(u'alpha'):
            # FIXME: Move next to lintian check box
            Logger.Info(__name__,
                        u'Enabling alpha feature "lintian overrides" option')
            self.lint_overrides = []
            btn_lint_overrides = CreateButton(self,
                                              label=GT(u'Lintian overrides'))
            btn_lint_overrides.Bind(wx.EVT_BUTTON, self.OnSetLintOverrides)

        btn_build = CreateButton(self, btnid.BUILD, GT(u'Build'), u'build', 64)

        # Display log
        dsp_log = OutputLog(self)

        SetPageToolTips(self)

        # *** Event Handling *** #

        btn_build.Bind(wx.EVT_BUTTON, self.OnBuild)

        # *** Layout *** #

        lyt_options = wx.GridBagSizer()

        next_row = 0
        prev_row = next_row
        for CHK in pnl_options.Children:
            row = next_row
            FLAGS = lyt.PAD_LR

            if CHK.col:
                row = prev_row
                FLAGS = wx.RIGHT

            lyt_options.Add(CHK, (row, CHK.col), flag=FLAGS, border=5)

            if not CHK.col:
                prev_row = next_row
                next_row += 1

        pnl_options.SetSizer(lyt_options)
        pnl_options.SetAutoLayout(True)
        pnl_options.Layout()

        lyt_buttons = BoxSizer(wx.HORIZONTAL)
        lyt_buttons.Add(btn_build, 1)

        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.AddSpacer(10)
        lyt_main.Add(wx.StaticText(self, label=GT(u'Extra Options')), 0,
                     lyt.ALGN_LB | wx.LEFT, 5)
        lyt_main.Add(pnl_options, 0, wx.LEFT, 5)
        lyt_main.AddSpacer(5)

        if UsingTest(u'alpha'):
            #lyt_main.Add(wx.StaticText(self, label=GT(u'Lintian overrides')), 0, wx.LEFT, 5)
            lyt_main.Add(btn_lint_overrides, 0, wx.LEFT, 5)

        lyt_main.AddSpacer(5)
        lyt_main.Add(lyt_buttons, 0, lyt.ALGN_C)
        lyt_main.Add(dsp_log, 2, wx.EXPAND | lyt.PAD_LRB, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()

    ## Method that builds the actual Debian package
    #
    #  \param task_list
    #		\b \e dict : Task string IDs & page data
    #  \param build_path
    #		\b \e unicode|str : Directory where .deb will be output
    #  \param filename
    #		\b \e unicode|str : Basename of output file without .deb extension
    #  \return
    #		\b \e dbrerror : SUCCESS if build completed successfully
    def Build(self, task_list, build_path, filename):
        # Declare this here in case of error before progress dialog created
        build_progress = None

        try:
            # Other mandatory tasks that will be processed
            mandatory_tasks = (
                u'stage',
                u'install_size',
                u'control',
                u'build',
            )

            # Add other mandatory tasks
            for T in mandatory_tasks:
                task_list[T] = None

            task_count = len(task_list)

            # Add each file for updating progress dialog
            if u'files' in task_list:
                task_count += len(task_list[u'files'])

            # Add each script for updating progress dialog
            if u'scripts' in task_list:
                task_count += len(task_list[u'scripts'])

            if DebugEnabled():
                task_msg = GT(u'Total tasks: {}').format(task_count)
                print(u'DEBUG: [{}] {}'.format(__name__, task_msg))
                for T in task_list:
                    print(u'\t{}'.format(T))

            create_changelog = u'changelog' in task_list
            create_copyright = u'copyright' in task_list

            pg_control = GetPage(pgid.CONTROL)
            pg_menu = GetPage(pgid.MENU)

            stage_dir = u'{}/{}__dbp__'.format(build_path, filename)

            if os.path.isdir(u'{}/DEBIAN'.format(stage_dir)):
                try:
                    shutil.rmtree(stage_dir)

                except OSError:
                    ShowErrorDialog(
                        GT(u'Could not free stage directory: {}').format(
                            stage_dir),
                        title=GT(u'Cannot Continue'))

                    return (dbrerrno.EEXIST, None)

            # Actual path to new .deb
            deb = u'"{}/{}.deb"'.format(build_path, filename)

            progress = 0

            task_msg = GT(u'Preparing build tree')
            Logger.Debug(__name__, task_msg)

            wx.Yield()
            build_progress = ProgressDialog(
                GetMainWindow(),
                GT(u'Building'),
                task_msg,
                maximum=task_count,
                style=PD_DEFAULT_STYLE | wx.PD_ELAPSED_TIME
                | wx.PD_ESTIMATED_TIME | wx.PD_CAN_ABORT)

            DIR_debian = ConcatPaths((stage_dir, u'DEBIAN'))

            # Make a fresh build tree
            os.makedirs(DIR_debian)
            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            def UpdateProgress(current_task, message=None):
                task_eval = u'{} / {}'.format(current_task, task_count)

                if message:
                    Logger.Debug(__name__,
                                 u'{} ({})'.format(message, task_eval))

                    wx.Yield()
                    build_progress.Update(current_task, message)

                    return

                wx.Yield()
                build_progress.Update(current_task)

            # *** Files *** #
            if u'files' in task_list:
                UpdateProgress(progress, GT(u'Copying files'))

                no_follow_link = GetField(GetPage(pgid.FILES),
                                          chkid.SYMLINK).IsChecked()

                # TODO: move this into a file functions module
                def _copy(f_src, f_tgt, exe=False):
                    # NOTE: Python 3 appears to have follow_symlinks option for shutil.copy
                    # FIXME: copying nested symbolic link may not work

                    if os.path.isdir(f_src):
                        if os.path.islink(f_src) and no_follow_link:
                            Logger.Debug(
                                __name__,
                                u'Adding directory symbolic link to stage: {}'.
                                format(f_tgt))

                            os.symlink(os.readlink(f_src), f_tgt)
                        else:
                            Logger.Debug(
                                __name__,
                                u'Adding directory to stage: {}'.format(f_tgt))

                            shutil.copytree(f_src, f_tgt)
                            os.chmod(f_tgt, 0o0755)
                    elif os.path.isfile(f_src):
                        if os.path.islink(f_src) and no_follow_link:
                            Logger.Debug(
                                __name__,
                                u'Adding file symbolic link to stage: {}'.
                                format(f_tgt))

                            os.symlink(os.readlink(f_src), f_tgt)
                        else:
                            if exe:
                                Logger.Debug(
                                    __name__,
                                    u'Adding executable to stage: {}'.format(
                                        f_tgt))
                            else:
                                Logger.Debug(
                                    __name__,
                                    u'Adding file to stage: {}'.format(f_tgt))

                            shutil.copy(f_src, f_tgt)

                            # Set FILE permissions
                            if exe:
                                os.chmod(f_tgt, 0o0755)

                            else:
                                os.chmod(f_tgt, 0o0644)

                files_data = task_list[u'files']
                for FILE in files_data:
                    file_defs = FILE.split(u' -> ')

                    source_file = file_defs[0]
                    target_file = u'{}{}/{}'.format(stage_dir, file_defs[2],
                                                    file_defs[1])
                    target_dir = os.path.dirname(target_file)

                    if not os.path.isdir(target_dir):
                        os.makedirs(target_dir)

                    # Remove asteriks from exectuables
                    exe = False
                    if source_file[-1] == u'*':
                        exe = True
                        source_file = source_file[:-1]

                    _copy(
                        source_file,
                        u'{}/{}'.format(target_dir,
                                        os.path.basename(source_file)), exe)

                    # Individual files
                    progress += 1
                    UpdateProgress(progress)

                # Entire file task
                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Strip files ***#
            # FIXME: Needs only be run if 'files' step is used
            if u'strip' in task_list:
                UpdateProgress(progress, GT(u'Stripping binaries'))

                for ROOT, DIRS, FILES in os.walk(stage_dir):  #@UnusedVariable
                    for F in FILES:
                        # Don't check files in DEBIAN directory
                        if ROOT != DIR_debian:
                            F = ConcatPaths((ROOT, F))

                            if FileUnstripped(F):
                                Logger.Debug(__name__,
                                             u'Unstripped file: {}'.format(F))

                                # FIXME: Strip command should be set as class member?
                                ExecuteCommand(GetExecutable(u'strip'), F)

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            package = GetField(pg_control, inputid.PACKAGE).GetValue()

            # Make sure that the directory is available in which to place documentation
            if create_changelog or create_copyright:
                doc_dir = u'{}/usr/share/doc/{}'.format(stage_dir, package)
                if not os.path.isdir(doc_dir):
                    os.makedirs(doc_dir)

            # *** Changelog *** #
            if create_changelog:
                UpdateProgress(progress, GT(u'Creating changelog'))

                # If changelog will be installed to default directory
                changelog_target = task_list[u'changelog'][0]
                if changelog_target == u'STANDARD':
                    changelog_target = ConcatPaths(
                        (u'{}/usr/share/doc'.format(stage_dir), package))

                else:
                    changelog_target = ConcatPaths(
                        (stage_dir, changelog_target))

                if not os.path.isdir(changelog_target):
                    os.makedirs(changelog_target)

                WriteFile(u'{}/changelog'.format(changelog_target),
                          task_list[u'changelog'][1])

                CMD_gzip = GetExecutable(u'gzip')

                if CMD_gzip:
                    UpdateProgress(progress, GT(u'Compressing changelog'))
                    c = u'{} -n --best "{}/changelog"'.format(
                        CMD_gzip, changelog_target)
                    clog_status = commands.getstatusoutput(c.encode(u'utf-8'))
                    if clog_status[0]:
                        ShowErrorDialog(GT(u'Could not compress changelog'),
                                        clog_status[1],
                                        warn=True,
                                        title=GT(u'Warning'))

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Copyright *** #
            if create_copyright:
                UpdateProgress(progress, GT(u'Creating copyright'))

                WriteFile(
                    u'{}/usr/share/doc/{}/copyright'.format(
                        stage_dir, package), task_list[u'copyright'])

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # Characters that should not be in filenames
            invalid_chars = (u' ', u'/')

            # *** Menu launcher *** #
            if u'launcher' in task_list:
                UpdateProgress(progress, GT(u'Creating menu launcher'))

                # This might be changed later to set a custom directory
                menu_dir = u'{}/usr/share/applications'.format(stage_dir)

                menu_filename = pg_menu.GetOutputFilename()

                # Remove invalid characters from filename
                for char in invalid_chars:
                    menu_filename = menu_filename.replace(char, u'_')

                if not os.path.isdir(menu_dir):
                    os.makedirs(menu_dir)

                WriteFile(u'{}/{}.desktop'.format(menu_dir, menu_filename),
                          task_list[u'launcher'])

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** md5sums file *** #
            # Good practice to create hashes before populating DEBIAN directory
            if u'md5sums' in task_list:
                UpdateProgress(progress, GT(u'Creating md5sums'))

                if not WriteMD5(stage_dir, parent=build_progress):
                    # Couldn't call md5sum command
                    build_progress.Cancel()

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Scripts *** #
            if u'scripts' in task_list:
                UpdateProgress(progress, GT(u'Creating scripts'))

                scripts = task_list[u'scripts']
                for SCRIPT in scripts:
                    script_name = SCRIPT
                    script_text = scripts[SCRIPT]

                    script_filename = ConcatPaths(
                        (stage_dir, u'DEBIAN', script_name))

                    WriteFile(script_filename, script_text)

                    # Make sure scipt path is wrapped in quotes to avoid whitespace errors
                    os.chmod(script_filename, 0755)
                    os.system((u'chmod +x "{}"'.format(script_filename)))

                    # Individual scripts
                    progress += 1
                    UpdateProgress(progress)

                # Entire script task
                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Control file *** #
            UpdateProgress(progress, GT(u'Getting installed size'))

            # Get installed-size
            installed_size = os.popen(
                (u'du -hsk "{}"'.format(stage_dir))).readlines()
            installed_size = installed_size[0].split(u'\t')
            installed_size = installed_size[0]

            # Insert Installed-Size into control file
            control_data = pg_control.Get().split(u'\n')
            control_data.insert(2,
                                u'Installed-Size: {}'.format(installed_size))

            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # Create final control file
            UpdateProgress(progress, GT(u'Creating control file'))

            # dpkg fails if there is no newline at end of file
            control_data = u'\n'.join(control_data).strip(u'\n')
            # Ensure there is only one empty trailing newline
            # Two '\n' to show physical empty line, but not required
            # Perhaps because string is not null terminated???
            control_data = u'{}\n\n'.format(control_data)

            WriteFile(u'{}/DEBIAN/control'.format(stage_dir),
                      control_data,
                      noStrip=u'\n')

            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Final build *** #
            UpdateProgress(progress, GT(u'Running dpkg'))

            working_dir = os.path.split(stage_dir)[0]
            c_tree = os.path.split(stage_dir)[1]
            deb_package = u'{}.deb'.format(filename)

            # Move the working directory becuase dpkg seems to have problems with spaces in path
            os.chdir(working_dir)

            # HACK to fix file/dir permissions
            for ROOT, DIRS, FILES in os.walk(stage_dir):
                for D in DIRS:
                    D = u'{}/{}'.format(ROOT, D)
                    os.chmod(D, 0o0755)
                for F in FILES:
                    F = u'{}/{}'.format(ROOT, F)
                    if os.access(F, os.X_OK):
                        os.chmod(F, 0o0755)
                    else:
                        os.chmod(F, 0o0644)

            # FIXME: Should check for working fakeroot & dpkg-deb executables
            build_status = commands.getstatusoutput(
                (u'{} {} -b "{}" "{}"'.format(GetExecutable(u'fakeroot'),
                                              GetExecutable(u'dpkg-deb'),
                                              c_tree, deb_package)))

            progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** Delete staged directory *** #
            if u'rmstage' in task_list:
                UpdateProgress(progress, GT(u'Removing temp directory'))

                try:
                    shutil.rmtree(stage_dir)

                except OSError:
                    ShowErrorDialog(GT(
                        u'An error occurred when trying to delete the build tree'
                    ),
                                    parent=build_progress)

                progress += 1

            if build_progress.WasCancelled():
                build_progress.Destroy()
                return (dbrerrno.ECNCLD, None)

            # *** ERROR CHECK
            if u'lintian' in task_list:
                UpdateProgress(progress, GT(u'Checking package for errors'))

                # FIXME: Should be set as class memeber?
                CMD_lintian = GetExecutable(u'lintian')
                errors = commands.getoutput((u'{} {}'.format(CMD_lintian,
                                                             deb)))

                if errors != wx.EmptyString:
                    e1 = GT(u'Lintian found some issues with the package.')
                    e2 = GT(u'Details saved to {}').format(filename)

                    WriteFile(u'{}/{}.lintian'.format(build_path, filename),
                              errors)

                    DetailedMessageDialog(build_progress,
                                          GT(u'Lintian Errors'),
                                          ICON_INFORMATION,
                                          u'{}\n{}.lintian'.format(e1, e2),
                                          errors).ShowModal()

                progress += 1

            # Close progress dialog
            wx.Yield()
            build_progress.Update(progress)
            build_progress.Destroy()

            # Build completed successfullly
            if not build_status[0]:
                return (dbrerrno.SUCCESS, deb_package)

            if PY_VER_MAJ <= 2:
                # Unicode decoder has trouble with certain characters. Replace any
                # non-decodable characters with � (0xFFFD).
                build_output = list(build_status[1])

                # String & unicode string incompatibilities
                index = 0
                for C in build_output:
                    try:
                        GS(C)

                    except UnicodeDecodeError:
                        build_output[index] = u'�'

                    index += 1

                build_status = (build_status[0], u''.join(build_output))

            # Build failed
            return (build_status[0], build_status[1])

        except:
            if build_progress:
                build_progress.Destroy()

            return (dbrerrno.EUNKNOWN, traceback.format_exc())

    ## TODO: Doxygen
    #
    #  \return
    #	\b \e tuple containing Return code & build details
    def BuildPrep(self):
        # Declare these here in case of error before dialogs created
        save_dia = None
        prebuild_progress = None

        try:
            # List of tasks for build process
            # 'stage' should be very first task
            task_list = {}

            # Control page
            pg_control = GetPage(pgid.CONTROL)
            fld_package = GetField(pg_control, inputid.PACKAGE)
            fld_version = GetField(pg_control, inputid.VERSION)
            fld_maint = GetField(pg_control, inputid.MAINTAINER)
            fld_email = GetField(pg_control, inputid.EMAIL)
            fields_control = (
                fld_package,
                fld_version,
                fld_maint,
                fld_email,
            )

            # Menu launcher page
            pg_launcher = GetPage(pgid.MENU)

            # Check to make sure that all required fields have values
            required = list(fields_control)

            if pg_launcher.IsOkay():
                task_list[u'launcher'] = pg_launcher.Get()

                required.append(GetField(pg_launcher, inputid.NAME))

                if not GetField(pg_launcher, chkid.FNAME).GetValue():
                    required.append(GetField(pg_launcher, inputid.FNAME))

            for item in required:
                if TextIsEmpty(item.GetValue()):
                    field_name = GT(item.GetName().title())
                    page_name = pg_control.GetName()
                    if item not in fields_control:
                        page_name = pg_launcher.GetName()

                    return (dbrerrno.FEMPTY,
                            u'{} ➜ {}'.format(page_name, field_name))

            # Get information from control page for default filename
            package = fld_package.GetValue()
            # Remove whitespace
            package = package.strip(u' \t')
            package = u'-'.join(package.split(u' '))

            version = fld_version.GetValue()
            # Remove whitespace
            version = version.strip(u' \t')
            version = u''.join(version.split())

            arch = GetField(pg_control, inputid.ARCH).GetStringSelection()

            # Dialog for save destination
            ttype = GT(u'Debian packages')
            save_dia = wx.FileDialog(
                self, GT(u'Save'), os.getcwd(), wx.EmptyString,
                u'{}|*.deb'.format(ttype),
                wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT | wx.FD_CHANGE_DIR)
            save_dia.SetFilename(u'{}_{}_{}.deb'.format(
                package, version, arch))
            if not save_dia.ShowModal() == wx.ID_OK:
                return (dbrerrno.ECNCLD, None)

            build_path = os.path.split(save_dia.GetPath())[0]
            filename = os.path.split(save_dia.GetPath())[1].split(u'.deb')[0]

            # Control, menu, & build pages not added to this list
            page_checks = (
                (pgid.FILES, u'files'),
                (pgid.SCRIPTS, u'scripts'),
                (pgid.CHANGELOG, u'changelog'),
                (pgid.COPYRIGHT, u'copyright'),
            )

            # Install step is not added to this list
            # 'control' should be after 'md5sums'
            # 'build' should be after 'control'
            other_checks = (
                (self.chk_md5, u'md5sums'),
                (self.chk_strip, u'strip'),
                (self.chk_rmstage, u'rmstage'),
                (self.chk_lint, u'lintian'),
            )

            prep_task_count = len(page_checks) + len(other_checks)

            progress = 0

            wx.Yield()
            prebuild_progress = ProgressDialog(GetMainWindow(),
                                               GT(u'Preparing to build'),
                                               maximum=prep_task_count)

            if wx.MAJOR_VERSION < 3:
                # Resize dialog for better fit
                pb_size = prebuild_progress.GetSizeTuple()
                pb_size = (pb_size[0] + 200, pb_size[1])
                prebuild_progress.SetSize(pb_size)
                prebuild_progress.CenterOnParent()

            for PID, id_string in page_checks:
                wx.Yield()
                prebuild_progress.Update(progress,
                                         GT(u'Checking {}').format(id_string))

                wizard_page = GetPage(PID)
                if wizard_page.IsOkay():
                    task_list[id_string] = wizard_page.Get()

                progress += 1

            for task_check, id_string in other_checks:
                wx.Yield()
                prebuild_progress.Update(
                    progress,
                    GT(u'Testing for: {}').format(task_check.GetLabel()))

                if task_check.GetValue():
                    task_list[id_string] = None

                progress += 1

            # Close progress dialog
            wx.Yield()
            prebuild_progress.Update(progress)
            prebuild_progress.Destroy()

            return (dbrerrno.SUCCESS, (task_list, build_path, filename))

        except:
            if save_dia:
                save_dia.Destroy()

            if prebuild_progress:
                prebuild_progress.Destroy()

            return (dbrerrno.EUNKNOWN, traceback.format_exc())

    ## TODO: Doxygen
    def GetSaveData(self):
        build_list = []

        options = (
            self.chk_md5,
            self.chk_rmstage,
            self.chk_lint,
        )

        for O in options:
            if O.GetValue():
                build_list.append(u'1')

            else:
                build_list.append(u'0')

        if self.chk_strip.GetValue():
            build_list.append(u'strip')

        return u'<<BUILD>>\n{}\n<</BUILD>>'.format(u'\n'.join(build_list))

    ## Installs the built .deb package onto the system
    #
    #  Uses the system's package installer:
    #	gdebi if available or dpkg
    #
    #  Shows a success dialog if installed. Otherwise shows an
    #  error dialog.
    #  \param package
    #		\b \e unicode|str : Path to package to be installed
    def InstallPackage(self, package):
        system_installer = GetSystemInstaller()

        if not system_installer:
            ShowErrorDialog(
                GT(u'Cannot install package'),
                GT(u'A compatible package manager could not be found on the system'
                   ),
                __name__,
                warn=True)

            return

        Logger.Info(__name__,
                    GT(u'Attempting to install package: {}').format(package))
        Logger.Info(__name__,
                    GT(u'Installing with {}').format(system_installer))

        install_cmd = (
            system_installer,
            package,
        )

        wx.Yield()
        # FIXME: Use ExecuteCommand here
        install_output = subprocess.Popen(install_cmd)

        # Command appears to not have been executed correctly
        if install_output == None:
            ShowErrorDialog(GT(u'Could not install package: {}'),
                            GT(u'An unknown error occurred'), __name__)

            return

        # Command executed but did not return success code
        if install_output.returncode:
            err_details = (
                GT(u'Process returned code {}').format(
                    install_output.returncode),
                GT(u'Command executed: {}').format(u' '.join(install_cmd)),
            )

            ShowErrorDialog(GT(u'An error occurred during installation'),
                            u'\n'.join(err_details), __name__)

            return

    ## TODO: Doxygen
    def OnBuild(self, event=None):
        # Build preparation
        ret_code, build_prep = self.BuildPrep()

        if ret_code == dbrerrno.ECNCLD:
            return

        if ret_code == dbrerrno.FEMPTY:
            err_dia = DetailedMessageDialog(
                GetMainWindow(),
                GT(u'Cannot Continue'),
                ICON_EXCLAMATION,
                text=u'{}\n{}'.format(
                    GT(u'One of the required fields is empty:'), build_prep))
            err_dia.ShowModal()
            err_dia.Destroy()

            return

        if ret_code == dbrerrno.SUCCESS:
            task_list, build_path, filename = build_prep

            # Actual build
            ret_code, result = self.Build(task_list, build_path, filename)

            # FIXME: Check .deb package timestamp to confirm build success
            if ret_code == dbrerrno.SUCCESS:
                DetailedMessageDialog(
                    GetMainWindow(),
                    GT(u'Success'),
                    ICON_INFORMATION,
                    text=GT(u'Package created successfully')).ShowModal()

                # Installing the package
                if FieldEnabled(
                        self.chk_install) and self.chk_install.GetValue():
                    self.InstallPackage(result)

                return

            if result:
                ShowErrorDialog(GT(u'Package build failed'), result)

            else:
                ShowErrorDialog(GT(u'Package build failed with unknown error'))

            return

        if build_prep:
            ShowErrorDialog(GT(u'Build preparation failed'), build_prep)

        else:
            ShowErrorDialog(GT(u'Build preparation failed with unknown error'))

    ## TODO: Doxygen
    #
    #  TODO: Show warning dialog that this could take a while
    #  TODO: Add cancel option to progress dialog
    #  FIXME: List should be cached so no need for re-scanning
    def OnSetLintOverrides(self, event=None):
        Logger.Debug(__name__, GT(u'Setting Lintian overrides...'))

        lintian_tags_file = u'{}/data/lintian/tags'.format(PATH_app)

        if not os.path.isfile(lintian_tags_file):
            Logger.Error(
                __name__,
                u'Lintian tags file is missing: {}'.format(lintian_tags_file))

            return False

        lint_tags = RemoveEmptyLines(ReadFile(lintian_tags_file, split=True))

        if lint_tags:
            Logger.Debug(__name__, u'Lintian tags set')

            # DEBUG: Start
            if DebugEnabled() and len(lint_tags) > 50:
                print(u'  Reducing tag count to 200 ...')

                lint_tags = lint_tags[:50]

            Logger.Debug(__name__,
                         u'Processing {} tags'.format(len(lint_tags)))
            # DEBUG: End

            tag_count = len(lint_tags)

            def GetProgressMessage(message, count=tag_count):
                return u'{} ({} {})'.format(message, count, GT(u'tags'))

            progress = TimedProgressDialog(
                GetMainWindow(), GT(u'Building Tag List'),
                GetProgressMessage(GT(u'Scanning default tags')))
            progress.Start()

            wx.Yield()

            # Create the dialog
            overrides_dialog = CheckListDialog(GetMainWindow(),
                                               title=GT(u'Lintian Overrides'),
                                               allow_custom=True)
            # FIXME: Needs progress dialog
            overrides_dialog.InitCheckList(tuple(lint_tags))

            progress.SetMessage(
                GetProgressMessage(GT(u'Setting selected overrides')))

            for T in lint_tags:
                if T in self.lint_overrides:
                    overrides_dialog.SetItemCheckedByLabel(T)
                    self.lint_overrides.remove(T)

            progress.SetMessage(
                GetProgressMessage(GT(u'Adding custom tags'),
                                   len(self.lint_overrides)))

            # Remaining tags should be custom entries
            # FIXME:
            if self.lint_overrides:
                for T in self.lint_overrides:
                    overrides_dialog.AddItem(T, True)

            progress.Stop()

            if overrides_dialog.ShowModal() == wx.ID_OK:
                # Remove old overrides
                self.lint_overrides = []
                for L in overrides_dialog.GetCheckedLabels():
                    Logger.Debug(__name__,
                                 GT(u'Adding Lintian override: {}').format(L))

                    self.lint_overrides.append(L)

            return True

        else:
            Logger.Debug(__name__, u'Setting lintian tags failed')

            return False

    ## TODO: Doxygen
    #
    #  TODO: Use string names in project file but retain
    #		compatibility with older projects that use
    #		integer values.
    def Set(self, data):
        # ???: Redundant
        self.Reset()
        build_data = data.split(u'\n')

        if GetExecutable(u'md5sum'):
            try:
                self.chk_md5.SetValue(int(build_data[0]))

            except IndexError:
                pass

        try:
            self.chk_rmstage.SetValue(int(build_data[1]))

        except IndexError:
            pass

        if GetExecutable(u'lintian'):
            try:
                self.chk_lint.SetValue(int(build_data[2]))

            except IndexError:
                pass

        self.chk_strip.SetValue(
            GetExecutable(u'strip') and u'strip' in build_data)

    ## TODO: Doxygen
    def SetSummary(self, event=None):
        pg_scripts = GetPage(pgid.SCRIPTS)

        # Make sure the page is not destroyed so no error is thrown
        if self:
            # Set summary when "Build" page is shown
            # Get the file count
            files_total = GetPage(pgid.FILES).GetFileCount()
            f = GT(u'File Count')
            file_count = u'{}: {}'.format(f, files_total)

            # Scripts to make
            scripts_to_make = []
            scripts = ((u'preinst', pg_scripts.chk_preinst),
                       (u'postinst', pg_scripts.chk_postinst),
                       (u'prerm', pg_scripts.chk_prerm),
                       (u'postrm', pg_scripts.chk_postrm))

            for script in scripts:
                if script[1].IsChecked():
                    scripts_to_make.append(script[0])

            s = GT(u'Scripts')
            if len(scripts_to_make):
                scripts_to_make = u'{}: {}'.format(s,
                                                   u', '.join(scripts_to_make))

            else:
                scripts_to_make = u'{}: 0'.format(s)

            self.summary.SetValue(u'\n'.join((file_count, scripts_to_make)))
예제 #7
0
    def __init__(self, parent):
        WizardPage.__init__(self, parent, pgid.SCRIPTS)

        preinst = DebianScript(self, ID_INST_PRE)
        postinst = DebianScript(self, ID_INST_POST)
        prerm = DebianScript(self, ID_RM_PRE)
        postrm = DebianScript(self, ID_RM_POST)

        # Check boxes for choosing scripts
        chk_preinst = CheckBox(self,
                               ID_INST_PRE,
                               GT(u'Make this script'),
                               name=GT(u'Pre-Install'))
        preinst.SetCheckBox(chk_preinst)
        chk_postinst = CheckBox(self,
                                ID_INST_POST,
                                GT(u'Make this script'),
                                name=GT(u'Post-Install'))
        postinst.SetCheckBox(chk_postinst)
        chk_prerm = CheckBox(self,
                             ID_RM_PRE,
                             GT(u'Make this script'),
                             name=GT(u'Pre-Remove'))
        prerm.SetCheckBox(chk_prerm)
        chk_postrm = CheckBox(self,
                              ID_RM_POST,
                              GT(u'Make this script'),
                              name=GT(u'Post-Remove'))
        postrm.SetCheckBox(chk_postrm)

        for S in chk_preinst, chk_postinst, chk_prerm, chk_postrm:
            S.SetToolTipString(u'{} {}'.format(
                S.GetName(), GT(u'script will be created from text below')))

            S.Bind(wx.EVT_CHECKBOX, self.OnToggleScripts)

        # Radio buttons for displaying between pre- and post- install scripts
        # FIXME: Names settings for tooltips are confusing
        rb_preinst = wx.RadioButton(self,
                                    preinst.GetId(),
                                    GT(u'Pre-Install'),
                                    name=preinst.FileName,
                                    style=wx.RB_GROUP)
        rb_postinst = wx.RadioButton(self,
                                     postinst.GetId(),
                                     GT(u'Post-Install'),
                                     name=postinst.FileName)
        rb_prerm = wx.RadioButton(self,
                                  prerm.GetId(),
                                  GT(u'Pre-Remove'),
                                  name=prerm.FileName)
        rb_postrm = wx.RadioButton(self,
                                   postrm.GetId(),
                                   GT(u'Post-Remove'),
                                   name=postrm.FileName)

        # TODO: Remove check boxes from lists (no longer needed)
        self.script_objects = (
            (
                preinst,
                chk_preinst,
                rb_preinst,
            ),
            (
                postinst,
                chk_postinst,
                rb_postinst,
            ),
            (
                prerm,
                chk_prerm,
                rb_prerm,
            ),
            (
                postrm,
                chk_postrm,
                rb_postrm,
            ),
        )

        for DS, CHK, RB in self.script_objects:
            CHK.Hide()

        # Set script text areas to default enabled/disabled setting
        self.OnToggleScripts()

        # *** Auto-Link *** #

        pnl_autolink = BorderedPanel(self)

        # Auto-Link path for new link
        txt_autolink = wx.StaticText(pnl_autolink,
                                     label=GT(u'Path'),
                                     name=u'target')
        self.ti_autolink = PathCtrl(pnl_autolink,
                                    value=u'/usr/bin',
                                    defaultValue=u'/usr/bin',
                                    warn=True)
        self.ti_autolink.SetName(u'target')
        self.ti_autolink.Default = self.ti_autolink.GetValue()

        # Auto-Link executables to be linked
        self.Executables = BasicFileList(pnl_autolink,
                                         size=(200, 200),
                                         hlExe=True,
                                         name=u'al list')

        # Auto-Link import, generate and remove buttons
        btn_al_import = CreateButton(pnl_autolink, btnid.IMPORT)
        btn_al_remove = CreateButton(pnl_autolink, btnid.REMOVE)
        btn_al_generate = CreateButton(pnl_autolink, image=u'build')

        # Auto-Link help
        btn_help = CreateButton(pnl_autolink, btnid.HELP, size=64)

        # Initialize script display
        self.ScriptSelect(None)

        SetPageToolTips(self)

        # *** Event Handling *** #

        for DS, CHK, RB in self.script_objects:
            RB.Bind(wx.EVT_RADIOBUTTON, self.ScriptSelect)

        wx.EVT_BUTTON(btn_al_import, btnid.IMPORT, self.ImportExes)
        wx.EVT_BUTTON(btn_al_generate, wx.ID_ANY, self.OnGenerate)
        wx.EVT_BUTTON(btn_al_remove, btnid.REMOVE, self.ImportExes)
        wx.EVT_BUTTON(btn_help, btnid.HELP, self.OnHelpButton)

        # *** Layout *** #

        # Organizing radio buttons
        lyt_sel_script = BoxSizer(wx.HORIZONTAL)
        lyt_sel_script.AddMany((
            (chk_preinst),
            (chk_postinst),
            (chk_prerm),
            (chk_postrm),
        ))

        lyt_sel_script.AddStretchSpacer(1)

        lyt_sel_script.AddMany((
            (rb_preinst),
            (rb_postinst),
            (rb_prerm),
            (rb_postrm),
        ))

        # Sizer for left half of scripts panel
        lyt_left = BoxSizer(wx.VERTICAL)
        lyt_left.Add(lyt_sel_script, 0, wx.EXPAND | wx.BOTTOM, 5)

        for DS, CHK, RB, in self.script_objects:
            lyt_left.Add(DS, 1, wx.EXPAND)

        # Auto-Link/Right side
        lyt_ti_autolink = BoxSizer(wx.HORIZONTAL)
        lyt_ti_autolink.Add(txt_autolink, 0, lyt.ALGN_C)
        lyt_ti_autolink.Add(self.ti_autolink, 1, lyt.ALGN_C)

        lyt_btn_autolink = BoxSizer(wx.HORIZONTAL)
        lyt_btn_autolink.Add(btn_al_import, 0)
        lyt_btn_autolink.Add(btn_al_remove, 0, lyt.PAD_LR, 5)
        lyt_btn_autolink.Add(btn_al_generate, 0)

        lyt_autolink = BoxSizer(wx.VERTICAL)
        lyt_autolink.Add(lyt_ti_autolink, 0, wx.EXPAND | lyt.PAD_LRT, 5)
        lyt_autolink.Add(self.Executables, 3, wx.EXPAND | lyt.PAD_LRT, 5)
        lyt_autolink.Add(lyt_btn_autolink, 0, lyt.ALGN_CH)
        lyt_autolink.Add(btn_help, 1, lyt.ALGN_C)

        pnl_autolink.SetSizer(lyt_autolink)
        pnl_autolink.SetAutoLayout(True)
        pnl_autolink.Layout()

        # Sizer for right half of scripts panel
        lyt_right = BoxSizer(wx.VERTICAL)
        # Line up panels to look even
        lyt_right.AddSpacer(32)
        lyt_right.Add(wx.StaticText(self, label=GT(u'Auto-Link Executables')),
                      0, lyt.ALGN_LB)
        lyt_right.Add(pnl_autolink, 0, wx.EXPAND)

        lyt_main = BoxSizer(wx.HORIZONTAL)
        lyt_main.Add(lyt_left, 1, wx.EXPAND | wx.ALL, 5)
        lyt_main.Add(lyt_right, 0, lyt.PAD_RB, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()
예제 #8
0
    def __init__(self, parent):
        WizardPage.__init__(self, parent,
                            pgid.MENU)  #, name=GT(u'Menu Launcher'))

        ## Override default label
        self.Label = GT(u'Menu Launcher')

        # --- Buttons to open/preview/save .desktop file
        btn_open = CreateButton(self,
                                btnid.BROWSE,
                                GT(u'Browse'),
                                u'browse',
                                name=u'btn browse')
        btn_save = CreateButton(self,
                                btnid.SAVE,
                                GT(u'Save'),
                                u'save',
                                name=u'btn save')
        btn_preview = CreateButton(self,
                                   btnid.PREVIEW,
                                   GT(u'Preview'),
                                   u'preview',
                                   name=u'btn preview')

        # --- CHECKBOX
        chk_enable = CheckBox(self, chkid.ENABLE,
                              GT(u'Create system menu launcher'))

        # --- TYPE
        opts_type = (
            u'Application',
            u'Link',
            u'Directory',
        )

        txt_type = wx.StaticText(self, label=GT(u'Type'), name=u'type')
        ti_type = ComboBoxESS(self,
                              inputid.TYPE,
                              choices=opts_type,
                              name=u'Type',
                              defaultValue=opts_type[0])

        # --- ENCODING
        opts_enc = (
            u'UTF-1',
            u'UTF-7',
            u'UTF-8',
            u'CESU-8',
            u'UTF-EBCDIC',
            u'UTF-16',
            u'UTF-32',
            u'SCSU',
            u'BOCU-1',
            u'Punycode',
            u'GB 18030',
        )

        txt_enc = wx.StaticText(self, label=GT(u'Encoding'), name=u'encoding')
        ti_enc = ComboBoxESS(self,
                             inputid.ENC,
                             choices=opts_enc,
                             name=u'Encoding',
                             defaultValue=opts_enc[2])

        # --- TERMINAL
        chk_term = CheckBoxESS(self,
                               chkid.TERM,
                               GT(u'Terminal'),
                               name=u'Terminal')

        # --- STARTUP NOTIFY
        chk_notify = CheckBoxESS(self,
                                 chkid.NOTIFY,
                                 GT(u'Startup Notify'),
                                 name=u'StartupNotify',
                                 defaultValue=True)

        # --- Custom output filename
        txt_filename = wx.StaticText(self,
                                     txtid.FNAME,
                                     GT(u'Filename'),
                                     name=u'filename')
        ti_filename = TextArea(self, inputid.FNAME, name=txt_filename.Name)

        chk_filename = CheckBox(
            self,
            chkid.FNAME,
            GT(u'Use "Name" as output filename (<Name>.desktop)'),
            name=u'filename chk',
            defaultValue=True)

        # --- NAME (menu)
        txt_name = wx.StaticText(self, label=GT(u'Name'), name=u'name*')
        ti_name = TextAreaESS(self, inputid.NAME, name=u'Name')
        ti_name.req = True

        # --- EXECUTABLE
        txt_exec = wx.StaticText(self, label=GT(u'Executable'), name=u'exec')
        ti_exec = TextAreaESS(self, inputid.EXEC, name=u'Exec')

        # --- COMMENT
        txt_comm = wx.StaticText(self, label=GT(u'Comment'), name=u'comment')
        ti_comm = TextAreaESS(self, inputid.DESCR, name=u'Comment')

        # --- ICON
        txt_icon = wx.StaticText(self, label=GT(u'Icon'), name=u'icon')
        ti_icon = TextAreaESS(self, inputid.ICON, name=u'Icon')

        txt_mime = wx.StaticText(self, label=GT(u'MIME Type'), name=u'mime')
        ti_mime = TextAreaESS(self,
                              inputid.MIME,
                              defaultValue=wx.EmptyString,
                              name=u'MimeType')

        # ----- OTHER/CUSTOM
        txt_other = wx.StaticText(self,
                                  label=GT(u'Custom Fields'),
                                  name=u'other')
        ti_other = TextAreaPanel(self, inputid.OTHER, name=txt_other.Name)
        ti_other.EnableDropTarget()

        # --- CATEGORIES
        opts_category = (
            u'2DGraphics',
            u'Accessibility',
            u'Application',
            u'ArcadeGame',
            u'Archiving',
            u'Audio',
            u'AudioVideo',
            u'BlocksGame',
            u'BoardGame',
            u'Calculator',
            u'Calendar',
            u'CardGame',
            u'Compression',
            u'ContactManagement',
            u'Core',
            u'DesktopSettings',
            u'Development',
            u'Dictionary',
            u'DiscBurning',
            u'Documentation',
            u'Email',
            u'FileManager',
            u'FileTransfer',
            u'Game',
            u'GNOME',
            u'Graphics',
            u'GTK',
            u'HardwareSettings',
            u'InstantMessaging',
            u'KDE',
            u'LogicGame',
            u'Math',
            u'Monitor',
            u'Network',
            u'OCR',
            u'Office',
            u'P2P',
            u'PackageManager',
            u'Photography',
            u'Player',
            u'Presentation',
            u'Printing',
            u'Qt',
            u'RasterGraphics',
            u'Recorder',
            u'RemoteAccess',
            u'Scanning',
            u'Screensaver',
            u'Security',
            u'Settings',
            u'Spreadsheet',
            u'System',
            u'Telephony',
            u'TerminalEmulator',
            u'TextEditor',
            u'Utility',
            u'VectorGraphics',
            u'Video',
            u'Viewer',
            u'WordProcessor',
            u'Wine',
            u'Wine-Programs-Accessories',
            u'X-GNOME-NetworkSettings',
            u'X-GNOME-PersonalSettings',
            u'X-GNOME-SystemSettings',
            u'X-KDE-More',
            u'X-Red-Hat-Base',
            u'X-SuSE-ControlCenter-System',
        )

        txt_category = wx.StaticText(self,
                                     label=GT(u'Categories'),
                                     name=u'category')

        # This option does not get set by importing a new project
        ti_category = ComboBox(self,
                               inputid.CAT,
                               choices=opts_category,
                               name=txt_category.Name,
                               defaultValue=opts_category[0])

        btn_catadd = CreateButton(self,
                                  btnid.ADD,
                                  GT(u'Add'),
                                  u'add',
                                  name=u'add category')
        btn_catdel = CreateButton(self,
                                  btnid.REMOVE,
                                  GT(u'Remove'),
                                  u'remove',
                                  name=u'rm category')
        btn_catclr = CreateButton(self,
                                  btnid.CLEAR,
                                  GT(u'Clear'),
                                  u'clear',
                                  name=u'clear category')

        # FIXME: Allow using multi-select + remove
        lst_categories = ListCtrl(self, listid.CAT, name=u'Categories')
        # Can't set LC_SINGLE_SEL in constructor for wx 3.0 (ListCtrl bug???)
        lst_categories.SetSingleStyle(wx.LC_SINGLE_SEL)

        self.OnToggle()

        SetPageToolTips(self)

        # *** Event Handling *** #

        btn_open.Bind(wx.EVT_BUTTON, self.OnLoadLauncher)
        btn_save.Bind(wx.EVT_BUTTON, self.OnExportLauncher)
        btn_preview.Bind(wx.EVT_BUTTON, self.OnPreviewLauncher)

        chk_enable.Bind(wx.EVT_CHECKBOX, self.OnToggle)

        chk_filename.Bind(wx.EVT_CHECKBOX, self.OnSetCustomFilename)

        wx.EVT_KEY_DOWN(ti_category, self.SetCategory)
        wx.EVT_KEY_DOWN(lst_categories, self.SetCategory)
        btn_catadd.Bind(wx.EVT_BUTTON, self.SetCategory)
        btn_catdel.Bind(wx.EVT_BUTTON, self.SetCategory)
        btn_catclr.Bind(wx.EVT_BUTTON, self.OnClearCategories)

        # *** Layout *** #

        LEFT_CENTER = wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL
        LEFT_BOTTOM = lyt.ALGN_LB
        RIGHT_BOTTOM = wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM

        lyt_top = BoxSizer(wx.HORIZONTAL)
        lyt_top.Add(chk_enable, 0, LEFT_BOTTOM)
        lyt_top.AddStretchSpacer(1)
        lyt_top.Add(btn_open, 0, wx.ALIGN_TOP)
        lyt_top.Add(btn_save, 0, wx.ALIGN_TOP)
        lyt_top.Add(btn_preview, 0, wx.ALIGN_TOP)

        lyt_opts1 = wx.FlexGridSizer()
        lyt_opts1.SetCols(3)
        lyt_opts1.SetRows(2)

        lyt_opts1.Add(txt_type, 0, LEFT_CENTER)
        lyt_opts1.Add(ti_type, 0, wx.EXPAND | wx.LEFT, 5)
        lyt_opts1.Add(chk_term, 0, LEFT_CENTER | wx.LEFT, 5)
        lyt_opts1.Add(txt_enc, 0, LEFT_CENTER | wx.TOP, 5)
        lyt_opts1.Add(ti_enc, 0, lyt.PAD_LT, 5)
        lyt_opts1.Add(chk_notify, 0, LEFT_CENTER | lyt.PAD_LT, 5)

        lyt_mid = wx.GridBagSizer()
        lyt_mid.SetCols(4)
        lyt_mid.AddGrowableCol(1)
        lyt_mid.AddGrowableCol(3)

        # Row 1
        row = 0
        lyt_mid.Add(txt_filename, (row, 0), flag=LEFT_CENTER)
        lyt_mid.Add(ti_filename, (row, 1), flag=wx.EXPAND | wx.LEFT, border=5)
        lyt_mid.Add(chk_filename, (row, 2),
                    span=(1, 2),
                    flag=LEFT_CENTER | wx.LEFT,
                    border=5)

        # Row 2
        row += 1
        lyt_mid.Add(txt_name, (row, 0), flag=LEFT_CENTER | wx.TOP, border=5)
        lyt_mid.Add(ti_name, (row, 1), flag=wx.EXPAND | lyt.PAD_LT, border=5)
        lyt_mid.Add(txt_exec, (row, 2),
                    flag=LEFT_CENTER | lyt.PAD_LT,
                    border=5)
        lyt_mid.Add(ti_exec, (row, 3), flag=wx.EXPAND | lyt.PAD_LT, border=5)

        # Row 3
        row += 1
        lyt_mid.Add(txt_comm, (row, 0), flag=LEFT_CENTER | wx.TOP, border=5)
        lyt_mid.Add(ti_comm, (row, 1), flag=wx.EXPAND | lyt.PAD_LT, border=5)
        lyt_mid.Add(txt_icon, (row, 2),
                    flag=LEFT_CENTER | lyt.PAD_LT,
                    border=5)
        lyt_mid.Add(ti_icon, (row, 3), flag=wx.EXPAND | lyt.PAD_LT, border=5)

        # Row 4
        row += 1
        lyt_mid.Add(txt_mime, (row, 0), flag=LEFT_CENTER | wx.TOP, border=5)
        lyt_mid.Add(ti_mime, (row, 1), flag=wx.EXPAND | lyt.PAD_LT, border=5)

        lyt_bottom = wx.GridBagSizer()

        row = 0
        lyt_bottom.Add(txt_other, (row, 0), flag=LEFT_BOTTOM)
        lyt_bottom.Add(txt_category, (row, 2),
                       flag=LEFT_BOTTOM | wx.LEFT,
                       border=5)
        lyt_bottom.Add(ti_category, (row, 3),
                       flag=LEFT_BOTTOM | wx.LEFT,
                       border=5)
        lyt_bottom.Add(btn_catadd, (row, 4), flag=RIGHT_BOTTOM)
        lyt_bottom.Add(btn_catdel, (row, 5), flag=RIGHT_BOTTOM)
        lyt_bottom.Add(btn_catclr, (row, 6), flag=RIGHT_BOTTOM)

        row += 1
        lyt_bottom.Add(ti_other, (row, 0), (1, 2), wx.EXPAND)
        lyt_bottom.Add(lst_categories, (row, 2), (1, 5), wx.EXPAND | wx.LEFT,
                       5)

        lyt_bottom.AddGrowableRow(1)
        lyt_bottom.AddGrowableCol(1)
        lyt_bottom.AddGrowableCol(4)

        # --- Page 5 Sizer --- #
        lyt_main = BoxSizer(wx.VERTICAL)
        lyt_main.AddSpacer(5)
        lyt_main.Add(lyt_top, 0, wx.EXPAND | lyt.PAD_LR, 5)
        lyt_main.Add(lyt_opts1, 0, wx.EXPAND | lyt.PAD_LRT, 5)
        lyt_main.Add(lyt_mid, 0, wx.EXPAND | lyt.PAD_LRT, 5)
        lyt_main.Add(lyt_bottom, 1, wx.EXPAND | wx.ALL, 5)

        self.SetAutoLayout(True)
        self.SetSizer(lyt_main)
        self.Layout()