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.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-08-31  |  37.8 KB  |  1,029 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (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.         else:
  307.             apt_error = False
  308.             
  309.             try:
  310.                 self.cache[pkg].markInstall(autoFix = True)
  311.             except SystemError:
  312.                 apt_error = True
  313.  
  314.             if self.cache._depcache.BrokenCount > 0 and self.cache._depcache.DelCount > 0 or apt_error:
  315.                 d = gtk.MessageDialog(parent = self.window_main, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE)
  316.                 d.set_title('')
  317.                 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))
  318.                 d.realize()
  319.                 d.window.set_functions(gtk.gdk.FUNC_MOVE)
  320.                 d.run()
  321.                 d.destroy()
  322.                 self.cache.clean()
  323.                 return None
  324.             
  325.         item.toInstall = not (item.toInstall)
  326.         if len(self.menu.pkg_to_app[item.pkgname]) > 1:
  327.             apps = self.menu.pkg_to_app[item.pkgname]
  328.             for app in apps:
  329.                 app.toInstall = item.toInstall
  330.             
  331.             self.treeview_packages.queue_draw()
  332.             if item.pkgname not in self.multiple_pkgs_seen:
  333.                 dia = DialogMultipleApps(self.datadir, self.window_main, apps, item.name)
  334.                 dia.run()
  335.                 dia.hide()
  336.                 self.multiple_pkgs_seen.add(item.pkgname)
  337.             
  338.         
  339.         self.button_apply.set_sensitive(self.menu.isChanged())
  340.  
  341.     
  342.     def addChannel(self, item):
  343.         '''Ask for confirmation to add the missing channel or
  344.            component of the current selected application'''
  345.         if item.thirdparty and item.channel:
  346.             dia = DialogProprietary(self.datadir, self.window_main, item)
  347.         else:
  348.             dia = DialogUnavailable(self.datadir, self.window_main, item)
  349.         res = dia.run()
  350.         dia.hide()
  351.         if res != gtk.RESPONSE_OK:
  352.             return False
  353.         
  354.         if item.component:
  355.             self.enableComponent(item.component)
  356.             if item.component == 'multiverse':
  357.                 for it in self.cache._cache.FileList:
  358.                     if it.Component != '' and it.Component == 'universe':
  359.                         break
  360.                         continue
  361.                 
  362.             
  363.         elif item.channel:
  364.             self.enableChannel(item.channel)
  365.         else:
  366.             print 'ERROR: addChannel() called without channel or component'
  367.             return False
  368.         self.reloadSources()
  369.         return True
  370.  
  371.     
  372.     def setupTreeview(self):
  373.         
  374.         def popcon_view_func(cell_layout, renderer, model, iter, self):
  375.             '''
  376.             Create a pixmap showing a row of stars representing the popularity
  377.             of the corresponding application
  378.             '''
  379.             (name, item, popcon) = model[iter]
  380.             rank = int(5 * item.popcon / self.menu.popcon_max)
  381.             pix_rating = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, 96, 16)
  382.             pix_rating.fill(0)
  383.             for i in range(5):
  384.                 if not i > rank:
  385.                     self.pixbuf_star.copy_area(0, 0, 16, 16, pix_rating, 20 * i, 0)
  386.                     continue
  387.             
  388.             renderer.set_property('pixbuf', pix_rating)
  389.  
  390.         
  391.         def package_view_func(cell_layout, renderer, model, iter):
  392.             app = model.get_value(iter, COL_ITEM)
  393.             name = app.name
  394.             desc = app.description
  395.             current = app.isInstalled
  396.             future = app.toInstall
  397.             available = app.available
  398.             if current != future:
  399.                 markup = '<b>%s</b>\n<small><b>%s</b></small>' % (name, desc)
  400.             else:
  401.                 markup = '%s\n<small>%s</small>' % (name, desc)
  402.             renderer.set_property('markup', markup)
  403.  
  404.         
  405.         def toggle_cell_func(column, cell, model, iter):
  406.             menuitem = model.get_value(iter, COL_ITEM)
  407.             cell.set_property('active', menuitem.toInstall)
  408.             cell.set_property('visible', True)
  409.             if menuitem.architectures and self.cache.getArch() not in menuitem.architectures:
  410.                 cell.set_property('activatable', False)
  411.             else:
  412.                 cell.set_property('activatable', True)
  413.  
  414.         
  415.         def icon_cell_func(column, cell, model, iter):
  416.             (menuitem,) = model.get(iter, COL_ITEM)
  417.             if menuitem == None or menuitem.iconname == None:
  418.                 cell.set_property('pixbuf', None)
  419.                 cell.set_property('visible', False)
  420.                 return None
  421.             
  422.             icon = menuitem.icontheme._getIcon(menuitem.iconname, 24)
  423.             cell.set_property('pixbuf', icon)
  424.             cell.set_property('visible', True)
  425.  
  426.         column = gtk.TreeViewColumn(_('Application'))
  427.         column.set_expand(True)
  428.         column.set_sort_column_id(COL_NAME)
  429.         column.set_resizable(True)
  430.         column.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY)
  431.         check_column = gtk.TreeViewColumn('')
  432.         renderer = gtk.CellRendererPixbuf()
  433.         renderer.set_property('xpad', 4)
  434.         popcon_column = gtk.TreeViewColumn(_('Popularity'), renderer)
  435.         popcon_column.set_sort_column_id(COL_POPCON)
  436.         popcon_column.set_cell_data_func(renderer, popcon_view_func, self)
  437.         self.pixbuf_star = self.icons.load_icon('gnome-app-install-star', 16, 0)
  438.         self.toggle_render = gtk.CellRendererToggle()
  439.         self.store_toggle_id = self.toggle_render.connect('toggled', self.on_install_toggle)
  440.         self.toggle_render.set_property('xalign', 0.29999999999999999)
  441.         check_column.pack_start(self.toggle_render, False)
  442.         check_column.set_cell_data_func(self.toggle_render, toggle_cell_func)
  443.         render = gtk.CellRendererPixbuf()
  444.         column.pack_start(render, False)
  445.         column.set_cell_data_func(render, icon_cell_func)
  446.         render = gtk.CellRendererText()
  447.         render.set_property('ellipsize', pango.ELLIPSIZE_END)
  448.         column.pack_start(render, True)
  449.         column.add_attribute(render, 'markup', COL_NAME)
  450.         column.set_cell_data_func(render, package_view_func)
  451.         self.treeview_packages.append_column(check_column)
  452.         self.treeview_packages.append_column(column)
  453.         self.treeview_packages.append_column(popcon_column)
  454.         column = gtk.TreeViewColumn('')
  455.         render = gtk.CellRendererPixbuf()
  456.         column.pack_start(render, False)
  457.         column.set_cell_data_func(render, icon_cell_func)
  458.         self.treeview_categories.set_search_column(COL_NAME)
  459.         render = gtk.CellRendererText()
  460.         render.set_property('scale', 1.0)
  461.         column.pack_start(render, True)
  462.         column.add_attribute(render, 'markup', COL_NAME)
  463.         self.treeview_categories.append_column(column)
  464.  
  465.     
  466.     def saveState(self):
  467.         ''' save the current state of the app '''
  468.         (self.to_add, self.to_rm) = self.menu.getChanges()
  469.         (self.cursor_categories_path, x) = self.treeview_categories.get_cursor()
  470.         model = self.treeview_packages.get_model()
  471.         (packages_path, x) = self.treeview_packages.get_cursor()
  472.         if packages_path:
  473.             it = model.get_iter(packages_path)
  474.             self.cursor_pkgname = model.get_value(it, COL_NAME)
  475.         else:
  476.             self.cursor_pkgname = None
  477.  
  478.     
  479.     def restoreState(self):
  480.         ''' restore the current state of the app '''
  481.         self.treeview_categories.set_cursor(self.cursor_categories_path)
  482.         model = self.treeview_packages.get_model()
  483.         query = self.search_entry.get_text()
  484.         if query:
  485.             self.on_search_timeout()
  486.         
  487.         for item in self.to_add:
  488.             if self.cache.has_key(item.pkgname):
  489.                 
  490.                 try:
  491.                     self.cache[item.pkgname].markInstall(autoFix = True)
  492.                 except SystemError:
  493.                     continue
  494.  
  495.                 apps = self.menu.pkg_to_app[item.pkgname]
  496.                 for app in apps:
  497.                     app.toInstall = item.toInstall
  498.                 
  499.         
  500.         for item in self.to_rm:
  501.             if self.cache.has_key(item.pkgname):
  502.                 
  503.                 try:
  504.                     self.cache[item.pkgname].markDelete(autoFix = True)
  505.                 except SystemError:
  506.                     continue
  507.  
  508.                 apps = self.menu.pkg_to_app[item.pkgname]
  509.                 for app in apps:
  510.                     app.toInstall = item.toInstall
  511.                 
  512.         
  513.         self.treeview_packages.queue_draw()
  514.         for it in iterate_list_store(model, model.get_iter_first()):
  515.             name = model.get_value(it, COL_NAME)
  516.             if name == self.last_toggle:
  517.                 self.last_toggle = None
  518.                 path = model.get_path(it)
  519.                 self.treeview_packages.set_cursor(path)
  520.                 self.on_install_toggle(None, path)
  521.                 break
  522.             
  523.             if name == self.cursor_pkgname and self.last_toggle == None:
  524.                 path = model.get_path(it)
  525.                 self.treeview_packages.set_cursor(path)
  526.                 break
  527.                 continue
  528.         
  529.  
  530.     
  531.     def updateCache(self, filter = SHOW_ONLY_MAIN):
  532.         self.window_main.set_sensitive(False)
  533.         self.setBusy(True)
  534.         progress = GtkOpProgressWindow(self.glade, self.window_main)
  535.         
  536.         try:
  537.             self.cache = MyCache(progress)
  538.         except Exception:
  539.             e = None
  540.             header = _('Failed to check for installed and available applications')
  541.             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'.")
  542.             print e
  543.             d = gtk.MessageDialog(parent = self.window_main, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE)
  544.             d.set_title('')
  545.             d.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  546.             d.realize()
  547.             d.window.set_functions(gtk.gdk.FUNC_MOVE)
  548.             d.run()
  549.             d.destroy()
  550.             sys.exit(1)
  551.  
  552.         self.menu = ApplicationMenu(self.desktopdir, self.datadir, self.cache, self.treeview_categories, self.treeview_packages, progress, filter)
  553.         self.treeview_categories.set_cursor((0,))
  554.         adj = self.scrolled_window.get_vadjustment()
  555.         adj.set_value(0)
  556.         self.setBusy(False)
  557.         self.window_main.set_sensitive(True)
  558.  
  559.     
  560.     def ignoreChanges(self):
  561.         '''
  562.         If any changes have been made, ask the user to apply them and return
  563.         a value based on the status.
  564.         Returns True if the changes should be thrown away and False otherwise
  565.         '''
  566.         if not self.menu.isChanged():
  567.             return True
  568.         
  569.         (to_add, to_rm) = self.menu.getChanges()
  570.         dia = DialogPendingChanges(self.datadir, self.window_main, to_add, to_rm)
  571.         header = _('Apply changes to installed applications before closing?')
  572.         msg = _('If you do not apply your changes they will be lost permanently.')
  573.         dia.label_pending.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  574.         dia.button_ignore_changes.set_label(_('_Close Without Applying'))
  575.         dia.button_ignore_changes.show()
  576.         dia.dialog_pending_changes.realize()
  577.         dia.dialog_pending_changes.window.set_functions(gtk.gdk.FUNC_MOVE)
  578.         res = dia.run()
  579.         dia.hide()
  580.         return res
  581.  
  582.     
  583.     def on_button_help_clicked(self, widget):
  584.         subprocess.Popen([
  585.             '/usr/bin/yelp',
  586.             'ghelp:gnome-app-install'])
  587.  
  588.     
  589.     def applyChanges(self, final = False):
  590.         (to_add, to_rm) = self.menu.getChanges()
  591.         self.setBusy(True)
  592.         ret = self.packageWorker.perform_action(self.window_main, to_add, to_rm)
  593.         if ret != 0:
  594.             self.setBusy(False)
  595.             return False
  596.         
  597.         if final != True:
  598.             self.updateCache(filter = self.menu.filter)
  599.         
  600.         self.button_apply.set_sensitive(self.menu.isChanged())
  601.         if len(to_add) > 0:
  602.             dia = DialogNewlyInstalled(self.datadir, self.window_main, to_add, self.cache)
  603.             dia.run()
  604.             dia.hide()
  605.         
  606.         self.setBusy(False)
  607.         return True
  608.  
  609.     
  610.     def on_button_ok_clicked(self, button):
  611.         if not self.menu.isChanged():
  612.             self.quit()
  613.         
  614.         if self.confirmChanges():
  615.             if self.applyChanges(final = True):
  616.                 if self.menu.mimeSearch:
  617.                     self.menu.mimeSearch.retry_open()
  618.                 
  619.                 self.quit()
  620.             
  621.         
  622.  
  623.     
  624.     def confirmChanges(self):
  625.         (to_add, to_rm) = self.menu.getChanges()
  626.         dia = DialogPendingChanges(self.datadir, self.window_main, to_add, to_rm)
  627.         header = _('Apply the following changes?')
  628.         msg = _('Please take a final look through the list of applications that will be installed or removed.')
  629.         dia.label_pending.set_markup('<big><b>%s</b></big>\n\n%s' % (header, msg))
  630.         res = dia.run()
  631.         dia.hide()
  632.         if res != gtk.RESPONSE_APPLY:
  633.             return False
  634.         else:
  635.             return True
  636.  
  637.     
  638.     def on_button_apply_clicked(self, button):
  639.         ret = self.confirmChanges()
  640.         if ret == True:
  641.             self.applyChanges()
  642.         
  643.  
  644.     
  645.     def on_search_timeout(self):
  646.         query = self.search_entry.get_text()
  647.         store = self.menu.treeview_packages.get_model()
  648.         if query.lstrip() != '':
  649.             self.menu.searchTerms = query.lower().split(' ')
  650.             self.sort_by_ranking = True
  651.         else:
  652.             self.menu.searchTerms = []
  653.             self.sort_by_ranking = False
  654.         self.on_treeview_categories_cursor_changed(self.treeview_categories)
  655.         if len(store) == 0:
  656.             self.show_no_results_msg()
  657.         else:
  658.             self.menu.treeview_packages.set_cursor(0)
  659.  
  660.     
  661.     def on_search_entry_changed(self, widget):
  662.         if self.search_timeout_id > 0:
  663.             gobject.source_remove(self.search_timeout_id)
  664.         
  665.         self.search_timeout_id = gobject.timeout_add(500, self.on_search_timeout)
  666.  
  667.     
  668.     def on_button_clear_clicked(self, button):
  669.         self.search_entry.set_text('')
  670.         self.menu.search(None)
  671.         self.button_clear.set_sensitive(False)
  672.  
  673.     
  674.     def on_item_about_activate(self, button):
  675.         VERSION = VERSION
  676.         import Version
  677.         self.dialog_about.set_version(VERSION)
  678.         self.dialog_about.run()
  679.         self.dialog_about.hide()
  680.  
  681.     
  682.     def on_reload_activate(self, item):
  683.         self.reloadSources()
  684.  
  685.     
  686.     def on_button_cancel_clicked(self, item):
  687.         self.quit()
  688.  
  689.     
  690.     def reloadSources(self):
  691.         self.window_main.set_sensitive(False)
  692.         ret = self.packageWorker.perform_action(self.window_main, action = PackageWorker.UPDATE)
  693.         self.updateCache(filter = self.menu.filter)
  694.         self.window_main.set_sensitive(True)
  695.         return ret
  696.  
  697.     
  698.     def enableChannel(self, channel):
  699.         ''' enables a channel with 3rd party software '''
  700.         channelpath = '%s/%s.list' % (self.channelsdir, channel)
  701.         channelkey = '%s/%s.key' % (self.channelsdir, channel)
  702.         if not os.path.exists(channelpath):
  703.             print "WARNING: channel '%s' not found" % channelpath
  704.             return None
  705.         
  706.         cmd = [
  707.             'gksu',
  708.             '--desktop',
  709.             '/usr/share/applications/gnome-app-install.desktop',
  710.             '--',
  711.             'cp',
  712.             channelpath,
  713.             apt_pkg.Config.FindDir('Dir::Etc::sourceparts')]
  714.         subprocess.call(cmd)
  715.         if os.path.exists(channelkey):
  716.             cmd = [
  717.                 'gksu',
  718.                 '--desktop',
  719.                 '/usr/share/applications/gnome-app-install.desktop',
  720.                 '--',
  721.                 'apt-key',
  722.                 'add',
  723.                 channelkey]
  724.             subprocess.call(cmd)
  725.         
  726.  
  727.     
  728.     def enableComponent(self, component):
  729.         ''' Enables a component of the current distribution
  730.             (in a seperate file in /etc/apt/sources.list.d/$dist-$comp)
  731.         '''
  732.         pipe = os.popen('lsb_release -c -s')
  733.         distro = pipe.read().strip()
  734.         del pipe
  735.         if component == '':
  736.             print 'no repo found in enableRepository'
  737.             return None
  738.         
  739.         mirror = 'http://archive.ubuntu.com/ubuntu'
  740.         sources = SourcesList()
  741.         newentry_sec = ''
  742.         newentry_updates = ''
  743.         for source in sources:
  744.             if source.invalid or source.disabled:
  745.                 continue
  746.             
  747.             if source.dist == '%s-updates' % distro:
  748.                 if component in source.comps:
  749.                     newentry_updates = ''
  750.                 else:
  751.                     newentry_updates = 'deb %s %s-updates %s\n' % (source.uri, distro, component)
  752.             
  753.             if source.dist == '%s-security' % distro:
  754.                 if component in source.comps:
  755.                     newentry_sec = ''
  756.                 else:
  757.                     newentry_sec = 'deb http://security.ubuntu.com/ubuntu %s-security %s\n' % (distro, component)
  758.             
  759.             if source.uri != mirror and is_mirror(mirror, source.uri):
  760.                 mirror = source.uri
  761.                 continue
  762.         
  763.         newentry = '# automatically added by gnome-app-install on %s\n' % datetime.today()
  764.         newentry += 'deb %s %s %s\n' % (mirror, distro, component)
  765.         if newentry_sec != '':
  766.             newentry += newentry_sec
  767.         
  768.         if newentry_updates != '':
  769.             newentry += newentry_updates
  770.         
  771.         channel_dir = apt_pkg.Config.FindDir('Dir::Etc::sourceparts')
  772.         channel_file = '%s-%s.list' % (distro, component)
  773.         channel = tempfile.NamedTemporaryFile()
  774.         channel.write(newentry)
  775.         channel.flush()
  776.         cmd = [
  777.             'gksu',
  778.             '--desktop',
  779.             '/usr/share/applications/gnome-app-install.desktop',
  780.             '--',
  781.             'install',
  782.             '-m',
  783.             '644',
  784.             '-o',
  785.             '0',
  786.             channel.name,
  787.             channel_dir + channel_file]
  788.         subprocess.call(cmd)
  789.  
  790.     
  791.     def on_window_main_delete_event(self, window, event):
  792.         if window.get_property('sensitive') == False:
  793.             return True
  794.         
  795.         if self.menu.isChanged():
  796.             ret = self.ignoreChanges()
  797.             if ret == gtk.RESPONSE_APPLY:
  798.                 if not self.applyChanges(final = True):
  799.                     return True
  800.                 
  801.             elif ret == gtk.RESPONSE_CANCEL:
  802.                 return True
  803.             elif ret == gtk.RESPONSE_CLOSE:
  804.                 self.quit()
  805.             
  806.         
  807.         self.quit()
  808.  
  809.     
  810.     def on_window_main_destroy_event(self, data = None):
  811.         self.quit()
  812.  
  813.     
  814.     def quit(self):
  815.         gtk.main_quit()
  816.         sys.exit(0)
  817.  
  818.     
  819.     def show_description(self, item):
  820.         '''Collect and show some information about the package that 
  821.            contains the selected application'''
  822.         details = []
  823.         clean_desc = ''
  824.         short_desc = ''
  825.         version = ''
  826.         desktop_environment = ''
  827.         icons = []
  828.         if self.cache.has_key(item.pkgname):
  829.             version = self.cache[item.pkgname].candidateVersion
  830.             for dependencies in desktop_environment_mapping:
  831.                 for dep in dependencies:
  832.                     if self.cache.pkgDependsOn(item.pkgname, dep):
  833.                         details.append(desktop_environment_mapping[dependencies][0] % item.name)
  834.                         if desktop_environment_mapping[dependencies][1] != None:
  835.                             icons.append([
  836.                                 desktop_environment_mapping[dependencies][1],
  837.                                 desktop_environment_mapping[dependencies][0] % item.name])
  838.                         
  839.                         break
  840.                         continue
  841.                 
  842.             
  843.         
  844.         if item.available:
  845.             pkg = self.cache[item.pkgname]
  846.             rough_desc = pkg.description.rstrip(' \n\t')
  847.             first_break = rough_desc.find('\n')
  848.             short_desc = rough_desc[:first_break].rstrip('\n\t ')
  849.             rough_desc = rough_desc[first_break + 1:].lstrip('\n\t ')
  850.             p = re.compile('^(\\s|\\t)*(\\*|0|-)', re.MULTILINE)
  851.             rough_desc = p.sub('\n*', rough_desc)
  852.             p = re.compile('\\n', re.MULTILINE)
  853.             rough_desc = p.sub(' ', rough_desc)
  854.             p = re.compile('\\s\\s+', re.MULTILINE)
  855.             rough_desc = p.sub('\n', rough_desc)
  856.             lines = rough_desc.split('\n')
  857.             for i in range(len(lines)):
  858.                 if lines[i].split() == []:
  859.                     continue
  860.                 
  861.                 first_chunk = lines[i].split()[0]
  862.                 if first_chunk == '*':
  863.                     p = re.compile('\\*\\s*', re.MULTILINE)
  864.                     lines[i] = p.sub('', lines[i])
  865.                     clean_desc += '\xe2\x80\xa2 %s\n' % lines[i]
  866.                     continue
  867.                 clean_desc += '%s\n' % lines[i]
  868.             
  869.         else:
  870.             msg = _('%s cannot be installed' % item.name)
  871.             for it in self.cache._cache.FileList:
  872.                 if (it.Component != '' or it.Component == item.component or item.architectures) and self.cache.getArch() not in item.architectures:
  873.                     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()))
  874.                     break
  875.                     continue
  876.             
  877.         if item.component == 'universe':
  878.             care_about_freedom = _('This application is brought to you by the Ubuntu community.')
  879.             icons.append([
  880.                 'application-community',
  881.                 care_about_freedom])
  882.         elif item.component == 'multiverse' or item.thirdparty:
  883.             care_about_freedom = _('The use, modification and distribution of %s is restricted by copyright or by legal terms in some countries.') % item.name
  884.             icons.append([
  885.                 'application-proprietary',
  886.                 care_about_freedom])
  887.         elif item.thirdparty or item.channel:
  888.             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
  889.             icons.append([
  890.                 'application-proprietary',
  891.                 care_about_freedom])
  892.         elif item.component == 'main' or item.supported:
  893.             icons.append([
  894.                 'application-supported',
  895.                 _('Canonical Ltd. supports %s with security updates') % item.name])
  896.             care_about_freedom = ''
  897.         else:
  898.             care_about_freedom = ''
  899.         s = ''
  900.         buffer = self.textview_description.get_buffer()
  901.         buffer.set_text('')
  902.         tag_table = buffer.get_tag_table()
  903.         tag_table.foreach((lambda tag, table: table.remove(tag)), tag_table)
  904.         iter = buffer.get_start_iter()
  905.         pango_context = self.textview_description.get_pango_context()
  906.         font_desc = pango_context.get_font_description()
  907.         font_size = font_desc.get_size() / pango.SCALE
  908.         buffer.insert_with_tags_by_name(iter, ' %s' % item.name, 'app-name')
  909.         if short_desc != '':
  910.             tag_name = buffer.create_tag('short-desc', weight = pango.WEIGHT_BOLD)
  911.             buffer.insert_with_tags_by_name(iter, '\n%s' % short_desc, 'short-desc')
  912.             for emblem in icons:
  913.                 image_emblem = gtk.Image()
  914.                 image_emblem.set_from_icon_name(emblem[0], gtk.ICON_SIZE_MENU)
  915.                 image_emblem.set_pixel_size(16)
  916.                 event = gtk.EventBox()
  917.                 style = self.textview_description.get_style()
  918.                 event.modify_bg(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
  919.                 event.add(image_emblem)
  920.                 self.tooltips.set_tip(event, emblem[1])
  921.                 buffer.insert(iter, ' ')
  922.                 anchor = buffer.create_child_anchor(iter)
  923.                 self.textview_description.add_child_at_anchor(event, anchor)
  924.                 event.show()
  925.                 image_emblem.show()
  926.             
  927.         elif care_about_freedom != '':
  928.             buffer.insert(iter, '\n%s' % care_about_freedom)
  929.         
  930.         if clean_desc != '':
  931.             buffer.insert(iter, '\n%s' % clean_desc)
  932.         
  933.         if version != '':
  934.             buffer.insert(iter, _('Version: %s (%s)') % (version, item.pkgname))
  935.         
  936.         if len(details) > 0:
  937.             for x in details:
  938.                 buffer.insert(iter, '\n%s' % x)
  939.             
  940.         
  941.  
  942.     
  943.     def clear_description(self):
  944.         buffer = self.textview_description.get_buffer()
  945.         buffer.set_text('')
  946.  
  947.     
  948.     def on_treeview_packages_row_activated(self, treeview, path, view_column):
  949.         iter = treeview.get_model().get_iter(path)
  950.         item = treeview.get_model().get_value(iter, COL_ITEM)
  951.         if item.architectures and self.cache.getArch() not in item.architectures:
  952.             return False
  953.         
  954.         self.on_install_toggle(None, path)
  955.  
  956.     
  957.     def on_treeview_categories_cursor_changed(self, treeview):
  958.         self.setBusy(True)
  959.         path = treeview.get_cursor()[0]
  960.         iter = treeview.get_model().get_iter(path)
  961.         (name, item) = treeview.get_model()[iter]
  962.         self.treeview_packages.set_model(item.applications)
  963.         self.menu._refilter()
  964.         if self.sort_by_ranking:
  965.             if not item.applications.has_default_sort_func():
  966.                 item.applications.set_default_sort_func(None)
  967.                 item.applications.set_default_sort_func(self.menu._ranking_sort_func)
  968.                 item.applications.set_sort_column_id(-1, gtk.SORT_ASCENDING)
  969.             else:
  970.                 item.applications.set_default_sort_func(None)
  971.                 item.applications.set_default_sort_func(self.menu._ranking_sort_func)
  972.                 item.applications.set_sort_column_id(-1, gtk.SORT_ASCENDING)
  973.         elif item.applications.has_default_sort_func():
  974.             item.applications.set_sort_column_id(COL_NAME, gtk.SORT_ASCENDING)
  975.         
  976.         item.applications.set_default_sort_func(None)
  977.         if len(self.menu.treeview_packages.get_model()) == 0:
  978.             self.show_no_results_msg()
  979.         else:
  980.             self.menu.treeview_packages.set_cursor(0)
  981.         self.setBusy(False)
  982.  
  983.     
  984.     def on_treeview_packages_cursor_changed(self, treeview):
  985.         path = treeview.get_cursor()[0]
  986.         iter = treeview.get_model().get_iter(path)
  987.         (name, item, popcon) = treeview.get_model()[iter]
  988.         self.show_description(item)
  989.  
  990.     
  991.     def show_no_results_msg(self):
  992.         ''' Give the user some hints if the search returned 
  993.             no results'''
  994.         buffer = self.textview_description.get_buffer()
  995.         buffer.set_text('')
  996.         tag_table = buffer.get_tag_table()
  997.         tag_table.foreach((lambda tag, table: table.remove(tag)), tag_table)
  998.         tag_header = buffer.create_tag('first-line', weight = pango.WEIGHT_BOLD, pixels_above_lines = 6)
  999.         msg = _('There is no matching application available.')
  1000.         iter = buffer.get_start_iter()
  1001.         buffer.insert_with_tags_by_name(iter, msg, 'first-line')
  1002.         if self.menu.filter != SHOW_ALL_FREE and self.menu.filter != SHOW_ALL:
  1003.             msg = '\n%s' % _("To broaden your search, choose 'Show all Open Source applications' or 'Show all available' applications.")
  1004.             buffer.insert_with_tags(iter, msg)
  1005.         
  1006.         if self.treeview_categories.get_cursor()[0] != (0,):
  1007.             msg = '\n%s' % _("To broaden your search, choose 'All' categories.")
  1008.             buffer.insert_with_tags(iter, msg)
  1009.         
  1010.  
  1011.     
  1012.     def show_intro(self):
  1013.         ''' Show a quick introduction to gnome-app-install 
  1014.             in the description view'''
  1015.         buffer = self.textview_description.get_buffer()
  1016.         buffer.set_text('')
  1017.         iter = buffer.get_start_iter()
  1018.         tag_header = buffer.create_tag('header', scale = pango.SCALE_LARGE, weight = pango.WEIGHT_BOLD, pixels_above_lines = 6)
  1019.         msg = _('To install an application check the box next to the application. Uncheck the box to remove the application.') + '\n'
  1020.         msg += _('To perform advanced tasks use the Synaptic package manager.')
  1021.         buffer.insert_with_tags(iter, '%s\n' % _('Quick Introduction'), tag_header)
  1022.         buffer.insert(iter, msg)
  1023.  
  1024.  
  1025. if __name__ == '__main__':
  1026.     app = AppInstall(os.path.abspath('menu-data'), os.path.abspath('data'), sys.argv)
  1027.     gtk.main()
  1028.  
  1029.