home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / python2.4 / site-packages / AppInstall / AppInstall.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-08-31  |  38.0 KB  |  1,037 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. import pygtk
  5. pygtk.require('2.0')
  6. import gtk
  7. import gtk.glade as gtk
  8. import gtk.gdk as gtk
  9. import gobject
  10. import gconf
  11. import pango
  12. import gettext
  13. from gettext import gettext as _
  14. app = 'gnome-app-install'
  15. gettext.textdomain(app)
  16. gettext.bindtextdomain(app)
  17. gtk.glade.textdomain(app)
  18. gtk.glade.bindtextdomain(app)
  19. import stat
  20. import glob
  21. import re
  22. import subprocess
  23. import tempfile
  24. import warnings
  25. import os
  26. import sys
  27. from datetime import datetime
  28. import dbus
  29. import dbus.service as dbus
  30. import dbus.glib as dbus
  31. from warnings import warn
  32. warnings.filterwarnings('ignore', 'ICON:.*', UserWarning)
  33. warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)
  34. import apt
  35. import apt_pkg
  36. from UpdateManager.Common.aptsources import SourcesList, is_mirror
  37. from DialogNewlyInstalled import DialogNewlyInstalled
  38. from DialogPendingChanges import DialogPendingChanges
  39. from DialogMultipleApps import DialogMultipleApps
  40. from DialogUnavailable import DialogUnavailable
  41. from DialogProprietary import DialogProprietary
  42. from PackageWorker import PackageWorker
  43. from Menu import ApplicationMenu
  44. from ReleaseNotesViewer import ReleaseNotesViewer
  45. from SimpleGladeApp import SimpleGladeApp
  46. from Progress import GtkOpProgressWindow
  47. from Util import *
  48. import common
  49. desktop_environment_mapping = {
  50.     ('kdelibs4c2a', 'python-kde3'): (_('%s integrates well into the Kubuntu desktop'), 'application-kubuntu'),
  51.     ('libgnome2-0', 'python-gnome2'): (_('%s integrates well into the Ubuntu desktop'), 'application-ubuntu'),
  52.     'libgnustep-base1.11': (_('%s integrates well into the Gnustep desktop'), None),
  53.     ('libxfce4util4',): (_('%s integrates well into the Xubuntu desktop'), None) }
  54. from Menu import SHOW_ALL, SHOW_ALL_SUPPORTED, SHOW_ALL_FREE, SHOW_ONLY_MAIN, SHOW_ONLY_PROPRIETARY, SHOW_ONLY_THIRD_PARTY
  55.  
  56. class AppInstallDbusControler(dbus.service.Object):
  57.     ''' this is a helper to provide the AppInstallIFace '''
  58.     
  59.     def __init__(self, parent, bus_name, object_path = '/org/freedesktop/AppInstallObject'):
  60.         dbus.service.Object.__init__(self, bus_name, object_path)
  61.         self.parent = parent
  62.  
  63.     
  64.     def bringToFront(self):
  65.         self.parent.window_main.present()
  66.         return True
  67.  
  68.     bringToFront = dbus.service.method('org.freedesktop.AppInstallIFace')(bringToFront)
  69.  
  70.  
  71. class AppInstall(SimpleGladeApp):
  72.     
  73.     def __init__(self, datadir, desktopdir, arguments = None, mime_search = None):
  74.         self.setupDbus()
  75.         self.search_timeout_id = 0
  76.         self.icons = common.ToughIconTheme()
  77.         gtk.window_set_default_icon(self.icons.load_icon('gnome-app-install', 32, 0))
  78.         SimpleGladeApp.__init__(self, domain = 'gnome-app-install', path = datadir + '/gnome-app-install.glade')
  79.         self.channelsdir = desktopdir + '/channels'
  80.         self.datadir = datadir
  81.         self.desktopdir = desktopdir
  82.         self.button_apply.set_sensitive(False)
  83.         self.setupTreeview()
  84.         self.sort_by_ranking = False
  85.         self.config = gconf.client_get_default()
  86.         self.config.add_dir('/apps/gnome-app-install', gconf.CLIENT_PRELOAD_NONE)
  87.         self.tooltips = gtk.Tooltips()
  88.         self.tipmap = { }
  89.         filter_to_restore = self.config.get_int('/apps/gnome-app-install/filter_applications')
  90.         if filter_to_restore not in range(5):
  91.             filter_to_restore = 0
  92.         
  93.         list_filters = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_INT)
  94.         self.combobox_filter.set_model(list_filters)
  95.         filter_renderer = gtk.CellRendererText()
  96.         self.combobox_filter.pack_start(filter_renderer)
  97.         for desc, sep, tooltip, filter in [
  98.             (_('All Open Source applications'), False, _('Show only applications which can be freely used, modified and distributed. This includes the main as well as the community maintained applications'), SHOW_ALL_FREE),
  99.             (_('All supported applications'), False, _('Show all applications (including commercial) that are supported by Canonical Ltd or third party vendors. '), SHOW_ALL_SUPPORTED),
  100.             (_('All available applications'), False, _('Show all available applications including unsupported, restricted and third party applications'), SHOW_ALL),
  101.             ('separator', True, 'separator', -1),
  102.             (_('Only main applications'), False, _('Show only the main applications which are officially supported by Canonical Ltd.'), SHOW_ONLY_MAIN),
  103.             (_('Only restricted applications'), False, _('Show only applications which are restricted in use or distribution by copyright or by legal issues in some countries'), SHOW_ONLY_PROPRIETARY),
  104.             (_('Only third party applications'), False, _('Show only applications which are provided and supported by third party vendors'), SHOW_ONLY_THIRD_PARTY)]:
  105.             list_filters.append((desc, sep, tooltip, filter))
  106.             self.combobox_filter.set_row_separator_func(self.separator_filter)
  107.             self.combobox_filter.set_cell_data_func(filter_renderer, self.tooltip_on_filter)
  108.             if filter == filter_to_restore:
  109.                 self.combobox_filter.set_active(len(list_filters) - 1)
  110.                 self.tooltips.set_tip(self.eventbox_filter, tooltip)
  111.                 continue
  112.         
  113.         self.combobox_filter.connect('changed', self.on_combobox_filter_changed)
  114.         self.textview_description = ReleaseNotesViewer()
  115.         self.textview_description.set_wrap_mode(gtk.WRAP_WORD)
  116.         self.textview_description.set_pixels_below_lines(3)
  117.         self.textview_description.set_right_margin(6)
  118.         self.textview_description.set_left_margin(6)
  119.         self.scrolled_description.add(self.textview_description)
  120.         self.scrolled_description.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
  121.         atk_desc = self.textview_description.get_accessible()
  122.         atk_desc.set_name(_('Description'))
  123.         if mime_search:
  124.             self.label_progress.set_markup('<big><b>%s</b></big>\n\n%s' % (_('Searching for appropriate applications'), _('Please wait. This might take a minute or two.')))
  125.         else:
  126.             self.window_main.show()
  127.         self.updateCache(filter_to_restore)
  128.         self.search_entry.grab_focus()
  129.         self.show_intro()
  130.         self.textview_description.show()
  131.         self.multiple_pkgs_seen = set()
  132.         if mime_search:
  133.             self.window_main.show()
  134.             self.menu.mimeSearch = mime_search
  135.             self.scrolledwindow_left.hide()
  136.             self.search_entry.set_text(mime_search.string)
  137.             self.on_search_timeout()
  138.         
  139.         self.packageWorker = PackageWorker()
  140.         self.last_toggle = None
  141.         time_cache = 0
  142.         for f in glob.glob('/var/lib/apt/lists/*Packages'):
  143.             mt = os.stat(f)[stat.ST_MTIME]
  144.             ct = os.stat(f)[stat.ST_CTIME]
  145.             if mt > time_cache:
  146.                 time_cache = mt
  147.             
  148.             if ct > time_cache:
  149.                 time_cache = ct
  150.                 continue
  151.         
  152.         time_source = os.stat('/etc/apt/sources.list')[stat.ST_MTIME]
  153.         for f in glob.glob('/etc/apt/sources.list.d/*.list'):
  154.             mt = os.stat(f)[stat.ST_MTIME]
  155.             ct = os.stat(f)[stat.ST_CTIME]
  156.             if mt > time_source:
  157.                 time_source = mt
  158.             
  159.             if ct > time_source:
  160.                 time_source = ct
  161.                 continue
  162.         
  163.         if time_cache < time_source:
  164.             self.dialog_cache_outdated.set_transient_for(self.window_main)
  165.             self.dialog_cache_outdated.realize()
  166.             self.dialog_cache_outdated.window.set_functions(gtk.gdk.FUNC_MOVE)
  167.             res = self.dialog_cache_outdated.run()
  168.             self.dialog_cache_outdated.hide()
  169.             if res == gtk.RESPONSE_YES:
  170.                 self.reloadSources()
  171.             
  172.         
  173.  
  174.     
  175.     def separator_filter(self, model, iter, user_data = None):
  176.         '''Used to draw a spearator in the combobox for the filters'''
  177.         return model.get_value(iter, 1)
  178.  
  179.     
  180.     def setupDbus(self):
  181.         ''' this sets up a dbus listener if none is installed alread '''
  182.         
  183.         try:
  184.             bus = dbus.SessionBus()
  185.         except:
  186.             print 'warning: could not initiate dbus'
  187.             return None
  188.  
  189.         proxy_obj = bus.get_object('org.freedesktop.AppInstall', '/org/freedesktop/AppInstallObject')
  190.         iface = dbus.Interface(proxy_obj, 'org.freedesktop.AppInstallIFace')
  191.         
  192.         try:
  193.             iface.bringToFront()
  194.             sys.exit(0)
  195.         except dbus.DBusException:
  196.             e = None
  197.             print 'no listening object (%s) ' % e
  198.             bus_name = dbus.service.BusName('org.freedesktop.AppInstall', bus)
  199.             self.dbusControler = AppInstallDbusControler(self, bus_name)
  200.  
  201.  
  202.     
  203.     def setBusy(self, flag):
  204.         ''' Show a watch cursor if the app is busy for more than 0.3 sec.
  205.             Furthermore provide a loop to handle user interface events '''
  206.         if self.window_main.window is None:
  207.             return None
  208.         
  209.         if flag == True:
  210.             self.window_main.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
  211.         else:
  212.             self.window_main.window.set_cursor(None)
  213.         while gtk.events_pending():
  214.             gtk.main_iteration()
  215.  
  216.     
  217.     def on_combobox_filter_changed(self, combobox):
  218.         '''The filter for the application list was changed'''
  219.         self.setBusy(True)
  220.         active = combobox.get_active()
  221.         model = combobox.get_model()
  222.         iter = model.get_iter(active)
  223.         filter = model.get_value(iter, 3)
  224.         if filter in range(6):
  225.             self.config.set_int('/apps/gnome-app-install/filter_applications', filter)
  226.             self.menu.filter = filter
  227.             self.menu._refilter()
  228.         
  229.         if len(self.menu.treeview_packages.get_model()) == 0:
  230.             self.show_no_results_msg()
  231.         else:
  232.             self.menu.treeview_packages.set_cursor(0)
  233.         tooltip = model.get_value(iter, 2)
  234.         self.tooltips.set_tip(self.eventbox_filter, tooltip)
  235.         self.setBusy(False)
  236.  
  237.     
  238.     def on_window_main_key_press_event(self, widget, event):
  239.         GDK_q = 113
  240.         if event.state & gtk.gdk.CONTROL_MASK and event.keyval == GDK_q:
  241.             self.on_window_main_delete_event(self.window_main, None)
  242.         
  243.  
  244.     
  245.     def error_not_available(self, item):
  246.         '''Show an error message that the application cannot be installed'''
  247.         header = _('%s cannot be installed on your computer type (%s)') % (item.name, self.cache.getArch())
  248.         msg = _('Either the application requires special hardware features or the vendor decided to not support your computer type.')
  249.         d = gtk.MessageDialog(parent = self.window_main, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE)
  250.         d.set_title('')
  251.         d.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  252.         d.realize()
  253.         d.window.set_functions(gtk.gdk.FUNC_MOVE)
  254.         d.run()
  255.         d.destroy()
  256.  
  257.     
  258.     def tooltip_on_filter(self, cell_view, cell_renderer, model, iter):
  259.         '''
  260.         Show a disclaimer in the tooltips of the filters
  261.         '''
  262.         id = model.get_path(iter)[0]
  263.         item_text = model.get_value(iter, 0)
  264.         item_disclaimer = model.get_value(iter, 2)
  265.         cell_renderer.set_property('text', item_text)
  266.         cell_parent = cell_view.get_parent()
  267.         if isinstance(cell_parent, gtk.MenuItem):
  268.             if cell_parent not in self.tipmap or self.tipmap[cell_parent] != item_disclaimer:
  269.                 self.tipmap[cell_parent] = item_disclaimer
  270.                 self.tooltips.set_tip(cell_parent, item_disclaimer)
  271.             
  272.  
  273.     
  274.     def on_install_toggle(self, renderer, path):
  275.         model = self.treeview_packages.get_model()
  276.         (name, item, popcon) = model[path]
  277.         pkg = item.pkgname
  278.         if not self.cache.has_key(pkg) and self.cache[pkg].candidateDownloadable:
  279.             for it in self.cache._cache.FileList:
  280.                 if it.Component != '' and it.Component == item.component:
  281.                     self.error_not_available(item)
  282.                     return False
  283.                     continue
  284.             
  285.             self.saveState()
  286.             if self.addChannel(item):
  287.                 self.last_toggle = name
  288.                 self.restoreState()
  289.             
  290.             return None
  291.         
  292.         if self.cache[pkg].isInstalled:
  293.             self.cache[pkg].markDelete(autoFix = False)
  294.             if self.cache._depcache.BrokenCount > 0:
  295.                 d = gtk.MessageDialog(parent = self.window_main, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE)
  296.                 d.set_title('')
  297.                 d.set_markup('<big><b>%s</b></big>\n\n%s' % (_("Cannot remove '%s'") % pkg, _("One or more applications depend on '%s'. To remove '%s' and the dependent applications, please switch to the advanced software manager.") % (pkg, pkg)))
  298.                 d.realize()
  299.                 d.window.set_functions(gtk.gdk.FUNC_MOVE)
  300.                 d.run()
  301.                 d.destroy()
  302.                 self.cache.clean()
  303.                 return None
  304.             
  305.             self.cache[pkg].markKeep()
  306.             if not self.cache._depcache.BrokenCount == 0:
  307.                 raise AssertionError
  308.             if not self.cache._depcache.DelCount == 0:
  309.                 raise AssertionError
  310.         else:
  311.             apt_error = False
  312.             
  313.             try:
  314.                 self.cache[pkg].markInstall(autoFix = True)
  315.             except SystemError:
  316.                 apt_error = True
  317.  
  318.             if self.cache._depcache.BrokenCount > 0 and self.cache._depcache.DelCount > 0 or apt_error:
  319.                 d = gtk.MessageDialog(parent = self.window_main, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE)
  320.                 d.set_title('')
  321.                 d.set_markup('<big><b>%s</b></big>\n\n%s' % (_("Cannot install '%s'") % pkg, _("This application conflicts with other installed software. To install '%s' the conflicting software must be removed before.\n\nSwitch to the advanced mode to resolve this conflict.") % pkg))
  322.                 d.realize()
  323.                 d.window.set_functions(gtk.gdk.FUNC_MOVE)
  324.                 d.run()
  325.                 d.destroy()
  326.                 self.cache.clean()
  327.                 if not self.cache._depcache.BrokenCount == 0:
  328.                     raise AssertionError
  329.                 if not self.cache._depcache.DelCount == 0:
  330.                     raise AssertionError
  331.                 return None
  332.             
  333.         item.toInstall = not (item.toInstall)
  334.         if len(self.menu.pkg_to_app[item.pkgname]) > 1:
  335.             apps = self.menu.pkg_to_app[item.pkgname]
  336.             for app in apps:
  337.                 app.toInstall = item.toInstall
  338.             
  339.             self.treeview_packages.queue_draw()
  340.             if item.pkgname not in self.multiple_pkgs_seen:
  341.                 dia = DialogMultipleApps(self.datadir, self.window_main, apps, item.name)
  342.                 dia.run()
  343.                 dia.hide()
  344.                 self.multiple_pkgs_seen.add(item.pkgname)
  345.             
  346.         
  347.         self.button_apply.set_sensitive(self.menu.isChanged())
  348.  
  349.     
  350.     def addChannel(self, item):
  351.         '''Ask for confirmation to add the missing channel or
  352.            component of the current selected application'''
  353.         if item.thirdparty and item.channel:
  354.             dia = DialogProprietary(self.datadir, self.window_main, item)
  355.         else:
  356.             dia = DialogUnavailable(self.datadir, self.window_main, item)
  357.         res = dia.run()
  358.         dia.hide()
  359.         if res != gtk.RESPONSE_OK:
  360.             return False
  361.         
  362.         if item.component:
  363.             self.enableComponent(item.component)
  364.             if item.component == 'multiverse':
  365.                 for it in self.cache._cache.FileList:
  366.                     if it.Component != '' and it.Component == 'universe':
  367.                         break
  368.                         continue
  369.                 
  370.             
  371.         elif item.channel:
  372.             self.enableChannel(item.channel)
  373.         else:
  374.             print 'ERROR: addChannel() called without channel or component'
  375.             return False
  376.         self.reloadSources()
  377.         return True
  378.  
  379.     
  380.     def setupTreeview(self):
  381.         
  382.         def popcon_view_func(cell_layout, renderer, model, iter, self):
  383.             '''
  384.             Create a pixmap showing a row of stars representing the popularity
  385.             of the corresponding application
  386.             '''
  387.             (name, item, popcon) = model[iter]
  388.             rank = int(5 * item.popcon / self.menu.popcon_max)
  389.             pix_rating = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 96, 16)
  390.             pix_rating.fill(0)
  391.             for i in range(5):
  392.                 if not i > rank:
  393.                     self.pixbuf_star.copy_area(0, 0, 16, 16, pix_rating, 20 * i, 0)
  394.                     continue
  395.             
  396.             renderer.set_property('pixbuf', pix_rating)
  397.  
  398.         
  399.         def package_view_func(cell_layout, renderer, model, iter):
  400.             app = model.get_value(iter, COL_ITEM)
  401.             name = app.name
  402.             desc = app.description
  403.             current = app.isInstalled
  404.             future = app.toInstall
  405.             available = app.available
  406.             if current != future:
  407.                 markup = '<b>%s</b>\n<small><b>%s</b></small>' % (name, desc)
  408.             else:
  409.                 markup = '%s\n<small>%s</small>' % (name, desc)
  410.             renderer.set_property('markup', markup)
  411.  
  412.         
  413.         def toggle_cell_func(column, cell, model, iter):
  414.             menuitem = model.get_value(iter, COL_ITEM)
  415.             cell.set_property('active', menuitem.toInstall)
  416.             cell.set_property('visible', True)
  417.             if menuitem.architectures and self.cache.getArch() not in menuitem.architectures:
  418.                 cell.set_property('activatable', False)
  419.             else:
  420.                 cell.set_property('activatable', True)
  421.  
  422.         
  423.         def icon_cell_func(column, cell, model, iter):
  424.             (menuitem,) = model.get(iter, COL_ITEM)
  425.             if menuitem == None or menuitem.iconname == None:
  426.                 cell.set_property('pixbuf', None)
  427.                 cell.set_property('visible', False)
  428.                 return None
  429.             
  430.             icon = menuitem.icontheme._getIcon(menuitem.iconname, 24)
  431.             cell.set_property('pixbuf', icon)
  432.             cell.set_property('visible', True)
  433.  
  434.         column = gtk.TreeViewColumn(_('Application'))
  435.         column.set_expand(True)
  436.         column.set_sort_column_id(COL_NAME)
  437.         column.set_resizable(True)
  438.         column.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
  439.         check_column = gtk.TreeViewColumn('')
  440.         renderer = gtk.CellRendererPixbuf()
  441.         renderer.set_property('xpad', 4)
  442.         popcon_column = gtk.TreeViewColumn(_('Popularity'), renderer)
  443.         popcon_column.set_sort_column_id(COL_POPCON)
  444.         popcon_column.set_cell_data_func(renderer, popcon_view_func, self)
  445.         self.pixbuf_star = self.icons.load_icon('gnome-app-install-star', 16, 0)
  446.         self.toggle_render = gtk.CellRendererToggle()
  447.         self.store_toggle_id = self.toggle_render.connect('toggled', self.on_install_toggle)
  448.         self.toggle_render.set_property('xalign', 0.29999999999999999)
  449.         check_column.pack_start(self.toggle_render, False)
  450.         check_column.set_cell_data_func(self.toggle_render, toggle_cell_func)
  451.         render = gtk.CellRendererPixbuf()
  452.         column.pack_start(render, False)
  453.         column.set_cell_data_func(render, icon_cell_func)
  454.         render = gtk.CellRendererText()
  455.         render.set_property('ellipsize', pango.ELLIPSIZE_END)
  456.         column.pack_start(render, True)
  457.         column.add_attribute(render, 'markup', COL_NAME)
  458.         column.set_cell_data_func(render, package_view_func)
  459.         self.treeview_packages.append_column(check_column)
  460.         self.treeview_packages.append_column(column)
  461.         self.treeview_packages.append_column(popcon_column)
  462.         column = gtk.TreeViewColumn('')
  463.         render = gtk.CellRendererPixbuf()
  464.         column.pack_start(render, False)
  465.         column.set_cell_data_func(render, icon_cell_func)
  466.         self.treeview_categories.set_search_column(COL_NAME)
  467.         render = gtk.CellRendererText()
  468.         render.set_property('scale', 1.0)
  469.         column.pack_start(render, True)
  470.         column.add_attribute(render, 'markup', COL_NAME)
  471.         self.treeview_categories.append_column(column)
  472.  
  473.     
  474.     def saveState(self):
  475.         ''' save the current state of the app '''
  476.         (self.to_add, self.to_rm) = self.menu.getChanges()
  477.         (self.cursor_categories_path, x) = self.treeview_categories.get_cursor()
  478.         model = self.treeview_packages.get_model()
  479.         (packages_path, x) = self.treeview_packages.get_cursor()
  480.         if packages_path:
  481.             it = model.get_iter(packages_path)
  482.             self.cursor_pkgname = model.get_value(it, COL_NAME)
  483.         else:
  484.             self.cursor_pkgname = None
  485.  
  486.     
  487.     def restoreState(self):
  488.         ''' restore the current state of the app '''
  489.         self.treeview_categories.set_cursor(self.cursor_categories_path)
  490.         model = self.treeview_packages.get_model()
  491.         query = self.search_entry.get_text()
  492.         if query:
  493.             self.on_search_timeout()
  494.         
  495.         for item in self.to_add:
  496.             if self.cache.has_key(item.pkgname):
  497.                 
  498.                 try:
  499.                     self.cache[item.pkgname].markInstall(autoFix = True)
  500.                 except SystemError:
  501.                     continue
  502.  
  503.                 apps = self.menu.pkg_to_app[item.pkgname]
  504.                 for app in apps:
  505.                     app.toInstall = item.toInstall
  506.                 
  507.         
  508.         for item in self.to_rm:
  509.             if self.cache.has_key(item.pkgname):
  510.                 
  511.                 try:
  512.                     self.cache[item.pkgname].markDelete(autoFix = True)
  513.                 except SystemError:
  514.                     continue
  515.  
  516.                 apps = self.menu.pkg_to_app[item.pkgname]
  517.                 for app in apps:
  518.                     app.toInstall = item.toInstall
  519.                 
  520.         
  521.         self.treeview_packages.queue_draw()
  522.         for it in iterate_list_store(model, model.get_iter_first()):
  523.             name = model.get_value(it, COL_NAME)
  524.             if name == self.last_toggle:
  525.                 self.last_toggle = None
  526.                 path = model.get_path(it)
  527.                 self.treeview_packages.set_cursor(path)
  528.                 self.on_install_toggle(None, path)
  529.                 break
  530.             
  531.             if name == self.cursor_pkgname and self.last_toggle == None:
  532.                 path = model.get_path(it)
  533.                 self.treeview_packages.set_cursor(path)
  534.                 break
  535.                 continue
  536.         
  537.  
  538.     
  539.     def updateCache(self, filter = SHOW_ONLY_MAIN):
  540.         self.window_main.set_sensitive(False)
  541.         self.setBusy(True)
  542.         progress = GtkOpProgressWindow(self.glade, self.window_main)
  543.         
  544.         try:
  545.             self.cache = MyCache(progress)
  546.         except Exception:
  547.             e = None
  548.             header = _('Failed to check for installed and available applications')
  549.             msg = _("This is a major failure of your software management system. Check the file permissions and correctness of the file '/etc/apt/sources.list' and reload the software information: 'sudo apt-get update'.")
  550.             print e
  551.             d = gtk.MessageDialog(parent = self.window_main, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE)
  552.             d.set_title('')
  553.             d.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  554.             d.realize()
  555.             d.window.set_functions(gtk.gdk.FUNC_MOVE)
  556.             d.run()
  557.             d.destroy()
  558.             sys.exit(1)
  559.  
  560.         self.menu = ApplicationMenu(self.desktopdir, self.datadir, self.cache, self.treeview_categories, self.treeview_packages, progress, filter)
  561.         self.treeview_categories.set_cursor((0,))
  562.         adj = self.scrolled_window.get_vadjustment()
  563.         adj.set_value(0)
  564.         self.setBusy(False)
  565.         self.window_main.set_sensitive(True)
  566.  
  567.     
  568.     def ignoreChanges(self):
  569.         '''
  570.         If any changes have been made, ask the user to apply them and return
  571.         a value based on the status.
  572.         Returns True if the changes should be thrown away and False otherwise
  573.         '''
  574.         if not self.menu.isChanged():
  575.             return True
  576.         
  577.         (to_add, to_rm) = self.menu.getChanges()
  578.         dia = DialogPendingChanges(self.datadir, self.window_main, to_add, to_rm)
  579.         header = _('Apply changes to installed applications before closing?')
  580.         msg = _('If you do not apply your changes they will be lost permanently.')
  581.         dia.label_pending.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  582.         dia.button_ignore_changes.set_label(_('_Close Without Applying'))
  583.         dia.button_ignore_changes.show()
  584.         dia.dialog_pending_changes.realize()
  585.         dia.dialog_pending_changes.window.set_functions(gtk.gdk.FUNC_MOVE)
  586.         res = dia.run()
  587.         dia.hide()
  588.         return res
  589.  
  590.     
  591.     def on_button_help_clicked(self, widget):
  592.         subprocess.Popen([
  593.             '/usr/bin/yelp',
  594.             'ghelp:gnome-app-install'])
  595.  
  596.     
  597.     def applyChanges(self, final = False):
  598.         (to_add, to_rm) = self.menu.getChanges()
  599.         self.setBusy(True)
  600.         ret = self.packageWorker.perform_action(self.window_main, to_add, to_rm)
  601.         if ret != 0:
  602.             self.setBusy(False)
  603.             return False
  604.         
  605.         if final != True:
  606.             self.updateCache(filter = self.menu.filter)
  607.         
  608.         self.button_apply.set_sensitive(self.menu.isChanged())
  609.         if len(to_add) > 0:
  610.             dia = DialogNewlyInstalled(self.datadir, self.window_main, to_add, self.cache)
  611.             dia.run()
  612.             dia.hide()
  613.         
  614.         self.setBusy(False)
  615.         return True
  616.  
  617.     
  618.     def on_button_ok_clicked(self, button):
  619.         if not self.menu.isChanged():
  620.             self.quit()
  621.         
  622.         if self.confirmChanges():
  623.             if self.applyChanges(final = True):
  624.                 if self.menu.mimeSearch:
  625.                     self.menu.mimeSearch.retry_open()
  626.                 
  627.                 self.quit()
  628.             
  629.         
  630.  
  631.     
  632.     def confirmChanges(self):
  633.         (to_add, to_rm) = self.menu.getChanges()
  634.         dia = DialogPendingChanges(self.datadir, self.window_main, to_add, to_rm)
  635.         header = _('Apply the following changes?')
  636.         msg = _('Please take a final look through the list of applications that will be installed or removed.')
  637.         dia.label_pending.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  638.         res = dia.run()
  639.         dia.hide()
  640.         if res != gtk.RESPONSE_APPLY:
  641.             return False
  642.         else:
  643.             return True
  644.  
  645.     
  646.     def on_button_apply_clicked(self, button):
  647.         ret = self.confirmChanges()
  648.         if ret == True:
  649.             self.applyChanges()
  650.         
  651.  
  652.     
  653.     def on_search_timeout(self):
  654.         query = self.search_entry.get_text()
  655.         store = self.menu.treeview_packages.get_model()
  656.         if query.lstrip() != '':
  657.             self.menu.searchTerms = query.lower().split(' ')
  658.             self.sort_by_ranking = True
  659.         else:
  660.             self.menu.searchTerms = []
  661.             self.sort_by_ranking = False
  662.         self.on_treeview_categories_cursor_changed(self.treeview_categories)
  663.         if len(store) == 0:
  664.             self.show_no_results_msg()
  665.         else:
  666.             self.menu.treeview_packages.set_cursor(0)
  667.  
  668.     
  669.     def on_search_entry_changed(self, widget):
  670.         if self.search_timeout_id > 0:
  671.             gobject.source_remove(self.search_timeout_id)
  672.         
  673.         self.search_timeout_id = gobject.timeout_add(500, self.on_search_timeout)
  674.  
  675.     
  676.     def on_button_clear_clicked(self, button):
  677.         self.search_entry.set_text('')
  678.         self.menu.search(None)
  679.         self.button_clear.set_sensitive(False)
  680.  
  681.     
  682.     def on_item_about_activate(self, button):
  683.         VERSION = VERSION
  684.         import Version
  685.         self.dialog_about.set_version(VERSION)
  686.         self.dialog_about.run()
  687.         self.dialog_about.hide()
  688.  
  689.     
  690.     def on_reload_activate(self, item):
  691.         self.reloadSources()
  692.  
  693.     
  694.     def on_button_cancel_clicked(self, item):
  695.         self.quit()
  696.  
  697.     
  698.     def reloadSources(self):
  699.         self.window_main.set_sensitive(False)
  700.         ret = self.packageWorker.perform_action(self.window_main, action = PackageWorker.UPDATE)
  701.         self.updateCache(filter = self.menu.filter)
  702.         self.window_main.set_sensitive(True)
  703.         return ret
  704.  
  705.     
  706.     def enableChannel(self, channel):
  707.         ''' enables a channel with 3rd party software '''
  708.         channelpath = '%s/%s.list' % (self.channelsdir, channel)
  709.         channelkey = '%s/%s.key' % (self.channelsdir, channel)
  710.         if not os.path.exists(channelpath):
  711.             print "WARNING: channel '%s' not found" % channelpath
  712.             return None
  713.         
  714.         cmd = [
  715.             'gksu',
  716.             '--desktop',
  717.             '/usr/share/applications/gnome-app-install.desktop',
  718.             '--',
  719.             'cp',
  720.             channelpath,
  721.             apt_pkg.Config.FindDir('Dir::Etc::sourceparts')]
  722.         subprocess.call(cmd)
  723.         if os.path.exists(channelkey):
  724.             cmd = [
  725.                 'gksu',
  726.                 '--desktop',
  727.                 '/usr/share/applications/gnome-app-install.desktop',
  728.                 '--',
  729.                 'apt-key',
  730.                 'add',
  731.                 channelkey]
  732.             subprocess.call(cmd)
  733.         
  734.  
  735.     
  736.     def enableComponent(self, component):
  737.         ''' Enables a component of the current distribution
  738.             (in a seperate file in /etc/apt/sources.list.d/$dist-$comp)
  739.         '''
  740.         pipe = os.popen('lsb_release -c -s')
  741.         distro = pipe.read().strip()
  742.         del pipe
  743.         if component == '':
  744.             print 'no repo found in enableRepository'
  745.             return None
  746.         
  747.         mirror = 'http://archive.ubuntu.com/ubuntu'
  748.         sources = SourcesList()
  749.         newentry_sec = ''
  750.         newentry_updates = ''
  751.         for source in sources:
  752.             if source.invalid or source.disabled:
  753.                 continue
  754.             
  755.             if source.dist == '%s-updates' % distro:
  756.                 if component in source.comps:
  757.                     newentry_updates = ''
  758.                 else:
  759.                     newentry_updates = 'deb %s %s-updates %s\n' % (source.uri, distro, component)
  760.             
  761.             if source.dist == '%s-security' % distro:
  762.                 if component in source.comps:
  763.                     newentry_sec = ''
  764.                 else:
  765.                     newentry_sec = 'deb http://security.ubuntu.com/ubuntu %s-security %s\n' % (distro, component)
  766.             
  767.             if source.uri != mirror and is_mirror(mirror, source.uri):
  768.                 mirror = source.uri
  769.                 continue
  770.         
  771.         newentry = '# automatically added by gnome-app-install on %s\n' % datetime.today()
  772.         newentry += 'deb %s %s %s\n' % (mirror, distro, component)
  773.         if newentry_sec != '':
  774.             newentry += newentry_sec
  775.         
  776.         if newentry_updates != '':
  777.             newentry += newentry_updates
  778.         
  779.         channel_dir = apt_pkg.Config.FindDir('Dir::Etc::sourceparts')
  780.         channel_file = '%s-%s.list' % (distro, component)
  781.         channel = tempfile.NamedTemporaryFile()
  782.         channel.write(newentry)
  783.         channel.flush()
  784.         cmd = [
  785.             'gksu',
  786.             '--desktop',
  787.             '/usr/share/applications/gnome-app-install.desktop',
  788.             '--',
  789.             'install',
  790.             '-m',
  791.             '644',
  792.             '-o',
  793.             '0',
  794.             channel.name,
  795.             channel_dir + channel_file]
  796.         subprocess.call(cmd)
  797.  
  798.     
  799.     def on_window_main_delete_event(self, window, event):
  800.         if window.get_property('sensitive') == False:
  801.             return True
  802.         
  803.         if self.menu.isChanged():
  804.             ret = self.ignoreChanges()
  805.             if ret == gtk.RESPONSE_APPLY:
  806.                 if not self.applyChanges(final = True):
  807.                     return True
  808.                 
  809.             elif ret == gtk.RESPONSE_CANCEL:
  810.                 return True
  811.             elif ret == gtk.RESPONSE_CLOSE:
  812.                 self.quit()
  813.             
  814.         
  815.         self.quit()
  816.  
  817.     
  818.     def on_window_main_destroy_event(self, data = None):
  819.         self.quit()
  820.  
  821.     
  822.     def quit(self):
  823.         gtk.main_quit()
  824.         sys.exit(0)
  825.  
  826.     
  827.     def show_description(self, item):
  828.         '''Collect and show some information about the package that 
  829.            contains the selected application'''
  830.         details = []
  831.         clean_desc = ''
  832.         short_desc = ''
  833.         version = ''
  834.         desktop_environment = ''
  835.         icons = []
  836.         if self.cache.has_key(item.pkgname):
  837.             version = self.cache[item.pkgname].candidateVersion
  838.             for dependencies in desktop_environment_mapping:
  839.                 for dep in dependencies:
  840.                     if self.cache.pkgDependsOn(item.pkgname, dep):
  841.                         details.append(desktop_environment_mapping[dependencies][0] % item.name)
  842.                         if desktop_environment_mapping[dependencies][1] != None:
  843.                             icons.append([
  844.                                 desktop_environment_mapping[dependencies][1],
  845.                                 desktop_environment_mapping[dependencies][0] % item.name])
  846.                         
  847.                         break
  848.                         continue
  849.                 
  850.             
  851.         
  852.         if item.available:
  853.             pkg = self.cache[item.pkgname]
  854.             rough_desc = pkg.description.rstrip(' \n\t')
  855.             first_break = rough_desc.find('\n')
  856.             short_desc = rough_desc[:first_break].rstrip('\n\t ')
  857.             rough_desc = rough_desc[first_break + 1:].lstrip('\n\t ')
  858.             p = re.compile('^(\\s|\\t)*(\\*|0|-)', re.MULTILINE)
  859.             rough_desc = p.sub('\n*', rough_desc)
  860.             p = re.compile('\\n', re.MULTILINE)
  861.             rough_desc = p.sub(' ', rough_desc)
  862.             p = re.compile('\\s\\s+', re.MULTILINE)
  863.             rough_desc = p.sub('\n', rough_desc)
  864.             lines = rough_desc.split('\n')
  865.             for i in range(len(lines)):
  866.                 if lines[i].split() == []:
  867.                     continue
  868.                 
  869.                 first_chunk = lines[i].split()[0]
  870.                 if first_chunk == '*':
  871.                     p = re.compile('\\*\\s*', re.MULTILINE)
  872.                     lines[i] = p.sub('', lines[i])
  873.                     clean_desc += '\xe2\x80\xa2 %s\n' % lines[i]
  874.                     continue
  875.                 clean_desc += '%s\n' % lines[i]
  876.             
  877.         else:
  878.             msg = _('%s cannot be installed' % item.name)
  879.             for it in self.cache._cache.FileList:
  880.                 if (it.Component != '' or it.Component == item.component or item.architectures) and self.cache.getArch() not in item.architectures:
  881.                     details.append(_('%s cannot be installed on your computer type (%s). Either the application requires special hardware features or the vendor decided to not support your computer type.') % (item.name, self.cache.getArch()))
  882.                     break
  883.                     continue
  884.             
  885.         if item.component == 'universe':
  886.             care_about_freedom = _('This application is brought to you by the Ubuntu community.')
  887.             icons.append([
  888.                 'application-community',
  889.                 care_about_freedom])
  890.         elif item.component == 'multiverse' or item.thirdparty:
  891.             care_about_freedom = _('The use, modification and distribution of %s is restricted by copyright or by legal terms in some countries.') % item.name
  892.             icons.append([
  893.                 'application-proprietary',
  894.                 care_about_freedom])
  895.         elif item.thirdparty or item.channel:
  896.             care_about_freedom = '%s is provided by a third party vendor and is therefore not an official part of Ubuntu. The third party vendor is responsible for support and security updates.' % item.name
  897.             icons.append([
  898.                 'application-proprietary',
  899.                 care_about_freedom])
  900.         elif item.component == 'main' or item.supported:
  901.             icons.append([
  902.                 'application-supported',
  903.                 _('Canonical Ltd. supports %s with security updates') % item.name])
  904.             care_about_freedom = ''
  905.         else:
  906.             care_about_freedom = ''
  907.         s = ''
  908.         buffer = self.textview_description.get_buffer()
  909.         buffer.set_text('')
  910.         tag_table = buffer.get_tag_table()
  911.         tag_table.foreach((lambda tag, table: table.remove(tag)), tag_table)
  912.         iter = buffer.get_start_iter()
  913.         pango_context = self.textview_description.get_pango_context()
  914.         font_desc = pango_context.get_font_description()
  915.         font_size = font_desc.get_size() / pango.SCALE
  916.         buffer.insert_with_tags_by_name(iter, ' %s' % item.name, 'app-name')
  917.         if short_desc != '':
  918.             tag_name = buffer.create_tag('short-desc', weight = pango.WEIGHT_BOLD)
  919.             buffer.insert_with_tags_by_name(iter, '\n%s' % short_desc, 'short-desc')
  920.             for emblem in icons:
  921.                 image_emblem = gtk.Image()
  922.                 image_emblem.set_from_icon_name(emblem[0], gtk.ICON_SIZE_MENU)
  923.                 image_emblem.set_pixel_size(16)
  924.                 event = gtk.EventBox()
  925.                 style = self.textview_description.get_style()
  926.                 event.modify_bg(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
  927.                 event.add(image_emblem)
  928.                 self.tooltips.set_tip(event, emblem[1])
  929.                 buffer.insert(iter, ' ')
  930.                 anchor = buffer.create_child_anchor(iter)
  931.                 self.textview_description.add_child_at_anchor(event, anchor)
  932.                 event.show()
  933.                 image_emblem.show()
  934.             
  935.         elif care_about_freedom != '':
  936.             buffer.insert(iter, '\n%s' % care_about_freedom)
  937.         
  938.         if clean_desc != '':
  939.             buffer.insert(iter, '\n%s' % clean_desc)
  940.         
  941.         if version != '':
  942.             buffer.insert(iter, _('Version: %s (%s)') % (version, item.pkgname))
  943.         
  944.         if len(details) > 0:
  945.             for x in details:
  946.                 buffer.insert(iter, '\n%s' % x)
  947.             
  948.         
  949.  
  950.     
  951.     def clear_description(self):
  952.         buffer = self.textview_description.get_buffer()
  953.         buffer.set_text('')
  954.  
  955.     
  956.     def on_treeview_packages_row_activated(self, treeview, path, view_column):
  957.         iter = treeview.get_model().get_iter(path)
  958.         item = treeview.get_model().get_value(iter, COL_ITEM)
  959.         if item.architectures and self.cache.getArch() not in item.architectures:
  960.             return False
  961.         
  962.         self.on_install_toggle(None, path)
  963.  
  964.     
  965.     def on_treeview_categories_cursor_changed(self, treeview):
  966.         self.setBusy(True)
  967.         path = treeview.get_cursor()[0]
  968.         iter = treeview.get_model().get_iter(path)
  969.         (name, item) = treeview.get_model()[iter]
  970.         self.treeview_packages.set_model(item.applications)
  971.         self.menu._refilter()
  972.         if self.sort_by_ranking:
  973.             if not item.applications.has_default_sort_func():
  974.                 item.applications.set_default_sort_func(None)
  975.                 item.applications.set_default_sort_func(self.menu._ranking_sort_func)
  976.                 item.applications.set_sort_column_id(-1, gtk.SORT_ASCENDING)
  977.             else:
  978.                 item.applications.set_default_sort_func(None)
  979.                 item.applications.set_default_sort_func(self.menu._ranking_sort_func)
  980.                 item.applications.set_sort_column_id(-1, gtk.SORT_ASCENDING)
  981.         elif item.applications.has_default_sort_func():
  982.             item.applications.set_sort_column_id(COL_NAME, gtk.SORT_ASCENDING)
  983.         
  984.         item.applications.set_default_sort_func(None)
  985.         if len(self.menu.treeview_packages.get_model()) == 0:
  986.             self.show_no_results_msg()
  987.         else:
  988.             self.menu.treeview_packages.set_cursor(0)
  989.         self.setBusy(False)
  990.  
  991.     
  992.     def on_treeview_packages_cursor_changed(self, treeview):
  993.         path = treeview.get_cursor()[0]
  994.         iter = treeview.get_model().get_iter(path)
  995.         (name, item, popcon) = treeview.get_model()[iter]
  996.         self.show_description(item)
  997.  
  998.     
  999.     def show_no_results_msg(self):
  1000.         ''' Give the user some hints if the search returned 
  1001.             no results'''
  1002.         buffer = self.textview_description.get_buffer()
  1003.         buffer.set_text('')
  1004.         tag_table = buffer.get_tag_table()
  1005.         tag_table.foreach((lambda tag, table: table.remove(tag)), tag_table)
  1006.         tag_header = buffer.create_tag('first-line', weight = pango.WEIGHT_BOLD, pixels_above_lines = 6)
  1007.         msg = _('There is no matching application available.')
  1008.         iter = buffer.get_start_iter()
  1009.         buffer.insert_with_tags_by_name(iter, msg, 'first-line')
  1010.         if self.menu.filter != SHOW_ALL_FREE and self.menu.filter != SHOW_ALL:
  1011.             msg = '\n%s' % _("To broaden your search, choose 'Show all Open Source applications' or 'Show all available' applications.")
  1012.             buffer.insert_with_tags(iter, msg)
  1013.         
  1014.         if self.treeview_categories.get_cursor()[0] != (0,):
  1015.             msg = '\n%s' % _("To broaden your search, choose 'All' categories.")
  1016.             buffer.insert_with_tags(iter, msg)
  1017.         
  1018.  
  1019.     
  1020.     def show_intro(self):
  1021.         ''' Show a quick introduction to gnome-app-install 
  1022.             in the description view'''
  1023.         buffer = self.textview_description.get_buffer()
  1024.         buffer.set_text('')
  1025.         iter = buffer.get_start_iter()
  1026.         tag_header = buffer.create_tag('header', scale = pango.SCALE_LARGE, weight = pango.WEIGHT_BOLD, pixels_above_lines = 6)
  1027.         msg = _('To install an application check the box next to the application. Uncheck the box to remove the application.') + '\n'
  1028.         msg += _('To perform advanced tasks use the Synaptic package manager.')
  1029.         buffer.insert_with_tags(iter, '%s\n' % _('Quick Introduction'), tag_header)
  1030.         buffer.insert(iter, msg)
  1031.  
  1032.  
  1033. if __name__ == '__main__':
  1034.     app = AppInstall(os.path.abspath('menu-data'), os.path.abspath('data'), sys.argv)
  1035.     gtk.main()
  1036.  
  1037.