#
# package_gui.py: package group and individual package selection screens
#
# Copyright 2001 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#

import rpm
import gui
import string
import sys
import checklist
import gdkpixbuf
from gtk import *
from gnome.ui import *
from iw_gui import *
from string import *
from thread import *
from examine_gui import *
from translate import _, N_


def queryUpgradeContinue(intf):
    rc = intf.messageWindow(_("Proceed with upgrade?"),
                       _("The filesystems of the Linux installation "
                         "you have chosen to upgrade have already been "
                         "mounted. You cannot go back past this point. "
                         "\n\n") + 
                     _( "Would you like to continue with the upgrade?"),
                                      type = "yesno")
    return rc

class IndividualPackageSelectionWindow (InstallWindow):

    windowTitle = N_("Individual Package Selection")
    htmlTag = "sel-indiv"

    def __init__ (self, ics):
	InstallWindow.__init__ (self, ics)
        self.ics = ics
        self.ics.setHelpEnabled (FALSE)
        self.DIR = 0
        self.DIR_UP = 1
        self.RPM = 2
        self.rownum = 0
        self.maxrows = 0
        self.updatingIcons = FALSE

    def getPrev (self):
        self.ics.setHelpEnabled (TRUE)
        return None
    
    def build_tree (self, x):
        if (x == ()): return ()
        if (len (x) == 1): return (x[0],)
        else: return (x[0], self.build_tree (x[1:]))

    def merge (self, a, b):
        if a == (): return self.build_tree (b)
        if b == (): return a
        if b[0] == a[0]:
            if len (a) > 1 and isinstance (a[1], type (())):
                return (a[0],) + (self.merge (a[1], b[1:]),) + a[2:]
            elif b[1:] == (): return a
            else: return (a[0],) + (self.build_tree (b[1:]),) + a[1:]
        else:
            return (a[0],) + self.merge (a[1:], b)

    def build_ctree (self, list, cur_parent = None, prev_node = None, path = ""):
        if (list == ()): return
        
        if (len (list) > 1 and isinstance (list[1], type (()))): leaf = FALSE
        else: leaf = TRUE
    
        if isinstance (list[0], type (())):
            self.build_ctree (list[0], prev_node, None, self.ctree.node_get_row_data (prev_node))
            self.build_ctree (list[1:], cur_parent, None, path)
        else:
            node = self.ctree.insert_node (cur_parent, None, (list[0],), 2,
                                           self.closed_p, self.closed_b, self.open_p, self.open_b, leaf)
            cur_path = path + "/" + list[0]
            self.ctree.node_set_row_data (node, cur_path)
            self.build_ctree (list[1:], cur_parent, node, path)

    def get_rpm_desc (self, header):
        desc = replace (header[rpm.RPMTAG_DESCRIPTION], "\n\n", "\x00")
        desc = replace (desc, "\n", " ")
        desc = replace (desc, "\x00", "\n\n")
        return desc

    def clear_package_desc (self):
        self.currentPackage = None
        self.packageDesc.freeze ()
        self.packageDesc.delete_text (0, -1)
        self.packageDesc.thaw ()
    
    def sort_list (self, args, col):
        self.packageList.freeze ()
        if col == 2:         #sort by column #2
            self.bubblesort(args, col)
            self.sortType = "Size"
        elif col == 1:       #sort by column #1
            self.packageList.set_sort_column (col)
            self.packageList.sort ()
            self.sortType = "Package"
        elif col == 0:       #sort by column #0
            self.bubblesort(args, col)
            self.sortType = "Selected"            
        self.packageList.thaw () 

    def bubblesort (self, args, col):
        count = 0

        #--For empty groups, don't sort.  Just return.
        if self.rownum == 0:
            return

        for i in range(self.rownum):
            for j in range(self.rownum-i):
                currow = j
                nextrow = j + 1

                #--depending on which column we're sorting by, we extract different data to compare
                if col == 0:                
                    (curr, row_data, header) = self.packageList.get_row_data (currow)
                    (next, row_data, header) = self.packageList.get_row_data (nextrow)

                elif col == 2:
                    curr = self.packageList.get_text(currow, col)
                    curr = string.atoi(curr)
                    next = self.packageList.get_text(nextrow, col)
                    next = string.atoi(next)

                if curr < next:
                    self.packageList.swap_rows(currow, nextrow)
                    count = count + 1
                    self.packageList._update_row(currow)
                    self.packageList._update_row(nextrow)


    def select_all (self, rownum, select_all):
        self.packageDesc.freeze ()
        self.packageDesc.delete_text (0, -1)
        self.packageDesc.thaw ()
        
        for i in range(self.rownum + 1):
             (val, row_data, header) = self.packageList.get_row_data (i)
             if select_all == 1:
                 header.select ()
                 self.packageList.set_row_data (i, (TRUE, row_data, header)) 
             elif select_all == 0:
                 header.unselect()
                 self.packageList.set_row_data (i, (FALSE, row_data, header)) 
             self.packageList._update_row (i)

        self.updateSize()

    def button_press (self, packageList, event):
        try:
            row, col  = self.packageList.get_selection_info (event.x, event.y)
            if row != None:
                if col == 0:   #--If click on checkbox, then toggle
                    self.toggle_row (row)
                elif col == 1 or col == 2:  #--If click pkg name, show description

                    (val, row_data, header) = self.packageList.get_row_data(row)
                    description = header[rpm.RPMTAG_DESCRIPTION]
                
                    self.packageDesc.freeze ()
                    self.packageDesc.delete_text (0, -1)

                    #-- Remove various end of line characters
                    description = string.replace (description, "\n\n", "\x00")
                    description = string.replace (description, "\n", " ")
                    description = string.replace (description, "\x00", "\n\n")
                    
                    self.packageDesc.insert_defaults (description)
                    self.packageDesc.thaw ()
        except:
            pass

    def toggle_row (self, row):
        (val, row_data, header) = self.packageList.get_row_data(row)

        val = not val
        self.packageList.set_row_data(row, (val, row_data, header))
        self.packageList._update_row (row)

        description = header[rpm.RPMTAG_DESCRIPTION]

        self.packageDesc.freeze ()
        self.packageDesc.delete_text (0, -1)

        #-- Remove various end of line characters
        description = string.replace (description, "\n\n", "\x00")
        description = string.replace (description, "\n", " ")
        description = string.replace (description, "\x00", "\n\n")

        self.packageDesc.insert_defaults (description)
        self.packageDesc.thaw ()

        if val == 0:
            header.unselect()
        else:
            header.select()
        
        if self.packageList.toggled_func != None:
            self.packageList.toggled_func(val, row_data)

        self.updateSize()

    def key_press_cb (self, clist, event):
        if event.keyval == ord(" ") and self.packageList.focus_row != -1:
            self.toggle_row (self.packageList.focus_row)

    def select (self, ctree, node, *args):
        self.pkgTreeNode = node
        self.clear_package_desc ()
        self.packageList.freeze ()
        self.packageList.clear ()

        self.maxrows = 0
        self.rownum = 0

        for x in node.children:
            dirName = ctree.get_node_info (x)[0]
            self.packageList.column_titles_passive ()
                
        try:
            # drop the leading slash off the package namespace
            for header in self.flat_groups[ctree.node_get_row_data (node)[1:]]:
                dirName = header[rpm.RPMTAG_NAME] 
                dirSize = header[rpm.RPMTAG_SIZE]
                dirDesc = header[rpm.RPMTAG_DESCRIPTION]

                dirSize = dirSize/1000000
                if dirSize > 1:
                    self.rownum = self.packageList.append_row((dirName, "%s" % dirSize), TRUE, dirDesc)
                else:
                    row = [ "", dirName, "1"]
                    self.rownum = self.packageList.append_row((dirName, "1"), TRUE, dirDesc)

                if header.isSelected():
                    self.packageList.set_row_data(self.rownum, (1, dirDesc, header))
                    self.maxrows = self.maxrows + 1
                else:
                    self.packageList._toggle_row(self.rownum)
                    self.packageList.set_row_data(self.rownum, (0, dirDesc, header))
                    self.maxrows = self.maxrows + 1

            if self.sortType == "Package":
                pass
            elif self.sortType == "Size":
                self.sort_list (args, 2)
            elif self.sortType == "Selected":
                self.sort_list (args, 0)

            self.packageList.column_titles_active ()
            self.selectAllButton.set_sensitive (TRUE)
            self.unselectAllButton.set_sensitive (TRUE)
            
        except:
            self.selectAllButton.set_sensitive (FALSE)
            self.unselectAllButton.set_sensitive (FALSE)
            pass

        self.packageList.thaw ()
        self.packageList.show_all ()

    def updateSize(self):
        self.totalSizeLabel.set_text(_("Total install size: ")+ str(self.comps.sizeStr()))

    def changePkgView(self, widget):
        if self.treeRadio.get_active():
            self.packageList.clear()
            self.packageList.column_title_active (0)
            self.packageList.column_title_active (1)
            self.packageList.column_title_active (2)
            list = self.sw.children()
            if list != []:
                self.sw.remove(self.ctreeAllPkgs)
                self.sw.add(self.ctree)
                try:   #If there was already a selected node in the self.ctree, we want to select it again
                    self.ctree.select(self.pkgTreeNode)
                except:  #If the self.ctree has no selected nodes, do nothing
                    pass
                
        elif self.flatRadio.get_active():
            list = self.sw.children()
            self.packageList.column_titles_passive ()
            
            if list != []:
                self.sw.remove(self.ctree)
                self.sw.add(self.ctreeAllPkgs)
                self.ctreeAllPkgs.show()

                self.packageList.clear()
                self.packageList.freeze()
                pkgList = self.pkgs.packages.keys()
                pkgList.sort()

                for key in pkgList:
                    header = self.pkgs.packages[key]
                    name = header[rpm.RPMTAG_NAME]
                    size = header[rpm.RPMTAG_SIZE]
                    size = size/1000000
                    if size < 1:   #We don't want packages with > 1MB to appear as 0 MB in the list
                        size = 1

                    desc = header[rpm.RPMTAG_DESCRIPTION]
                    
                    if header.isSelected():
                        self.rownum = self.packageList.append_row((name, "%s" %size), TRUE, desc)
                        self.packageList.set_row_data(self.rownum, (1, desc, header))
                    else:
                        self.rownum = self.packageList.append_row((name, "%s" %size), FALSE, desc)
                        self.packageList.set_row_data(self.rownum, (0, desc, header))
            self.packageList.thaw()
            
    # IndividualPackageSelectionWindow tag="sel-indiv"
    def getScreen (self, comps, hdList):
	self.comps = comps

        self.pkgs = hdList
        
        self.path_mapping = {}
        self.ctree = GtkCTree()
        self.ctree.set_selection_mode (SELECTION_BROWSE)
        self.ctree.set_expander_style(CTREE_EXPANDER_TRIANGLE)
        self.ctree.set_line_style(CTREE_LINES_NONE)

        self.ctreeAllPkgs = GtkCTree()
        self.ctreeAllPkgs.set_selection_mode (SELECTION_BROWSE)
        self.ctreeAllPkgs.set_expander_style(CTREE_EXPANDER_TRIANGLE)
        self.ctreeAllPkgs.set_line_style(CTREE_LINES_NONE)

        # Kludge to get around CTree s extremely broken focus behavior
        # self.ctree.unset_flags (CAN_FOCUS)     

        if (not self.__dict__.has_key ("open_p")):
            fn = self.ics.findPixmap("directory-open.png")
            p = gdkpixbuf.new_from_file (fn)
            if p:
                self.open_p, self.open_b = p.render_pixmap_and_mask()
            fn = self.ics.findPixmap("directory-closed.png")
            p = gdkpixbuf.new_from_file (fn)
            if p:
                self.closed_p, self.closed_b = p.render_pixmap_and_mask()
            
        groups = {}

        # go through all the headers and grok out the group names, placing
        # packages in lists in the groups dictionary.        
        for key in hdList.packages.keys():
            header = hdList.packages[key]
            if not groups.has_key (header[rpm.RPMTAG_GROUP]):
                groups[header[rpm.RPMTAG_GROUP]] = []
            # don't display package if it is in the Base group
            if not comps["Base"].includesPackage (header):
                groups[header[rpm.RPMTAG_GROUP]].append (header)

        keys = groups.keys ()
        keys.sort ()
        self.flat_groups = groups

        # now insert the groups into the list, then each group's packages
        # after sorting the list
        def cmpHdrName(first, second):
            if first[rpm.RPMTAG_NAME] < second[rpm.RPMTAG_NAME]:
                return -1
            elif first[rpm.RPMTAG_NAME] == second[rpm.RPMTAG_NAME]:
                return 0
            return 1

        groups = ()
        for key in keys:
            self.flat_groups[key].sort (cmpHdrName)
            groups = self.merge (groups, split (key, "/"))
        self.ctree.freeze ()
        self.build_ctree (groups)

        for base_node in self.ctree.base_nodes ():
            self.ctree.expand_recursive (base_node)
        self.ctree.columns_autosize ()
        for base_node in self.ctree.base_nodes ():
            self.ctree.collapse_recursive (base_node)
        self.ctree.thaw ()

        self.ctree.connect ("tree_select_row", self.select)
        self.sw = GtkScrolledWindow ()
        self.sw.set_policy (POLICY_NEVER, POLICY_AUTOMATIC)

        # Set the style for the tree
        self.ctree.set_expander_style(CTREE_EXPANDER_TRIANGLE)
        self.ctree.set_line_style(CTREE_LINES_NONE)

        self.sw.add(self.ctree)
        packageHBox = GtkHBox()

        self.leftVBox = GtkVBox(FALSE)
        optionHBox = GtkHBox()

        self.treeRadio = GtkRadioButton(None, (_("Tree View")))
        self.treeRadio.connect("clicked", self.changePkgView)
        self.flatRadio = GtkRadioButton(self.treeRadio, (_("Flat View")))

        optionHBox.pack_start(self.treeRadio)
        optionHBox.pack_start(self.flatRadio)
        
        self.leftVBox.pack_start(optionHBox, FALSE)
        self.leftVBox.pack_start(self.sw, TRUE)
        packageHBox.pack_start(self.leftVBox, FALSE)

        self.packageList = checklist.CheckList(2)

        self.sortType = "Package"
        self.packageList.set_column_title (1, (_("Package")))
        self.packageList.set_column_auto_resize (1, TRUE)
        self.packageList.set_column_title (2, (_("Size (MB)")))
        self.packageList.set_column_auto_resize (2, TRUE)
        self.packageList.column_titles_show ()

        self.packageList.set_column_min_width(0, 16)
        self.packageList.column_title_active (0)
        self.packageList.column_title_active (1)
        self.packageList.column_title_active (2)
        self.packageList.connect ('click-column', self.sort_list)
        self.packageList.connect ('button_press_event', self.button_press)
        self.packageList.connect ("key_press_event", self.key_press_cb)

        self.packageListSW = GtkScrolledWindow ()
        self.packageListSW.set_border_width (5)
        self.packageListSW.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC)
        self.packageListSW.add(self.packageList)

        self.packageListVAdj = self.packageListSW.get_vadjustment ()
        self.packageListSW.set_vadjustment(self.packageListVAdj)
        self.packageListHAdj = self.packageListSW.get_hadjustment () 
        self.packageListSW.set_hadjustment(self.packageListHAdj)

        packageHBox.pack_start (self.packageListSW)

        descVBox = GtkVBox ()        
        descVBox.pack_start (GtkHSeparator (), FALSE, padding=2)

        hbox = GtkHBox ()
        bb = GtkHButtonBox ()
        bb.set_layout (BUTTONBOX_END)

        self.totalSizeLabel = GtkLabel (_("Total size: "))
        hbox.pack_start (self.totalSizeLabel, FALSE, FALSE, 0)

        self.selectAllButton = GtkButton (_("Select all in group"))
        bb.pack_start (self.selectAllButton, FALSE)
        self.selectAllButton.connect ('clicked', self.select_all, 1)

        self.unselectAllButton = GtkButton(_("Unselect all in group"))
        bb.pack_start(self.unselectAllButton, FALSE)
        self.unselectAllButton.connect ('clicked', self.select_all, 0)
        
        hbox.pack_start (bb)

        self.selectAllButton.set_sensitive (FALSE)
        self.unselectAllButton.set_sensitive (FALSE)

        descVBox.pack_start (hbox, FALSE)

        descSW = GtkScrolledWindow ()
        descSW.set_border_width (5)
        descSW.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC)

        self.packageDesc = GtkText ()
        self.packageDesc.set_word_wrap (TRUE)
        self.packageDesc.set_line_wrap (TRUE)
        self.packageDesc.set_editable (FALSE)
        descSW.add (self.packageDesc)
        descSW.set_usize (-1, 100)

        descVBox.pack_start (descSW)

        vbox = GtkVBox ()
        vbox.pack_start (packageHBox)
        vbox.pack_start (descVBox, FALSE)

	self.updateSize()

        return vbox

class PackageSelectionWindow (InstallWindow):
    windowTitle = N_("Package Group Selection")
    htmlTag = "sel-group"

    def __init__ (self, ics):
	InstallWindow.__init__ (self, ics)
        self.ics = ics
        self.ics.setNextEnabled (1)
        self.files_found = "FALSE"
        
    def getPrev (self):
	self.comps.setSelectionState(self.origSelection)

    def getNext (self):
	if self.individualPackages.get_active():
	    self.dispatch.skipStep("indivpackage", skip = 0)
	else:
	    self.dispatch.skipStep("indivpackage")

        return None

    def setSize(self):
        self.sizelabel.set_text (_("Total install size: %s") % 
					self.comps.sizeStr())

    def componentToggled(self, widget, comp):
        # turn on all the comps we selected
	if widget.get_active ():
	    comp.select ()
	else:
	    comp.unselect ()

	self.setSize()

    def getScreen(self, comps, dispatch):
    # PackageSelectionWindow tag="sel-group"
	self.comps = comps
	self.dispatch = dispatch

	self.origSelection = self.comps.getSelectionState()
            
        sw = GtkScrolledWindow ()
        sw.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC)

        box = GtkVBox (FALSE, 2)

        self.checkButtons = []
        for comp in self.comps:
            if not comp.hidden:
                pixname = string.replace (comp.name, ' ', '-')
                pixname = string.replace (pixname, '/', '-')
                pixname = string.replace (pixname, '.', '-')
                pixname = string.replace (pixname, '(', '-')
                pixname = string.replace (pixname, ')', '-')
                pixname = string.lower (pixname) + ".png"
                checkButton = None
                pix = self.ics.readPixmap (pixname)
                if pix:
                    hbox = GtkHBox (FALSE, 5)
                    hbox.pack_start (pix, FALSE, FALSE, 0)
                    label = GtkLabel (_(comp.name))
                    label.set_alignment (0.0, 0.5)
                    hbox.pack_start (label, TRUE, TRUE, 0)
                    checkButton = GtkCheckButton ()
                    checkButton.add (hbox)
                else:
                    checkButton = GtkCheckButton (comp.name)

                checkButton.set_active (comp.isSelected(justManual = 1))
                checkButton.connect('toggled', self.componentToggled, comp)
                self.checkButtons.append ((checkButton, comp))
                box.pack_start (checkButton)

        wrapper = GtkVBox (FALSE, 0)
        wrapper.pack_start (box, FALSE)
        
        sw.add_with_viewport (wrapper)
        viewport = sw.children()[0]
        viewport.set_shadow_type (SHADOW_ETCHED_IN)
        box.set_focus_hadjustment(sw.get_hadjustment ())
        box.set_focus_vadjustment(sw.get_vadjustment ())

        hbox = GtkHBox (FALSE, 5)

        self.individualPackages = GtkCheckButton (
		_("Select individual packages"))
        self.individualPackages.set_active (
		not dispatch.stepInSkipList("indivpackage"))
        hbox.pack_start (self.individualPackages, FALSE)

        self.sizelabel = GtkLabel ("")
        self.setSize()
        hbox.pack_start (self.sizelabel, TRUE)
        
        vbox = GtkVBox (FALSE, 5)
        vbox.pack_start (sw, TRUE)
        vbox.pack_start (hbox, FALSE)
        vbox.set_border_width (5)

        return vbox

