Source

vpackager / src / vpackager / guitools.py

Full commit
#!/usr/bin/env python

#    This file is part of vpackager.
#
#    vpackager is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License v2 as published by
#    the Free Software Foundation.
#
#    vpackager is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with vpackager.  If not, see <http://www.gnu.org/licenses/>.

import gtk
import os
import buildutils
import message

__author__ = 'M0E-lnx'
__author_email__ = 'moc.liamg@xnl.E0M'[::-1]
__version__ = '0.1'

class About(gtk.AboutDialog):
    """ About dialog for vpackager"""
    def __init__(self):
        gtk.AboutDialog.__init__(self)
        self.set_name('vpackager')
        self.set_version('2.0')
        datadir = os.path.dirname(os.path.abspath(__file__))
        datadir = os.path.join(datadir, 'data')
        self.set_authors(['Moises Henriquez (M0E-Lnx)',
            'Rodrigo Bistolfi (rbistolfi)'])
        f = open(os.path.join(datadir, 'LICENSE'), 'r')
        self.set_license(f.read())
        f.close()
        self.set_website('https://bitbucket.org/m0e_lnx/vpackager')
        self.set_copyright('(c) 2008-2011 Moises Henriquez')
        logo = gtk.gdk.pixbuf_new_from_file(
                os.path.join(datadir, 'vpackager_logo.png'))
        self.set_logo(logo)
        self.set_comments("Compile software from source code.")

class vButton(gtk.Button):
    '''Button class that can use a stock image with a custon label'''
    def __init__(self, stock=None, label=''):
        gtk.Button.__init__(self)
        if stock is not None:
            img = gtk.Image()
            img.set_from_stock(stock, gtk.ICON_SIZE_BUTTON)
            self.set_image(img)

        self.set_label(label)

class AddPatchBox(gtk.Dialog):
    ''' Dialog used to add a patch to a build job'''
    def __init__(self, title='Add Patch', parent=None):
        gtk.Dialog.__init__(self,parent=parent)
        self.set_title('Add patch')
        self.URI = None
        body = self.get_content_area()
        self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
        self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        hb = gtk.HBox()
        lb = gtk.Label('\
                Enter patch URL or browse for local files')
        body.pack_start(lb, False, False, 2)
        body.pack_start(hb, False, True, 2)
        uriEntry = gtk.Entry()
        self.uriEntry = uriEntry
        uriEntry.connect('changed', self._uriEntry_changed)
        btBrowse = vButton(stock=gtk.STOCK_OPEN,
                label='Browse')
        btBrowse.connect('clicked', self._btBrowse_clicked)
        hb.pack_start(uriEntry, True, True, 2)
        hb.pack_start(btBrowse, False, False, 2)
        self.set_size_request(600,100)

        self.show_all()

    def _uriEntry_changed(self, widget=None):
        ''' Set the dialog's URI property'''
        self.URI = widget.get_text()
        return

    def _btBrowse_clicked(self, widget=None):
        ''' Launch a filechooser dialog to select a patch file '''
        dia = FilePicker(title='Select patch file')
        res = dia.run()
        dia.hide()
        if res == gtk.RESPONSE_OK:
            self.uriEntry.set_text(dia.get_filename())
            self.URI=self.uriEntry.get_text()
        return

class FilePicker(gtk.FileChooserDialog):
    ''' File chooser used in vpackager. Based on the gtk.FileChooserDialog but
    has some extra convinience methods.'''
    def __init__(self, title=None, forsrc=False, plaintxt=False):
        _buttons = (gtk.STOCK_OK, gtk.RESPONSE_OK,
                gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        _filter_src = gtk.FileFilter()
        _filter_src.add_pattern('*.[bgx]z*')
        _filter_src.set_name('Compressed Files')
        _filter_txt = gtk.FileFilter()
        _filter_txt.add_mime_type('text/plain')
        _filter_txt.set_name('Text files')
        _filter_all = gtk.FileFilter()
        _filter_all.set_name('All Files')
        _filter_all.add_pattern('*.*')

        gtk.FileChooserDialog.__init__(self,
                buttons=_buttons)
        self.set_title(title)
        if forsrc:
            self.add_filter(_filter_src)
        elif plaintxt:
            self.add_filter(_filter_txt)
        else:
            self.add_filter(_filter_all)

        self.set_current_folder('/home')

class JobAddDialog(gtk.Dialog):
    '''Dialog window used to add a job to the queue'''
    def __init__(self, title='Add Job', parent=None):
        gtk.Dialog.__init__(self)
        self.SrcURI = None
        self.DescURI = None
        self.app = None
        self.ver = None
        self.patches = []
        self.release = 1
        self.install = False
        self.patchesbox = self._patchesbox()
        self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
        self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        self.set_has_separator(True)
        self.set_default_response(gtk.RESPONSE_OK)
        body = self.get_content_area()
        self.set_title('Add New Job')
        self.set_size_request(500,320)
        frmSrc = gtk.Frame('Source Archive')
        body.pack_start(frmSrc, False, True, 8)
        hsrc = gtk.HBox()
        frmSrc.add(hsrc)
        self.srcentry = gtk.Entry()
        self.srcentry.set_tooltip_text('Enter path or URL')
        self.srcentry.connect('changed', self._src_entry_event)
        hsrc.pack_start(self.srcentry, True, True, 2)
        self.btBrowse = vButton(stock=gtk.STOCK_OPEN,
                label='Browse')
        self.btBrowse.connect('clicked', self._browse)
        hsrc.pack_start(self.btBrowse, False, False, 2)
        self.okButton = self.get_action_area().get_children()[1]
        self.okButton.set_property('sensitive', False)

        frmDesc = gtk.Frame('Package Description')
        body.pack_start(frmDesc, False, True, 4)
        hbdesc = gtk.HBox()
        frmDesc.add(hbdesc)
        self.descEntry = gtk.Entry()
        self.descEntry.set_tooltip_text('Enter path or URL')
        self.descEntry.connect('changed', self._descURI_change)
        btDescBrowse = vButton(stock=gtk.STOCK_OPEN, label='Browse')
        btDescBrowse.connect('clicked', self._desc_browse)
        hbdesc.pack_start(self.descEntry, True, True, 2)
        hbdesc.pack_start(btDescBrowse, False, True, 2)

        lb = gtk.Label('Application')
        self.appEntry = gtk.Entry()
        bapp = gtk.HBox()
        body.pack_start(bapp, False, True, 4)
        bapp.pack_start(lb, False, False, 2)
        bapp.pack_start(self.appEntry,True, True, 2)

        lbv = gtk.Label('Version')
        self.verEntry = gtk.Entry()
        bapp.pack_start(lbv, False, False, 2)
        bapp.pack_start(self.verEntry, True, True, 2)

        self.appEntry.connect('changed', self._appname_changed)
        self.verEntry.connect('changed', self._appver_changed)

        bbox = gtk.HBox()
        lbuild = gtk.Label('Release')
        rels = gtk.ListStore(int)
        for x in xrange(1, 5):
            rels.append([x])
        cbRel = gtk.ComboBox(rels)
        cr = gtk.CellRendererText()
        cbRel.pack_start(cr)
        cbRel.add_attribute(cr, 'text', 0)
        cbRel.set_active(0)

        lSrcType = gtk.Label('Source Type')
        types = gtk.ListStore(str)
        for _type in ('Auto','Autotools','Python','CMake','Perl','Waf'):
            types.append([_type])
        cbsrcsel = gtk.ComboBox(types)
        cbsrcsel.connect('changed', self._srctype_changed)
        crt = gtk.CellRendererText()
        cbsrcsel.pack_start(crt)
        cbsrcsel.add_attribute(crt, 'text', 0)
        cbsrcsel.set_active(0)

        bbox.pack_start(lbuild, False, False, 2)
        bbox.pack_start(cbRel, False, False, 2)
        bbox.pack_start(lSrcType, False, False, 2)
        bbox.pack_start(cbsrcsel, False, False, 2)
        body.pack_start(bbox, False, True, 2)
        # Patches box
        #exPatches = gtk.expander_new_with_mnemonic('Patches')
        exPatches = gtk.Expander('Patches')
        exPatches.add(self.patchesbox)
        exPatches.connect('notify::expanded', self._expander_event)
        body.pack_start(exPatches, True, True, 4)

        # Option to install package after building
        cbInstallPkg = gtk.CheckButton(
                label='Install this package after it is compiled')
        cbInstallPkg.connect('toggled', self._cbInstallpkg_changed)
        body.pack_start(cbInstallPkg, False, True, 2)


        cbRel.connect('changed', self._release_changed)

        self.body = body
        body.show_all()

    def _del_patch_event(self, widget=None):
        sel = self.patchestree.get_selection().get_selected()[0][0][0]
        if sel:
            self.patches.remove(sel)
        # Refresh the GUI that shows the patches
        self.__refresh_patches_tree()

    def _add_patch_event(self, widget=None):
        dia = AddPatchBox(parent=self)
        res = dia.run()
        if res == gtk.RESPONSE_OK:
            self.patches.append(dia.URI)
        dia.destroy()
        # Refresh the GUI list that shows the patches
        self.__refresh_patches_tree()

    def __refresh_patches_tree(self, widget=None):
        model = self.patchestree.get_model()
        model.clear()
        for patch in self.patches:
            model.append([patch])
        return

    def _patchesbox(self):
        box = gtk.HBox()
        rbox = gtk.VBox()
        lst = gtk.ListStore(str)
        cr = gtk.CellRendererText()
        tree = gtk.TreeView(lst)
        col = gtk.TreeViewColumn()
        col.pack_start(cr)
        col.add_attribute(cr, 'text', 0)
        tree.append_column(col)
        self.patchestree = tree
        tree.set_headers_visible(False)
        scrollw = gtk.ScrolledWindow()
        scrollw.set_policy(gtk.POLICY_AUTOMATIC,
                gtk.POLICY_AUTOMATIC)
        scrollw.add(tree)
        box.pack_start(scrollw, True, True, 2)
        box.pack_start(rbox, False, False, 2)
        btAddPatch = vButton(stock=gtk.STOCK_ADD,
                label='Add')
        btAddPatch.connect('clicked', self._add_patch_event)
        btDelPatch = vButton(stock=gtk.STOCK_REMOVE,
                label='Remove')
        btDelPatch.connect('clicked', self._del_patch_event)
        rbox.pack_start(btAddPatch, False ,False, 2)
        rbox.pack_start(btDelPatch, False ,False, 2)
        return box

    def _expander_event(self, widget=None, user_data=None):
        ''' Resize the dialog to display the newly added widgets'''
        ht = self.patchesbox.get_allocation()[-2]
        ht = int(max(ht, 495)*0.25)
        wwid,whei =  self.get_size()
        if widget.get_expanded():
            self.patchesbox.show()
            self.resize(wwid, whei + ht)
        else:
            self.patchesbox.hide()
            self.resize(wwid, whei - ht)
        return

    def _cbInstallpkg_changed(self, widget=None):
        self.install = widget.get_active()

    def _srctype_changed(self, widget=None):
        selected = widget.get_model()[widget.get_active()][0].lower()
        if selected in ('auto', 'autotools'):
            self.srctype = 'default'
        else:
            self.srctype = selected

    def _release_changed(self, widget=None):
        self.release = widget.get_model()[widget.get_active()][0]

    def _appname_changed(self, widget=None):
        self.app = widget.get_text()
        self.okButton.set_property('sensitive',
                self.verEntry.get_text() != '')
        return

    def _appver_changed(self, widget=None):
        self.ver = widget.get_text()
        self.okButton.set_property('sensitive',
                self.appEntry.get_text() != '')
        return

    def _descURI_change(self, widget=None):
        self.DescURI = widget.get_text()

    def _desc_browse(self, widget=None):
        dia = FilePicker(
                title='Select description file',
                plaintxt = True)
        res = dia.run()
        dia.hide()
        if res == gtk.RESPONSE_OK:
            self.descEntry.set_text(dia.get_filename())
        dia.destroy()


    def _browse(self, widget=None):
        """ Browse for source archive """
        dia = FilePicker(title='Choose source archive',
                forsrc = True)
        res = dia.run()
        dia.hide()
        if res == gtk.RESPONSE_OK:
            self.SrcURI = dia.get_filename()
            self.srcentry.set_text(self.SrcURI)
            try:
                s = buildutils.Source(self.SrcURI)
                self.appEntry.set_text(s.app)
                self.verEntry.set_text(s.ver)
                del s
            except:
                dia = message.Error(parent = self,
                        text = 'Error creating source object')
                if dia.run():
                    dia.destroy()

        return


    def _src_entry_event(self, widget=None):
        starts = ('http:', 'https:','ftp:')
        ends = ('.tar.gz', '.xz', '.tar.bz2', '.tgz')
        uri = widget.get_text()
        enable = False
        for i in starts:
            if uri.startswith(i):
                #enable = True
                #self.okButton.set_property('sensitive', enable)
                self.SrcURI = widget.get_text()
                return
        for x in ends:
            if uri.endswith(x):
                if os.path.exists(uri):
                    enable = True
                    self.okButton.set_property('sensitive', enable)
                    self.SrcURI = widget.get_text()
                    return