1 #!/usr/bin/python2.4
2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 # Use is subject to license terms.
24 #
25
26 # Progress:
27 # Startup Progress has two phases:
28 # - Start phase:
29 # The start phase should be fairly constant at around a few seconds and so is given 5%
30 # of the total progress bar.
31 # - Package entry loading phase:
32 # The package entry loading phase is given the remaining 95% of the bar for progress.
33
34 INITIAL_PROGRESS_TIME_INTERVAL = 0.5 # Time to update progress during start phase
35 INITIAL_PROGRESS_TIME_PERCENTAGE = 0.005 # Amount to update progress during start phase
36 INITIAL_PROGRESS_TOTAL_PERCENTAGE = 0.05 # Total progress for start phase
37 PACKAGE_PROGRESS_TOTAL_INCREMENTS = 95 # Total increments for loading phase
38 PACKAGE_PROGRESS_PERCENT_INCREMENT = 0.01 # Amount to update progress during loading phase
39 PACKAGE_PROGRESS_PERCENT_TOTAL = 1.0 # Total progress for loading phase
40 MAX_DESC_LEN = 60 # Max length of the description
41 MAX_INFO_CACHE_LIMIT = 100 # Max number of package descriptions to cache
42 NOTEBOOK_PACKAGE_LIST_PAGE = 0 # Main Package List page index
43 NOTEBOOK_START_PAGE = 1 # Main View Start page index
44 INFO_NOTEBOOK_LICENSE_PAGE = 3 # License Tab index
45 SHOW_INFO_DELAY = 600 # Delay before showing selected pacakge information
46 SHOW_LICENSE_DELAY = 600 # Delay before showing license information
47 SEARCH_STR_FORMAT = "<%s>"
48 MIN_APP_WIDTH = 750 # Minimum application width
49 MIN_APP_HEIGHT = 500 # Minimum application height
50 INITIAL_APP_WIDTH_PREFERENCES = "/apps/packagemanager/preferences/initial_app_width"
51 INITIAL_APP_HEIGHT_PREFERENCES = "/apps/packagemanager/preferences/initial_app_height"
52 INITIAL_APP_HPOS_PREFERENCES = "/apps/packagemanager/preferences/initial_app_hposition"
53 INITIAL_APP_VPOS_PREFERENCES = "/apps/packagemanager/preferences/initial_app_vposition"
54 INITIAL_SHOW_FILTER_PREFERENCES = "/apps/packagemanager/preferences/initial_show_filter"
55 INITIAL_SECTION_PREFERENCES = "/apps/packagemanager/preferences/initial_section"
56 SHOW_STARTPAGE_PREFERENCES = "/apps/packagemanager/preferences/show_startpage"
57 API_SEARCH_ERROR_PREFERENCES = "/apps/packagemanager/preferences/api_search_error"
58 CATEGORIES_STATUS_COLUMN_INDEX = 0 # Index of Status Column in Categories TreeView
59
60 STATUS_COLUMN_INDEX = 2 # Index of Status Column in Application TreeView
61
62 PKG_CLIENT_NAME = "packagemanager"
63
64 # Location for themable icons
65 ICON_LOCATION = "usr/share/package-manager/icons"
66 # Load Start Page from lang dir if available
67 START_PAGE_CACHE_LANG_BASE = "var/pkg/gui_cache/startpagebase/%s/%s"
68 START_PAGE_LANG_BASE = "usr/share/package-manager/data/startpagebase/%s/%s"
69 START_PAGE_HOME = "startpage.html" # Default page
70
71 # StartPage Action support for url's on StartPage pages
72 PM_ACTION = 'pm-action' # Action field for StartPage url's
73
74 # Internal Example: <a href="pm?pm-action=internal&uri=top_picks.html">
75 ACTION_INTERNAL = 'internal' # Internal Action value: pm-action=internal
76 INTERNAL_URI = 'uri' # Internal field: uri to navigate to in StartPage
77 # without protocol scheme specified
78
79 # External Example: <a href="pm?pm-action=external&uri=www.opensolaris.com">
80 ACTION_EXTERNAL = 'external' # External Action value: pm-action=external
81 EXTERNAL_URI = 'uri' # External field: uri to navigate to in external
82 # default browser without protocol scheme specified
83 EXTERNAL_PROTOCOL = 'protocol' # External field: optional protocol scheme,
84 # defaults to http
85 DEFAULT_PROTOCOL = 'http'
86
87 import getopt
88 import pwd
89 import os
90 import sys
91 import time
92 import locale
93 import itertools
94 import urllib
95 import urlparse
96 import socket
97 import gettext
98 import signal
99 from threading import Thread
100 from threading import Lock
101 from urllib2 import HTTPError, URLError
102 from cPickle import UnpicklingError
103
104 try:
105 import gobject
106 import gnome
107 gobject.threads_init()
108 import gconf
109 import gtk
110 import gtk.glade
111 import pygtk
112 pygtk.require("2.0")
113 import gtkhtml2
114 import pango
115 from glib import GError
116 except ImportError:
117 sys.exit(1)
118 import pkg.misc as misc
119 import pkg.client.progress as progress
120 import pkg.client.api_errors as api_errors
121 import pkg.client.api as api
122 import pkg.client.publisher as publisher
123 import pkg.portable as portable
124 import pkg.fmri as fmri
125 import pkg.gui.repository as repository
126 import pkg.gui.beadmin as beadm
127 import pkg.gui.cache as cache
128 import pkg.gui.misc as gui_misc
129 import pkg.gui.imageinfo as imageinfo
130 import pkg.gui.installupdate as installupdate
131 import pkg.gui.enumerations as enumerations
132 import pkg.gui.parseqs as parseqs
133 import pkg.gui.webinstall as webinstall
134 from pkg.client import global_settings
135
136 # Put _() in the global namespace
137 import __builtin__
138 __builtin__._ = gettext.gettext
139
140 (
141 DISPLAY_LINK,
142 CLICK_LINK,
143 ) = range(2)
144
145 class PackageManager:
146 def __init__(self):
147 signal.signal(signal.SIGINT, self.__main_application_quit)
148 # We reset the HOME directory in case the user called us
149 # with gksu and had NFS mounted home directory in which
150 # case dbus called from gconf cannot write to the directory.
151 if os.getuid() == 0:
152 dir = self.__find_root_home_dir()
153 os.putenv('HOME', dir)
154 self.api_o = None
155 self.cache_o = None
156 self.img_timestamp = None
157 self.client = gconf.client_get_default()
158 try:
159 self.initial_show_filter = \
160 self.client.get_int(INITIAL_SHOW_FILTER_PREFERENCES)
161 self.initial_section = \
162 self.client.get_int(INITIAL_SECTION_PREFERENCES)
163 self.show_startpage = \
164 self.client.get_bool(SHOW_STARTPAGE_PREFERENCES)
165 self.gconf_not_show_repos = \
166 self.client.get_string(API_SEARCH_ERROR_PREFERENCES)
167 self.initial_app_width = \
168 self.client.get_int(INITIAL_APP_WIDTH_PREFERENCES)
169 self.initial_app_height = \
170 self.client.get_int(INITIAL_APP_HEIGHT_PREFERENCES)
171 self.initial_app_hpos = \
172 self.client.get_int(INITIAL_APP_HPOS_PREFERENCES)
173 self.initial_app_vpos = \
174 self.client.get_int(INITIAL_APP_VPOS_PREFERENCES)
175 except GError:
176 # Default values - the same as in the
177 # packagemanager-preferences.schemas
178 self.initial_show_filter = 0
179 self.initial_section = 3
180 self.show_startpage = True
181 self.gconf_not_show_repos = ""
182 self.initial_app_width = 800
183 self.initial_app_height = 600
184 self.initial_app_hpos = 200
185 self.initial_app_vpos = 320
186
187 if not self.gconf_not_show_repos:
188 self.gconf_not_show_repos = ""
189 self.set_show_filter = 0
190 self.set_section = 0
191 self.current_search_option = 0
192 self.in_search_mode = False
193
194 # Override default PKG_TIMEOUT_MAX and PKG_CLIENT_TIMEOUT
195 # if a value has been specified in the environment.
196 global_settings.PKG_TIMEOUT_MAX = int(os.environ.get(
197 "PKG_TIMEOUT_MAX", global_settings.PKG_TIMEOUT_MAX))
198
199 global_settings.PKG_CLIENT_TIMEOUT = int(os.environ.get(
200 "PKG_CLIENT_TIMEOUT", global_settings.PKG_CLIENT_TIMEOUT))
201
202 # This call only affects sockets created by Python. The
203 # transport framework must set the timeout value internally
204 #
205 # value is in seconds
206 socket.setdefaulttimeout(global_settings.PKG_TIMEOUT_MAX)
207
208 global_settings.client_name = PKG_CLIENT_NAME
209
210 try:
211 self.application_dir = os.environ["PACKAGE_MANAGER_ROOT"]
212 except KeyError:
213 self.application_dir = "/"
214 misc.setlocale(locale.LC_ALL, "")
215 for module in (gettext, gtk.glade):
216 module.bindtextdomain("pkg", os.path.join(
217 self.application_dir,
218 "usr/share/locale"))
219 module.textdomain("pkg")
220 # XXX Remove and use _() where self._ and self.parent._ are being used
221 self.main_window_title = _('Package Manager')
222 self.user_rights = portable.is_admin()
223 self.cancelled = False # For background processes
224 self.image_directory = None
225 self.description_thread_running = False # For background processes
226 gtk.rc_parse('~/.gtkrc-1.2-gnome2') # Load gtk theme
227 self.progress_stop_timer_thread = False
228 self.progress_fraction_time_count = 0
229 self.progress_canceled = False
230 self.catalog_loaded = False
231 self.image_dir_arg = None
232 self.update_all_proceed = False
233 self.ua_be_name = None
234 self.application_path = None
235 self.default_publisher = None
236 self.current_repos_with_search_errors = []
237 self.first_run = True
238 self.in_reload = False
239 self.selected_pkgstem = None
240 self.selected_model = None
241 self.selected_path = None
242 self.info_cache = {}
243 self.selected = 0
244 self.selected_pkgs = {}
245 self.to_install_update = {}
246 self.to_remove = {}
247 self.in_startpage_startup = self.show_startpage
248 self.lang = None
249 self.lang_root = None
250 self.visible_status_id = 0
251 self.categories_status_id = 0
252 self.icon_theme = gtk.IconTheme()
253 icon_location = os.path.join(self.application_dir, ICON_LOCATION)
254 self.icon_theme.append_search_path(icon_location)
255 self.search_options = [
256 ('ips-search',
257 gui_misc.get_icon(self.icon_theme, 'search', 20),
258 _("_Current Repository"),
259 _("Search Current Repository")),
260 ('ips-search-all',
261 gui_misc.get_icon(self.icon_theme, 'search_all', 20),
262 _("_All Repositories"),
263 _("Search All Repositories"))
264 ]
265 self.__register_iconsets(self.search_options)
266 self.visible_repository = None
267 self.visible_repository_uptodate = False
268 self.last_active_publisher = None
269 self.search_start = 0
270 self.search_time_sec = 0
271 self.current_search_publisher = None
272 self.section_list = None
273 self.filter_list = self.__get_new_filter_liststore()
274 self.application_list = None
275 self.a11y_application_treeview = None
276 self.a11y_categories_treeview = None
277 self.application_treeview_range = None
278 self.application_treeview_initialized = False
279 self.categories_treeview_range = None
280 self.categories_treeview_initialized = False
281 self.category_list = None
282 self.repositories_list = None
283 self.pr = progress.NullProgressTracker()
284 self.pylintstub = None
285 self.release_notes_url = "http://www.opensolaris.org"
286 self.__image_activity_lock = Lock()
287
288 # Create Widgets and show gui
289 self.gladefile = os.path.join(self.application_dir,
290 "usr/share/package-manager/packagemanager.glade")
291 w_tree_main = gtk.glade.XML(self.gladefile, "mainwindow")
292 w_tree_progress = gtk.glade.XML(self.gladefile, "progressdialog")
293 w_tree_preferences = gtk.glade.XML(self.gladefile, "preferencesdialog")
294 w_tree_api_search_error = gtk.glade.XML(self.gladefile,
295 "api_search_error")
296 self.w_preferencesdialog = \
297 w_tree_preferences.get_widget("preferencesdialog")
298 self.w_startpage_checkbutton = \
299 w_tree_preferences.get_widget("startpage_checkbutton")
300 self.api_search_error_dialog = \
301 w_tree_api_search_error.get_widget("api_search_error")
302 self.api_search_error_textview = \
303 w_tree_api_search_error.get_widget("api_search_error_text")
304 self.api_search_checkbox = \
305 w_tree_api_search_error.get_widget("api_search_checkbox")
306 self.api_search_button = \
307 w_tree_api_search_error.get_widget("api_search_button")
308 infobuffer = self.api_search_error_textview.get_buffer()
309 infobuffer.create_tag("bold", weight=pango.WEIGHT_BOLD)
310
311 self.w_main_window = w_tree_main.get_widget("mainwindow")
312 self.w_main_hpaned = \
313 w_tree_main.get_widget("main_hpaned")
314 self.w_main_vpaned = \
315 w_tree_main.get_widget("main_vpaned")
316
317 self.w_application_treeview = \
318 w_tree_main.get_widget("applicationtreeview")
319 self.w_categories_treeview = w_tree_main.get_widget("categoriestreeview")
320 self.w_info_notebook = w_tree_main.get_widget("details_notebook")
321 self.w_generalinfo_textview = \
322 w_tree_main.get_widget("generalinfotextview")
323 self.w_generalinfo_textview.set_wrap_mode(gtk.WRAP_WORD)
324 self.w_installedfiles_textview = \
325 w_tree_main.get_widget("installedfilestextview")
326 self.w_license_textview = \
327 w_tree_main.get_widget("licensetextview")
328 self.w_dependencies_textview = \
329 w_tree_main.get_widget("dependenciestextview")
330 self.w_packagename_label = w_tree_main.get_widget("packagenamelabel")
331 self.w_shortdescription_label = \
332 w_tree_main.get_widget("shortdescriptionlabel")
333 w_package_hbox = \
334 w_tree_main.get_widget("package_hbox")
335 self.w_general_info_label = \
336 w_tree_main.get_widget("general_info_label")
337 self.w_startpage_frame = \
338 w_tree_main.get_widget("startpage_frame")
339 self.w_startpage_eventbox = \
340 w_tree_main.get_widget("startpage_eventbox")
341 self.w_startpage_eventbox.modify_bg(gtk.STATE_NORMAL,
342 gtk.gdk.color_parse("white"))
343
344 self.w_main_statusbar = w_tree_main.get_widget("statusbar")
345 self.w_infosearch_frame = w_tree_main.get_widget("infosearch_frame")
346 self.w_infosearch_button = w_tree_main.get_widget("infosearch_button")
347
348 self.w_main_view_notebook = \
349 w_tree_main.get_widget("main_view_notebook")
350 self.w_searchentry = w_tree_main.get_widget("searchentry")
351 self.w_installupdate_button = \
352 w_tree_main.get_widget("install_update_button")
353 self.w_remove_button = w_tree_main.get_widget("remove_button")
354 self.w_updateall_button = w_tree_main.get_widget("update_all_button")
355 self.w_reload_button = w_tree_main.get_widget("reloadbutton")
356 self.w_repository_combobox = w_tree_main.get_widget("repositorycombobox")
357 self.w_sections_combobox = w_tree_main.get_widget("sectionscombobox")
358 self.w_filter_combobox = w_tree_main.get_widget("filtercombobox")
359 self.w_packageicon_image = w_tree_main.get_widget("packageimage")
360 self.w_installupdate_menuitem = \
361 w_tree_main.get_widget("package_install_update")
362 self.w_remove_menuitem = w_tree_main.get_widget("package_remove")
363 self.w_updateall_menuitem = w_tree_main.get_widget("package_update_all")
364 self.w_cut_menuitem = w_tree_main.get_widget("edit_cut")
365 self.w_copy_menuitem = w_tree_main.get_widget("edit_copy")
366 self.w_paste_menuitem = w_tree_main.get_widget("edit_paste")
367 self.w_clear_menuitem = w_tree_main.get_widget("edit_clear")
368 self.w_selectall_menuitem = w_tree_main.get_widget("edit_select_all")
369 self.w_selectupdates_menuitem = \
370 w_tree_main.get_widget("edit_select_updates")
371 self.w_deselect_menuitem = w_tree_main.get_widget("edit_deselect")
372 self.w_main_clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)
373 self.w_progress_dialog = w_tree_progress.get_widget("progressdialog")
374 self.w_progress_dialog.connect('delete-event', lambda stub1, stub2: True)
375 self.w_progress_dialog.set_title(_("Update All"))
376 self.w_progressinfo_label = w_tree_progress.get_widget("progressinfo")
377 self.w_progressinfo_label.set_text(_(
378 "Checking SUNWipkg and SUNWipkg-gui versions\n\nPlease wait ..."))
379 self.w_progressbar = w_tree_progress.get_widget("progressbar")
380 self.w_progressbar.set_pulse_step(0.1)
381 self.w_progress_cancel = w_tree_progress.get_widget("progresscancel")
382 self.progress_canceled = False
383 self.w_clear_search_button = w_tree_main.get_widget("clear_search")
384 self.w_clear_search_button.set_sensitive(False)
385 clear_search_image = w_tree_main.get_widget("clear_image")
386 clear_search_image.set_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_MENU)
387 self.saved_filter_combobox_active = self.initial_show_filter
388 self.search_image = w_tree_main.get_widget("search_image")
389 self.search_button = w_tree_main.get_widget("set_search")
390 self.a11y_search_button = self.search_button.get_accessible()
391 self.is_search_all = False
392 self.searchmenu = gtk.Menu()
393 self.search_image.set_from_pixbuf(self.search_options[0][1])
394 self.a11y_search_button.set_description(self.search_options[0][3])
395 for stock_id, pixbuf, label, description in self.search_options:
396 action = gtk.Action(stock_id, label, None, stock_id)
397 action.connect('activate',
398 self.__search_menu_item_activate)
399 menu_item = action.create_menu_item()
400 self.searchmenu.append(menu_item)
401 self.pylintstub = description
402 self.pylintstub = pixbuf
403 self.changing_search_option = False
404 self.saved_repository_combobox_active = -1
405 self.saved_sections_combobox_active = 0
406 self.saved_application_list = None
407 self.saved_application_list_filter = None
408 self.saved_application_list_sort = None
409 self.saved_category_list = None
410 self.saved_section_list = None
411 self.saved_selected_application_path = None
412 self.statusbar_message_id = 0
413 toolbar = w_tree_main.get_widget("toolbutton2")
414 toolbar.set_expand(True)
415 self.__init_repository_tree_view()
416 self.install_button_tooltip = gtk.Tooltips()
417 self.remove_button_tooltip = gtk.Tooltips()
418 self.__update_reload_button()
419 self.w_main_window.set_title(self.main_window_title)
420 self.w_searchentry.grab_focus()
421
422 # Update All Completed Dialog
423 w_xmltree_ua_completed = gtk.glade.XML(self.gladefile,
424 "ua_completed_dialog")
425 self.w_ua_completed_dialog = w_xmltree_ua_completed.get_widget(
426 "ua_completed_dialog")
427 self.w_ua_completed_dialog .connect("destroy",
428 self.__on_ua_completed_close)
429 self.w_ua_completed_release_label = w_xmltree_ua_completed.get_widget(
430 "ua_completed_release_label")
431 self.w_ua_completed_linkbutton = w_xmltree_ua_completed.get_widget(
432 "ua_completed_linkbutton")
433
434 # Setup Start Page
435 self.document = None
436 self.view = None
437 self.current_url = None
438 self.opener = None
439 self.__setup_startpage(self.show_startpage)
440
441 try:
442 dic_mainwindow = \
443 {
444 "on_mainwindow_delete_event": \
445 self.__on_mainwindow_delete_event,
446 "on_searchentry_changed":self.__on_searchentry_changed,
447 "on_searchentry_focus_in_event": \
448 self.__on_searchentry_focus_in,
449 "on_searchentry_focus_out_event": \
450 self.__on_searchentry_focus_out,
451 "on_searchentry_activate": \
452 self.__on_searchentry_activate,
453 "on_sectionscombobox_changed": \
454 self.__on_sectionscombobox_changed,
455 "on_filtercombobox_changed": \
456 self.__on_filtercombobox_changed,
457 "on_repositorycombobox_changed": \
458 self.__on_repositorycombobox_changed,
459 #menu signals
460 "on_file_quit_activate":self.__on_file_quit_activate,
461 "on_file_be_activate":self.__on_file_be_activate,
462 "on_package_install_update_activate": \
463 self.__on_install_update,
464 "on_settings_edit_repositories_activate": \
465 self.__on_edit_repositories_activate,
466 "on_package_remove_activate":self.__on_remove,
467 "on_help_about_activate":self.__on_help_about,
468 "on_help_help_activate":self.__on_help_help,
469 "on_edit_paste_activate":self.__on_edit_paste,
470 "on_edit_clear_activate":self.__on_clear_paste,
471 "on_edit_copy_activate":self.__on_copy,
472 "on_edit_cut_activate":self.__on_cut,
473 "on_edit_search_activate":self.__on_edit_search_clicked,
474 "on_set_search_clicked":self.__on_set_search_clicked,
475 "on_set_search_button_press_event":self.__on_set_search,
476 "on_clear_search_clicked":self.__on_clear_search,
477 "on_edit_select_all_activate":self.__on_select_all,
478 "on_edit_select_updates_activate": \
479 self.__on_select_updates,
480 "on_edit_deselect_activate":self.__on_deselect,
481 "on_edit_preferences_activate":self.__on_preferences,
482 # XXX disabled until new API
483 "on_package_update_all_activate":self.__on_update_all,
484 #toolbar signals
485 # XXX disabled until new API
486 "on_update_all_button_clicked":self.__on_update_all,
487 "on_reload_button_clicked":self.__on_reload,
488 "on_install_update_button_clicked": \
489 self.__on_install_update,
490 "on_remove_button_clicked":self.__on_remove,
491 "on_help_start_page_activate":self.__on_startpage,
492 "on_details_notebook_switch_page": \
493 self.__on_notebook_change,
494 "on_infosearch_button_clicked": \
495 self.__on_infosearch_button_clicked,
496 }
497 dic_progress = \
498 {
499 "on_cancel_progressdialog_clicked": \
500 self.__on_cancel_progressdialog_clicked,
501 }
502 dic_preferences = \
503 {
504 "on_startpage_checkbutton_toggled": \
505 self.__on_startpage_checkbutton_toggled,
506 "on_preferenceshelp_clicked": \
507 self.__on_preferenceshelp_clicked,
508 "on_preferencesclose_clicked": \
509 self.__on_preferencesclose_clicked,
510 }
511 dic_api_search_error = \
512 {
513 "on_api_search_checkbox_toggled": \
514 self.__on_api_search_checkbox_toggled,
515 "on_api_search_button_clicked": \
516 self.__on_api_search_button_clicked,
517 "on_api_search_error_delete_event": \
518 self.__on_api_search_error_delete_event,
519 }
520 dic_completed = \
521 {
522 "on_ua_complete_close_button_clicked": \
523 self.__on_ua_completed_close,
524 "on_ua_completed_linkbutton_clicked": \
525 self.__on_ua_completed_linkbutton_clicked,
526 }
527 w_xmltree_ua_completed.signal_autoconnect(dic_completed)
528
529
530 w_tree_main.signal_autoconnect(dic_mainwindow)
531 w_tree_progress.signal_autoconnect(dic_progress)
532 w_tree_preferences.signal_autoconnect(dic_preferences)
533 w_tree_api_search_error.signal_autoconnect(
534 dic_api_search_error)
535 except AttributeError, error:
536 print _(
537 "GUI will not respond to any event! %s."
538 "Check declare_signals()") \
539 % error
540
541 self.package_selection = None
542 self.category_list_filter = None
543 self.application_list_filter = None
544 self.application_list_sort = None
545 self.application_refilter_id = 0
546 self.application_refilter_idle_id = 0
547 self.last_show_info_id = 0
548 self.show_info_id = 0
549 self.last_show_licenses_id = 0
550 self.show_licenses_id = 0
551 self.showing_empty_details = False
552 self.in_setup = True
553 if self.initial_app_width >= MIN_APP_WIDTH and \
554 self.initial_app_height >= MIN_APP_HEIGHT:
555 self.w_main_window.resize(self.initial_app_width,
556 self.initial_app_height)
557 if self.initial_app_hpos > 0:
558 self.w_main_hpaned.set_position(self.initial_app_hpos)
559 if self.initial_app_vpos > 0:
560 self.w_main_vpaned.set_position(self.initial_app_vpos)
561 self.w_main_window.show_all()
562 gdk_win = self.w_main_window.get_window()
563 self.gdk_window = gtk.gdk.Window(gdk_win, gtk.gdk.screen_width(),
564 gtk.gdk.screen_height(), gtk.gdk.WINDOW_CHILD, 0, gtk.gdk.INPUT_ONLY)
565 gdk_cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
566 self.gdk_window.set_cursor(gdk_cursor)
567 # Until package icons become available hide Package Icon Panel
568 w_package_hbox.hide()
569 if self.show_startpage:
570 self.w_main_view_notebook.set_current_page(NOTEBOOK_START_PAGE)
571 else:
572 self.w_main_view_notebook.set_current_page(
573 NOTEBOOK_PACKAGE_LIST_PAGE)
574 self.api_search_error_dialog.set_transient_for(self.w_main_window)
575 self.__setup_text_signals()
576
577 def __setup_text_signals(self):
578 self.w_generalinfo_textview.get_buffer().connect(
579 "notify::has-selection", self.__on_text_buffer_has_selection)
580 self.w_installedfiles_textview.get_buffer().connect(
581 "notify::has-selection", self.__on_text_buffer_has_selection)
582 self.w_dependencies_textview.get_buffer().connect(
583 "notify::has-selection", self.__on_text_buffer_has_selection)
584 self.w_license_textview.get_buffer().connect(
585 "notify::has-selection", self.__on_text_buffer_has_selection)
586 self.w_searchentry.connect(
587 "notify::cursor-position", self.__on_searchentry_selection)
588 self.w_searchentry.connect(
589 "notify::selection-bound", self.__on_searchentry_selection)
590 self.w_generalinfo_textview.connect(
591 "focus-in-event", self.__on_textview_focus_in)
592 self.w_installedfiles_textview.connect(
593 "focus-in-event", self.__on_textview_focus_in)
594 self.w_dependencies_textview.connect(
595 "focus-in-event", self.__on_textview_focus_in)
596 self.w_license_textview.connect(
597 "focus-in-event", self.__on_textview_focus_in)
598 self.w_generalinfo_textview.connect(
599 "focus-out-event", self.__on_textview_focus_out)
600 self.w_installedfiles_textview.connect(
601 "focus-out-event", self.__on_textview_focus_out)
602 self.w_dependencies_textview.connect(
603 "focus-out-event", self.__on_textview_focus_out)
604 self.w_license_textview.connect(
605 "focus-out-event", self.__on_textview_focus_out)
606
607 def __on_textview_focus_in(self, widget, event):
608 char_count = widget.get_buffer().get_char_count()
609 if char_count > 0:
610 self.w_selectall_menuitem.set_sensitive(True)
611 else:
612 self.w_selectall_menuitem.set_sensitive(False)
613 bounds = widget.get_buffer().get_selection_bounds()
614 if bounds:
615 offset1 = bounds[0].get_offset()
616 offset2 = bounds[1].get_offset()
617 if abs(offset2 - offset1) == char_count:
618 self.w_selectall_menuitem.set_sensitive(False)
619 self.w_deselect_menuitem.set_sensitive(True)
620 self.w_copy_menuitem.set_sensitive(True)
621 else:
622 self.w_deselect_menuitem.set_sensitive(False)
623
624 def __on_textview_focus_out(self, widget, event):
625 self.__enable_disable_select_all()
626 self.__enable_disable_deselect()
627 self.w_copy_menuitem.set_sensitive(False)
628
629 def __on_text_buffer_has_selection(self, object, pspec):
630 if object.get_selection_bounds():
631 self.w_copy_menuitem.set_sensitive(True)
632 self.w_deselect_menuitem.set_sensitive(True)
633 else:
634 self.w_copy_menuitem.set_sensitive(False)
635 self.w_deselect_menuitem.set_sensitive(False)
636
637 def __register_iconsets(self, icon_info):
638 factory = gtk.IconFactory()
639 for stock_id, pixbuf, name, description in icon_info:
640 iconset = gtk.IconSet(pixbuf)
641 factory.add(stock_id, iconset)
642 self.pylintstub = name
643 self.pylintstub = description
644 factory.add_default()
645
646 def __set_search_option(self, i):
647 # The value i is the index in the table search_options
648 # of the current choice.
649 # Index 0 corresponds to Current Repository.
650 # We assume that anything else is search all.
651 # This may need to be revisited if more search options are
652 # added.
653 if i == self.current_search_option:
654 return
655 self.current_search_option = i
656 self.changing_search_option = True
657 is_search_all = (i != 0)
658 self.__update_repository_combobox_for_search(is_search_all)
659 if is_search_all:
660 self.__setup_before_search_all_mode()
661 else:
662 self.__restore_setup_for_browse()
663 self.changing_search_option = False
664
665 def __update_repository_combobox_for_search(self, is_search_all):
666 if is_search_all:
667 self.saved_repository_combobox_active = \
668 self.w_repository_combobox.get_active()
669 self.__disconnect_repository_model()
670 if is_search_all:
671 self.repositories_list.prepend(
672 [-1, _("All Repositories Search Results"), ])
673 else:
674 self.repositories_list.remove(
675 self.repositories_list.get_iter_first())
676 self.w_repository_combobox.set_model(self.repositories_list)
677 #self.visible_repository = None
678
679
680 def __link_load_blank(self):
681 self.document.clear()
682 self.document.open_stream('text/html')
683 self.document.write_stream(_(
684 "<html><head></head><body></body></html>"))
685 self.document.close_stream()
686
687 def __search_menu_item_activate(self, widget):
688 name = widget.get_name()
689 i = 0
690 for stock_id, pixbuf, label, description in self.search_options:
691 if stock_id == name:
692 self.__set_search_option(i)
693 self.search_image.set_from_pixbuf(pixbuf)
694 self.a11y_search_button.set_description(description)
695 break
696 i += 1
697 self.pylintstub = label
698
699 def __setup_startpage(self, show_startpage):
700 self.opener = urllib.FancyURLopener()
701 self.document = gtkhtml2.Document()
702 self.document.connect('request_url', self.__request_url)
703 self.document.connect('link_clicked', self.__handle_link)
704 self.document.clear()
705
706 self.view = gtkhtml2.View()
707 self.view.set_document(self.document)
708 self.view.connect('request_object', self.__request_object)
709 self.view.connect('on_url', self.__on_url)
710
711 try:
712 self.lang, encode = locale.getlocale(locale.LC_CTYPE)
713 if debug:
714 print "Lang: %s: Encode: %s" % (self.lang, encode)
715 except locale.Error:
716 self.lang = "C"
717 if self.lang == None or self.lang == "":
718 self.lang = "C"
719 self.lang_root = self.lang.split('_')[0]
720 if show_startpage:
721 self.__load_startpage()
722 self.w_startpage_frame.add(self.view)
723
724 # Stub handler required by GtkHtml widget
725 def __request_object(self, *vargs):
726 pass
727
728 def __load_startpage(self):
729 if self.__load_startpage_locale(START_PAGE_CACHE_LANG_BASE):
730 return
731 if self.__load_startpage_locale(START_PAGE_LANG_BASE):
732 return
733 self.__handle_startpage_load_error(start_page_url)
734
735
736 def __load_startpage_locale(self, start_page_lang_base):
737 start_page_url = os.path.join(self.application_dir,
738 start_page_lang_base % (self.lang, START_PAGE_HOME))
739 if self.__load_uri(self.document, start_page_url):
740 return True
741
742 if self.lang_root != None and self.lang_root != self.lang:
743 start_page_url = os.path.join(self.application_dir,
744 start_page_lang_base % (self.lang_root, START_PAGE_HOME))
745 if self.__load_uri(self.document, start_page_url):
746 return True
747
748 start_page_url = os.path.join(self.application_dir,
749 start_page_lang_base % ("C", START_PAGE_HOME))
750 if self.__load_uri(self.document, start_page_url):
751 return True
752 return False
753
754 def __handle_startpage_load_error(self, start_page_url):
755 self.document.open_stream('text/html')
756 self.document.write_stream(_(
757 "<html><head></head><body><H2>Welcome to"
758 "PackageManager!</H2><br>"
759 "<font color='#0000FF'>Warning: Unable to "
760 "load Start Page:<br>%s</font></body></html>"
761 % (start_page_url)))
762 self.document.close_stream()
763
764 def __process_api_search_error(self, error):
765 self.current_repos_with_search_errors = []
766
767 for pub, err in error.failed_servers:
768 self.current_repos_with_search_errors.append(
769 (pub, _("failed to respond"), err))
770 for pub in error.invalid_servers:
771 self.current_repos_with_search_errors.append(
772 (pub, _("invalid response"),
773 _("A valid response was not returned.")))
774 for pub, err in error.unsupported_servers:
775 self.current_repos_with_search_errors.append(
776 (pub, _("unsupported search"), err))
777
778 def __on_infosearch_button_clicked(self, widget):
779 self.__handle_api_search_error(True)
780
781 def __handle_api_search_error(self, show_all=False):
782 if len(self.current_repos_with_search_errors) == 0:
783 self.w_infosearch_frame.hide()
784 return
785
786 repo_count = 0
787 for pub, err_type, err_str in self.current_repos_with_search_errors:
788 if show_all or (pub not in self.gconf_not_show_repos):
789 repo_count += 1
790 if repo_count == 0:
791 self.w_infosearch_frame.hide()
792 return
793
794 self.w_infosearch_button.set_size_request(26, 22)
795 self.w_infosearch_frame.show()
796 infobuffer = self.api_search_error_textview.get_buffer()
797 infobuffer.set_text("")
798 textiter = infobuffer.get_end_iter()
799 for pub, err_type, err_str in self.current_repos_with_search_errors:
800
801 if show_all or (pub not in self.gconf_not_show_repos):
802 infobuffer.insert_with_tags_by_name(textiter,
803 "%(pub)s (%(err_type)s)\n" % {"pub": pub,
804 "err_type": err_type}, "bold")
805 infobuffer.insert(textiter, "%s\n" % (err_str))
806
807 self.api_search_checkbox.set_active(False)
808 self.api_search_error_dialog.show()
809 self.api_search_button.grab_focus()
810
811 def __get_repo_publishers(self):
812 repo_pub_dict = {}
813 pubs = self.api_o.get_publishers()
814 for pub in pubs:
815 repo = pub.selected_repository
816 origin = repo.origins[0]
817 repo_pub_dict[origin.uri] = pub.prefix
818 return repo_pub_dict
819
820 def __on_url(self, view, link):
821 # Handle mouse over events on links and reset when not on link
822 if link == None or link == "":
823 self.update_statusbar()
824 else:
825 display_link = self.__handle_link(None, link, DISPLAY_LINK)
826 if display_link != None:
827 self.w_main_statusbar.push(0, display_link)
828 else:
829 self.update_statusbar()
830
831 @staticmethod
832 def __is_relative_to_server(url):
833 parts = urlparse.urlparse(url)
834 if parts[0] or parts[1]:
835 return 0
836 return 1
837
838 def __open_url(self, url):
839 uri = self.__resolve_uri(url)
840 return self.opener.open(uri)
841
842 def __resolve_uri(self, uri):
843 if self.__is_relative_to_server(uri) and self.current_url != uri:
844 return urlparse.urljoin(self.current_url, uri)
845 return uri
846
847 def __request_url(self, document, url, stream):
848 f = self.__open_url(url)
849 stream.set_cancel_func(self.__stream_cancel)
850 stream.write(f.read())
851
852 # Stub handler required by GtkHtml widget or widget will assert
853 def __stream_cancel(self, *vargs):
854 pass
855
856 def __load_uri(self, document, link):
857 self.w_main_statusbar.push(0, _("Loading... " + link))
858 try:
859 f = self.__open_url(link)
860 except (IOError, OSError), err:
861 if debug:
862 print "err: %s" % (err)
863 self.w_main_statusbar.push(0, _("Stopped"))
864 return False
865 self.current_url = self.__resolve_uri(link)
866
867 self.document.clear()
868 headers = f.info()
869 mime = headers.getheader('Content-type').split(';')[0]
870 if mime:
871 self.document.open_stream(mime)
872 else:
873 self.document.open_stream('text/plain')
874
875 self.document.write_stream(f.read())
876 self.document.close_stream()
877 self.w_main_statusbar.push(0, _("Done"))
878 return True
879
880 def __link_load_error(self, link):
881 self.document.clear()
882 self.document.open_stream('text/html')
883 self.document.write_stream(_(
884 "<html><head></head><body><font color='#000000'>\
885 <a href='stub'></a></font>\
886 <a href='pm?%s=internal&uri=%s'>\
887 <IMG SRC = 'startpage_star.png' \
888 style='border-style: none'></a> <br><br>\
889 <h2><font color='#0000FF'>Warning: Unable to \
890 load URL</font></h2><br>%s</body></html>"
891 % (PM_ACTION, START_PAGE_HOME, link)))
892 self.document.close_stream()
893
894 def __handle_link(self, document, link, handle_what = CLICK_LINK):
895 query_dict = self.__urlparse_qs(link)
896
897 action = None
898 if query_dict.has_key(PM_ACTION):
899 action = query_dict[PM_ACTION][0]
900 elif handle_what == DISPLAY_LINK:
901 return link
902 ext_uri = ""
903 protocol = None
904
905 # Internal Browse
906 if action == ACTION_INTERNAL:
907 if query_dict.has_key(INTERNAL_URI):
908 int_uri = query_dict[INTERNAL_URI][0]
909 if handle_what == DISPLAY_LINK:
910 return int_uri
911 else:
912 if handle_what == CLICK_LINK:
913 self.__link_load_error(_("No URI specified"))
914 return
915 if handle_what == CLICK_LINK and \
916 not self.__load_uri(document, int_uri):
917 self.__link_load_error(int_uri)
918 return
919 # External browse
920 elif action == ACTION_EXTERNAL:
921 if query_dict.has_key(EXTERNAL_URI):
922 ext_uri = query_dict[EXTERNAL_URI][0]
923 else:
924 if handle_what == CLICK_LINK:
925 self.__link_load_error(_("No URI specified"))
926 return
927 if query_dict.has_key(EXTERNAL_PROTOCOL):
928 protocol = query_dict[EXTERNAL_PROTOCOL][0]
929 else:
930 protocol = DEFAULT_PROTOCOL
931
932 if handle_what == DISPLAY_LINK:
933 return protocol + "://" + ext_uri
934 try:
935 gnome.url_show(protocol + "://" + ext_uri)
936 except gobject.GError:
937 self.__link_load_error(protocol + "://" + ext_uri)
938 elif handle_what == DISPLAY_LINK:
939 return None
940 elif action == None:
941 try:
942 gnome.url_show(link)
943 except gobject.GError:
944 self.__link_load_error(link)
945 # Handle empty and unsupported actions
946 elif action == "":
947 self.__link_load_error(_("Empty Action not supported"
948 % action))
949 return
950 elif action != None:
951 self.__link_load_error(_("Action not supported: %s"
952 % action))
953 return
954
955 @staticmethod
956 def __urlparse_qs(url, keep_blank_values=0, strict_parsing=0):
957 scheme, netloc, url, params, querystring, fragment = urlparse.urlparse(
958 url)
959 if debug:
960 print ("Query: scheme %s, netloc %s, url %s, params %s,"
961 "querystring %s, fragment %s"
962 % (scheme, netloc, url, params, querystring, fragment))
963 return parseqs.parse_qs(querystring)
964
965 @staticmethod
966 def __get_new_application_liststore():
967 return gtk.ListStore(
968 gobject.TYPE_BOOLEAN, # enumerations.MARK_COLUMN
969 gtk.gdk.Pixbuf, # enumerations.STATUS_ICON_COLUMN
970 gobject.TYPE_STRING, # enumerations.NAME_COLUMN
971 gobject.TYPE_STRING, # enumerations.DESCRIPTION_COLUMN
972 gobject.TYPE_INT, # enumerations.STATUS_COLUMN
973 gobject.TYPE_PYOBJECT, # enumerations.FMRI_COLUMN
974 gobject.TYPE_STRING, # enumerations.STEM_COLUMN
975 gobject.TYPE_STRING, # enumerations.DISPLAY_NAME_COLUMN
976 gobject.TYPE_BOOLEAN, # enumerations.IS_VISIBLE_COLUMN
977 gobject.TYPE_PYOBJECT, # enumerations.CATEGORY_LIST_COLUMN
978 gobject.TYPE_STRING # enumerations.REPOSITORY_COLUMN
979 )
980
981 @staticmethod
982 def __get_new_category_liststore():
983 return gtk.ListStore(
984 gobject.TYPE_INT, # enumerations.CATEGORY_ID
985 gobject.TYPE_STRING, # enumerations.CATEGORY_NAME
986 gobject.TYPE_STRING, # enumerations.CATEGORY_DESCRIPTION
987 gtk.gdk.Pixbuf, # enumerations.CATEGORY_ICON
988 gobject.TYPE_BOOLEAN, # enumerations.CATEGORY_ICON_VISIBLE
989 gobject.TYPE_BOOLEAN, # enumerations.CATEGORY_VISIBLE
990 gobject.TYPE_PYOBJECT, # enumerations.SECTION_LIST_OBJECT
991 )
992
993 @staticmethod
994 def __get_new_section_liststore():
995 return gtk.ListStore(
996 gobject.TYPE_INT, # enumerations.SECTION_ID
997 gobject.TYPE_STRING, # enumerations.SECTION_NAME
998 gobject.TYPE_STRING, # enumerations.SECTION_SUBCATEGORY
999 gobject.TYPE_BOOLEAN, # enumerations.SECTION_ENABLED
1000 )
1001
1002 @staticmethod
1003 def __get_new_filter_liststore():
1004 return gtk.ListStore(
1005 gobject.TYPE_INT, # enumerations.FILTER_ID
1006 gobject.TYPE_STRING, # enumerations.FILTER_NAME
1007 )
1008
1009 @staticmethod
1010 def __get_new_repositories_liststore():
1011 return gtk.ListStore(
1012 gobject.TYPE_INT, # enumerations.REPOSITORY_ID
1013 gobject.TYPE_STRING, # enumerations.REPOSITORY_NAME
1014 )
1015
1016 def __init_application_tree_view(self, application_list,
1017 application_list_filter, application_list_sort,
1018 application_sort_column):
1019 ##APPLICATION MAIN TREEVIEW
1020 if application_list_filter == None:
1021 application_list_filter = application_list.filter_new()
1022 if application_list_sort == None:
1023 application_list_sort = \
1024 gtk.TreeModelSort(application_list_filter)
1025 application_list_sort.set_sort_column_id(
1026 application_sort_column, gtk.SORT_ASCENDING)
1027 application_list_sort.set_sort_func(
1028 enumerations.STATUS_ICON_COLUMN, self.__status_sort_func)
1029 toggle_renderer = gtk.CellRendererToggle()
1030
1031 column = gtk.TreeViewColumn("", toggle_renderer, \
1032 active = enumerations.MARK_COLUMN)
1033 column.set_sort_column_id(enumerations.MARK_COLUMN)
1034 column.set_sort_indicator(True)
1035 column.set_cell_data_func(toggle_renderer, self.cell_data_function, None)
1036 column.connect_after('clicked',
1037 self.__application_treeview_column_sorted, None)
1038 self.w_application_treeview.append_column(column)
1039 name_renderer = gtk.CellRendererText()
1040 column = gtk.TreeViewColumn(_("Name"), name_renderer,
1041 text = enumerations.NAME_COLUMN)
1042 column.set_resizable(True)
1043 column.set_min_width(150)
1044 column.set_sort_column_id(enumerations.NAME_COLUMN)
1045 column.set_sort_indicator(True)
1046 column.set_cell_data_func(name_renderer, self.cell_data_function, None)
1047 column.connect_after('clicked',
1048 self.__application_treeview_column_sorted, None)
1049 self.w_application_treeview.append_column(column)
1050 column = self.__create_icon_column(_("Status"), True,
1051 enumerations.STATUS_ICON_COLUMN, True)
1052 column.set_sort_column_id(enumerations.STATUS_ICON_COLUMN)
1053 column.set_sort_indicator(True)
1054 column.connect_after('clicked',
1055 self.__application_treeview_column_sorted, None)
1056 self.w_application_treeview.append_column(column)
1057 if self.is_search_all:
1058 repository_renderer = gtk.CellRendererText()
1059 column = gtk.TreeViewColumn(_('Repository'),
1060 repository_renderer,
1061 text = enumerations.AUTHORITY_COLUMN)
1062 column.set_sort_column_id(enumerations.AUTHORITY_COLUMN)
1063 column.set_resizable(True)
1064 column.set_sort_indicator(True)
1065 column.set_cell_data_func(repository_renderer,
1066 self.cell_data_function, None)
1067 column.connect_after('clicked',
1068 self.__application_treeview_column_sorted, None)
1069 self.w_application_treeview.append_column(column)
1070 description_renderer = gtk.CellRendererText()
1071 column = gtk.TreeViewColumn(_('Description'),
1072 description_renderer,
1073 text = enumerations.DESCRIPTION_COLUMN)
1074 column.set_sort_column_id(enumerations.DESCRIPTION_COLUMN)
1075 column.set_resizable(True)
1076 column.set_sort_indicator(True)
1077 column.set_cell_data_func(description_renderer,
1078 self.cell_data_function, None)
1079 column.connect_after('clicked',
1080 self.__application_treeview_column_sorted, None)
1081 self.w_application_treeview.append_column(column)
1082 #Added selection listener
1083 self.package_selection = self.w_application_treeview.get_selection()
1084 self.application_list = application_list
1085 self.application_list_filter = application_list_filter
1086 self.application_list_sort = application_list_sort
1087 toggle_renderer.connect('toggled', self.__active_pane_toggle,
1088 application_list_sort)
1089
1090 def __init_tree_views(self, application_list, category_list,
1091 section_list, application_list_filter = None,
1092 application_list_sort = None,
1093 application_sort_column = enumerations.NAME_COLUMN):
1094 '''This function connects treeviews with their models and also applies
1095 filters'''
1096 if category_list == None:
1097 self.w_application_treeview.set_model(None)
1098 self.__remove_treeview_columns(self.w_application_treeview)
1099 elif application_list == None:
1100 self.w_categories_treeview.set_model(None)
1101 self.__remove_treeview_columns(self.w_categories_treeview)
1102 else:
1103 self.__disconnect_models()
1104 self.__remove_treeview_columns(self.w_application_treeview)
1105 self.__remove_treeview_columns(self.w_categories_treeview)
1106 # The logic for set section needs to be here as some sections
1107 # might be not enabled. In such situation we are setting the set
1108 # section to "All Categories" one.
1109 if section_list != None:
1110 row = section_list[self.set_section]
1111 if row[enumerations.SECTION_ENABLED] and \
1112 self.set_section >= 0 and \
1113 self.set_section < len(section_list):
1114 if row[enumerations.SECTION_ID] != self.set_section:
1115 self.set_section = 0
1116 else:
1117 self.set_section = 0
1118
1119 if application_list != None:
1120 self.__init_application_tree_view(application_list,
1121 application_list_filter, application_list_sort,
1122 application_sort_column)
1123
1124 if self.first_run:
1125 # When vadj changes we need to set image descriptions
1126 # on visible status icons. This catches moving the scroll bars
1127 # and scrolling up and down using keyboard.
1128 vadj = self.w_application_treeview.get_vadjustment()
1129 vadj.connect('value-changed',
1130 self.__application_treeview_vadjustment_changed, None)
1131 vadj = self.w_categories_treeview.get_vadjustment()
1132 vadj.connect('value-changed',
1133 self.__categories_treeview_vadjustment_changed, None)
1134
1135 # When the size of the application_treeview changes
1136 # we need to set image descriptions on visible status icons.
1137 self.w_application_treeview.connect('size-allocate',
1138 self.__application_treeview_size_allocate, None)
1139 self.w_categories_treeview.connect('size-allocate',
1140 self.__categories_treeview_size_allocate, None)
1141
1142 if category_list != None:
1143 ##CATEGORIES TREEVIEW
1144 #enumerations.CATEGORY_NAME
1145 category_list_filter = category_list.filter_new()
1146 column = self.__create_icon_column("", False,
1147 enumerations.CATEGORY_ICON, False)
1148 self.w_categories_treeview.append_column(column)
1149 enumerations.CATEGORY_NAME_renderer = gtk.CellRendererText()
1150 column = gtk.TreeViewColumn(_('Name'),
1151 enumerations.CATEGORY_NAME_renderer,
1152 text = enumerations.CATEGORY_NAME)
1153 self.w_categories_treeview.append_column(column)
1154 #Added selection listener
1155 category_selection = self.w_categories_treeview.get_selection()
1156 category_selection.set_mode(gtk.SELECTION_SINGLE)
1157
1158 if self.first_run:
1159 ##SECTION COMBOBOX
1160 #enumerations.SECTION_NAME
1161 cell = gtk.CellRendererText()
1162 self.w_sections_combobox.pack_start(cell, True)
1163 self.w_sections_combobox.add_attribute(cell, 'text',
1164 enumerations.SECTION_NAME)
1165 self.w_sections_combobox.set_row_separator_func(
1166 self.combobox_id_separator)
1167 self.w_sections_combobox.add_attribute( cell,
1168 'sensitive', enumerations.SECTION_ENABLED )
1169 ##FILTER COMBOBOX
1170 #enumerations.FILTER_NAME
1171 cell = gtk.CellRendererText()
1172 self.w_filter_combobox.pack_start(cell, True)
1173 self.w_filter_combobox.add_attribute(cell, 'text',
1174 enumerations.FILTER_NAME)
1175 self.w_filter_combobox.set_row_separator_func(
1176 self.combobox_id_separator)
1177
1178 if section_list != None:
1179 self.section_list = section_list
1180 if category_list != None:
1181 self.category_list = category_list
1182 self.category_list_filter = category_list_filter
1183 self.w_categories_treeview.set_model(category_list_filter)
1184 if not self.is_search_all:
1185 category_list_filter.set_visible_func(
1186 self.category_filter)
1187 self.__set_categories_visibility(self.set_section)
1188 self.a11y_categories_treeview = \
1189 self.w_categories_treeview.get_accessible()
1190 if application_list != None:
1191 if category_list != None:
1192 self.w_sections_combobox.set_model(section_list)
1193 self.w_sections_combobox.set_active(self.set_section)
1194 self.w_filter_combobox.set_model(self.filter_list)
1195 self.w_filter_combobox.set_active(self.set_show_filter)
1196 self.w_application_treeview.set_model(
1197 self.application_list_sort)
1198 if not self.in_search_mode:
1199 if application_list_filter == None:
1200 self.application_list_filter.set_visible_func(
1201 self.__application_filter)
1202
1203 category_selection = self.w_categories_treeview.get_selection()
1204 category_model, category_iter = category_selection.get_selected()
1205 self.pylintstub = category_model
1206 if not category_iter and not self.in_search_mode:
1207 #no category was selected, so select "All"
1208 category_selection.select_path(0)
1209 category_model, category_iter = category_selection.get_selected()
1210 if self.first_run:
1211 category_selection.connect("changed",
1212 self.__on_category_selection_changed, None)
1213 self.w_categories_treeview.connect("row-activated",
1214 self.__on_category_row_activated, None)
1215 self.w_categories_treeview.connect("focus-in-event",
1216 self.__on_category_focus_in, None)
1217 self.package_selection.set_mode(gtk.SELECTION_SINGLE)
1218 self.package_selection.connect("changed",
1219 self.__on_package_selection_changed, None)
1220
1221 self.a11y_application_treeview = \
1222 self.w_application_treeview.get_accessible()
1223 self.process_package_list_end()
1224
1225 def __categories_treeview_size_allocate(self, widget, allocation, user_data):
1226 # We ignore any changes in the size during initialization.
1227 if self.categories_treeview_initialized:
1228 if self.categories_status_id == 0:
1229 self.categories_status_id = gobject.idle_add(
1230 self.__set_accessible_categories_visible_status)
1231
1232 def __categories_treeview_vadjustment_changed(self, widget, user_data):
1233 self.__set_accessible_categories_visible_status()
1234
1235 def __set_accessible_categories_status(self, model, itr):
1236 status = model.get_value(itr, enumerations.CATEGORY_ICON)
1237 if status != None:
1238 desc = _("Updates Available")
1239 else:
1240 desc = None
1241 if desc != None:
1242 obj = self.a11y_categories_treeview.ref_at(
1243 int(model.get_string_from_iter(itr)),
1244 CATEGORIES_STATUS_COLUMN_INDEX)
1245 obj.set_image_description(desc)
1246
1247 def __set_accessible_categories_visible_status(self):
1248 self.categories_status_id = 0
1249 if self.a11y_categories_treeview.get_n_accessible_children() == 0:
1250 # accessibility is not enabled
1251 return
1252
1253 visible_range = self.w_categories_treeview.get_visible_range()
1254 if visible_range == None:
1255 return
1256 start = visible_range[0][0]
1257 end = visible_range[1][0]
1258 # We try to minimize the range of accessible objects
1259 # on which we set image descriptions
1260 if self.categories_treeview_range != None:
1261 old_start = self.categories_treeview_range[0][0]
1262 old_end = self.categories_treeview_range[1][0]
1263 # Old range is the same or smaller than new range
1264 # so do nothing
1265 if start >= old_start and end <= old_end:
1266 return
1267 if start < old_end:
1268 if end < old_end:
1269 if end >= old_start:
1270 end = old_start
1271 else:
1272 start = old_end
1273 self.categories_treeview_range = visible_range
1274 model = self.category_list_filter
1275 itr = model.get_iter_from_string(str(start))
1276 while start <= end:
1277 start += 1
1278 self.__set_accessible_categories_status(model, itr)
1279 itr = model.iter_next(itr)
1280
1281 def __application_treeview_column_sorted(self, widget, user_data):
1282 self.__set_visible_status(False)
1283
1284 def __init_repository_tree_view(self):
1285 cell = gtk.CellRendererText()
1286 self.w_repository_combobox.pack_start(cell, True)
1287 self.w_repository_combobox.add_attribute(cell, 'text',
1288 enumerations.REPOSITORY_NAME)
1289 self.w_repository_combobox.set_row_separator_func(
1290 self.combobox_id_separator)
1291
1292 def __application_treeview_size_allocate(self, widget, allocation, user_data):
1293 # We ignore any changes in the size during initialization.
1294 if self.visible_status_id == 0:
1295 self.visible_status_id = gobject.idle_add(
1296 self.__set_visible_status)
1297
1298 def __application_treeview_vadjustment_changed(self, widget, user_data):
1299 self.__set_visible_status()
1300
1301 def __set_accessible_status(self, model, itr):
1302 status = model.get_value(itr, enumerations.STATUS_COLUMN)
1303 if status == enumerations.INSTALLED:
1304 desc = _("Installed")
1305 elif status == enumerations.NOT_INSTALLED:
1306 desc = _("Not Installed")
1307 elif status == enumerations.UPDATABLE:
1308 desc = _("Updates Available")
1309 else:
1310 desc = None
1311 if desc != None:
1312 obj = self.a11y_application_treeview.ref_at(
1313 int(model.get_string_from_iter(itr)),
1314 STATUS_COLUMN_INDEX)
1315 obj.set_image_description(desc)
1316
1317 def __set_visible_status(self, check_range = True):
1318 self.visible_status_id = 0
1319 if self.w_main_view_notebook.get_current_page() != \
1320 NOTEBOOK_PACKAGE_LIST_PAGE:
1321 return
1322 if self.__doing_search():
1323 return
1324
1325 a11y_enabled = False
1326 if self.a11y_application_treeview.get_n_accessible_children() != 0:
1327 a11y_enabled = True
1328
1329 visible_range = self.w_application_treeview.get_visible_range()
1330 if visible_range == None:
1331 return
1332 start = visible_range[0][0]
1333 end = visible_range[1][0]
1334 if debug_descriptions:
1335 print "Range Start: %d End: %d" % (start, end)
1336
1337 # Switching Publishers need to use default range
1338 active_pub = self.__get_active_publisher()
1339 if self.last_active_publisher != active_pub:
1340 check_range = False
1341 self.last_active_publisher = active_pub
1342 if self.in_search_mode:
1343 check_range = False
1344
1345 if self.application_treeview_range != None:
1346 if check_range:
1347 old_start = self.application_treeview_range[0][0]
1348 old_end = self.application_treeview_range[1][0]
1349 # Old range is the same or smaller than new range
1350 # so do nothing
1351 if start >= old_start and end <= old_end:
1352 return
1353 if start < old_end:
1354 if end < old_end:
1355 if end >= old_start:
1356 end = old_start
1357 else:
1358 start = old_end
1359 if debug_descriptions:
1360 print "Adjusted Range Start: %d End: %d" % (start, end)
1361 self.application_treeview_range = visible_range
1362
1363 sort_filt_model = \
1364 self.w_application_treeview.get_model() #gtk.TreeModelSort
1365 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1366 model = filt_model.get_model() #gtk.ListStore
1367 sf_itr = sort_filt_model.get_iter_from_string(str(start))
1368 pkg_stems_and_itr_to_fetch = {}
1369 while start <= end:
1370 filtered_itr = sort_filt_model.convert_iter_to_child_iter(None,
1371 sf_itr)
1372 app_itr = filt_model.convert_iter_to_child_iter(filtered_itr)
1373
1374 desc = sort_filt_model.get_value(sf_itr,
1375 enumerations.DESCRIPTION_COLUMN)
1376 # Only Fetch description for packages without a
1377 # description
1378 if desc == '...':
1379 fmri = sort_filt_model.get_value(sf_itr,
1380 enumerations.FMRI_COLUMN)
1381 if fmri != None:
1382 pkg_stem = fmri.get_pkg_stem(
1383 include_scheme = True)
1384 pkg_stems_and_itr_to_fetch[pkg_stem] = \
1385 model.get_string_from_iter(app_itr)
1386 if a11y_enabled:
1387 self.__set_accessible_status(sort_filt_model, sf_itr)
1388 start += 1
1389 sf_itr = sort_filt_model.iter_next(sf_itr)
1390
1391 if debug_descriptions:
1392 print "PKGS to FETCH: \n%s" % pkg_stems_and_itr_to_fetch
1393 if len(pkg_stems_and_itr_to_fetch) > 0:
1394 Thread(target = self.__get_pkg_descriptions,
1395 args = [pkg_stems_and_itr_to_fetch, model]).start()
1396
1397 def __doing_search(self):
1398 return self.search_start > 0
1399
1400 def __get_pkg_descriptions(self, pkg_stems_and_itr_to_fetch, orig_model):
1401 # Note: no need to aquire lock even though this can be called from
1402 # multiple threads, it is just creating an update job and dispatching it
1403 # to the idle handler, not modifying any global state
1404 info = None
1405 if not self.__doing_search():
1406 gobject.idle_add(self.__update_statusbar_message,
1407 _("Fetching descriptions..."))
1408 try:
1409 info = self.api_o.info(pkg_stems_and_itr_to_fetch.keys(), False,
1410 frozenset([api.PackageInfo.IDENTITY,
1411 api.PackageInfo.SUMMARY]))
1412 except api_errors.TransportError:
1413 self.update_statusbar()
1414 return
1415 if info and len(info.get(0)) == 0:
1416 self.update_statusbar()
1417 return
1418 pkg_infos = info.get(0)
1419 pkg_descriptions_for_update = []
1420 for pkg_info in pkg_infos:
1421 short_fmri = fmri.PkgFmri(pkg_info.fmri).get_pkg_stem(
1422 include_scheme = True)
1423 pkg_descriptions_for_update.append((short_fmri,
1424 pkg_stems_and_itr_to_fetch[short_fmri],
1425 pkg_info.summary))
1426 if debug_descriptions:
1427 print "FETCHED PKGS: \n%s" % pkg_descriptions_for_update
1428 gobject.idle_add(self.__update_description_from_iter,
1429 pkg_descriptions_for_update, orig_model)
1430
1431 def __update_description_from_iter(self, pkg_descriptions_for_update, orig_model):
1432 #If doing a search abandon description updates
1433 if self.__doing_search():
1434 return
1435
1436 sort_filt_model = \
1437 self.w_application_treeview.get_model() #gtk.TreeModelSort
1438 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1439 model = filt_model.get_model() #gtk.ListStore
1440
1441 #If model has changed abandon description updates
1442 if orig_model != model:
1443 return
1444
1445 if debug_descriptions:
1446 print "UPDATE DESCRIPTIONS: \n%s" % pkg_descriptions_for_update
1447 for pkg_stem, path, summary in pkg_descriptions_for_update:
1448 itr = model.get_iter_from_string(path)
1449 stored_pkg_fmri = model.get_value(itr, enumerations.FMRI_COLUMN)
1450 stored_pkg_stem = stored_pkg_fmri.get_pkg_stem(
1451 include_scheme = True)
1452
1453 if pkg_stem != stored_pkg_stem:
1454 if debug:
1455 print ("__update_description_from_iter(): "
1456 "model not consistent so abandoning "
1457 "these description updates.")
1458 self.update_statusbar()
1459 return
1460 model.set_value(itr, enumerations.DESCRIPTION_COLUMN, summary)
1461 if not self.__doing_search():
1462 self.update_statusbar()
1463
1464 def __create_icon_column(self, name, expand_pixbuf, enum_value, set_data_func):
1465 column = gtk.TreeViewColumn()
1466 column.set_title(name)
1467 #Commented, since there was funny jumping of the icons
1468 #column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
1469 render_pixbuf = gtk.CellRendererPixbuf()
1470 column.pack_start(render_pixbuf, expand = expand_pixbuf)
1471 column.add_attribute(render_pixbuf, "pixbuf", enum_value)
1472 column.set_fixed_width(32)
1473 if set_data_func:
1474 column.set_cell_data_func(render_pixbuf,
1475 self.cell_data_function, None)
1476 return column
1477
1478 def __disconnect_models(self):
1479 self.w_application_treeview.set_model(None)
1480 self.w_categories_treeview.set_model(None)
1481 self.w_sections_combobox.set_model(None)
1482 self.w_filter_combobox.set_model(None)
1483
1484 def __disconnect_repository_model(self):
1485 self.w_repository_combobox.set_model(None)
1486
1487 @staticmethod
1488 def __status_sort_func(treemodel, iter1, iter2, user_data=None):
1489 get_val = treemodel.get_value
1490 status1 = get_val(iter1, enumerations.STATUS_COLUMN)
1491 status2 = get_val(iter2, enumerations.STATUS_COLUMN)
1492 return cmp(status1, status2)
1493
1494 @staticmethod
1495 def __remove_treeview_columns(treeview):
1496 columns = treeview.get_columns()
1497 if columns:
1498 for column in columns:
1499 treeview.remove_column(column)
1500
1501 @staticmethod
1502 def __init_sections(section_list):
1503 '''This function is for initializing sections combo box, also adds "All"
1504 Category. It sets active section combobox entry "All"'''
1505 cat_path = None
1506 enabled = True
1507 # We enable only first section and later we might enable the rest,
1508 # depending if there are some packages connected with them
1509 section_list.append([0, _('All Categories'), cat_path, enabled ])
1510 section_list.append([-1, "", cat_path, enabled ])
1511 enabled = False
1512 section_list.append([2, _('Meta Packages'), cat_path, enabled ])
1513 section_list.append([3, _('Applications'), cat_path, enabled ])
1514 section_list.append([4, _('Desktop (GNOME)'), cat_path, enabled ])
1515 section_list.append([5, _('Development'), cat_path, enabled ])
1516 section_list.append([6, _('Distributions'), cat_path, enabled ])
1517 section_list.append([7, _('Drivers'), cat_path, enabled ])
1518 section_list.append([8, _('System'), cat_path, enabled ])
1519 section_list.append([9, _('Web Services'), cat_path, enabled ])
1520
1521 def __init_show_filter(self):
1522 self.filter_list.append([enumerations.FILTER_ALL, _('All Packages'), ])
1523 self.filter_list.append([enumerations.FILTER_INSTALLED,
1524 _('Installed Packages'), ])
1525 self.filter_list.append([enumerations.FILTER_UPDATES,
1526 _('Updates'), ])
1527 self.filter_list.append([enumerations.FILTER_NOT_INSTALLED,
1528 _('Non-installed Packages'), ])
1529 self.filter_list.append([-1, "", ])
1530 self.filter_list.append([enumerations.FILTER_SELECTED,
1531 _('Selected Packages'), ])
1532 if self.initial_show_filter >= enumerations.FILTER_ALL and \
1533 self.initial_show_filter < len(self.filter_list):
1534 row = self.filter_list[self.initial_show_filter]
1535 if row[enumerations.SECTION_ID] != self.initial_show_filter:
1536 self.initial_show_filter = enumerations.FILTER_ALL
1537 else:
1538 self.initial_show_filter = enumerations.FILTER_ALL
1539
1540
1541 def __on_cancel_progressdialog_clicked(self, widget):
1542 self.progress_canceled = True
1543 self.progress_stop_timer_thread = True
1544
1545 def __on_mainwindow_delete_event(self, widget, event):
1546 ''' handler for delete event of the main window '''
1547 if self.__check_if_something_was_changed() == True:
1548 # XXX Change this to not quit and show dialog
1549 # XXX if some changes were applied:
1550 self.__main_application_quit()
1551 return True
1552 else:
1553 self.__main_application_quit()
1554
1555 def __on_api_search_error_delete_event(self, widget, event):
1556 self.__on_api_search_button_clicked(None)
1557
1558 def __on_api_search_button_clicked(self, widget):
1559 self.api_search_error_dialog.hide()
1560
1561 def __on_file_quit_activate(self, widget):
1562 ''' handler for quit menu event '''
1563 self.__on_mainwindow_delete_event(None, None)
1564
1565 def __on_ua_completed_close(self, widget):
1566 self.w_ua_completed_dialog.hide()
1567 self.__on_mainwindow_delete_event(None, None)
1568
1569 def __on_edit_repositories_activate(self, widget):
1570 ''' handler for repository menu event '''
1571 repository.Repository(self)
1572
1573 def __on_file_be_activate(self, widget):
1574 ''' handler for be menu event '''
1575 beadm.Beadmin(self)
1576
1577 def __on_searchentry_changed(self, widget):
1578 if widget.get_text_length() > 0:
1579 self.w_clear_search_button.set_sensitive(True)
1580 else:
1581 self.w_clear_search_button.set_sensitive(False)
1582 self.__enable_disable_entry_selection(widget)
1583 if self.is_search_all and not self.changing_search_option:
1584 if self.w_searchentry.get_text() == "":
1585 self.w_infosearch_frame.hide()
1586 self.__link_load_blank()
1587 self.w_main_view_notebook.set_current_page(
1588 NOTEBOOK_START_PAGE)
1589 self.__update_statusbar_for_search()
1590 self.w_searchentry.grab_focus()
1591
1592 def __update_statusbar_for_search(self):
1593 self.__update_statusbar_message(
1594 self.search_options[self.current_search_option][3])
1595
1596 def __update_statusbar_message(self, message):
1597 if self.statusbar_message_id > 0:
1598 self.w_main_statusbar.remove(0, self.statusbar_message_id)
1599 self.statusbar_message_id = 0
1600 self.statusbar_message_id = self.w_main_statusbar.push(0, message)
1601
1602 def __setup_before_search_all_mode(self):
1603 self.is_search_all = True
1604 self.w_infosearch_frame.hide()
1605
1606 self.__save_setup_before_search()
1607 self.w_repository_combobox.set_active(0)
1608 self.__link_load_blank()
1609 self.w_main_view_notebook.set_current_page(
1610 NOTEBOOK_START_PAGE)
1611 self.__update_statusbar_for_search()
1612 self.w_searchentry.grab_focus()
1613 if len(self.w_searchentry.get_text()) > 0:
1614 start, end = self.w_searchentry.get_selection_bounds()
1615 self.w_searchentry.select_region(end, end)
1616 self.__unselect_category()
1617
1618 def __clear_before_search(self):
1619 self.in_setup = True
1620 application_list = self.__get_new_application_liststore()
1621 self.__set_empty_details_panel()
1622 self.__set_main_view_package_list()
1623 self.__init_tree_views(application_list, None, None)
1624 self.__unselect_category()
1625
1626 def __restore_setup_for_browse(self):
1627 self.in_search_mode = False
1628 self.is_search_all = False
1629 self.w_infosearch_frame.hide()
1630
1631 self.set_busy_cursor()
1632 self.w_repository_combobox.set_active(
1633 self.saved_repository_combobox_active)
1634 self.set_section = self.saved_sections_combobox_active
1635 self.set_show_filter = self.saved_filter_combobox_active
1636 if self.saved_category_list == self.category_list:
1637 self.__init_tree_views(self.saved_application_list,
1638 None, None,
1639 self.saved_application_list_filter,
1640 self.saved_application_list_sort)
1641 else:
1642 self.__init_tree_views(self.saved_application_list,
1643 self.saved_category_list, self.saved_section_list,
1644 self.saved_application_list_filter,
1645 self.saved_application_list_sort)
1646
1647 self.__set_main_view_package_list()
1648
1649 def __save_setup_before_search(self, single_search=False):
1650 #Do not save search data models
1651 if self.in_search_mode:
1652 return
1653 self.saved_sections_combobox_active = \
1654 self.w_sections_combobox.get_active()
1655 self.saved_filter_combobox_active = \
1656 self.w_filter_combobox.get_active()
1657 self.saved_application_list = self.application_list
1658 self.saved_application_list_sort = \
1659 self.application_list_sort
1660 self.saved_application_list_filter = \
1661 self.application_list_filter
1662 self.saved_category_list = self.category_list
1663 self.saved_section_list = self.section_list
1664 if single_search:
1665 self.saved_repository_combobox_active = \
1666 self.w_repository_combobox.get_active()
1667 self.w_filter_combobox.set_active(0)
1668
1669 def __do_search(self):
1670 self.search_start = 0
1671 if self.changing_search_option:
1672 return
1673 active = self.w_filter_combobox.get_active()
1674 if active != enumerations.FILTER_SELECTED:
1675 self.saved_filter_combobox_active = active
1676 if len(self.w_searchentry.get_text()) == 0:
1677 return
1678 if not self.is_search_all:
1679 self.__save_setup_before_search(single_search=True)
1680 self.__clear_before_search()
1681 self.set_busy_cursor()
1682 self.in_search_mode = True
1683
1684 self.w_infosearch_frame.hide()
1685 self.__update_statusbar_message(_("Searching..."))
1686 if not self.is_search_all:
1687 Thread(target = self.__do_api_search,
1688 args = (self.is_search_all, )).start()
1689 else:
1690 Thread(target = self.__do_api_search,
1691 args = ()).start()
1692
1693 def __unselect_category(self):
1694 selection = self.w_categories_treeview.get_selection()
1695 model, itr = selection.get_selected()
1696 if itr:
1697 cat_path = model.get_string_from_iter(itr)
1698 selected_section = self.w_sections_combobox.get_active()
1699 section_row = self.section_list[selected_section]
1700 section_row[enumerations.SECTION_SUBCATEGORY] = cat_path
1701 selection.unselect_all()
1702
1703 def __process_after_search_failure(self):
1704 self.search_start = 0
1705 self.search_time_sec = 0
1706 self.application_list = []
1707 self.update_statusbar()
1708 self.unset_busy_cursor()
1709 self.in_setup = False
1710
1711 def __get_origin_uri(self, repo):
1712 if repo == None:
1713 return None
1714 origin_uri = repo.origins[0]
1715 ret_uri = None
1716 if isinstance(origin_uri, str):
1717 if len(origin_uri) > 0:
1718 ret_uri = origin_uri.strip("/")
1719 elif isinstance(origin_uri, publisher.RepositoryURI):
1720 uri = origin_uri.uri
1721 if uri != None and len(uri) > 0:
1722 ret_uri = uri.strip("/")
1723 return ret_uri
1724
1725
1726 def __do_api_search(self, search_all = True):
1727 self.search_start = time.time()
1728 self.search_time_sec = 0
1729 text = self.w_searchentry.get_text()
1730 # Here we call the search API to get the results
1731 searches = []
1732 servers = []
1733 result = []
1734 pargs = []
1735 search_str = SEARCH_STR_FORMAT % text
1736 pargs.append(search_str)
1737 if search_all:
1738 servers = None
1739 else:
1740 pub_prefix = self.__get_active_publisher()
1741 if pub_prefix != None:
1742 pub = self.api_o.get_publisher(prefix=pub_prefix)
1743 else:
1744 pub = self.api_o.get_preferred_publisher()
1745 origin_uri = self.__get_origin_uri(pub.selected_repository)
1746 servers.append({"origin": origin_uri})
1747 if debug:
1748 print "Search: pargs %s servers: %s" % (pargs, servers)
1749
1750 #TBD If we ever search just Installed pkgs should allow for a local search
1751 case_sensitive = False
1752 return_actions = True
1753 searches.append(self.api_o.remote_search(
1754 [api.Query(" ".join(pargs), case_sensitive, return_actions)],
1755 servers=servers))
1756 if debug:
1757 print "Search Args: %s : cs: %s : retact: %s" % \
1758 ("".join(pargs), case_sensitive, return_actions)
1759
1760 last_name = ""
1761 self.current_search_publisher = None
1762
1763 # Sorting results by Name gives best overall appearance and flow
1764 sort_col = enumerations.NAME_COLUMN
1765 try:
1766 for query_num, publisher, (v, return_type, tmp) in \
1767 itertools.chain(*searches):
1768 if v < 1 or return_type != api.Query.RETURN_PACKAGES:
1769 gobject.idle_add(self.w_progress_dialog.hide)
1770 self.__process_after_search_failure()
1771 return
1772
1773 pub = None
1774 if publisher is not None \
1775 and "prefix" in publisher:
1776 pub = publisher["prefix"]
1777 name = fmri.PkgFmri(str(tmp)).get_name()
1778 if last_name != name:
1779 if debug:
1780 print "Result Name: %s (%s)" % (name, pub)
1781 a_res = name, pub
1782 result.append(a_res)
1783 #Ignore Status when fetching
1784 application_list = \
1785 self.__get_min_list_from_search(result)
1786 self.current_search_publisher = pub
1787 self.in_setup = True
1788 gobject.idle_add(self.__init_tree_views,
1789 application_list, None, None, None, None,
1790 sort_col)
1791 last_name = name
1792 self.pylintstub = query_num
1793 except api_errors.ProblematicSearchServers, ex:
1794 self.__process_api_search_error(ex)
1795 gobject.idle_add(self.w_progress_dialog.hide)
1796 gobject.idle_add(self.__handle_api_search_error)
1797 if len(result) == 0:
1798 self.__process_after_search_with_zero_results()
1799 return
1800 except Exception, ex:
1801 # We are not interested in this error
1802 gobject.idle_add(self.w_progress_dialog.hide)
1803 self.__process_after_search_failure()
1804 return
1805 if debug:
1806 print "Number of search results:", len(result)
1807 if len(result) == 0:
1808 if debug:
1809 print "No search results"
1810 self.__process_after_search_with_zero_results()
1811 return
1812 # We cannot get status of the packages if catalogs have not
1813 # been loaded so we pause for up to 5 seconds here to
1814 # allow catalogs to be loaded
1815 times = 5
1816 while self.catalog_loaded == False:
1817 if times == 0:
1818 break
1819 time.sleep(1)
1820 times -= 1
1821
1822 #Now fetch full result set with Status
1823 self.in_setup = True
1824 application_list = self.__get_full_list_from_search(result)
1825 gobject.idle_add(self.__init_tree_views, application_list, None, None, \
1826 None, None, sort_col)
1827
1828 if self.search_start > 0:
1829 self.search_time_sec = int(time.time() - self.search_start)
1830 if debug:
1831 print "Search time: %d (sec)" % self.search_time_sec
1832 self.search_start = 0
1833
1834 def __process_after_search_with_zero_results(self):
1835 if self.search_start > 0:
1836 self.search_time_sec = int(time.time() - self.search_start)
1837 self.search_start = 0
1838 self.in_setup = True
1839 application_list = self.__get_new_application_liststore()
1840 gobject.idle_add(self.__set_empty_details_panel)
1841 gobject.idle_add(self.__set_main_view_package_list)
1842 gobject.idle_add(self.__init_tree_views, application_list, None, None)
1843
1844 def __get_min_list_from_search(self, search_result):
1845 application_list = self.__get_new_application_liststore()
1846 for name, publisher in search_result:
1847 application_list.append(
1848 [False, None, name, '...', enumerations.NOT_INSTALLED, None,
1849 "pkg://" + publisher + "/" + name, None, True, None,
1850 publisher])
1851 return application_list
1852
1853 def __get_full_list_from_search(self, search_result):
1854 application_list = self.__get_new_application_liststore()
1855 self.__add_pkgs_to_list_from_search(search_result,
1856 application_list)
1857 return application_list
1858
1859 def __add_pkgs_to_list_from_search(self, search_result,
1860 application_list):
1861 pargs = []
1862 default_pub = self.api_o.get_preferred_publisher().prefix
1863 for name, publisher in search_result:
1864 pargs.append("pkg://" + publisher + "/" + name)
1865 # We now need to get the status for each package
1866 if debug_descriptions:
1867 print "pargs:", pargs
1868 try:
1869 pkgs_known = self.__get_inventory_list(pargs,
1870 True, True)
1871 except api_errors.InventoryException:
1872 # This can happen if load_catalogs has not been run
1873 err = _("Unable to get status for search results.\n"
1874 "The catalogs have not been loaded.\n"
1875 "Please try after few seconds.\n")
1876 gobject.idle_add(self.w_progress_dialog.hide)
1877 gobject.idle_add(self.error_occurred, err)
1878 return
1879 return self.__add_pkgs_to_lists(pkgs_known, application_list,
1880 None, None)
1881
1882 def __application_refilter(self):
1883 ''' Disconnecting the model from the treeview improves
1884 performance when assistive technologies are enabled'''
1885 if self.in_setup:
1886 return
1887 self.application_refilter_id = 0
1888 self.application_refilter_idle_id = 0
1889 if not self.in_search_mode:
1890 model = self.w_application_treeview.get_model()
1891 self.w_application_treeview.set_model(None)
1892 self.application_list_filter.refilter()
1893 self.w_application_treeview.set_model(model)
1894 gobject.idle_add(self.__set_empty_details_panel)
1895 gobject.idle_add(self.__enable_disable_selection_menus)
1896 gobject.idle_add(self.__enable_disable_install_remove)
1897 self.application_treeview_initialized = True
1898 self.application_treeview_range = None
1899 if self.visible_status_id == 0:
1900 self.visible_status_id = gobject.idle_add(
1901 self.__set_visible_status)
1902 self.categories_treeview_initialized = True
1903 self.categories_treeview_range = None
1904 if self.categories_status_id == 0:
1905 self.categories_status_id = gobject.idle_add(
1906 self.__set_accessible_categories_visible_status)
1907 return False
1908
1909 def __on_edit_paste(self, widget):
1910 self.w_searchentry.paste_clipboard()
1911
1912 def __on_clear_paste(self, widget):
1913 bounds = self.w_searchentry.get_selection_bounds()
1914 self.w_searchentry.delete_text(bounds[0], bounds[1])
1915 return
1916
1917 def __on_copy(self, widget):
1918 focus_widget = self.w_main_window.get_focus()
1919 if focus_widget == self.w_searchentry:
1920 self.w_searchentry.copy_clipboard()
1921 self.w_paste_menuitem.set_sensitive(True)
1922 elif self.__is_a_textview(focus_widget):
1923 focus_widget.get_buffer().copy_clipboard(
1924 self.w_main_clipboard)
1925
1926 def __on_cut(self, widget):
1927 self.w_searchentry.cut_clipboard()
1928 self.w_paste_menuitem.set_sensitive(True)
1929
1930 def __popup_position_func(self, menu):
1931 ''' Position popup menu immediately below search button'''
1932 root = self.w_main_window.window.get_origin()
1933 alloc = self.search_button.get_allocation()
1934 return (root[0] + alloc.x, root[1] + alloc.y + alloc.height, False)
1935
1936 def __on_set_search(self, widget, event):
1937 if event.type == gtk.gdk.BUTTON_PRESS:
1938 self.searchmenu.popup(None, None, self.__popup_position_func,
1939 event.button, event.time)
1940 return True
1941 return False
1942
1943 def __on_set_search_clicked(self, widget):
1944 self.searchmenu.popup(None, None, self.__popup_position_func,
1945 0, 0)
1946 return True
1947
1948 def __on_edit_search_clicked(self, widget):
1949 self.w_searchentry.grab_focus()
1950
1951 def __on_clear_search(self, widget):
1952 self.w_searchentry.delete_text(0, -1)
1953 self.__do_search()
1954 return
1955
1956 def __on_startpage(self, widget):
1957 self.__load_startpage()
1958 self.w_main_view_notebook.set_current_page(NOTEBOOK_START_PAGE)
1959
1960 def __on_notebook_change(self, widget, event, pagenum):
1961 if (pagenum == INFO_NOTEBOOK_LICENSE_PAGE and
1962 not self.showing_empty_details):
1963 licbuffer = self.w_license_textview.get_buffer()
1964 leg_txt = _("Fetching legal information...")
1965 licbuffer.set_text(leg_txt)
1966 if self.show_licenses_id != 0:
1967 gobject.source_remove(self.show_licenses_id)
1968 self.show_licenses_id = 0
1969 self.last_show_licenses_id = self.show_licenses_id = \
1970 gobject.timeout_add(SHOW_LICENSE_DELAY,
1971 self.__show_licenses)
1972
1973 def __is_a_textview(self, widget):
1974 if (widget == self.w_generalinfo_textview or
1975 widget == self.w_installedfiles_textview or
1976 widget == self.w_dependencies_textview or
1977 widget == self.w_license_textview):
1978 return True
1979 else:
1980 return False
1981
1982
1983 def __on_select_all(self, widget):
1984 focus_widget = self.w_main_window.get_focus()
1985 if self.__is_a_textview(focus_widget):
1986 focus_widget.emit('select-all', True)
1987 self.w_selectall_menuitem.set_sensitive(False)
1988 self.w_deselect_menuitem.set_sensitive(True)
1989 return
1990 elif focus_widget == self.w_searchentry:
1991 focus_widget.select_region(0, -1)
1992 self.w_selectall_menuitem.set_sensitive(False)
1993 self.w_deselect_menuitem.set_sensitive(True)
1994 return
1995
1996 sort_filt_model = \
1997 self.w_application_treeview.get_model() #gtk.TreeModelSort
1998 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1999 model = filt_model.get_model() #gtk.ListStore
2000 iter_next = sort_filt_model.get_iter_first()
2001 list_of_paths = []
2002 while iter_next != None:
2003 sorted_path = sort_filt_model.get_path(iter_next)
2004 filtered_path = \
2005 sort_filt_model.convert_path_to_child_path(sorted_path)
2006 path = filt_model.convert_path_to_child_path(filtered_path)
2007 list_of_paths.append(path)
2008 iter_next = sort_filt_model.iter_next(iter_next)
2009 for path in list_of_paths:
2010 itr = model.get_iter(path)
2011 already_marked = model.get_value(itr, enumerations.MARK_COLUMN)
2012 if not already_marked:
2013 model.set_value(itr, enumerations.MARK_COLUMN, True)
2014 pkg_stem = model.get_value(itr,
2015 enumerations.STEM_COLUMN)
2016 pkg_status = model.get_value(itr,
2017 enumerations.STATUS_COLUMN)
2018 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2019 self.w_selectall_menuitem.set_sensitive(False)
2020 self.w_deselect_menuitem.set_sensitive(True)
2021 self.__enable_disable_selection_menus()
2022 self.update_statusbar()
2023 self.__enable_disable_install_remove()
2024
2025 def __on_select_updates(self, widget):
2026 sort_filt_model = \
2027 self.w_application_treeview.get_model() #gtk.TreeModelSort
2028 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
2029 model = filt_model.get_model() #gtk.ListStore
2030 iter_next = sort_filt_model.get_iter_first()
2031 list_of_paths = []
2032 while iter_next != None:
2033 sorted_path = sort_filt_model.get_path(iter_next)
2034 filtered_iter = sort_filt_model.convert_iter_to_child_iter(None, \
2035 iter_next)
2036 app_iter = filt_model.convert_iter_to_child_iter(filtered_iter)
2037
2038 filtered_path = \
2039 sort_filt_model.convert_path_to_child_path(sorted_path)
2040 path = filt_model.convert_path_to_child_path(filtered_path)
2041 if model.get_value(app_iter, \
2042 enumerations.STATUS_COLUMN) == enumerations.UPDATABLE:
2043 list_of_paths.append(path)
2044 iter_next = sort_filt_model.iter_next(iter_next)
2045 for path in list_of_paths:
2046 itr = model.get_iter(path)
2047 model.set_value(itr, enumerations.MARK_COLUMN, True)
2048 pkg_stem = model.get_value(itr, enumerations.STEM_COLUMN)
2049 pkg_status = model.get_value(itr, enumerations.STATUS_COLUMN)
2050 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2051 self.__enable_disable_selection_menus()
2052 self.update_statusbar()
2053 self.__enable_disable_install_remove()
2054
2055 def __on_deselect(self, widget):
2056 focus_widget = self.w_main_window.get_focus()
2057 if self.__is_a_textview(focus_widget):
2058 focus_widget.emit('select-all', False)
2059 self.w_deselect_menuitem.set_sensitive(False)
2060 self.w_selectall_menuitem.set_sensitive(True)
2061 return
2062 elif focus_widget == self.w_searchentry:
2063 focus_widget.select_region(0, 0)
2064 self.w_deselect_menuitem.set_sensitive(False)
2065 self.w_selectall_menuitem.set_sensitive(True)
2066 return
2067
2068 sort_filt_model = \
2069 self.w_application_treeview.get_model() #gtk.TreeModelSort
2070 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
2071 model = filt_model.get_model() #gtk.ListStore
2072 iter_next = sort_filt_model.get_iter_first()
2073 list_of_paths = []
2074 while iter_next != None:
2075 sorted_path = sort_filt_model.get_path(iter_next)
2076 filtered_iter = sort_filt_model.convert_iter_to_child_iter(None, \
2077 iter_next)
2078 app_iter = filt_model.convert_iter_to_child_iter(filtered_iter)
2079 filtered_path = \
2080 sort_filt_model.convert_path_to_child_path(sorted_path)
2081 path = filt_model.convert_path_to_child_path(filtered_path)
2082 if model.get_value(app_iter, enumerations.MARK_COLUMN):
2083 list_of_paths.append(path)
2084 iter_next = sort_filt_model.iter_next(iter_next)
2085 for path in list_of_paths:
2086 itr = model.get_iter(path)
2087 already_deselected = not model.get_value(itr,
2088 enumerations.MARK_COLUMN)
2089 if not already_deselected:
2090 model.set_value(itr, enumerations.MARK_COLUMN, False)
2091 self.__remove_pkg_stem_from_list(model.get_value(itr,
2092 enumerations.STEM_COLUMN))
2093 self.w_selectall_menuitem.set_sensitive(True)
2094 self.w_deselect_menuitem.set_sensitive(False)
2095 self.__enable_disable_selection_menus()
2096 self.update_statusbar()
2097 self.__enable_disable_install_remove()
2098
2099 def __on_preferences(self, widget):
2100 self.w_startpage_checkbutton.set_active(self.show_startpage)
2101 self.w_preferencesdialog.show()
2102
2103 def __on_preferencesclose_clicked(self, widget):
2104 self.w_preferencesdialog.hide()
2105
2106 def __on_preferenceshelp_clicked(self, widget):
2107 gui_misc.display_help(self.application_dir, "pm_win")
2108
2109 def __on_startpage_checkbutton_toggled(self, widget):
2110 self.show_startpage = self.w_startpage_checkbutton.get_active()
2111 try:
2112 self.client.set_bool(SHOW_STARTPAGE_PREFERENCES,
2113 self.show_startpage)
2114 except GError:
2115 pass
2116
2117 def __on_api_search_checkbox_toggled(self, widget):
2118 active = self.api_search_checkbox.get_active()
2119 if len(self.current_repos_with_search_errors) > 0:
2120 if active:
2121 for pub, err_type, err_str in \
2122 self.current_repos_with_search_errors:
2123 if pub not in self.gconf_not_show_repos:
2124 self.gconf_not_show_repos += pub + ","
2125 else:
2126 for pub, err_type, err_str in \
2127 self.current_repos_with_search_errors:
2128 self.gconf_not_show_repos = \
2129 self.gconf_not_show_repos.replace(
2130 pub + ",", "")
2131 try:
2132 self.client.set_string(API_SEARCH_ERROR_PREFERENCES,
2133 self.gconf_not_show_repos)
2134 except GError:
2135 pass
2136
2137 def __on_searchentry_focus_in(self, widget, event):
2138 if self.w_main_clipboard.wait_is_text_available():
2139 self.w_paste_menuitem.set_sensitive(True)
2140 char_count = widget.get_text_length()
2141 if char_count > 0:
2142 self.w_selectall_menuitem.set_sensitive(True)
2143 else:
2144 self.w_selectall_menuitem.set_sensitive(False)
2145 bounds = widget.get_selection_bounds()
2146 if bounds:
2147 offset1 = bounds[0]
2148 offset2 = bounds[1]
2149 if abs(offset2 - offset1) == char_count:
2150 self.w_selectall_menuitem.set_sensitive(False)
2151 self.w_deselect_menuitem.set_sensitive(True)
2152 self.w_copy_menuitem.set_sensitive(True)
2153 else:
2154 self.w_deselect_menuitem.set_sensitive(False)
2155
2156 def __on_searchentry_focus_out(self, widget, event):
2157 self.w_paste_menuitem.set_sensitive(False)
2158 self.__enable_disable_select_all()
2159 self.__enable_disable_deselect()
2160 self.w_cut_menuitem.set_sensitive(False)
2161 self.w_copy_menuitem.set_sensitive(False)
2162 self.w_clear_menuitem.set_sensitive(False)
2163 return False
2164
2165 def __on_searchentry_activate(self, widget):
2166 self.__do_search()
2167
2168 def __on_searchentry_selection(self, widget, pspec):
2169 self.__enable_disable_entry_selection(widget)
2170
2171 def __enable_disable_entry_selection(self, widget):
2172 char_count = widget.get_text_length()
2173 bounds = widget.get_selection_bounds()
2174 if bounds:
2175 #enable selection functions
2176 self.w_cut_menuitem.set_sensitive(True)
2177 self.w_copy_menuitem.set_sensitive(True)
2178 self.w_clear_menuitem.set_sensitive(True)
2179 if char_count == abs(bounds[1] - bounds[0]):
2180 self.w_selectall_menuitem.set_sensitive(False)
2181 else:
2182 self.w_selectall_menuitem.set_sensitive(True)
2183 self.w_deselect_menuitem.set_sensitive(True)
2184 else:
2185 self.w_cut_menuitem.set_sensitive(False)
2186 self.w_copy_menuitem.set_sensitive(False)
2187 self.w_clear_menuitem.set_sensitive(False)
2188 self.w_deselect_menuitem.set_sensitive(False)
2189 if char_count == 0:
2190 self.w_selectall_menuitem.set_sensitive(False)
2191 else:
2192 self.w_selectall_menuitem.set_sensitive(True)
2193
2194 def __refilter_on_idle(self):
2195 if self.application_refilter_id != 0:
2196 gobject.source_remove(self.application_refilter_id)
2197 self.application_refilter_id = 0
2198 if self.application_refilter_idle_id == 0:
2199 self.application_refilter_idle_id = gobject.idle_add(
2200 self.__application_refilter)
2201
2202 def __on_category_focus_in(self, widget, event, user):
2203 self.__on_category_row_activated(None, None, None, user)
2204
2205 def __on_category_row_activated(self, view, path, col, user):
2206 '''This function is for handling category double click activations'''
2207 if self.w_filter_combobox.get_model():
2208 self.w_filter_combobox.set_active(self.saved_filter_combobox_active)
2209 self.w_searchentry.delete_text(0, -1)
2210 if self.in_search_mode or self.is_search_all:
2211 self.__unset_search(True)
2212 if self.selected == 0:
2213 gobject.idle_add(self.__enable_disable_install_remove)
2214 return
2215 self.__set_main_view_package_list()
2216 self.set_busy_cursor()
2217 self.__refilter_on_idle()
2218 if self.selected == 0:
2219 gobject.idle_add(self.__enable_disable_install_remove)
2220
2221 def __set_main_view_package_list(self):
2222 # Only switch from Start Page View to List view if we are not in startup
2223 if not self.in_startpage_startup:
2224 self.w_main_view_notebook.set_current_page(
2225 NOTEBOOK_PACKAGE_LIST_PAGE)
2226
2227 def __on_category_selection_changed(self, selection, widget):
2228 '''This function is for handling category selection changes'''
2229 if self.in_setup or self.changing_search_option:
2230 return
2231 model, itr = selection.get_selected()
2232 if itr:
2233 cat_path = model.get_string_from_iter(itr)
2234 if self.is_search_all:
2235 selected_section = self.set_section
2236 else:
2237 selected_section = self.w_sections_combobox.get_active()
2238 section_row = self.section_list[selected_section]
2239 section_row[enumerations.SECTION_SUBCATEGORY] = cat_path
2240
2241 if self.in_search_mode or self.is_search_all:
2242 return
2243
2244 if self.saved_filter_combobox_active != None:
2245 self.w_filter_combobox.set_active(self.saved_filter_combobox_active)
2246 self.__set_main_view_package_list()
2247
2248 self.set_busy_cursor()
2249 self.__refilter_on_idle()
2250 if self.selected == 0:
2251 gobject.idle_add(self.__enable_disable_install_remove)
2252
2253 def __process_package_selection(self):
2254 model, itr = self.package_selection.get_selected()
2255 if self.show_info_id != 0:
2256 gobject.source_remove(self.show_info_id)
2257 self.show_info_id = 0
2258 if itr:
2259 self.__enable_disable_install_remove()
2260 self.selected_pkgstem = \
2261 model.get_value(itr, enumerations.STEM_COLUMN)
2262 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
2263 gobject.idle_add(self.__show_fetching_package_info, pkg)
2264 self.showing_empty_details = False
2265 self.last_show_info_id = self.show_info_id = \
2266 gobject.timeout_add(SHOW_INFO_DELAY,
2267 self.__show_info, model, model.get_path(itr))
2268 if (self.w_info_notebook.get_current_page() ==
2269 INFO_NOTEBOOK_LICENSE_PAGE):
2270 self.__on_notebook_change(None, None,
2271 INFO_NOTEBOOK_LICENSE_PAGE)
2272 else:
2273 self.selected_model = None
2274 self.selected_path = None
2275 self.selected_pkgstem = None
2276
2277 def __on_package_selection_changed(self, selection, widget):
2278 '''This function is for handling package selection changes'''
2279 if self.in_setup:
2280 return
2281 self.__process_package_selection()
2282
2283 def __on_filtercombobox_changed(self, widget):
2284 '''On filter combobox changed'''
2285 if self.in_setup or self.changing_search_option:
2286 return
2287 active = self.w_filter_combobox.get_active()
2288 if active != enumerations.FILTER_SELECTED:
2289 self.saved_filter_combobox_active = active
2290 self.__set_main_view_package_list()
2291 if self.in_search_mode or self.is_search_all:
2292 self.set_busy_cursor()
2293 self.saved_filter_combobox_active = \
2294 self.w_filter_combobox.get_active()
2295 self.__unset_search(True)
2296 return
2297 self.set_busy_cursor()
2298 self.__refilter_on_idle()
2299 if self.selected == 0:
2300 gobject.idle_add(self.__enable_disable_install_remove)
2301
2302 def __set_categories_visibility(self, selected_section):
2303 self.category_list[0][enumerations.CATEGORY_ICON] = None
2304 if selected_section == 0:
2305 for category in self.category_list:
2306 category[enumerations.CATEGORY_VISIBLE] = True
2307 else:
2308 for category in self.category_list:
2309 if category[enumerations.CATEGORY_ID] == 0:
2310 category[enumerations.CATEGORY_VISIBLE] = True
2311 else:
2312 category_list = \
2313 category[enumerations.SECTION_LIST_OBJECT]
2314 if not category_list:
2315 category[enumerations.CATEGORY_VISIBLE] \
2316 = False
2317 else:
2318 for section in category_list:
2319 if section == selected_section:
2320 category[enumerations. \
2321 CATEGORY_VISIBLE] = \
2322 True
2323 else:
2324 category[enumerations. \
2325 CATEGORY_VISIBLE] = \
2326 False
2327
2328 # Set category icon for All if a visible category has it
2329 for category in self.category_list:
2330 if category[enumerations.CATEGORY_ICON] != None:
2331 self.category_list[0][enumerations.CATEGORY_ICON] = \
2332 category[enumerations.CATEGORY_ICON]
2333 break
2334
2335 section_row = self.section_list[selected_section]
2336 cat_path = section_row[enumerations.SECTION_SUBCATEGORY]
2337 if cat_path != None:
2338 itr = self.category_list_filter.get_iter_from_string(cat_path)
2339 path = self.category_list_filter.get_path(itr)
2340 self.w_categories_treeview.set_cursor(path,
2341 None, start_editing=False)
2342
2343 def __on_sectionscombobox_changed(self, widget):
2344 '''On section combobox changed'''
2345 if self.in_setup:
2346 return
2347 if self.changing_search_option:
2348 return
2349 self.__set_main_view_package_list()
2350 self.set_busy_cursor()
2351 self.__set_first_category_text()
2352 self.__set_categories_visibility(widget.get_active())
2353 self.category_list_filter.refilter()
2354 if self.in_search_mode or self.is_search_all:
2355 self.saved_sections_combobox_active = \
2356 self.w_sections_combobox.get_active()
2357 self.__unset_search(True)
2358 return
2359 self.__refilter_on_idle()
2360 if self.selected == 0:
2361 gobject.idle_add(self.__enable_disable_install_remove)
2362
2363 def __set_first_category_text(self):
2364 active_section = self.w_sections_combobox.get_active()
2365 all_cat_text = _("All")
2366 if active_section != 0:
2367 all_cat_text += " " + self.section_list[active_section][1]
2368 category_model = self.w_categories_treeview.get_model()
2369 if category_model:
2370 list_store = category_model.get_model()
2371 list_store[0][1] = all_cat_text
2372
2373 def __unset_search(self, same_repo):
2374 self.w_infosearch_frame.hide()
2375 self.changing_search_option = True
2376 self.current_search_option = 0
2377 visible_repository = self.__get_visible_repository_name()
2378 if visible_repository in self.selected_pkgs:
2379 self.selected_pkgs.pop(visible_repository)
2380 if visible_repository in self.to_install_update:
2381 self.to_install_update.pop(visible_repository)
2382 if visible_repository in self.to_remove:
2383 self.to_remove.pop(visible_repository)
2384 self.__update_tooltips()
2385 if self.is_search_all:
2386 self.__update_repository_combobox_for_search(False)
2387 pixbuf = self.search_options[0][1]
2388 self.search_image.set_from_pixbuf(pixbuf)
2389 self.in_search_mode = False
2390 self.is_search_all = False
2391 if same_repo:
2392 self.__restore_setup_for_browse()
2393 self.changing_search_option = False
2394
2395 def __on_repositorycombobox_changed(self, widget):
2396 '''On repository combobox changed'''
2397 if self.changing_search_option:
2398 return
2399 self.changing_search_option = True
2400 active_publisher = self.__get_active_publisher()
2401 if self.is_search_all:
2402 same_repo = False
2403 active = self.w_repository_combobox.get_active() - 1
2404 if active == -1:
2405 # We get here is we choose "Add ..." when
2406 # doing api search
2407 self.changing_search_option = False
2408 return
2409 if not active_publisher == _("Add..."):
2410 if self.saved_repository_combobox_active == active:
2411 same_repo = True
2412 self.__unset_search(same_repo)
2413 self.w_repository_combobox.set_active(active)
2414
2415 if same_repo:
2416 self.changing_search_option = False
2417 return
2418 active_publisher = self.__get_active_publisher()
2419 self.changing_search_option = False
2420 if self.visible_repository == active_publisher:
2421 # If we are coming back to the same repository, we do
2422 # not want to setup publishers. This is the case when
2423 # we are calling Add... then we are firing the event for
2424 # Add... case and immediately coming back to the
2425 # previously selected repository.
2426 return
2427 # Checking for Add... is fine enough, as the repository
2428 # name cannot contain "..." in the name.
2429 if active_publisher == _("Add..."):
2430 index = -1
2431 if self.is_search_all:
2432 index = 0
2433 else:
2434 model = self.w_repository_combobox.get_model()
2435 for entry in model:
2436 if entry[1] == self.visible_repository:
2437 index = entry[0]
2438 break
2439 # We do not want to switch permanently to the Add...
2440 self.w_repository_combobox.set_active(index)
2441 self.__on_edit_repositories_activate(None)
2442 return
2443 self.cancelled = True
2444 self.in_setup = True
2445 self.set_busy_cursor()
2446 self.__set_empty_details_panel()
2447 if self.in_search_mode:
2448 self.__unset_search(False)
2449 self.w_searchentry.grab_focus()
2450 if len(self.w_searchentry.get_text()) > 0:
2451 start, end = self.w_searchentry.get_selection_bounds()
2452 self.w_searchentry.select_region(end, end)
2453
2454 pub = [active_publisher, ]
2455 self.set_show_filter = self.initial_show_filter
2456 self.set_section = self.initial_section
2457 Thread(target = self.__setup_publisher, args = [pub]).start()
2458 self.__set_main_view_package_list()
2459
2460 def __get_active_publisher(self):
2461 pub_iter = self.w_repository_combobox.get_active_iter()
2462 if pub_iter == None:
2463 return None
2464 return self.repositories_list.get_value(pub_iter, \
2465 enumerations.REPOSITORY_NAME)
2466
2467 def __setup_publisher(self, publishers=[]):
2468 self.saved_filter_combobox_active = self.initial_show_filter
2469 application_list, category_list , section_list = \
2470 self.__get_application_categories_lists(publishers)
2471 self.__unset_saved()
2472 gobject.idle_add(self.__init_tree_views, application_list,
2473 category_list, section_list)
2474
2475 def __unset_saved(self):
2476 self.saved_application_list = None
2477 self.saved_application_list_filter = None
2478 self.saved_application_list_sort = None
2479 self.saved_category_list = None
2480 self.saved_section_list = None
2481
2482 def __get_application_categories_lists(self, publishers=[]):
2483 if not self.visible_repository:
2484 self.visible_repository = self.__get_active_publisher()
2485 application_list = self.__get_new_application_liststore()
2486 category_list = self.__get_new_category_liststore()
2487 section_list = self.__get_new_section_liststore()
2488 first_loop = True
2489 for publisher in publishers:
2490 uptodate = False
2491 try:
2492 uptodate = self.__check_if_cache_uptodate(publisher)
2493 if uptodate:
2494 self.__add_pkgs_to_lists_from_cache(publisher,
2495 application_list, category_list,
2496 section_list)
2497 except (UnpicklingError, EOFError, IOError):
2498 #Most likely cache is corrupted, silently load list
2499 #from api.
2500 application_list = self.__get_new_application_liststore()
2501 category_list = self.__get_new_category_liststore()
2502 uptodate = False
2503 if not uptodate:
2504 if first_loop == True:
2505 first_loop = False
2506 gobject.idle_add(self.setup_progressdialog_show)
2507 self.api_o.refresh(pubs=[publisher])
2508 self.__add_pkgs_to_lists_from_api(publisher,
2509 application_list, category_list, section_list)
2510 category_list.prepend([0, _('All'), None, None, False,
2511 True, None])
2512 if self.application_list and self.category_list and \
2513 not self.visible_repository_uptodate:
2514 if self.visible_repository:
2515 dump_list = self.application_list
2516 if self.saved_application_list != None:
2517 dump_list = \
2518 self.saved_application_list
2519 self.__dump_datamodels(self.visible_repository,
2520 dump_list, self.category_list,
2521 self.section_list)
2522 self.visible_repository = self.__get_active_publisher()
2523 self.visible_repository_uptodate = uptodate
2524 return application_list, category_list, section_list
2525
2526 def __check_if_cache_uptodate(self, publisher):
2527 if self.cache_o:
2528 return self.cache_o.check_if_cache_uptodate(publisher)
2529 return False
2530
2531 def __dump_datamodels(self, publisher, application_list, category_list,
2532 section_list):
2533 #Consistency check - only dump models if publisher passed in matches
2534 #publisher in application list
2535 if application_list == None:
2536 return
2537 app_pub = application_list[0][enumerations.AUTHORITY_COLUMN]
2538 if publisher != app_pub:
2539 if debug:
2540 print "ERROR: __dump_data_models(): INCONSISTENT " \
2541 "pub %s != app_list_pub %s" % \
2542 (publisher, app_pub)
2543 return
2544
2545 if self.cache_o:
2546 if self.img_timestamp == \
2547 self.cache_o.get_index_timestamp():
2548 Thread(target = self.cache_o.dump_datamodels,
2549 args = (publisher, application_list, category_list,
2550 section_list)).start()
2551 else:
2552 self.__remove_cache()
2553
2554 def __remove_cache(self):
2555 model = self.w_repository_combobox.get_model()
2556 for publisher in model:
2557 pub_name = publisher[1]
2558 if pub_name and pub_name != _("Add..."):
2559 Thread(target = self.cache_o.remove_datamodel,
2560 args = [publisher[1]]).start()
2561
2562 def __on_install_update(self, widget):
2563 self.api_o.reset()
2564 install_update = []
2565 if self.selected == 0:
2566 model, itr = self.package_selection.get_selected()
2567 if itr:
2568 install_update.append(
2569 model.get_value(itr, enumerations.STEM_COLUMN))
2570 else:
2571 visible_repository = self.__get_visible_repository_name()
2572 pkgs = self.selected_pkgs.get(visible_repository)
2573 if pkgs:
2574 for pkg_stem in pkgs:
2575 status = pkgs.get(pkg_stem)
2576 if status == enumerations.NOT_INSTALLED or \
2577 status == enumerations.UPDATABLE:
2578 install_update.append(pkg_stem)
2579
2580 if self.img_timestamp != self.cache_o.get_index_timestamp():
2581 self.img_timestamp = None
2582 self.__remove_cache()
2583
2584 installupdate.InstallUpdate(install_update, self, \
2585 self.api_o, ips_update = False, \
2586 action = enumerations.INSTALL_UPDATE)
2587
2588 def __on_update_all(self, widget):
2589 self.api_o.reset()
2590 installupdate.InstallUpdate([], self,
2591 self.api_o, ips_update = False,
2592 action = enumerations.IMAGE_UPDATE, be_name = self.ua_be_name,
2593 parent_name = _("Package Manager"),
2594 pkg_list = ["SUNWipkg", "SUNWipkg-gui"],
2595 main_window = self.w_main_window)
2596 return
2597
2598 def __on_ua_completed_linkbutton_clicked(self, widget):
2599 try:
2600 gnome.url_show(self.release_notes_url)
2601 except gobject.GError:
2602 self.error_occurred(_("Unable to navigate to:\n\t%s") %
2603 self.release_notes_url)
2604
2605 def __on_help_about(self, widget):
2606 wTreePlan = gtk.glade.XML(self.gladefile, "aboutdialog")
2607 aboutdialog = wTreePlan.get_widget("aboutdialog")
2608 aboutdialog.connect("response", lambda x = None, \
2609 y = None: aboutdialog.destroy())
2610 aboutdialog.run()
2611
2612 def __on_help_help(self, widget):
2613 gui_misc.display_help(self.application_dir)
2614
2615 def __on_remove(self, widget):
2616 self.api_o.reset()
2617 remove_list = []
2618 if self.selected == 0:
2619 model, itr = self.package_selection.get_selected()
2620 if itr:
2621 remove_list.append(
2622 model.get_value(itr, enumerations.STEM_COLUMN))
2623 else:
2624 visible_repository = self.__get_visible_repository_name()
2625 pkgs = self.selected_pkgs.get(visible_repository)
2626 if pkgs:
2627 for pkg_stem in pkgs:
2628 status = pkgs.get(pkg_stem)
2629 if status == enumerations.INSTALLED or \
2630 status == enumerations.UPDATABLE:
2631 remove_list.append(pkg_stem)
2632
2633 if self.img_timestamp != self.cache_o.get_index_timestamp():
2634 self.img_timestamp = None
2635 self.__remove_cache()
2636
2637 installupdate.InstallUpdate(remove_list, self,
2638 self.api_o, ips_update = False,
2639 action = enumerations.REMOVE)
2640
2641 def __on_reload(self, widget):
2642 if self.description_thread_running:
2643 self.cancelled = True
2644 if self.in_search_mode or self.is_search_all:
2645 self.__unset_search(False)
2646 self.__set_empty_details_panel()
2647 self.in_setup = True
2648 self.visible_repository = None
2649 if widget != None:
2650 self.__remove_cache()
2651 self.w_progress_dialog.set_title(_("Refreshing catalogs"))
2652 self.w_progressinfo_label.set_text(_("Refreshing catalogs..."))
2653 self.progress_stop_timer_thread = False
2654 Thread(target = self.__progressdialog_progress_pulse).start()
2655 self.w_progress_dialog.show()
2656 self.w_progress_cancel.hide()
2657 self.__disconnect_models()
2658 self.in_reload = True
2659 Thread(target = self.__catalog_refresh).start()
2660
2661 def __catalog_refresh_done(self):
2662 self.progress_stop_timer_thread = True
2663 #Let the progress_pulse finish. This should be done other way, but at
2664 #The moment this works fine
2665 time.sleep(0.2)
2666 gobject.idle_add(self.w_progress_cancel.show)
2667 gobject.idle_add(self.process_package_list_start,
2668 self.image_directory)
2669
2670 def __main_application_quit(self, be_name = None):
2671 '''quits the main gtk loop'''
2672 self.cancelled = True
2673 if self.in_setup:
2674 return
2675
2676 if be_name:
2677 if self.image_dir_arg:
2678 gobject.spawn_async([self.application_path, "-R",
2679 self.image_dir_arg, "-U", be_name])
2680 else:
2681 gobject.spawn_async([self.application_path,
2682 "-U", be_name])
2683 elif self.in_search_mode:
2684 self.__dump_datamodels(self.visible_repository,
2685 self.saved_application_list, self.category_list,
2686 self.section_list)
2687 else:
2688 visible_repository = self.__get_visible_repository_name()
2689 self.__dump_datamodels(visible_repository,
2690 self.application_list, self.category_list,
2691 self.section_list)
2692
2693 width, height = self.w_main_window.get_size()
2694 hpos = self.w_main_hpaned.get_position()
2695 vpos = self.w_main_vpaned.get_position()
2696 try:
2697 self.client.set_int(INITIAL_APP_WIDTH_PREFERENCES, width)
2698 self.client.set_int(INITIAL_APP_HEIGHT_PREFERENCES, height)
2699 self.client.set_int(INITIAL_APP_HPOS_PREFERENCES, hpos)
2700 self.client.set_int(INITIAL_APP_VPOS_PREFERENCES, vpos)
2701 except GError:
2702 pass
2703
2704 self.w_main_window.hide()
2705 while gtk.events_pending():
2706 gtk.main_iteration(False)
2707 gtk.main_quit()
2708 sys.exit(0)
2709 return True
2710
2711 def __check_if_something_was_changed(self):
2712 ''' Returns True if any of the check boxes for package was changed, false
2713 if not'''
2714 if self.application_list:
2715 for pkg in self.application_list:
2716 if pkg[enumerations.MARK_COLUMN] == True:
2717 return True
2718 return False
2719
2720 def __setup_repositories_combobox(self, api_o, repositories_list):
2721 self.__disconnect_repository_model()
2722 default_pub = api_o.get_preferred_publisher().prefix
2723 if self.default_publisher != default_pub:
2724 self.__clear_pkg_selections()
2725 self.default_publisher = default_pub
2726 selected_repos = []
2727 enabled_repos = []
2728 for repo in self.selected_pkgs:
2729 selected_repos.append(repo)
2730 i = 0
2731 active = 0
2732 for pub in api_o.get_publishers():
2733 if pub.disabled:
2734 continue
2735 prefix = pub.prefix
2736 if cmp(prefix, self.default_publisher) == 0:
2737 active = i
2738 repositories_list.append([i, prefix, ])
2739 enabled_repos.append(prefix)
2740 i = i + 1
2741 repositories_list.append([-1, "", ])
2742 repositories_list.append([-1, _("Add..."), ])
2743 pkgs_to_remove = []
2744 for repo_name in selected_repos:
2745 if repo_name not in enabled_repos:
2746 pkg_stems = self.selected_pkgs.get(repo_name)
2747 for pkg_stem in pkg_stems:
2748 pkgs_to_remove.append(pkg_stem)
2749 for pkg_stem in pkgs_to_remove:
2750 self.__remove_pkg_stem_from_list(pkg_stem)
2751 self.w_repository_combobox.set_model(repositories_list)
2752 if self.default_publisher:
2753 self.w_repository_combobox.set_active(active)
2754 else:
2755 self.w_repository_combobox.set_active(0)
2756
2757 def __active_pane_toggle(self, cell, path, model_sort):
2758 '''Toggle function for column enumerations.MARK_COLUMN'''
2759 applicationModel = model_sort.get_model()
2760 applicationPath = model_sort.convert_path_to_child_path(path)
2761 filterModel = applicationModel.get_model()
2762 child_path = applicationModel.convert_path_to_child_path(applicationPath)
2763 itr = filterModel.get_iter(child_path)
2764 if itr:
2765 modified = filterModel.get_value(itr, enumerations.MARK_COLUMN)
2766 filterModel.set_value(itr, enumerations.MARK_COLUMN,
2767 not modified)
2768 pkg_status = filterModel.get_value(itr,
2769 enumerations.STATUS_COLUMN)
2770 pkg_stem = filterModel.get_value(itr, enumerations.STEM_COLUMN)
2771 if modified:
2772 self.__remove_pkg_stem_from_list(pkg_stem)
2773 else:
2774 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2775 self.update_statusbar()
2776 self.__enable_disable_selection_menus()
2777
2778 def __update_reload_button(self):
2779 if self.user_rights:
2780 self.w_reload_button.set_sensitive(True)
2781 else:
2782 self.w_reload_button.set_sensitive(False)
2783
2784 def __add_pkg_stem_to_list(self, stem, status):
2785 publisher = self.__get_active_publisher()
2786 if self.selected_pkgs.get(publisher) == None:
2787 self.selected_pkgs[publisher] = {}
2788 self.selected_pkgs.get(publisher)[stem] = status
2789 if status == enumerations.NOT_INSTALLED or \
2790 status == enumerations.UPDATABLE:
2791 if self.to_install_update.get(publisher) == None:
2792 self.to_install_update[publisher] = 1
2793 else:
2794 self.to_install_update[publisher] += 1
2795 if status == enumerations.UPDATABLE or status == enumerations.INSTALLED:
2796 if self.to_remove.get(publisher) == None:
2797 self.to_remove[publisher] = 1
2798 else:
2799 self.to_remove[publisher] += 1
2800 self.__update_tooltips()
2801
2802 def __update_tooltips(self):
2803 to_remove = None
2804 to_install = None
2805 no_iter = 0
2806 for publisher in self.to_remove:
2807 packages = self.to_remove.get(publisher)
2808 if packages > 0:
2809 if no_iter == 0:
2810 to_remove = _("Selected for Removal:")
2811 to_remove += "\n %s: %d" % (publisher, packages)
2812 no_iter += 1
2813 no_iter = 0
2814 for publisher in self.to_install_update:
2815 packages = self.to_install_update.get(publisher)
2816 if packages > 0:
2817 if no_iter == 0:
2818 to_install = _("Selected for Install/Update:")
2819 to_install += "\n %s: %d" % (publisher, packages)
2820 no_iter += 1
2821 if not to_install:
2822 to_install = _("Select packages by marking the checkbox "
2823 "and click to Install/Update.")
2824 self.w_installupdate_button.set_tooltip(self.install_button_tooltip,
2825 to_install)
2826 if not to_remove:
2827 to_remove = _("Select packages by marking the checkbox "
2828 "and click to Remove selected.")
2829 self.w_remove_button.set_tooltip(self.remove_button_tooltip, to_remove)
2830
2831 def __remove_pkg_stem_from_list(self, stem):
2832 remove_pub = []
2833 for publisher in self.selected_pkgs:
2834 pkgs = self.selected_pkgs.get(publisher)
2835 status = None
2836 if stem in pkgs:
2837 status = pkgs.pop(stem)
2838 if status == enumerations.NOT_INSTALLED or \
2839 status == enumerations.UPDATABLE:
2840 if self.to_install_update.get(publisher) == None:
2841 self.to_install_update[publisher] = 0
2842 else:
2843 self.to_install_update[publisher] -= 1
2844 if status == enumerations.UPDATABLE or \
2845 status == enumerations.INSTALLED:
2846 if self.to_remove.get(publisher) == None:
2847 self.to_remove[publisher] = 0
2848 else:
2849 self.to_remove[publisher] -= 1
2850 if len(pkgs) == 0:
2851 remove_pub.append(publisher)
2852 for publisher in remove_pub:
2853 self.selected_pkgs.pop(publisher)
2854 self.__update_tooltips()
2855
2856 def __clear_pkg_selections(self):
2857 # We clear the selections as the preffered repository was changed
2858 # and pkg stems are not valid.
2859 remove_pub = []
2860 for publisher in self.selected_pkgs:
2861 stems = self.selected_pkgs.get(publisher)
2862 for pkg_stem in stems:
2863 remove_pub.append(pkg_stem)
2864 for pkg_stem in remove_pub:
2865 self.__remove_pkg_stem_from_list(pkg_stem)
2866
2867 def __set_empty_details_panel(self):
2868 self.showing_empty_details = True
2869 if self.show_info_id != 0:
2870 gobject.source_remove(self.show_info_id)
2871 self.show_info_id = 0
2872 if self.show_licenses_id != 0:
2873 gobject.source_remove(self.show_licenses_id)
2874 self.show_licenses_id = 0
2875 pkg_name = _("Package Name")
2876 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2877 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2878 self.w_installedfiles_textview.get_buffer().set_text("")
2879 self.w_dependencies_textview.get_buffer().set_text("")
2880 self.w_generalinfo_textview.get_buffer().set_text("")
2881 self.w_license_textview.get_buffer().set_text("")
2882 return
2883
2884 def __show_fetching_package_info(self, pkg):
2885 pkg_name = pkg.get_name()
2886 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2887 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2888
2889 pkg_stem = pkg.get_pkg_stem()
2890 if self.__setting_from_cache(pkg_stem):
2891 return
2892
2893 self.w_shortdescription_label.set_text(
2894 _("Fetching description..."))
2895 instbuffer = self.w_installedfiles_textview.get_buffer()
2896 depbuffer = self.w_dependencies_textview.get_buffer()
2897 infobuffer = self.w_generalinfo_textview.get_buffer()
2898 fetching_text = _("Fetching information...")
2899 instbuffer.set_text(fetching_text)
2900 depbuffer.set_text(fetching_text)
2901 infobuffer.set_text(fetching_text)
2902 return
2903
2904 def __setting_from_cache(self, pkg_stem):
2905 if len(self.info_cache) > MAX_INFO_CACHE_LIMIT:
2906 self.info_cache = {}
2907
2908 if self.info_cache.has_key(pkg_stem):
2909 self.w_shortdescription_label.set_text(
2910 self.info_cache[pkg_stem][0])
2911 instbuffer = self.w_installedfiles_textview.get_buffer()
2912 depbuffer = self.w_dependencies_textview.get_buffer()
2913 infobuffer = self.w_generalinfo_textview.get_buffer()
2914 infobuffer.set_text(self.info_cache[pkg_stem][1])
2915 instbuffer.set_text(self.info_cache[pkg_stem][2])
2916 depbuffer.set_text(self.info_cache[pkg_stem][3])
2917 return True
2918 else:
2919 return False
2920
2921 def __update_package_info(self, pkg, local_info, remote_info, info_id):
2922 if self.showing_empty_details or (info_id !=
2923 self.last_show_info_id):
2924 return
2925 pkg_name = pkg.get_name()
2926 pkg_stem = pkg.get_pkg_stem()
2927 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2928 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2929 installed = True
2930
2931 if self.__setting_from_cache(pkg_stem):
2932 return
2933
2934 instbuffer = self.w_installedfiles_textview.get_buffer()
2935 depbuffer = self.w_dependencies_textview.get_buffer()
2936 infobuffer = self.w_generalinfo_textview.get_buffer()
2937
2938 if not local_info and not remote_info:
2939 network_str = \
2940 _("\nThis might be caused by network problem "
2941 "while accessing the repository.")
2942 self.w_shortdescription_label.set_text(
2943 _("Description not available for this package...") +
2944 network_str)
2945 instbuffer.set_text( \
2946 _("Files Details not available for this package...") +
2947 network_str)
2948 depbuffer.set_text(_(
2949 "Dependencies info not available for this package...") +
2950 network_str)
2951 infobuffer.set_text(
2952 _("Information not available for this package...") +
2953 network_str)
2954 return
2955
2956 if not local_info:
2957 # Package is not installed
2958 local_info = remote_info
2959 installed = False
2960
2961 if not remote_info:
2962 remote_info = local_info
2963 installed = True
2964
2965 description = local_info.summary
2966 #XXX long term need to have something more robust here for multi byte
2967 if len(description) > MAX_DESC_LEN:
2968 description = description[:MAX_DESC_LEN] + " ..."
2969 self.w_shortdescription_label.set_text(description)
2970 inst_str = _("Root: %s\n") % self.api_o.img.get_root()
2971 dep_str = _("Dependencies:\n")
2972
2973 if local_info.dependencies:
2974 dep_str += ''.join(
2975 ["\t%s\n" % x for x in local_info.dependencies])
2976 if local_info.dirs:
2977 inst_str += ''.join(["\t%s\n" % x for x in local_info.dirs])
2978 if local_info.files:
2979 inst_str += ''.join(["\t%s\n" % x for x in local_info.files])
2980 if local_info.hardlinks:
2981 inst_str += ''.join(["\t%s\n" % x for x in local_info.hardlinks])
2982 if local_info.links:
2983 inst_str += ''.join(["\t%s\n" % x for x in local_info.links])
2984 info_str = ""
2985 labs = {}
2986 labs["sum"] = _("Summary:\t\t")
2987 labs["size"] = _("Size:\t\t\t")
2988 labs["cat"] = _("Category:\t\t")
2989 labs["ins"] = _("Installed Version:\t")
2990 labs["lat"] = _("Latest Version:\t")
2991 labs["pkg_date"] = _("Packaging Date:\t")
2992 labs["fmri"] = _("FMRI:\t\t\t")
2993 labs["repository"] = _("Repository:\t\t")
2994 max_len = 0
2995 for lab in labs:
2996 if len(labs[lab]) > max_len:
2997 max_len = len(labs[lab])
2998 categories = _("None")
2999 if local_info.category_info_list:
3000 verbose = len(local_info.category_info_list) > 1
3001 categories = ""
3002 categories += local_info.category_info_list[0].__str__(verbose)
3003 if len(local_info.category_info_list) > 1:
3004 for ci in local_info.category_info_list[1:]:
3005 categories += ", " + ci.__str__(verbose)
3006 summary = _("None")
3007 if local_info.summary:
3008 summary = local_info.summary
3009 info_str += " %s %s" % (labs["sum"], summary)
3010 info_str += "\n %s %s" % (labs["size"],
3011 misc.bytes_to_str(local_info.size))
3012 info_str += "\n %s %s" % (labs["cat"], categories)
3013 if installed:
3014 info_str += "\n %s %s,%s-%s" % (labs["ins"], local_info.version,
3015 local_info.build_release, local_info.branch)
3016 info_str += "\n %s %s,%s-%s" % (labs["lat"], remote_info.version,
3017 remote_info.build_release, remote_info.branch)
3018 info_str += "\n %s %s" % (labs["pkg_date"], local_info.packaging_date)
3019 info_str += "\n %s %s" % (labs["fmri"], local_info.fmri)
3020 info_str += "\n %s %s" % (labs["repository"], local_info.publisher)
3021 infobuffer.set_text(info_str)
3022 instbuffer.set_text(inst_str)
3023 depbuffer.set_text(dep_str)
3024 self.info_cache[pkg_stem] = \
3025 (description, info_str, inst_str, dep_str)
3026
3027 def __update_package_license(self, licenses, license_id):
3028 if self.showing_empty_details or (license_id !=
3029 self.last_show_licenses_id):
3030 return
3031 lic = ""
3032 lic_u = ""
3033 if licenses == None:
3034 lic_u = _("Not available")
3035 else:
3036 for licens in licenses:
3037 lic += licens.get_text()
3038 lic += "\n"
3039 try:
3040 lic_u = unicode(lic, "utf-8")
3041 except UnicodeDecodeError:
3042 lic_u += ""
3043 licbuffer = self.w_license_textview.get_buffer()
3044 licbuffer.set_text(lic_u)
3045
3046 def __show_licenses(self):
3047 self.show_licenses_id = 0
3048 if self.catalog_loaded == False:
3049 return
3050 Thread(target = self.__show_package_licenses,
3051 args = (self.selected_pkgstem, self.last_show_licenses_id,)).start()
3052
3053 def __show_package_licenses(self, selected_pkgstem, license_id):
3054 if selected_pkgstem == None:
3055 gobject.idle_add(self.__update_package_license, None,
3056 self.last_show_licenses_id)
3057 return
3058 info = None
3059 try:
3060 info = self.api_o.info([selected_pkgstem],
3061 True, frozenset([api.PackageInfo.LICENSES]))
3062 except (api_errors.TransportError):
3063 pass
3064 if self.showing_empty_details or (license_id !=
3065 self.last_show_licenses_id):
3066 return
3067 if not info or (info and len(info.get(0)) == 0):
3068 try:
3069 # Get license from remote
3070 info = self.api_o.info([selected_pkgstem],
3071 False, frozenset([api.PackageInfo.LICENSES]))
3072 except (api_errors.TransportError):
3073 pass
3074 if self.showing_empty_details or (license_id !=
3075 self.last_show_licenses_id):
3076 return
3077 pkgs_info = None
3078 package_info = None
3079 no_licenses = 0
3080 if info:
3081 pkgs_info = info[0]
3082 if pkgs_info:
3083 package_info = pkgs_info[0]
3084 if package_info:
3085 no_licenses = len(package_info.licenses)
3086 if no_licenses == 0:
3087 gobject.idle_add(self.__update_package_license, None,
3088 license_id)
3089 return
3090 else:
3091 gobject.idle_add(self.__update_package_license,
3092 package_info.licenses, license_id)
3093
3094 def __get_pkg_info(self, pkg_stem, local):
3095 info = None
3096 try:
3097 info = self.api_o.info([pkg_stem], local,
3098 api.PackageInfo.ALL_OPTIONS -
3099 frozenset([api.PackageInfo.LICENSES]))
3100 except (api_errors.TransportError):
3101 return info
3102 pkgs_info = None
3103 package_info = None
3104 if info:
3105 pkgs_info = info[0]
3106 if pkgs_info:
3107 package_info = pkgs_info[0]
3108 if package_info:
3109 return package_info
3110 else:
3111 return None
3112
3113 def __show_info(self, model, path):
3114 self.show_info_id = 0
3115 if self.catalog_loaded == False:
3116 self.selected_model = model
3117 self.selected_path = path
3118 return
3119 if not (model and path):
3120 return
3121 if self.selected_model != None:
3122 if (self.selected_model != model or
3123 self.selected_path != path):
3124 # This can happen after catalogs are loaded in
3125 # enable_disable_update_all and a different
3126 # package is selected before enable_disable_update_all
3127 # calls __show_info. We set these variable to None
3128 # so that when __show_info is called it does nothing.
3129 self.selected_model = None
3130 self.selected_path = None
3131
3132 itr = model.get_iter(path)
3133 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
3134 pkg_stem = model.get_value(itr, enumerations.STEM_COLUMN)
3135 pkg_status = model.get_value(itr, enumerations.STATUS_COLUMN)
3136 if self.info_cache.has_key(pkg_stem):
3137 return
3138 Thread(target = self.__show_package_info,
3139 args = (pkg, pkg_stem, pkg_status, self.last_show_info_id)).start()
3140
3141 def __show_package_info(self, pkg, pkg_stem, pkg_status, info_id):
3142 self.api_o.log_operation_start("info")
3143 local_info = None
3144 remote_info = None
3145 if not self.showing_empty_details and (info_id ==
3146 self.last_show_info_id) and (pkg_status ==
3147 enumerations.INSTALLED or pkg_status ==
3148 enumerations.UPDATABLE):
3149 local_info = self.__get_pkg_info(pkg_stem, True)
3150 if not self.showing_empty_details and (info_id ==
3151 self.last_show_info_id) and (pkg_status ==
3152 enumerations.NOT_INSTALLED or pkg_status ==
3153 enumerations.UPDATABLE):
3154 remote_info = self.__get_pkg_info(pkg_stem, False)
3155 if not self.showing_empty_details and (info_id ==
3156 self.last_show_info_id):
3157 gobject.idle_add(self.__update_package_info, pkg,
3158 local_info, remote_info, info_id)
3159 self.api_o.log_operation_end()
3160 return
3161
3162 # This function is ported from pkg.actions.generic.distinguished_name()
3163 @staticmethod
3164 def __locale_distinguished_name(action):
3165 if action.key_attr == None:
3166 return str(action)
3167 return "%s: %s" % \
3168 (_(action.name), action.attrs.get(action.key_attr, "???"))
3169
3170 def __application_filter(self, model, itr):
3171 '''This function is used to filter content in the main
3172 application view'''
3173 if self.in_setup or self.cancelled:
3174 return False
3175 filter_id = self.w_filter_combobox.get_active()
3176 if filter_id == enumerations.FILTER_SELECTED:
3177 return model.get_value(itr, enumerations.MARK_COLUMN)
3178 # XXX Show filter, chenge text to integers
3179 selected_category = 0
3180 category_selection = self.w_categories_treeview.get_selection()
3181 category_model, category_iter = category_selection.get_selected()
3182 if category_iter:
3183 selected_category = category_model.get_value(category_iter,
3184 enumerations.CATEGORY_ID)
3185 category_list = model.get_value(itr, enumerations.CATEGORY_LIST_COLUMN)
3186 selected_section = self.w_sections_combobox.get_active()
3187 category = False
3188 if selected_section == 0 and selected_category == 0:
3189 #For section "All" and category "All" always true
3190 category = True
3191 elif selected_category != 0:
3192 if category_list and selected_category in category_list:
3193 category = True
3194 elif category_list:
3195 #The selected category is "All" so we need to check
3196 #If the package belongs to one of the visible categories
3197 for visible_category in category_model:
3198 visible_id = visible_category[enumerations.CATEGORY_ID]
3199 if visible_id in category_list:
3200 category = True
3201 break
3202 if (model.get_value(itr, enumerations.IS_VISIBLE_COLUMN) == False):
3203 return False
3204 return (category &
3205 self.__is_package_filtered(model, itr, filter_id))
3206
3207 @staticmethod
3208 def __is_package_filtered(model, itr, filter_id):
3209 '''Function for filtercombobox'''
3210 if filter_id == enumerations.FILTER_ALL:
3211 return True
3212 status = model.get_value(itr, enumerations.STATUS_COLUMN)
3213 if filter_id == enumerations.FILTER_INSTALLED:
3214 return (status == enumerations.INSTALLED or status == \
3215 enumerations.UPDATABLE)
3216 elif filter_id == enumerations.FILTER_UPDATES:
3217 return status == enumerations.UPDATABLE
3218 elif filter_id == enumerations.FILTER_NOT_INSTALLED:
3219 return status == enumerations.NOT_INSTALLED
3220
3221 def __is_pkg_repository_visible(self, model, itr):
3222 if len(self.repositories_list) <= 1:
3223 return True
3224 else:
3225 visible_repository = self.__get_visible_repository_name()
3226 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
3227 if not pkg:
3228 return False
3229 if cmp(pkg.get_publisher(), visible_repository) == 0:
3230 return True
3231 else:
3232 return False
3233
3234 def __get_visible_repository_name(self):
3235 pub_iter = self.w_repository_combobox.get_active_iter()
3236 if pub_iter == None:
3237 return None
3238 visible = self.repositories_list.get_value(pub_iter, \
3239 enumerations.REPOSITORY_NAME)
3240 return visible
3241
3242 def __enable_disable_selection_menus(self):
3243 if self.in_setup:
3244 return
3245 self.__enable_disable_select_updates()
3246 if not self.__doing_search():
3247 self.unset_busy_cursor()
3248
3249 def __enable_disable_select_all(self):
3250 if self.in_setup:
3251 return
3252 if len(self.w_application_treeview.get_model()) > 0:
3253 for row in self.w_application_treeview.get_model():
3254 if not row[enumerations.MARK_COLUMN]:
3255 self.w_selectall_menuitem.set_sensitive(True)
3256 return
3257 self.w_selectall_menuitem.set_sensitive(False)
3258 else:
3259 self.w_selectall_menuitem.set_sensitive(False)
3260
3261 def __enable_disable_install_remove(self):
3262 if not self.user_rights:
3263 self.w_installupdate_button.set_sensitive(False)
3264 self.w_installupdate_menuitem.set_sensitive(False)
3265 self.w_remove_button.set_sensitive(False)
3266 self.w_remove_menuitem.set_sensitive(False)
3267 return
3268 selected_removal = self.__enable_if_selected_for_removal()
3269 selected_install_update = self.__enable_if_selected_for_install_update()
3270 if selected_removal or selected_install_update:
3271 return
3272 remove = False
3273 install = False
3274 if self.selected == 0:
3275 model, itr = self.package_selection.get_selected()
3276 if itr:
3277 status = \
3278 model.get_value(itr, enumerations.STATUS_COLUMN)
3279 if status == enumerations.NOT_INSTALLED:
3280 remove = False
3281 install = True
3282 elif status == enumerations.UPDATABLE:
3283 remove = True
3284 install = True
3285 elif status == enumerations.INSTALLED:
3286 remove = True
3287 install = False
3288 self.w_installupdate_button.set_sensitive(install)
3289 self.w_installupdate_menuitem.set_sensitive(install)
3290 self.w_remove_button.set_sensitive(remove)
3291 self.w_remove_menuitem.set_sensitive(remove)
3292
3293 def __enable_if_selected_for_removal(self):
3294 sensitive = False
3295 visible_repository = self.__get_visible_repository_name()
3296 selected = self.to_remove.get(visible_repository)
3297 if selected > 0:
3298 sensitive = True
3299 self.w_remove_button.set_sensitive(sensitive)
3300 self.w_remove_menuitem.set_sensitive(sensitive)
3301 return sensitive
3302
3303 def __enable_if_selected_for_install_update(self):
3304 sensitive = False
3305 visible_repository = self.__get_visible_repository_name()
3306 selected = self.to_install_update.get(visible_repository)
3307 if selected > 0:
3308 sensitive = True
3309 self.w_installupdate_button.set_sensitive(sensitive)
3310 self.w_installupdate_menuitem.set_sensitive(sensitive)
3311 return sensitive
3312
3313 def __enable_disable_select_updates(self):
3314 for row in self.w_application_treeview.get_model():
3315 if row[enumerations.STATUS_COLUMN] == enumerations.UPDATABLE:
3316 if not row[enumerations.MARK_COLUMN]:
3317 self.w_selectupdates_menuitem. \
3318 set_sensitive(True)
3319 return
3320 self.w_selectupdates_menuitem.set_sensitive(False)
3321 return
3322
3323 def __get_inventory_list(self, pargs, all_known, all_versions):
3324 self.__image_activity_lock.acquire()
3325 try:
3326 res = misc.get_inventory_list(self.api_o.img,
3327 pargs, all_known, all_versions)
3328 finally:
3329 self.__image_activity_lock.release()
3330 return res
3331
3332 def __enable_disable_update_all(self):
3333 #XXX Api to provide fast information if there are some updates
3334 #available within image
3335 gobject.idle_add(self.w_updateall_button.set_sensitive, False)
3336 gobject.idle_add(self.w_updateall_menuitem.set_sensitive, False)
3337 update_available = self.__check_if_updates_available()
3338 gobject.idle_add(self.__g_enable_disable_update_all, update_available)
3339 gobject.idle_add(self.__show_info_after_catalog_load)
3340 return False
3341
3342 def __show_info_after_catalog_load(self):
3343 self.__show_info(self.selected_model, self.selected_path)
3344 self.selected_model = None
3345 self.selected_path = None
3346 if (self.w_info_notebook.get_current_page() ==
3347 INFO_NOTEBOOK_LICENSE_PAGE and
3348 not self.showing_empty_details):
3349 self.__show_licenses()
3350
3351 def __check_if_updates_available(self):
3352 try:
3353 self.catalog_loaded = False
3354 self.api_o.refresh()
3355 self.catalog_loaded = True
3356 res = self.__get_inventory_list([], False, False)
3357 for pfmri, state in res:
3358 if state["upgradable"]:
3359 self.pylintstub = pfmri
3360 return True
3361
3362 except api_errors.InventoryException:
3363 gobject.idle_add(self.__set_empty_details_panel)
3364 return False
3365 return False
3366
3367 def __g_enable_disable_update_all(self, update_available):
3368 self.w_updateall_button.set_sensitive(update_available)
3369 self.w_updateall_menuitem.set_sensitive(update_available)
3370 self.__enable_disable_install_remove()
3371
3372 def __enable_disable_deselect(self):
3373 if self.w_application_treeview.get_model():
3374 for row in self.w_application_treeview.get_model():
3375 if row[enumerations.MARK_COLUMN]:
3376 self.w_deselect_menuitem.set_sensitive(True)
3377 return
3378 self.w_deselect_menuitem.set_sensitive(False)
3379 return
3380
3381 def __catalog_refresh(self, reload_gui=True):
3382 """Update image's catalogs."""
3383 try:
3384 # Since the user requested the refresh, perform it
3385 # immediately for all publishers.
3386 self.api_o.refresh(immediate=True)
3387 # Refresh will load the catalogs.
3388 self.catalog_loaded = True
3389 except api_errors.PublisherError:
3390 # In current implementation, this will never happen
3391 # We are not refreshing specific publisher
3392 self.__catalog_refresh_done()
3393 raise
3394 except api_errors.PermissionsException:
3395 #Error will already have been reported in
3396 #Manage Repository dialog
3397 self.__catalog_refresh_done()
3398 return -1
3399 except api_errors.CatalogRefreshException, cre:
3400 total = cre.total
3401 succeeded = cre.succeeded
3402 ermsg = _("Network problem.\n\n")
3403 ermsg += _("Details:\n")
3404 ermsg += "%s/%s" % (succeeded, total)
3405 ermsg += _(" catalogs successfully updated:\n")
3406 for pub, err in cre.failed:
3407 if isinstance(err, HTTPError):
3408 ermsg += " %s: %s - %s\n" % \
3409 (err.filename, err.code, err.msg)
3410 elif isinstance(err, URLError):
3411 if err.args[0][0] == 8:
3412 ermsg += " %s: %s\n" % \
3413 (urlparse.urlsplit(
3414 pub["origin"])[1].split(":")[0],
3415 err.args[0][1])
3416 else:
3417 if isinstance(err.args[0], \
3418 socket.timeout):
3419 ermsg += " %s: %s\n" % \
3420 (pub["origin"], "timeout")
3421 else:
3422 ermsg += " %s: %s\n" % \
3423 (pub["origin"], \
3424 err.args[0][1])
3425 elif "data" in err.__dict__ and err.data:
3426 ermsg += err.data
3427 else:
3428 ermsg += _("Unknown error")
3429 ermsg += "\n"
3430
3431 gobject.idle_add(self.error_occurred, ermsg,
3432 None, gtk.MESSAGE_INFO)
3433 self.__catalog_refresh_done()
3434 return -1
3435 except api_errors.InvalidDepotResponseException, idrex:
3436 err = str(idrex)
3437 gobject.idle_add(self.error_occurred, err,
3438 None, gtk.MESSAGE_INFO)
3439 self.__catalog_refresh_done()
3440 return -1
3441 except api_errors.PublisherError:
3442 self.__catalog_refresh_done()
3443 raise
3444 except Exception:
3445 self.__catalog_refresh_done()
3446 raise
3447 if reload_gui:
3448 self.__catalog_refresh_done()
3449 return 0
3450
3451 def __add_pkgs_to_lists_from_cache(self, publisher, application_list,
3452 category_list, section_list):
3453 if self.cache_o:
3454 self.cache_o.load_application_list(publisher, application_list,
3455 self.selected_pkgs)
3456 self.cache_o.load_category_list(publisher, category_list)
3457 self.cache_o.load_section_list(publisher, section_list)
3458
3459 def __add_pkgs_to_lists_from_api(self, publisher, application_list,
3460 category_list, section_list):
3461 """ This method set up image from the given directory and
3462 returns the image object or None"""
3463 pargs = []
3464 pargs.append("pkg://" + publisher + "/*")
3465 try:
3466 pkgs_known = self.__get_inventory_list(pargs,
3467 True, True)
3468 except api_errors.InventoryException:
3469 # This can happen if the repository does not
3470 # contain any packages
3471 err = _("Selected repository does not contain any packages.")
3472 gobject.idle_add(self.w_progress_dialog.hide)
3473 gobject.idle_add(self.error_occurred, err, None,
3474 gtk.MESSAGE_INFO)
3475 self.unset_busy_cursor()
3476 pkgs_known = []
3477
3478 return self.__add_pkgs_to_lists(pkgs_known, application_list,
3479 category_list, section_list)
3480
3481 def __add_pkgs_to_lists(self, pkgs_known, application_list,
3482 category_list, section_list):
3483 if section_list != None:
3484 self.__init_sections(section_list)
3485 #Only one instance of those icons should be in memory
3486 update_available_icon = gui_misc.get_icon(self.icon_theme,
3487 "status_newupdate")
3488 installed_icon = gui_misc.get_icon(self.icon_theme,
3489 "status_installed")
3490 update_for_category_icon = \
3491 self.get_icon_pixbuf_from_glade_dir("legend_newupdate")
3492 #Imageinfo for categories
3493 imginfo = imageinfo.ImageInfo()
3494 sectioninfo = imageinfo.ImageInfo()
3495 pubs = [p.prefix for p in self.api_o.get_publishers()]
3496 categories = {}
3497 sections = {}
3498 share_path = "/usr/share/package-manager/data/"
3499 for pub in pubs:
3500 category = imginfo.read(self.application_dir +
3501 share_path + pub)
3502 if len(category) == 0:
3503 category = imginfo.read(self.application_dir +
3504 share_path + "opensolaris.org")
3505 categories[pub] = category
3506 section = sectioninfo.read(self.application_dir +
3507 share_path + pub + ".sections")
3508 if len(section) == 0:
3509 section = sectioninfo.read(self.application_dir +
3510 share_path + "opensolaris.org.sections")
3511 sections[pub] = section
3512 pkg_count = 0
3513 pkg_add = 0
3514 progress_percent = INITIAL_PROGRESS_TOTAL_PERCENTAGE
3515 total_pkg_count = len(pkgs_known)
3516 progress_increment = \
3517 total_pkg_count / PACKAGE_PROGRESS_TOTAL_INCREMENTS
3518 self.progress_stop_timer_thread = True
3519 while gtk.events_pending():
3520 gtk.main_iteration(False)
3521 prev_stem = ""
3522 prev_pfmri_str = ""
3523 next_app = None
3524 pkg_name = None
3525 pkg_publisher = None
3526 prev_state = None
3527 category_icon = None
3528 for pkg, state in pkgs_known:
3529 if prev_pfmri_str and \
3530 prev_pfmri_str == pkg.get_short_fmri() and \
3531 prev_state == state:
3532 pkg_count += 1
3533 continue
3534 if prev_stem and \
3535 prev_stem == pkg.get_pkg_stem() and \
3536 prev_state["state"] == "known" and \
3537 state["state"] == "installed":
3538 pass
3539 elif next_app != None:
3540 self.__add_package_to_list(next_app,
3541 application_list,
3542 pkg_add, pkg_name,
3543 category_icon,
3544 categories, category_list, pkg_publisher)
3545 pkg_add += 1
3546 prev_stem = pkg.get_pkg_stem()
3547 prev_pfmri_str = pkg.get_short_fmri()
3548 prev_state = state
3549
3550 if progress_increment > 0 and pkg_count % progress_increment == 0:
3551 progress_percent += PACKAGE_PROGRESS_PERCENT_INCREMENT
3552 if progress_percent <= PACKAGE_PROGRESS_PERCENT_TOTAL:
3553 self.__progressdialog_progress_percent(
3554 progress_percent, pkg_count, total_pkg_count)
3555 while gtk.events_pending():
3556 gtk.main_iteration(False)
3557
3558 status_icon = None
3559 category_icon = None
3560 pkg_name = pkg.get_name()
3561 pkg_name = gui_misc.get_pkg_name(pkg_name)
3562 pkg_stem = pkg.get_pkg_stem()
3563 pkg_publisher = pkg.get_publisher()
3564 pkg_state = enumerations.NOT_INSTALLED
3565 if state["state"] == "installed":
3566 pkg_state = enumerations.INSTALLED
3567 if state["upgradable"] == True:
3568 status_icon = update_available_icon
3569 category_icon = update_for_category_icon
3570 pkg_state = enumerations.UPDATABLE
3571 else:
3572 status_icon = installed_icon
3573 marked = False
3574 if not self.is_search_all:
3575 pkgs = self.selected_pkgs.get(pkg_publisher)
3576 if pkgs != None:
3577 if pkg_stem in pkgs:
3578 marked = True
3579 next_app = \
3580 [
3581 marked, status_icon, pkg_name, '...', pkg_state,
3582 pkg, pkg_stem, None, True, None, pkg_publisher
3583 ]
3584 pkg_count += 1
3585
3586 if next_app:
3587 self.__add_package_to_list(next_app, application_list,
3588 pkg_add, pkg_name, category_icon, categories,
3589 category_list, pkg_publisher)
3590 pkg_add += 1
3591 if category_list != None:
3592 self.__add_categories_to_sections(sections,
3593 category_list, section_list)
3594 self.__progressdialog_progress_percent(PACKAGE_PROGRESS_PERCENT_TOTAL,
3595 total_pkg_count, total_pkg_count)
3596 return
3597
3598 def __add_categories_to_sections(self, sections, category_list, section_list):
3599 for publisher in sections:
3600 for section in sections[publisher]:
3601 for category in sections[publisher][section].split(","):
3602 self.__add_category_to_section(_(category),
3603 _(section), category_list, section_list)
3604
3605 #1915 Sort the Categories into alphabetical order and prepend All Category
3606 if len(category_list) > 0:
3607 rows = [tuple(r) + (i,) for i, r in enumerate(category_list)]
3608 rows.sort(self.__sort)
3609 r = []
3610 category_list.reorder([r[-1] for r in rows])
3611 return
3612
3613 def __add_package_to_list(self, app, application_list, pkg_add,
3614 pkg_name, category_icon, categories, category_list, publisher):
3615 row_iter = application_list.insert(pkg_add, app)
3616 if category_list == None:
3617 return
3618 cat_pub = categories.get(publisher)
3619 if pkg_name in cat_pub:
3620 pkg_categories = cat_pub.get(pkg_name)
3621 for pcat in pkg_categories.split(","):
3622 self.__add_package_to_category(_(pcat), None,
3623 category_icon, row_iter, application_list,
3624 category_list)
3625
3626 @staticmethod
3627 def __add_package_to_category(category_name, category_description,
3628 category_icon, package, application_list, category_list):
3629 if not package or category_name == _('All'):
3630 return
3631 if not category_name:
3632 return
3633 category_id = None
3634 icon_visible = False
3635 if category_icon:
3636 icon_visible = True
3637 for category in category_list:
3638 if category[enumerations.CATEGORY_NAME] == category_name:
3639 category_id = category[enumerations.CATEGORY_ID]
3640 if category_icon:
3641 category[enumerations.CATEGORY_ICON] = \
3642 category_icon
3643 category[enumerations.CATEGORY_ICON_VISIBLE] = \
3644 icon_visible
3645 break
3646 if not category_id: # Category not exists
3647 category_id = len(category_list) + 1
3648 category_list.append([category_id, category_name,
3649 category_description, category_icon, icon_visible,
3650 True, None])
3651 if application_list.get_value(package,
3652 enumerations.CATEGORY_LIST_COLUMN):
3653 a = application_list.get_value(package,
3654 enumerations.CATEGORY_LIST_COLUMN)
3655 a.append(category_id)
3656 else:
3657 category_list = []
3658 category_list.append(category_id)
3659 application_list.set(package,
3660 enumerations.CATEGORY_LIST_COLUMN, category_list)
3661
3662 @staticmethod
3663 def __add_category_to_section(category_name, section_name, category_list,
3664 section_list):
3665 '''Adds the section to section list in category. If there is no such
3666 section, than it is not added. If there was already section than it
3667 is skipped. Sections must be case sensitive'''
3668 if not category_name:
3669 return
3670 for section in section_list:
3671 if section[enumerations.SECTION_NAME] == section_name:
3672 section_id = section[enumerations.SECTION_ID]
3673 for category in category_list:
3674 if category[enumerations.CATEGORY_NAME] == \
3675 category_name:
3676 section_lst = category[ \
3677 enumerations.SECTION_LIST_OBJECT]
3678 section[enumerations.SECTION_ENABLED] = \
3679 True
3680 if not section_lst:
3681 category[ \
3682 enumerations.SECTION_LIST_OBJECT] = \
3683 [section_id, ]
3684 else:
3685 if not section_name in \
3686 section_lst:
3687 section_lst.append(
3688 section_id)
3689
3690 def __progressdialog_progress_pulse(self):
3691 while not self.progress_stop_timer_thread:
3692 gobject.idle_add(self.w_progressbar.pulse)
3693 time.sleep(0.1)
3694 gobject.idle_add(self.w_progress_dialog.hide)
3695 self.progress_stop_timer_thread = False
3696
3697 # For initial setup before loading package entries allow 5% of progress bar
3698 # update it on a time base as we have no other way to judge progress at this point
3699 def __progressdialog_progress_time(self):
3700 while not self.progress_stop_timer_thread and \
3701 self.progress_fraction_time_count <= \
3702 INITIAL_PROGRESS_TOTAL_PERCENTAGE:
3703
3704 gobject.idle_add(self.w_progressbar.set_fraction,
3705 self.progress_fraction_time_count)
3706 self.progress_fraction_time_count += \
3707 INITIAL_PROGRESS_TIME_PERCENTAGE
3708 time.sleep(INITIAL_PROGRESS_TIME_INTERVAL)
3709 self.progress_stop_timer_thread = False
3710 self.progress_fraction_time_count = 0
3711
3712 def __progressdialog_progress_percent(self, fraction, count, total):
3713 gobject.idle_add(self.w_progressinfo_label.set_text, _(
3714 "Processing package entries: %d of %d") % (count, total) )
3715 gobject.idle_add(self.w_progressbar.set_fraction, fraction)
3716
3717 def error_occurred(self, error_msg, msg_title=None, msg_type=gtk.MESSAGE_ERROR):
3718 if msg_title:
3719 title = msg_title
3720 else:
3721 title = _("Package Manager")
3722 gui_misc.error_occurred(self.w_main_window, error_msg,
3723 title, msg_type, use_markup=True)
3724
3725
3726 msgbox = gtk.MessageDialog(parent =
3727 self.w_main_window,
3728 buttons = gtk.BUTTONS_CLOSE,
3729 flags = gtk.DIALOG_MODAL,
3730 type = msg_type,
3731 message_format = None)
3732 msgbox.set_property('text', error_msg)
3733 title = None
3734 if msg_title:
3735 title = msg_title
3736 else:
3737 title = _("Package Manager")
3738 msgbox.set_title(title)
3739 msgbox.run()
3740 msgbox.destroy()
3741
3742 #-----------------------------------------------------------------------------#
3743 # Static Methods
3744 #-----------------------------------------------------------------------------#
3745
3746 #@staticmethod
3747 #def N_(message):
3748 # return message
3749
3750 @staticmethod
3751 def __sort(a, b):
3752 return cmp(a[1], b[1])
3753
3754 @staticmethod
3755 def cell_data_function(column, renderer, model, itr, data):
3756 '''Function which sets the background colour to black if package is
3757 selected'''
3758 if itr:
3759 if model.get_value(itr, enumerations.MARK_COLUMN):
3760 renderer.set_property("cell-background", "#ffe5cc")
3761 renderer.set_property("cell-background-set", True)
3762 else:
3763 renderer.set_property("cell-background-set", False)
3764
3765 @staticmethod
3766 def combobox_separator(model, itr):
3767 return model.get_value(itr, enumerations.FILTER_NAME) == ""
3768
3769 @staticmethod
3770 def combobox_id_separator(model, itr):
3771 return model.get_value(itr, 0) == -1 and \
3772 model.get_value(itr, 1) == ""
3773
3774 @staticmethod
3775 def category_filter(model, itr):
3776 '''This function filters category in the main application view'''
3777 return model.get_value(itr, enumerations.CATEGORY_VISIBLE)
3778
3779 @staticmethod
3780 def get_datetime(version):
3781 dt = None
3782 try:
3783 dt = version.get_datetime()
3784 except AttributeError:
3785 dt = version.get_timestamp()
3786 return dt
3787
3788 @staticmethod
3789 def get_installed_version(api_o, pkg):
3790 info = api_o.info([pkg], False, frozenset(
3791 [api.PackageInfo.STATE, api.PackageInfo.IDENTITY]))
3792 found = info[api.ImageInterface.INFO_FOUND]
3793 try:
3794 version = found[0]
3795 except IndexError:
3796 version = None
3797 return version
3798
3799 #-----------------------------------------------------------------------------#
3800 # Public Methods
3801 #-----------------------------------------------------------------------------#
3802 def setup_progressdialog_show(self):
3803 self.w_progress_dialog.set_title(_("Loading Repository Information"))
3804 self.w_progressinfo_label.set_text(
3805 _( "Fetching package entries ..."))
3806 self.w_progress_cancel.hide()
3807 self.w_progress_dialog.show()
3808 Thread(target = self.__progressdialog_progress_time).start()
3809
3810 def setup_progressdialog_hide(self):
3811 self.progress_stop_timer_thread = True
3812 self.w_progress_dialog.hide()
3813
3814 def init_show_filter(self):
3815 self.__init_show_filter() #Initiates filter
3816
3817 def reload_packages(self):
3818 self.api_o = gui_misc.get_api_object(self.image_directory,
3819 self.pr, self.w_main_window)
3820 self.cache_o = self.__get_cache_obj(self.icon_theme,
3821 self.application_dir, self.api_o)
3822 self.__on_reload(None)
3823
3824 def set_busy_cursor(self):
3825 self.gdk_window.show()
3826
3827 def unset_busy_cursor(self):
3828 self.gdk_window.hide()
3829
3830 def process_package_list_start(self, image_directory):
3831 self.image_directory = image_directory
3832 if not self.api_o:
3833 self.api_o = gui_misc.get_api_object(image_directory,
3834 self.pr, self.w_main_window)
3835 self.cache_o = self.__get_cache_obj(self.icon_theme,
3836 self.application_dir, self.api_o)
3837 self.img_timestamp = self.cache_o.get_index_timestamp()
3838 self.repositories_list = self.__get_new_repositories_liststore()
3839 self.__setup_repositories_combobox(self.api_o, self.repositories_list)
3840
3841 @staticmethod
3842 def __get_cache_obj(icon_theme, application_dir, api_o):
3843 cache_o = cache.CacheListStores(icon_theme, application_dir,
3844 api_o)
3845 return cache_o
3846
3847 def process_package_list_end(self):
3848 self.__set_first_category_text()
3849 self.in_startpage_startup = False
3850 if self.update_all_proceed:
3851 # TODO: Handle situation where only SUNWipkg/SUNWipg-gui have been updated
3852 # in update all: bug 6357
3853 self.__on_update_all(None)
3854 self.update_all_proceed = False
3855 self.setup_progressdialog_hide()
3856 self.__enable_disable_install_remove()
3857 self.update_statusbar()
3858 self.in_setup = False
3859 self.cancelled = False
3860 if self.set_section != 0 or \
3861 self.set_show_filter != enumerations.FILTER_ALL:
3862 self.__application_refilter()
3863 else:
3864 self.unset_busy_cursor()
3865
3866 if self.first_run or self.in_reload:
3867 Thread(target = self.__enable_disable_update_all).start()
3868 self.first_run = False
3869 self.in_reload = False
3870
3871 def get_icon_pixbuf_from_glade_dir(self, icon_name):
3872 return gui_misc.get_pixbuf_from_path(self.application_dir +
3873 "/usr/share/package-manager/", icon_name)
3874
3875 def update_statusbar(self):
3876 '''Function which updates statusbar'''
3877 if self.statusbar_message_id > 0:
3878 self.w_main_statusbar.remove(0, self.statusbar_message_id)
3879 self.statusbar_message_id = 0
3880 search_text = self.w_searchentry.get_text()
3881
3882 if not self.in_search_mode:
3883 installed = 0
3884 self.selected = 0
3885 sel = 0
3886 if self.application_list == None:
3887 return
3888 visible_repository = self.__get_visible_repository_name()
3889 pkgs = self.selected_pkgs.get(visible_repository)
3890 if pkgs:
3891 self.selected = len(pkgs)
3892 for pkg_row in self.application_list:
3893 if pkg_row[enumerations.STATUS_COLUMN] == \
3894 enumerations.INSTALLED \
3895 or pkg_row[enumerations.STATUS_COLUMN] == \
3896 enumerations.UPDATABLE:
3897 installed = installed + 1
3898 if pkg_row[enumerations.MARK_COLUMN]:
3899 sel = sel + 1
3900 listed_str = _('%d listed') % len(self.application_list)
3901 sel_str = _('%d selected') % sel
3902 inst_str = _('%d installed') % installed
3903 status_str = _("%s: %s , %s, %s.") % (visible_repository,
3904 listed_str, inst_str, sel_str)
3905 self.w_main_statusbar.push(0, status_str)
3906 return
3907
3908 # In Search Mode
3909 active = ""
3910 if self.is_search_all:
3911 if self.__doing_search():
3912 if self.current_search_publisher != None:
3913 active = "(" + self.current_search_publisher + \
3914 ") "
3915 opt_str = _('Searching... '
3916 '%(active)sfor "%(search_text)s"') % \
3917 {"active": active, "search_text": search_text}
3918 else:
3919 opt_str = _('Searched All for "%s"') % (search_text)
3920 else:
3921 search_str = _("Searched")
3922 if self.__doing_search():
3923 search_str = _("Searching...")
3924 if self.last_active_publisher != None:
3925 active = "(" + self.last_active_publisher + ") "
3926 opt_str = \
3927 _('%(search)s %(last_active)sfor "%(search_text)s"') \
3928 % {"search": search_str, "last_active" : active,
3929 "search_text" : search_text}
3930 fmt_str = _("%(option_str)s: %(number)d found %(time)s")
3931 time_str = ""
3932 if self.search_time_sec > 0:
3933 time_str = _("in %d seconds") % self.search_time_sec
3934 status_str = fmt_str % {"option_str" : opt_str, "number" :
3935 len(self.application_list), "time" : time_str}
3936 self.w_main_statusbar.push(0, status_str)
3937
3938 def update_package_list(self, update_list):
3939 if update_list == None and self.img_timestamp:
3940 return
3941 visible_repository = self.__get_visible_repository_name()
3942 default_publisher = self.default_publisher
3943 self.api_o.refresh()
3944 if not self.img_timestamp:
3945 self.img_timestamp = self.cache_o.get_index_timestamp()
3946 self.__on_reload(None)
3947 return
3948 self.img_timestamp = self.cache_o.get_index_timestamp()
3949 installed_icon = gui_misc.get_icon(self.icon_theme,
3950 "status_installed")
3951 visible_list = update_list.get(visible_repository)
3952 if visible_list:
3953 i = 0
3954 while i < len(visible_list):
3955 visible_list[i] = gui_misc.get_pkg_name(
3956 visible_list[i])
3957 i += 1
3958 for row in self.application_list:
3959 if row[enumerations.NAME_COLUMN] in visible_list:
3960 pkg = row[enumerations.FMRI_COLUMN]
3961 pkg_stem = row[enumerations.STEM_COLUMN]
3962 self.__remove_pkg_stem_from_list(pkg_stem)
3963 if self.info_cache.has_key(pkg_stem):
3964 del self.info_cache[pkg_stem]
3965 package_info = self.get_installed_version(
3966 self.api_o, pkg_stem)
3967 package_installed = (package_info.state
3968 == api.PackageInfo.INSTALLED)
3969 print pkg_stem, package_installed
3970 if package_installed:
3971 row[enumerations.STATUS_COLUMN] = \
3972 enumerations.INSTALLED
3973 row[enumerations.STATUS_ICON_COLUMN] = \
3974 installed_icon
3975 else:
3976 row[enumerations.STATUS_COLUMN] = \
3977 enumerations.NOT_INSTALLED
3978 row[enumerations.STATUS_ICON_COLUMN] = \
3979 None
3980 row[enumerations.MARK_COLUMN] = False
3981 self.__dump_datamodels(visible_repository,
3982 self.application_list, self.category_list,
3983 self.section_list)
3984 for publisher in update_list:
3985 if publisher != visible_repository:
3986 pkg_list = update_list.get(publisher)
3987 for pkg in pkg_list:
3988 pkg_stem = None
3989 if publisher != default_publisher:
3990 pkg_stem = "pkg://%s/%s" % \
3991 (publisher, pkg)
3992 else:
3993 pkg_stem = "pkg:/%s" % pkg
3994 if pkg_stem:
3995 if self.info_cache.has_key(pkg_stem):
3996 del self.info_cache[pkg_stem]
3997 self.__remove_pkg_stem_from_list(pkg_stem)
3998 self.__process_package_selection()
3999 self.__enable_disable_selection_menus()
4000 self.__enable_disable_install_remove()
4001 self.update_statusbar()
4002 Thread(target = self.__enable_disable_update_all).start()
4003
4004 @staticmethod
4005 def __find_root_home_dir():
4006 return_str = '/var/tmp'
4007
4008 try:
4009 lines = pwd.getpwnam('root')
4010 except KeyError:
4011 if debug:
4012 print "Error getting passwd database entry for root"
4013 return return_str
4014 try:
4015 return_str = lines[5]
4016 except IndexError:
4017 if debug:
4018 print "Error getting home directory for root"
4019 return return_str
4020
4021 def restart_after_ips_update(self, be_name):
4022 self.__main_application_quit(be_name)
4023
4024 def shutdown_after_image_update(self):
4025 info_str = _("The Update All action is now complete and "
4026 "Package Manager will close.\n\nReview the posted release notes "
4027 "before rebooting your system:\n\n"
4028 )
4029 self.w_ua_completed_release_label.set_text(info_str.strip('\n'))
4030
4031 info_str = misc.get_release_notes_url()
4032 self.w_ua_completed_linkbutton.set_uri(info_str)
4033 self.w_ua_completed_linkbutton.set_label(info_str)
4034 self.release_notes_url = info_str
4035
4036 self.w_ua_completed_dialog.set_title(_("Update All Complete"))
4037 self.w_ua_completed_dialog.show()
4038
4039 ###############################################################################
4040 #-----------------------------------------------------------------------------#
4041 # Main
4042 #-----------------------------------------------------------------------------#
4043
4044 def main():
4045 gtk.main()
4046 return 0
4047
4048 if __name__ == '__main__':
4049 debug = False
4050 debug_descriptions = False
4051 update_all_proceed = False
4052 ua_be_name = None
4053 app_path = None
4054 image_dir = None
4055 info_install_arg = None
4056 save_selected = _("Save selected...")
4057 save_selected_pkgs = _("Save selected packages...")
4058 reboot_needed = _("The installed package(s) require a reboot before "
4059 "installation can be completed.")
4060
4061 try:
4062 opts, args = getopt.getopt(sys.argv[1:], "hR:U:i:", \
4063 ["help", "image-dir=", "update-all=", "info-install="])
4064 except getopt.error, msg:
4065 print "%s, for help use --help" % msg
4066 sys.exit(2)
4067
4068 if os.path.isabs(sys.argv[0]):
4069 app_path = sys.argv[0]
4070 else:
4071 cmd = os.path.join(os.getcwd(), sys.argv[0])
4072 app_path = os.path.realpath(cmd)
4073
4074 for option, argument in opts:
4075 if option in ("-h", "--help"):
4076 print """\
4077 Use -R (--image-dir) to specify image directory.
4078 Use -U (--update-all) to proceed with Update All"""
4079 sys.exit(0)
4080 if option in ("-R", "--image-dir"):
4081 image_dir = argument
4082 if option in ("-U", "--update-all"):
4083 update_all_proceed = True
4084 ua_be_name = argument
4085 if option in ("-i", "--info-install"):
4086 info_install_arg = argument
4087
4088 if image_dir == None:
4089 try:
4090 image_dir = os.environ["PKG_IMAGE"]
4091 except KeyError:
4092 image_dir = os.getcwd()
4093 try:
4094 gtk.init_check()
4095 except RuntimeError, e:
4096 print _("Unable to initialize gtk")
4097 print str(e)
4098 sys.exit(1)
4099
4100 # Setup webinstall
4101 if info_install_arg or len(sys.argv) == 2:
4102 webinstall = webinstall.Webinstall(image_dir)
4103 if len(sys.argv) == 2:
4104 info_install_arg = sys.argv[1]
4105 webinstall.process_param(info_install_arg)
4106 main()
4107 sys.exit(0)
4108
4109 # Setup packagemanager
4110 packagemanager = PackageManager()
4111 packagemanager.application_path = app_path
4112 packagemanager.image_dir_arg = image_dir
4113 packagemanager.update_all_proceed = update_all_proceed
4114 packagemanager.ua_be_name = ua_be_name
4115
4116 while gtk.events_pending():
4117 gtk.main_iteration(False)
4118
4119 packagemanager.init_show_filter()
4120
4121 packagemanager.process_package_list_start(image_dir)
4122
4123 main()