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 print "__get_application_categories_lists: ", self.visible_repository
2484 if not self.visible_repository:
2485 self.visible_repository = self.__get_active_publisher()
2486 print "** __get_application_categories_lists: ", self.visible_repository
2487 application_list = self.__get_new_application_liststore()
2488 category_list = self.__get_new_category_liststore()
2489 section_list = self.__get_new_section_liststore()
2490 first_loop = True
2491 for publisher in publishers:
2492 uptodate = False
2493 try:
2494 uptodate = self.__check_if_cache_uptodate(publisher)
2495 if uptodate:
2496 self.__add_pkgs_to_lists_from_cache(publisher,
2497 application_list, category_list,
2498 section_list)
2499 except (UnpicklingError, EOFError, IOError):
2500 #Most likely cache is corrupted, silently load list
2501 #from api.
2502 application_list = self.__get_new_application_liststore()
2503 category_list = self.__get_new_category_liststore()
2504 uptodate = False
2505 if not uptodate:
2506 if first_loop == True:
2507 first_loop = False
2508 gobject.idle_add(self.setup_progressdialog_show)
2509 self.api_o.refresh(pubs=[publisher])
2510 self.__add_pkgs_to_lists_from_api(publisher,
2511 application_list, category_list, section_list)
2512 category_list.prepend([0, _('All'), None, None, False,
2513 True, None])
2514 if self.application_list and self.category_list and \
2515 not self.visible_repository_uptodate:
2516 if self.visible_repository:
2517 dump_list = self.application_list
2518 if self.saved_application_list != None:
2519 dump_list = \
2520 self.saved_application_list
2521 self.__dump_datamodels(self.visible_repository,
2522 dump_list, self.category_list,
2523 self.section_list)
2524 self.visible_repository = self.__get_active_publisher()
2525 self.visible_repository_uptodate = uptodate
2526 return application_list, category_list, section_list
2527
2528 def __check_if_cache_uptodate(self, publisher):
2529 if self.cache_o:
2530 return self.cache_o.check_if_cache_uptodate(publisher)
2531 return False
2532
2533 def __dump_datamodels(self, publisher, application_list, category_list,
2534 section_list):
2535 #Consistency check - only dump models if publisher passed in matches
2536 #publisher in application list
2537 if application_list == None:
2538 return
2539 app_pub = application_list[0][enumerations.AUTHORITY_COLUMN]
2540 if publisher != app_pub:
2541 if debug:
2542 print "ERROR: __dump_data_models(): INCONSISTENT " \
2543 "pub %s != app_list_pub %s" % \
2544 (publisher, app_pub)
2545 return
2546
2547 if self.cache_o:
2548 if self.img_timestamp == \
2549 self.cache_o.get_index_timestamp():
2550 Thread(target = self.cache_o.dump_datamodels,
2551 args = (publisher, application_list, category_list,
2552 section_list)).start()
2553 else:
2554 self.__remove_cache()
2555
2556 def __remove_cache(self):
2557 model = self.w_repository_combobox.get_model()
2558 for publisher in model:
2559 pub_name = publisher[1]
2560 if pub_name and pub_name != _("Add..."):
2561 Thread(target = self.cache_o.remove_datamodel,
2562 args = [publisher[1]]).start()
2563
2564 def __on_install_update(self, widget):
2565 self.api_o.reset()
2566 install_update = []
2567 if self.selected == 0:
2568 model, itr = self.package_selection.get_selected()
2569 if itr:
2570 install_update.append(
2571 model.get_value(itr, enumerations.STEM_COLUMN))
2572 else:
2573 visible_repository = self.__get_visible_repository_name()
2574 pkgs = self.selected_pkgs.get(visible_repository)
2575 if pkgs:
2576 for pkg_stem in pkgs:
2577 status = pkgs.get(pkg_stem)
2578 if status == enumerations.NOT_INSTALLED or \
2579 status == enumerations.UPDATABLE:
2580 install_update.append(pkg_stem)
2581
2582 if self.img_timestamp != self.cache_o.get_index_timestamp():
2583 self.img_timestamp = None
2584 self.__remove_cache()
2585
2586 installupdate.InstallUpdate(install_update, self, \
2587 self.api_o, ips_update = False, \
2588 action = enumerations.INSTALL_UPDATE)
2589
2590 def __on_update_all(self, widget):
2591 self.api_o.reset()
2592 installupdate.InstallUpdate([], self,
2593 self.api_o, ips_update = False,
2594 action = enumerations.IMAGE_UPDATE, be_name = self.ua_be_name,
2595 parent_name = _("Package Manager"),
2596 pkg_list = ["SUNWipkg", "SUNWipkg-gui"],
2597 main_window = self.w_main_window)
2598 return
2599
2600 def __on_ua_completed_linkbutton_clicked(self, widget):
2601 try:
2602 gnome.url_show(self.release_notes_url)
2603 except gobject.GError:
2604 self.error_occurred(_("Unable to navigate to:\n\t%s") %
2605 self.release_notes_url)
2606
2607 def __on_help_about(self, widget):
2608 wTreePlan = gtk.glade.XML(self.gladefile, "aboutdialog")
2609 aboutdialog = wTreePlan.get_widget("aboutdialog")
2610 aboutdialog.connect("response", lambda x = None, \
2611 y = None: aboutdialog.destroy())
2612 aboutdialog.run()
2613
2614 def __on_help_help(self, widget):
2615 gui_misc.display_help(self.application_dir)
2616
2617 def __on_remove(self, widget):
2618 self.api_o.reset()
2619 remove_list = []
2620 if self.selected == 0:
2621 model, itr = self.package_selection.get_selected()
2622 if itr:
2623 remove_list.append(
2624 model.get_value(itr, enumerations.STEM_COLUMN))
2625 else:
2626 visible_repository = self.__get_visible_repository_name()
2627 pkgs = self.selected_pkgs.get(visible_repository)
2628 if pkgs:
2629 for pkg_stem in pkgs:
2630 status = pkgs.get(pkg_stem)
2631 if status == enumerations.INSTALLED or \
2632 status == enumerations.UPDATABLE:
2633 remove_list.append(pkg_stem)
2634
2635 if self.img_timestamp != self.cache_o.get_index_timestamp():
2636 self.img_timestamp = None
2637 self.__remove_cache()
2638
2639 installupdate.InstallUpdate(remove_list, self,
2640 self.api_o, ips_update = False,
2641 action = enumerations.REMOVE)
2642
2643 def __on_reload(self, widget):
2644 if self.description_thread_running:
2645 self.cancelled = True
2646 if self.in_search_mode or self.is_search_all:
2647 self.__unset_search(False)
2648 self.__set_empty_details_panel()
2649 self.in_setup = True
2650 self.visible_repository = None
2651 if widget != None:
2652 self.__remove_cache()
2653 self.w_progress_dialog.set_title(_("Refreshing catalogs"))
2654 self.w_progressinfo_label.set_text(_("Refreshing catalogs..."))
2655 self.progress_stop_timer_thread = False
2656 Thread(target = self.__progressdialog_progress_pulse).start()
2657 self.w_progress_dialog.show()
2658 self.w_progress_cancel.hide()
2659 self.__disconnect_models()
2660 self.in_reload = True
2661 Thread(target = self.__catalog_refresh).start()
2662
2663 def __catalog_refresh_done(self):
2664 self.progress_stop_timer_thread = True
2665 #Let the progress_pulse finish. This should be done other way, but at
2666 #The moment this works fine
2667 time.sleep(0.2)
2668 gobject.idle_add(self.w_progress_cancel.show)
2669 gobject.idle_add(self.process_package_list_start,
2670 self.image_directory)
2671
2672 def __main_application_quit(self, be_name = None):
2673 '''quits the main gtk loop'''
2674 self.cancelled = True
2675 if self.in_setup:
2676 return
2677
2678 if be_name:
2679 if self.image_dir_arg:
2680 gobject.spawn_async([self.application_path, "-R",
2681 self.image_dir_arg, "-U", be_name])
2682 else:
2683 gobject.spawn_async([self.application_path,
2684 "-U", be_name])
2685 elif self.in_search_mode:
2686 self.__dump_datamodels(self.visible_repository,
2687 self.saved_application_list, self.category_list,
2688 self.section_list)
2689 else:
2690 visible_repository = self.__get_visible_repository_name()
2691 self.__dump_datamodels(visible_repository,
2692 self.application_list, self.category_list,
2693 self.section_list)
2694
2695 width, height = self.w_main_window.get_size()
2696 hpos = self.w_main_hpaned.get_position()
2697 vpos = self.w_main_vpaned.get_position()
2698 try:
2699 self.client.set_int(INITIAL_APP_WIDTH_PREFERENCES, width)
2700 self.client.set_int(INITIAL_APP_HEIGHT_PREFERENCES, height)
2701 self.client.set_int(INITIAL_APP_HPOS_PREFERENCES, hpos)
2702 self.client.set_int(INITIAL_APP_VPOS_PREFERENCES, vpos)
2703 except GError:
2704 pass
2705
2706 self.w_main_window.hide()
2707 while gtk.events_pending():
2708 gtk.main_iteration(False)
2709 gtk.main_quit()
2710 sys.exit(0)
2711 return True
2712
2713 def __check_if_something_was_changed(self):
2714 ''' Returns True if any of the check boxes for package was changed, false
2715 if not'''
2716 if self.application_list:
2717 for pkg in self.application_list:
2718 if pkg[enumerations.MARK_COLUMN] == True:
2719 return True
2720 return False
2721
2722 def __setup_repositories_combobox(self, api_o, repositories_list):
2723 self.__disconnect_repository_model()
2724 default_pub = api_o.get_preferred_publisher().prefix
2725 if self.default_publisher != default_pub:
2726 self.__clear_pkg_selections()
2727 self.default_publisher = default_pub
2728 selected_repos = []
2729 enabled_repos = []
2730 for repo in self.selected_pkgs:
2731 selected_repos.append(repo)
2732 i = 0
2733 active = 0
2734 for pub in api_o.get_publishers():
2735 if pub.disabled:
2736 continue
2737 prefix = pub.prefix
2738 if cmp(prefix, self.default_publisher) == 0:
2739 active = i
2740 repositories_list.append([i, prefix, ])
2741 enabled_repos.append(prefix)
2742 i = i + 1
2743 repositories_list.append([-1, "", ])
2744 repositories_list.append([-1, _("Add..."), ])
2745 pkgs_to_remove = []
2746 for repo_name in selected_repos:
2747 if repo_name not in enabled_repos:
2748 pkg_stems = self.selected_pkgs.get(repo_name)
2749 for pkg_stem in pkg_stems:
2750 pkgs_to_remove.append(pkg_stem)
2751 for pkg_stem in pkgs_to_remove:
2752 self.__remove_pkg_stem_from_list(pkg_stem)
2753 self.w_repository_combobox.set_model(repositories_list)
2754 if self.default_publisher:
2755 self.w_repository_combobox.set_active(active)
2756 else:
2757 self.w_repository_combobox.set_active(0)
2758
2759 def __active_pane_toggle(self, cell, path, model_sort):
2760 '''Toggle function for column enumerations.MARK_COLUMN'''
2761 applicationModel = model_sort.get_model()
2762 applicationPath = model_sort.convert_path_to_child_path(path)
2763 filterModel = applicationModel.get_model()
2764 child_path = applicationModel.convert_path_to_child_path(applicationPath)
2765 itr = filterModel.get_iter(child_path)
2766 if itr:
2767 modified = filterModel.get_value(itr, enumerations.MARK_COLUMN)
2768 filterModel.set_value(itr, enumerations.MARK_COLUMN,
2769 not modified)
2770 pkg_status = filterModel.get_value(itr,
2771 enumerations.STATUS_COLUMN)
2772 pkg_stem = filterModel.get_value(itr, enumerations.STEM_COLUMN)
2773 if modified:
2774 self.__remove_pkg_stem_from_list(pkg_stem)
2775 else:
2776 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2777 self.update_statusbar()
2778 self.__enable_disable_selection_menus()
2779
2780 def __update_reload_button(self):
2781 if self.user_rights:
2782 self.w_reload_button.set_sensitive(True)
2783 else:
2784 self.w_reload_button.set_sensitive(False)
2785
2786 def __add_pkg_stem_to_list(self, stem, status):
2787 publisher = self.__get_active_publisher()
2788 if self.selected_pkgs.get(publisher) == None:
2789 self.selected_pkgs[publisher] = {}
2790 self.selected_pkgs.get(publisher)[stem] = status
2791 if status == enumerations.NOT_INSTALLED or \
2792 status == enumerations.UPDATABLE:
2793 if self.to_install_update.get(publisher) == None:
2794 self.to_install_update[publisher] = 1
2795 else:
2796 self.to_install_update[publisher] += 1
2797 if status == enumerations.UPDATABLE or status == enumerations.INSTALLED:
2798 if self.to_remove.get(publisher) == None:
2799 self.to_remove[publisher] = 1
2800 else:
2801 self.to_remove[publisher] += 1
2802 self.__update_tooltips()
2803
2804 def __update_tooltips(self):
2805 to_remove = None
2806 to_install = None
2807 no_iter = 0
2808 for publisher in self.to_remove:
2809 packages = self.to_remove.get(publisher)
2810 if packages > 0:
2811 if no_iter == 0:
2812 to_remove = _("Selected for Removal:")
2813 to_remove += "\n %s: %d" % (publisher, packages)
2814 no_iter += 1
2815 no_iter = 0
2816 for publisher in self.to_install_update:
2817 packages = self.to_install_update.get(publisher)
2818 if packages > 0:
2819 if no_iter == 0:
2820 to_install = _("Selected for Install/Update:")
2821 to_install += "\n %s: %d" % (publisher, packages)
2822 no_iter += 1
2823 if not to_install:
2824 to_install = _("Select packages by marking the checkbox "
2825 "and click to Install/Update.")
2826 self.w_installupdate_button.set_tooltip(self.install_button_tooltip,
2827 to_install)
2828 if not to_remove:
2829 to_remove = _("Select packages by marking the checkbox "
2830 "and click to Remove selected.")
2831 self.w_remove_button.set_tooltip(self.remove_button_tooltip, to_remove)
2832
2833 def __remove_pkg_stem_from_list(self, stem):
2834 remove_pub = []
2835 for publisher in self.selected_pkgs:
2836 pkgs = self.selected_pkgs.get(publisher)
2837 status = None
2838 if stem in pkgs:
2839 status = pkgs.pop(stem)
2840 if status == enumerations.NOT_INSTALLED or \
2841 status == enumerations.UPDATABLE:
2842 if self.to_install_update.get(publisher) == None:
2843 self.to_install_update[publisher] = 0
2844 else:
2845 self.to_install_update[publisher] -= 1
2846 if status == enumerations.UPDATABLE or \
2847 status == enumerations.INSTALLED:
2848 if self.to_remove.get(publisher) == None:
2849 self.to_remove[publisher] = 0
2850 else:
2851 self.to_remove[publisher] -= 1
2852 if len(pkgs) == 0:
2853 remove_pub.append(publisher)
2854 for publisher in remove_pub:
2855 self.selected_pkgs.pop(publisher)
2856 self.__update_tooltips()
2857
2858 def __clear_pkg_selections(self):
2859 # We clear the selections as the preffered repository was changed
2860 # and pkg stems are not valid.
2861 remove_pub = []
2862 for publisher in self.selected_pkgs:
2863 stems = self.selected_pkgs.get(publisher)
2864 for pkg_stem in stems:
2865 remove_pub.append(pkg_stem)
2866 for pkg_stem in remove_pub:
2867 self.__remove_pkg_stem_from_list(pkg_stem)
2868
2869 def __set_empty_details_panel(self):
2870 self.showing_empty_details = True
2871 if self.show_info_id != 0:
2872 gobject.source_remove(self.show_info_id)
2873 self.show_info_id = 0
2874 if self.show_licenses_id != 0:
2875 gobject.source_remove(self.show_licenses_id)
2876 self.show_licenses_id = 0
2877 pkg_name = _("Package Name")
2878 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2879 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2880 self.w_installedfiles_textview.get_buffer().set_text("")
2881 self.w_dependencies_textview.get_buffer().set_text("")
2882 self.w_generalinfo_textview.get_buffer().set_text("")
2883 self.w_license_textview.get_buffer().set_text("")
2884 return
2885
2886 def __show_fetching_package_info(self, pkg):
2887 pkg_name = pkg.get_name()
2888 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2889 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2890
2891 pkg_stem = pkg.get_pkg_stem()
2892 if self.__setting_from_cache(pkg_stem):
2893 return
2894
2895 self.w_shortdescription_label.set_text(
2896 _("Fetching description..."))
2897 instbuffer = self.w_installedfiles_textview.get_buffer()
2898 depbuffer = self.w_dependencies_textview.get_buffer()
2899 infobuffer = self.w_generalinfo_textview.get_buffer()
2900 fetching_text = _("Fetching information...")
2901 instbuffer.set_text(fetching_text)
2902 depbuffer.set_text(fetching_text)
2903 infobuffer.set_text(fetching_text)
2904 return
2905
2906 def __setting_from_cache(self, pkg_stem):
2907 if len(self.info_cache) > MAX_INFO_CACHE_LIMIT:
2908 self.info_cache = {}
2909
2910 if self.info_cache.has_key(pkg_stem):
2911 self.w_shortdescription_label.set_text(
2912 self.info_cache[pkg_stem][0])
2913 instbuffer = self.w_installedfiles_textview.get_buffer()
2914 depbuffer = self.w_dependencies_textview.get_buffer()
2915 infobuffer = self.w_generalinfo_textview.get_buffer()
2916 infobuffer.set_text(self.info_cache[pkg_stem][1])
2917 instbuffer.set_text(self.info_cache[pkg_stem][2])
2918 depbuffer.set_text(self.info_cache[pkg_stem][3])
2919 return True
2920 else:
2921 return False
2922
2923 def __update_package_info(self, pkg, local_info, remote_info, info_id):
2924 if self.showing_empty_details or (info_id !=
2925 self.last_show_info_id):
2926 return
2927 pkg_name = pkg.get_name()
2928 pkg_stem = pkg.get_pkg_stem()
2929 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2930 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2931 installed = True
2932
2933 if self.__setting_from_cache(pkg_stem):
2934 return
2935
2936 instbuffer = self.w_installedfiles_textview.get_buffer()
2937 depbuffer = self.w_dependencies_textview.get_buffer()
2938 infobuffer = self.w_generalinfo_textview.get_buffer()
2939
2940 if not local_info and not remote_info:
2941 network_str = \
2942 _("\nThis might be caused by network problem "
2943 "while accessing the repository.")
2944 self.w_shortdescription_label.set_text(
2945 _("Description not available for this package...") +
2946 network_str)
2947 instbuffer.set_text( \
2948 _("Files Details not available for this package...") +
2949 network_str)
2950 depbuffer.set_text(_(
2951 "Dependencies info not available for this package...") +
2952 network_str)
2953 infobuffer.set_text(
2954 _("Information not available for this package...") +
2955 network_str)
2956 return
2957
2958 if not local_info:
2959 # Package is not installed
2960 local_info = remote_info
2961 installed = False
2962
2963 if not remote_info:
2964 remote_info = local_info
2965 installed = True
2966
2967 description = local_info.summary
2968 #XXX long term need to have something more robust here for multi byte
2969 if len(description) > MAX_DESC_LEN:
2970 description = description[:MAX_DESC_LEN] + " ..."
2971 self.w_shortdescription_label.set_text(description)
2972 inst_str = _("Root: %s\n") % self.api_o.img.get_root()
2973 dep_str = _("Dependencies:\n")
2974
2975 if local_info.dependencies:
2976 dep_str += ''.join(
2977 ["\t%s\n" % x for x in local_info.dependencies])
2978 if local_info.dirs:
2979 inst_str += ''.join(["\t%s\n" % x for x in local_info.dirs])
2980 if local_info.files:
2981 inst_str += ''.join(["\t%s\n" % x for x in local_info.files])
2982 if local_info.hardlinks:
2983 inst_str += ''.join(["\t%s\n" % x for x in local_info.hardlinks])
2984 if local_info.links:
2985 inst_str += ''.join(["\t%s\n" % x for x in local_info.links])
2986 info_str = ""
2987 labs = {}
2988 labs["sum"] = _("Summary:\t\t")
2989 labs["size"] = _("Size:\t\t\t")
2990 labs["cat"] = _("Category:\t\t")
2991 labs["ins"] = _("Installed Version:\t")
2992 labs["lat"] = _("Latest Version:\t")
2993 labs["pkg_date"] = _("Packaging Date:\t")
2994 labs["fmri"] = _("FMRI:\t\t\t")
2995 labs["repository"] = _("Repository:\t\t")
2996 max_len = 0
2997 for lab in labs:
2998 if len(labs[lab]) > max_len:
2999 max_len = len(labs[lab])
3000 categories = _("None")
3001 if local_info.category_info_list:
3002 verbose = len(local_info.category_info_list) > 1
3003 categories = ""
3004 categories += local_info.category_info_list[0].__str__(verbose)
3005 if len(local_info.category_info_list) > 1:
3006 for ci in local_info.category_info_list[1:]:
3007 categories += ", " + ci.__str__(verbose)
3008 summary = _("None")
3009 if local_info.summary:
3010 summary = local_info.summary
3011 info_str += " %s %s" % (labs["sum"], summary)
3012 info_str += "\n %s %s" % (labs["size"],
3013 misc.bytes_to_str(local_info.size))
3014 info_str += "\n %s %s" % (labs["cat"], categories)
3015 if installed:
3016 info_str += "\n %s %s,%s-%s" % (labs["ins"], local_info.version,
3017 local_info.build_release, local_info.branch)
3018 info_str += "\n %s %s,%s-%s" % (labs["lat"], remote_info.version,
3019 remote_info.build_release, remote_info.branch)
3020 info_str += "\n %s %s" % (labs["pkg_date"], local_info.packaging_date)
3021 info_str += "\n %s %s" % (labs["fmri"], local_info.fmri)
3022 info_str += "\n %s %s" % (labs["repository"], local_info.publisher)
3023 infobuffer.set_text(info_str)
3024 instbuffer.set_text(inst_str)
3025 depbuffer.set_text(dep_str)
3026 self.info_cache[pkg_stem] = \
3027 (description, info_str, inst_str, dep_str)
3028
3029 def __update_package_license(self, licenses, license_id):
3030 if self.showing_empty_details or (license_id !=
3031 self.last_show_licenses_id):
3032 return
3033 lic = ""
3034 lic_u = ""
3035 if licenses == None:
3036 lic_u = _("Not available")
3037 else:
3038 for licens in licenses:
3039 lic += licens.get_text()
3040 lic += "\n"
3041 try:
3042 lic_u = unicode(lic, "utf-8")
3043 except UnicodeDecodeError:
3044 lic_u += ""
3045 licbuffer = self.w_license_textview.get_buffer()
3046 licbuffer.set_text(lic_u)
3047
3048 def __show_licenses(self):
3049 self.show_licenses_id = 0
3050 if self.catalog_loaded == False:
3051 return
3052 Thread(target = self.__show_package_licenses,
3053 args = (self.selected_pkgstem, self.last_show_licenses_id,)).start()
3054
3055 def __show_package_licenses(self, selected_pkgstem, license_id):
3056 if selected_pkgstem == None:
3057 gobject.idle_add(self.__update_package_license, None,
3058 self.last_show_licenses_id)
3059 return
3060 info = None
3061 try:
3062 info = self.api_o.info([selected_pkgstem],
3063 True, frozenset([api.PackageInfo.LICENSES]))
3064 except (api_errors.TransportError):
3065 pass
3066 if self.showing_empty_details or (license_id !=
3067 self.last_show_licenses_id):
3068 return
3069 if not info or (info and len(info.get(0)) == 0):
3070 try:
3071 # Get license from remote
3072 info = self.api_o.info([selected_pkgstem],
3073 False, frozenset([api.PackageInfo.LICENSES]))
3074 except (api_errors.TransportError):
3075 pass
3076 if self.showing_empty_details or (license_id !=
3077 self.last_show_licenses_id):
3078 return
3079 pkgs_info = None
3080 package_info = None
3081 no_licenses = 0
3082 if info:
3083 pkgs_info = info[0]
3084 if pkgs_info:
3085 package_info = pkgs_info[0]
3086 if package_info:
3087 no_licenses = len(package_info.licenses)
3088 if no_licenses == 0:
3089 gobject.idle_add(self.__update_package_license, None,
3090 license_id)
3091 return
3092 else:
3093 gobject.idle_add(self.__update_package_license,
3094 package_info.licenses, license_id)
3095
3096 def __get_pkg_info(self, pkg_stem, local):
3097 info = None
3098 try:
3099 info = self.api_o.info([pkg_stem], local,
3100 api.PackageInfo.ALL_OPTIONS -
3101 frozenset([api.PackageInfo.LICENSES]))
3102 except (api_errors.TransportError):
3103 return info
3104 pkgs_info = None
3105 package_info = None
3106 if info:
3107 pkgs_info = info[0]
3108 if pkgs_info:
3109 package_info = pkgs_info[0]
3110 if package_info:
3111 return package_info
3112 else:
3113 return None
3114
3115 def __show_info(self, model, path):
3116 self.show_info_id = 0
3117 if self.catalog_loaded == False:
3118 self.selected_model = model
3119 self.selected_path = path
3120 return
3121 if not (model and path):
3122 return
3123 if self.selected_model != None:
3124 if (self.selected_model != model or
3125 self.selected_path != path):
3126 # This can happen after catalogs are loaded in
3127 # enable_disable_update_all and a different
3128 # package is selected before enable_disable_update_all
3129 # calls __show_info. We set these variable to None
3130 # so that when __show_info is called it does nothing.
3131 self.selected_model = None
3132 self.selected_path = None
3133
3134 itr = model.get_iter(path)
3135 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
3136 pkg_stem = model.get_value(itr, enumerations.STEM_COLUMN)
3137 pkg_status = model.get_value(itr, enumerations.STATUS_COLUMN)
3138 if self.info_cache.has_key(pkg_stem):
3139 return
3140 Thread(target = self.__show_package_info,
3141 args = (pkg, pkg_stem, pkg_status, self.last_show_info_id)).start()
3142
3143 def __show_package_info(self, pkg, pkg_stem, pkg_status, info_id):
3144 self.api_o.log_operation_start("info")
3145 local_info = None
3146 remote_info = None
3147 if not self.showing_empty_details and (info_id ==
3148 self.last_show_info_id) and (pkg_status ==
3149 enumerations.INSTALLED or pkg_status ==
3150 enumerations.UPDATABLE):
3151 local_info = self.__get_pkg_info(pkg_stem, True)
3152 if not self.showing_empty_details and (info_id ==
3153 self.last_show_info_id) and (pkg_status ==
3154 enumerations.NOT_INSTALLED or pkg_status ==
3155 enumerations.UPDATABLE):
3156 remote_info = self.__get_pkg_info(pkg_stem, False)
3157 if not self.showing_empty_details and (info_id ==
3158 self.last_show_info_id):
3159 gobject.idle_add(self.__update_package_info, pkg,
3160 local_info, remote_info, info_id)
3161 self.api_o.log_operation_end()
3162 return
3163
3164 # This function is ported from pkg.actions.generic.distinguished_name()
3165 @staticmethod
3166 def __locale_distinguished_name(action):
3167 if action.key_attr == None:
3168 return str(action)
3169 return "%s: %s" % \
3170 (_(action.name), action.attrs.get(action.key_attr, "???"))
3171
3172 def __application_filter(self, model, itr):
3173 '''This function is used to filter content in the main
3174 application view'''
3175 if self.in_setup or self.cancelled:
3176 return False
3177 filter_id = self.w_filter_combobox.get_active()
3178 if filter_id == enumerations.FILTER_SELECTED:
3179 return model.get_value(itr, enumerations.MARK_COLUMN)
3180 # XXX Show filter, chenge text to integers
3181 selected_category = 0
3182 category_selection = self.w_categories_treeview.get_selection()
3183 category_model, category_iter = category_selection.get_selected()
3184 if category_iter:
3185 selected_category = category_model.get_value(category_iter,
3186 enumerations.CATEGORY_ID)
3187 category_list = model.get_value(itr, enumerations.CATEGORY_LIST_COLUMN)
3188 selected_section = self.w_sections_combobox.get_active()
3189 category = False
3190 if selected_section == 0 and selected_category == 0:
3191 #For section "All" and category "All" always true
3192 category = True
3193 elif selected_category != 0:
3194 if category_list and selected_category in category_list:
3195 category = True
3196 elif category_list:
3197 #The selected category is "All" so we need to check
3198 #If the package belongs to one of the visible categories
3199 for visible_category in category_model:
3200 visible_id = visible_category[enumerations.CATEGORY_ID]
3201 if visible_id in category_list:
3202 category = True
3203 break
3204 if (model.get_value(itr, enumerations.IS_VISIBLE_COLUMN) == False):
3205 return False
3206 return (category &
3207 self.__is_package_filtered(model, itr, filter_id))
3208
3209 @staticmethod
3210 def __is_package_filtered(model, itr, filter_id):
3211 '''Function for filtercombobox'''
3212 if filter_id == enumerations.FILTER_ALL:
3213 return True
3214 status = model.get_value(itr, enumerations.STATUS_COLUMN)
3215 if filter_id == enumerations.FILTER_INSTALLED:
3216 return (status == enumerations.INSTALLED or status == \
3217 enumerations.UPDATABLE)
3218 elif filter_id == enumerations.FILTER_UPDATES:
3219 return status == enumerations.UPDATABLE
3220 elif filter_id == enumerations.FILTER_NOT_INSTALLED:
3221 return status == enumerations.NOT_INSTALLED
3222
3223 def __is_pkg_repository_visible(self, model, itr):
3224 if len(self.repositories_list) <= 1:
3225 return True
3226 else:
3227 visible_repository = self.__get_visible_repository_name()
3228 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
3229 if not pkg:
3230 return False
3231 if cmp(pkg.get_publisher(), visible_repository) == 0:
3232 return True
3233 else:
3234 return False
3235
3236 def __get_visible_repository_name(self):
3237 pub_iter = self.w_repository_combobox.get_active_iter()
3238 if pub_iter == None:
3239 return None
3240 visible = self.repositories_list.get_value(pub_iter, \
3241 enumerations.REPOSITORY_NAME)
3242 return visible
3243
3244 def __enable_disable_selection_menus(self):
3245 if self.in_setup:
3246 return
3247 self.__enable_disable_select_updates()
3248 if not self.__doing_search():
3249 self.unset_busy_cursor()
3250
3251 def __enable_disable_select_all(self):
3252 if self.in_setup:
3253 return
3254 if len(self.w_application_treeview.get_model()) > 0:
3255 for row in self.w_application_treeview.get_model():
3256 if not row[enumerations.MARK_COLUMN]:
3257 self.w_selectall_menuitem.set_sensitive(True)
3258 return
3259 self.w_selectall_menuitem.set_sensitive(False)
3260 else:
3261 self.w_selectall_menuitem.set_sensitive(False)
3262
3263 def __enable_disable_install_remove(self):
3264 if not self.user_rights:
3265 self.w_installupdate_button.set_sensitive(False)
3266 self.w_installupdate_menuitem.set_sensitive(False)
3267 self.w_remove_button.set_sensitive(False)
3268 self.w_remove_menuitem.set_sensitive(False)
3269 return
3270 selected_removal = self.__enable_if_selected_for_removal()
3271 selected_install_update = self.__enable_if_selected_for_install_update()
3272 if selected_removal or selected_install_update:
3273 return
3274 remove = False
3275 install = False
3276 if self.selected == 0:
3277 model, itr = self.package_selection.get_selected()
3278 if itr:
3279 status = \
3280 model.get_value(itr, enumerations.STATUS_COLUMN)
3281 if status == enumerations.NOT_INSTALLED:
3282 remove = False
3283 install = True
3284 elif status == enumerations.UPDATABLE:
3285 remove = True
3286 install = True
3287 elif status == enumerations.INSTALLED:
3288 remove = True
3289 install = False
3290 self.w_installupdate_button.set_sensitive(install)
3291 self.w_installupdate_menuitem.set_sensitive(install)
3292 self.w_remove_button.set_sensitive(remove)
3293 self.w_remove_menuitem.set_sensitive(remove)
3294
3295 def __enable_if_selected_for_removal(self):
3296 sensitive = False
3297 visible_repository = self.__get_visible_repository_name()
3298 selected = self.to_remove.get(visible_repository)
3299 if selected > 0:
3300 sensitive = True
3301 self.w_remove_button.set_sensitive(sensitive)
3302 self.w_remove_menuitem.set_sensitive(sensitive)
3303 return sensitive
3304
3305 def __enable_if_selected_for_install_update(self):
3306 sensitive = False
3307 visible_repository = self.__get_visible_repository_name()
3308 selected = self.to_install_update.get(visible_repository)
3309 if selected > 0:
3310 sensitive = True
3311 self.w_installupdate_button.set_sensitive(sensitive)
3312 self.w_installupdate_menuitem.set_sensitive(sensitive)
3313 return sensitive
3314
3315 def __enable_disable_select_updates(self):
3316 for row in self.w_application_treeview.get_model():
3317 if row[enumerations.STATUS_COLUMN] == enumerations.UPDATABLE:
3318 if not row[enumerations.MARK_COLUMN]:
3319 self.w_selectupdates_menuitem. \
3320 set_sensitive(True)
3321 return
3322 self.w_selectupdates_menuitem.set_sensitive(False)
3323 return
3324
3325 def __get_inventory_list(self, pargs, all_known, all_versions):
3326 self.__image_activity_lock.acquire()
3327 try:
3328 res = misc.get_inventory_list(self.api_o.img,
3329 pargs, all_known, all_versions)
3330 finally:
3331 self.__image_activity_lock.release()
3332 return res
3333
3334 def __enable_disable_update_all(self):
3335 #XXX Api to provide fast information if there are some updates
3336 #available within image
3337 gobject.idle_add(self.w_updateall_button.set_sensitive, False)
3338 gobject.idle_add(self.w_updateall_menuitem.set_sensitive, False)
3339 update_available = self.__check_if_updates_available()
3340 gobject.idle_add(self.__g_enable_disable_update_all, update_available)
3341 gobject.idle_add(self.__show_info_after_catalog_load)
3342 return False
3343
3344 def __show_info_after_catalog_load(self):
3345 self.__show_info(self.selected_model, self.selected_path)
3346 self.selected_model = None
3347 self.selected_path = None
3348 if (self.w_info_notebook.get_current_page() ==
3349 INFO_NOTEBOOK_LICENSE_PAGE and
3350 not self.showing_empty_details):
3351 self.__show_licenses()
3352
3353 def __check_if_updates_available(self):
3354 try:
3355 self.catalog_loaded = False
3356 self.api_o.refresh()
3357 self.catalog_loaded = True
3358 res = self.__get_inventory_list([], False, False)
3359 for pfmri, state in res:
3360 if state["upgradable"]:
3361 self.pylintstub = pfmri
3362 return True
3363
3364 except api_errors.InventoryException:
3365 gobject.idle_add(self.__set_empty_details_panel)
3366 return False
3367 return False
3368
3369 def __g_enable_disable_update_all(self, update_available):
3370 self.w_updateall_button.set_sensitive(update_available)
3371 self.w_updateall_menuitem.set_sensitive(update_available)
3372 self.__enable_disable_install_remove()
3373
3374 def __enable_disable_deselect(self):
3375 if self.w_application_treeview.get_model():
3376 for row in self.w_application_treeview.get_model():
3377 if row[enumerations.MARK_COLUMN]:
3378 self.w_deselect_menuitem.set_sensitive(True)
3379 return
3380 self.w_deselect_menuitem.set_sensitive(False)
3381 return
3382
3383 def __catalog_refresh(self, reload_gui=True):
3384 """Update image's catalogs."""
3385 try:
3386 # Since the user requested the refresh, perform it
3387 # immediately for all publishers.
3388 self.api_o.refresh(immediate=True)
3389 # Refresh will load the catalogs.
3390 self.catalog_loaded = True
3391 except api_errors.PublisherError:
3392 # In current implementation, this will never happen
3393 # We are not refreshing specific publisher
3394 self.__catalog_refresh_done()
3395 raise
3396 except api_errors.PermissionsException:
3397 #Error will already have been reported in
3398 #Manage Repository dialog
3399 self.__catalog_refresh_done()
3400 return -1
3401 except api_errors.CatalogRefreshException, cre:
3402 total = cre.total
3403 succeeded = cre.succeeded
3404 ermsg = _("Network problem.\n\n")
3405 ermsg += _("Details:\n")
3406 ermsg += "%s/%s" % (succeeded, total)
3407 ermsg += _(" catalogs successfully updated:\n")
3408 for pub, err in cre.failed:
3409 if isinstance(err, HTTPError):
3410 ermsg += " %s: %s - %s\n" % \
3411 (err.filename, err.code, err.msg)
3412 elif isinstance(err, URLError):
3413 if err.args[0][0] == 8:
3414 ermsg += " %s: %s\n" % \
3415 (urlparse.urlsplit(
3416 pub["origin"])[1].split(":")[0],
3417 err.args[0][1])
3418 else:
3419 if isinstance(err.args[0], \
3420 socket.timeout):
3421 ermsg += " %s: %s\n" % \
3422 (pub["origin"], "timeout")
3423 else:
3424 ermsg += " %s: %s\n" % \
3425 (pub["origin"], \
3426 err.args[0][1])
3427 elif "data" in err.__dict__ and err.data:
3428 ermsg += err.data
3429 else:
3430 ermsg += _("Unknown error")
3431 ermsg += "\n"
3432
3433 gobject.idle_add(self.error_occurred, ermsg,
3434 None, gtk.MESSAGE_INFO)
3435 self.__catalog_refresh_done()
3436 return -1
3437 except api_errors.InvalidDepotResponseException, idrex:
3438 err = str(idrex)
3439 gobject.idle_add(self.error_occurred, err,
3440 None, gtk.MESSAGE_INFO)
3441 self.__catalog_refresh_done()
3442 return -1
3443 except api_errors.PublisherError:
3444 self.__catalog_refresh_done()
3445 raise
3446 except Exception:
3447 self.__catalog_refresh_done()
3448 raise
3449 if reload_gui:
3450 self.__catalog_refresh_done()
3451 return 0
3452
3453 def __add_pkgs_to_lists_from_cache(self, publisher, application_list,
3454 category_list, section_list):
3455 if self.cache_o:
3456 self.cache_o.load_application_list(publisher, application_list,
3457 self.selected_pkgs)
3458 self.cache_o.load_category_list(publisher, category_list)
3459 self.cache_o.load_section_list(publisher, section_list)
3460
3461 def __add_pkgs_to_lists_from_api(self, publisher, application_list,
3462 category_list, section_list):
3463 """ This method set up image from the given directory and
3464 returns the image object or None"""
3465 pargs = []
3466 pargs.append("pkg://" + publisher + "/*")
3467 try:
3468 pkgs_known = self.__get_inventory_list(pargs,
3469 True, True)
3470 except api_errors.InventoryException:
3471 # This can happen if the repository does not
3472 # contain any packages
3473 err = _("Selected repository does not contain any packages.")
3474 gobject.idle_add(self.w_progress_dialog.hide)
3475 gobject.idle_add(self.error_occurred, err, None,
3476 gtk.MESSAGE_INFO)
3477 self.unset_busy_cursor()
3478 pkgs_known = []
3479
3480 return self.__add_pkgs_to_lists(pkgs_known, application_list,
3481 category_list, section_list)
3482
3483 def __add_pkgs_to_lists(self, pkgs_known, application_list,
3484 category_list, section_list):
3485 if section_list != None:
3486 self.__init_sections(section_list)
3487 #Only one instance of those icons should be in memory
3488 update_available_icon = gui_misc.get_icon(self.icon_theme,
3489 "status_newupdate")
3490 installed_icon = gui_misc.get_icon(self.icon_theme,
3491 "status_installed")
3492 update_for_category_icon = \
3493 self.get_icon_pixbuf_from_glade_dir("legend_newupdate")
3494 #Imageinfo for categories
3495 imginfo = imageinfo.ImageInfo()
3496 sectioninfo = imageinfo.ImageInfo()
3497 pubs = [p.prefix for p in self.api_o.get_publishers()]
3498 categories = {}
3499 sections = {}
3500 share_path = "/usr/share/package-manager/data/"
3501 for pub in pubs:
3502 category = imginfo.read(self.application_dir +
3503 share_path + pub)
3504 if len(category) == 0:
3505 category = imginfo.read(self.application_dir +
3506 share_path + "opensolaris.org")
3507 categories[pub] = category
3508 section = sectioninfo.read(self.application_dir +
3509 share_path + pub + ".sections")
3510 if len(section) == 0:
3511 section = sectioninfo.read(self.application_dir +
3512 share_path + "opensolaris.org.sections")
3513 sections[pub] = section
3514 pkg_count = 0
3515 pkg_add = 0
3516 progress_percent = INITIAL_PROGRESS_TOTAL_PERCENTAGE
3517 total_pkg_count = len(pkgs_known)
3518 progress_increment = \
3519 total_pkg_count / PACKAGE_PROGRESS_TOTAL_INCREMENTS
3520 self.progress_stop_timer_thread = True
3521 while gtk.events_pending():
3522 gtk.main_iteration(False)
3523 prev_stem = ""
3524 prev_pfmri_str = ""
3525 next_app = None
3526 pkg_name = None
3527 pkg_publisher = None
3528 prev_state = None
3529 category_icon = None
3530 for pkg, state in pkgs_known:
3531 if prev_pfmri_str and \
3532 prev_pfmri_str == pkg.get_short_fmri() and \
3533 prev_state == state:
3534 pkg_count += 1
3535 continue
3536 if prev_stem and \
3537 prev_stem == pkg.get_pkg_stem() and \
3538 prev_state["state"] == "known" and \
3539 state["state"] == "installed":
3540 pass
3541 elif next_app != None:
3542 self.__add_package_to_list(next_app,
3543 application_list,
3544 pkg_add, pkg_name,
3545 category_icon,
3546 categories, category_list, pkg_publisher)
3547 pkg_add += 1
3548 prev_stem = pkg.get_pkg_stem()
3549 prev_pfmri_str = pkg.get_short_fmri()
3550 prev_state = state
3551
3552 if progress_increment > 0 and pkg_count % progress_increment == 0:
3553 progress_percent += PACKAGE_PROGRESS_PERCENT_INCREMENT
3554 if progress_percent <= PACKAGE_PROGRESS_PERCENT_TOTAL:
3555 self.__progressdialog_progress_percent(
3556 progress_percent, pkg_count, total_pkg_count)
3557 while gtk.events_pending():
3558 gtk.main_iteration(False)
3559
3560 status_icon = None
3561 category_icon = None
3562 pkg_name = pkg.get_name()
3563 pkg_name = gui_misc.get_pkg_name(pkg_name)
3564 pkg_stem = pkg.get_pkg_stem()
3565 pkg_publisher = pkg.get_publisher()
3566 pkg_state = enumerations.NOT_INSTALLED
3567 if state["state"] == "installed":
3568 pkg_state = enumerations.INSTALLED
3569 if state["upgradable"] == True:
3570 status_icon = update_available_icon
3571 category_icon = update_for_category_icon
3572 pkg_state = enumerations.UPDATABLE
3573 else:
3574 status_icon = installed_icon
3575 marked = False
3576 if not self.is_search_all:
3577 pkgs = self.selected_pkgs.get(pkg_publisher)
3578 if pkgs != None:
3579 if pkg_stem in pkgs:
3580 marked = True
3581 next_app = \
3582 [
3583 marked, status_icon, pkg_name, '...', pkg_state,
3584 pkg, pkg_stem, None, True, None, pkg_publisher
3585 ]
3586 pkg_count += 1
3587
3588 if next_app:
3589 self.__add_package_to_list(next_app, application_list,
3590 pkg_add, pkg_name, category_icon, categories,
3591 category_list, pkg_publisher)
3592 pkg_add += 1
3593 if category_list != None:
3594 self.__add_categories_to_sections(sections,
3595 category_list, section_list)
3596 self.__progressdialog_progress_percent(PACKAGE_PROGRESS_PERCENT_TOTAL,
3597 total_pkg_count, total_pkg_count)
3598 return
3599
3600 def __add_categories_to_sections(self, sections, category_list, section_list):
3601 for publisher in sections:
3602 for section in sections[publisher]:
3603 for category in sections[publisher][section].split(","):
3604 self.__add_category_to_section(_(category),
3605 _(section), category_list, section_list)
3606
3607 #1915 Sort the Categories into alphabetical order and prepend All Category
3608 if len(category_list) > 0:
3609 rows = [tuple(r) + (i,) for i, r in enumerate(category_list)]
3610 rows.sort(self.__sort)
3611 r = []
3612 category_list.reorder([r[-1] for r in rows])
3613 return
3614
3615 def __add_package_to_list(self, app, application_list, pkg_add,
3616 pkg_name, category_icon, categories, category_list, publisher):
3617 row_iter = application_list.insert(pkg_add, app)
3618 if category_list == None:
3619 return
3620 cat_pub = categories.get(publisher)
3621 if pkg_name in cat_pub:
3622 pkg_categories = cat_pub.get(pkg_name)
3623 for pcat in pkg_categories.split(","):
3624 self.__add_package_to_category(_(pcat), None,
3625 category_icon, row_iter, application_list,
3626 category_list)
3627
3628 @staticmethod
3629 def __add_package_to_category(category_name, category_description,
3630 category_icon, package, application_list, category_list):
3631 if not package or category_name == _('All'):
3632 return
3633 if not category_name:
3634 return
3635 category_id = None
3636 icon_visible = False
3637 if category_icon:
3638 icon_visible = True
3639 for category in category_list:
3640 if category[enumerations.CATEGORY_NAME] == category_name:
3641 category_id = category[enumerations.CATEGORY_ID]
3642 if category_icon:
3643 category[enumerations.CATEGORY_ICON] = \
3644 category_icon
3645 category[enumerations.CATEGORY_ICON_VISIBLE] = \
3646 icon_visible
3647 break
3648 if not category_id: # Category not exists
3649 category_id = len(category_list) + 1
3650 category_list.append([category_id, category_name,
3651 category_description, category_icon, icon_visible,
3652 True, None])
3653 if application_list.get_value(package,
3654 enumerations.CATEGORY_LIST_COLUMN):
3655 a = application_list.get_value(package,
3656 enumerations.CATEGORY_LIST_COLUMN)
3657 a.append(category_id)
3658 else:
3659 category_list = []
3660 category_list.append(category_id)
3661 application_list.set(package,
3662 enumerations.CATEGORY_LIST_COLUMN, category_list)
3663
3664 @staticmethod
3665 def __add_category_to_section(category_name, section_name, category_list,
3666 section_list):
3667 '''Adds the section to section list in category. If there is no such
3668 section, than it is not added. If there was already section than it
3669 is skipped. Sections must be case sensitive'''
3670 if not category_name:
3671 return
3672 for section in section_list:
3673 if section[enumerations.SECTION_NAME] == section_name:
3674 section_id = section[enumerations.SECTION_ID]
3675 for category in category_list:
3676 if category[enumerations.CATEGORY_NAME] == \
3677 category_name:
3678 section_lst = category[ \
3679 enumerations.SECTION_LIST_OBJECT]
3680 section[enumerations.SECTION_ENABLED] = \
3681 True
3682 if not section_lst:
3683 category[ \
3684 enumerations.SECTION_LIST_OBJECT] = \
3685 [section_id, ]
3686 else:
3687 if not section_name in \
3688 section_lst:
3689 section_lst.append(
3690 section_id)
3691
3692 def __progressdialog_progress_pulse(self):
3693 while not self.progress_stop_timer_thread:
3694 gobject.idle_add(self.w_progressbar.pulse)
3695 time.sleep(0.1)
3696 gobject.idle_add(self.w_progress_dialog.hide)
3697 self.progress_stop_timer_thread = False
3698
3699 # For initial setup before loading package entries allow 5% of progress bar
3700 # update it on a time base as we have no other way to judge progress at this point
3701 def __progressdialog_progress_time(self):
3702 while not self.progress_stop_timer_thread and \
3703 self.progress_fraction_time_count <= \
3704 INITIAL_PROGRESS_TOTAL_PERCENTAGE:
3705
3706 gobject.idle_add(self.w_progressbar.set_fraction,
3707 self.progress_fraction_time_count)
3708 self.progress_fraction_time_count += \
3709 INITIAL_PROGRESS_TIME_PERCENTAGE
3710 time.sleep(INITIAL_PROGRESS_TIME_INTERVAL)
3711 self.progress_stop_timer_thread = False
3712 self.progress_fraction_time_count = 0
3713
3714 def __progressdialog_progress_percent(self, fraction, count, total):
3715 gobject.idle_add(self.w_progressinfo_label.set_text, _(
3716 "Processing package entries: %d of %d") % (count, total) )
3717 gobject.idle_add(self.w_progressbar.set_fraction, fraction)
3718
3719 def error_occurred(self, error_msg, msg_title=None, msg_type=gtk.MESSAGE_ERROR):
3720 if msg_title:
3721 title = msg_title
3722 else:
3723 title = _("Package Manager")
3724 gui_misc.error_occurred(self.w_main_window, error_msg,
3725 title, msg_type, use_markup=True)
3726
3727
3728 msgbox = gtk.MessageDialog(parent =
3729 self.w_main_window,
3730 buttons = gtk.BUTTONS_CLOSE,
3731 flags = gtk.DIALOG_MODAL,
3732 type = msg_type,
3733 message_format = None)
3734 msgbox.set_property('text', error_msg)
3735 title = None
3736 if msg_title:
3737 title = msg_title
3738 else:
3739 title = _("Package Manager")
3740 msgbox.set_title(title)
3741 msgbox.run()
3742 msgbox.destroy()
3743
3744 #-----------------------------------------------------------------------------#
3745 # Static Methods
3746 #-----------------------------------------------------------------------------#
3747
3748 #@staticmethod
3749 #def N_(message):
3750 # return message
3751
3752 @staticmethod
3753 def __sort(a, b):
3754 return cmp(a[1], b[1])
3755
3756 @staticmethod
3757 def cell_data_function(column, renderer, model, itr, data):
3758 '''Function which sets the background colour to black if package is
3759 selected'''
3760 if itr:
3761 if model.get_value(itr, enumerations.MARK_COLUMN):
3762 renderer.set_property("cell-background", "#ffe5cc")
3763 renderer.set_property("cell-background-set", True)
3764 else:
3765 renderer.set_property("cell-background-set", False)
3766
3767 @staticmethod
3768 def combobox_separator(model, itr):
3769 return model.get_value(itr, enumerations.FILTER_NAME) == ""
3770
3771 @staticmethod
3772 def combobox_id_separator(model, itr):
3773 return model.get_value(itr, 0) == -1 and \
3774 model.get_value(itr, 1) == ""
3775
3776 @staticmethod
3777 def category_filter(model, itr):
3778 '''This function filters category in the main application view'''
3779 return model.get_value(itr, enumerations.CATEGORY_VISIBLE)
3780
3781 @staticmethod
3782 def get_datetime(version):
3783 dt = None
3784 try:
3785 dt = version.get_datetime()
3786 except AttributeError:
3787 dt = version.get_timestamp()
3788 return dt
3789
3790 @staticmethod
3791 def get_installed_version(api_o, pkg):
3792 info = api_o.info([pkg], False, frozenset(
3793 [api.PackageInfo.STATE, api.PackageInfo.IDENTITY]))
3794 found = info[api.ImageInterface.INFO_FOUND]
3795 try:
3796 version = found[0]
3797 except IndexError:
3798 version = None
3799 return version
3800
3801 #-----------------------------------------------------------------------------#
3802 # Public Methods
3803 #-----------------------------------------------------------------------------#
3804 def setup_progressdialog_show(self):
3805 self.w_progress_dialog.set_title(_("Loading Repository Information"))
3806 self.w_progressinfo_label.set_text(
3807 _( "Fetching package entries ..."))
3808 self.w_progress_cancel.hide()
3809 self.w_progress_dialog.show()
3810 Thread(target = self.__progressdialog_progress_time).start()
3811
3812 def setup_progressdialog_hide(self):
3813 self.progress_stop_timer_thread = True
3814 self.w_progress_dialog.hide()
3815
3816 def init_show_filter(self):
3817 self.__init_show_filter() #Initiates filter
3818
3819 def reload_packages(self):
3820 self.api_o = gui_misc.get_api_object(self.image_directory,
3821 self.pr, self.w_main_window)
3822 self.cache_o = self.__get_cache_obj(self.icon_theme,
3823 self.application_dir, self.api_o)
3824 self.__on_reload(None)
3825
3826 def set_busy_cursor(self):
3827 self.gdk_window.show()
3828
3829 def unset_busy_cursor(self):
3830 self.gdk_window.hide()
3831
3832 def process_package_list_start(self, image_directory):
3833 self.image_directory = image_directory
3834 if not self.api_o:
3835 self.api_o = gui_misc.get_api_object(image_directory,
3836 self.pr, self.w_main_window)
3837 self.cache_o = self.__get_cache_obj(self.icon_theme,
3838 self.application_dir, self.api_o)
3839 self.img_timestamp = self.cache_o.get_index_timestamp()
3840 self.repositories_list = self.__get_new_repositories_liststore()
3841 self.__setup_repositories_combobox(self.api_o, self.repositories_list)
3842
3843 @staticmethod
3844 def __get_cache_obj(icon_theme, application_dir, api_o):
3845 cache_o = cache.CacheListStores(icon_theme, application_dir,
3846 api_o)
3847 return cache_o
3848
3849 def process_package_list_end(self):
3850 self.__set_first_category_text()
3851 self.in_startpage_startup = False
3852 if self.update_all_proceed:
3853 # TODO: Handle situation where only SUNWipkg/SUNWipg-gui have been updated
3854 # in update all: bug 6357
3855 self.__on_update_all(None)
3856 self.update_all_proceed = False
3857 self.setup_progressdialog_hide()
3858 self.__enable_disable_install_remove()
3859 self.update_statusbar()
3860 self.in_setup = False
3861 self.cancelled = False
3862 if self.set_section != 0 or \
3863 self.set_show_filter != enumerations.FILTER_ALL:
3864 self.__application_refilter()
3865 else:
3866 self.unset_busy_cursor()
3867
3868 if self.first_run or self.in_reload:
3869 Thread(target = self.__enable_disable_update_all).start()
3870 self.first_run = False
3871 self.in_reload = False
3872
3873 def get_icon_pixbuf_from_glade_dir(self, icon_name):
3874 return gui_misc.get_pixbuf_from_path(self.application_dir +
3875 "/usr/share/package-manager/", icon_name)
3876
3877 def update_statusbar(self):
3878 '''Function which updates statusbar'''
3879 if self.statusbar_message_id > 0:
3880 self.w_main_statusbar.remove(0, self.statusbar_message_id)
3881 self.statusbar_message_id = 0
3882 search_text = self.w_searchentry.get_text()
3883
3884 if not self.in_search_mode:
3885 installed = 0
3886 self.selected = 0
3887 sel = 0
3888 if self.application_list == None:
3889 return
3890 visible_repository = self.__get_visible_repository_name()
3891 pkgs = self.selected_pkgs.get(visible_repository)
3892 if pkgs:
3893 self.selected = len(pkgs)
3894 for pkg_row in self.application_list:
3895 if pkg_row[enumerations.STATUS_COLUMN] == \
3896 enumerations.INSTALLED \
3897 or pkg_row[enumerations.STATUS_COLUMN] == \
3898 enumerations.UPDATABLE:
3899 installed = installed + 1
3900 if pkg_row[enumerations.MARK_COLUMN]:
3901 sel = sel + 1
3902 listed_str = _('%d listed') % len(self.application_list)
3903 sel_str = _('%d selected') % sel
3904 inst_str = _('%d installed') % installed
3905 status_str = _("%s: %s , %s, %s.") % (visible_repository,
3906 listed_str, inst_str, sel_str)
3907 self.w_main_statusbar.push(0, status_str)
3908 return
3909
3910 # In Search Mode
3911 active = ""
3912 if self.is_search_all:
3913 if self.__doing_search():
3914 if self.current_search_publisher != None:
3915 active = "(" + self.current_search_publisher + \
3916 ") "
3917 opt_str = _('Searching... '
3918 '%(active)sfor "%(search_text)s"') % \
3919 {"active": active, "search_text": search_text}
3920 else:
3921 opt_str = _('Searched All for "%s"') % (search_text)
3922 else:
3923 search_str = _("Searched")
3924 if self.__doing_search():
3925 search_str = _("Searching...")
3926 if self.last_active_publisher != None:
3927 active = "(" + self.last_active_publisher + ") "
3928 opt_str = \
3929 _('%(search)s %(last_active)sfor "%(search_text)s"') \
3930 % {"search": search_str, "last_active" : active,
3931 "search_text" : search_text}
3932 fmt_str = _("%(option_str)s: %(number)d found %(time)s")
3933 time_str = ""
3934 if self.search_time_sec > 0:
3935 time_str = _("in %d seconds") % self.search_time_sec
3936 status_str = fmt_str % {"option_str" : opt_str, "number" :
3937 len(self.application_list), "time" : time_str}
3938 self.w_main_statusbar.push(0, status_str)
3939
3940 def update_package_list(self, update_list):
3941 if update_list == None and self.img_timestamp:
3942 return
3943 visible_repository = self.__get_visible_repository_name()
3944 default_publisher = self.default_publisher
3945 self.api_o.refresh()
3946 if not self.img_timestamp:
3947 self.img_timestamp = self.cache_o.get_index_timestamp()
3948 self.__on_reload(None)
3949 return
3950 self.img_timestamp = self.cache_o.get_index_timestamp()
3951 installed_icon = gui_misc.get_icon(self.icon_theme,
3952 "status_installed")
3953 visible_list = update_list.get(visible_repository)
3954 if visible_list:
3955 i = 0
3956 while i < len(visible_list):
3957 visible_list[i] = gui_misc.get_pkg_name(
3958 visible_list[i])
3959 i += 1
3960 for row in self.application_list:
3961 if row[enumerations.NAME_COLUMN] in visible_list:
3962 pkg = row[enumerations.FMRI_COLUMN]
3963 pkg_stem = row[enumerations.STEM_COLUMN]
3964 self.__remove_pkg_stem_from_list(pkg_stem)
3965 if self.info_cache.has_key(pkg_stem):
3966 del self.info_cache[pkg_stem]
3967 package_info = self.get_installed_version(
3968 self.api_o, pkg_stem)
3969 package_installed = (package_info.state
3970 == api.PackageInfo.INSTALLED)
3971 print pkg_stem, package_installed
3972 if package_installed:
3973 row[enumerations.STATUS_COLUMN] = \
3974 enumerations.INSTALLED
3975 row[enumerations.STATUS_ICON_COLUMN] = \
3976 installed_icon
3977 else:
3978 row[enumerations.STATUS_COLUMN] = \
3979 enumerations.NOT_INSTALLED
3980 row[enumerations.STATUS_ICON_COLUMN] = \
3981 None
3982 row[enumerations.MARK_COLUMN] = False
3983 self.__dump_datamodels(visible_repository,
3984 self.application_list, self.category_list,
3985 self.section_list)
3986 for publisher in update_list:
3987 if publisher != visible_repository:
3988 pkg_list = update_list.get(publisher)
3989 for pkg in pkg_list:
3990 pkg_stem = None
3991 if publisher != default_publisher:
3992 pkg_stem = "pkg://%s/%s" % \
3993 (publisher, pkg)
3994 else:
3995 pkg_stem = "pkg:/%s" % pkg
3996 if pkg_stem:
3997 if self.info_cache.has_key(pkg_stem):
3998 del self.info_cache[pkg_stem]
3999 self.__remove_pkg_stem_from_list(pkg_stem)
4000 self.__process_package_selection()
4001 self.__enable_disable_selection_menus()
4002 self.__enable_disable_install_remove()
4003 self.update_statusbar()
4004 Thread(target = self.__enable_disable_update_all).start()
4005
4006 @staticmethod
4007 def __find_root_home_dir():
4008 return_str = '/var/tmp'
4009
4010 try:
4011 lines = pwd.getpwnam('root')
4012 except KeyError:
4013 if debug:
4014 print "Error getting passwd database entry for root"
4015 return return_str
4016 try:
4017 return_str = lines[5]
4018 except IndexError:
4019 if debug:
4020 print "Error getting home directory for root"
4021 return return_str
4022
4023 def restart_after_ips_update(self, be_name):
4024 self.__main_application_quit(be_name)
4025
4026 def shutdown_after_image_update(self):
4027 info_str = _("The Update All action is now complete and "
4028 "Package Manager will close.\n\nReview the posted release notes "
4029 "before rebooting your system:\n\n"
4030 )
4031 self.w_ua_completed_release_label.set_text(info_str.strip('\n'))
4032
4033 info_str = misc.get_release_notes_url()
4034 self.w_ua_completed_linkbutton.set_uri(info_str)
4035 self.w_ua_completed_linkbutton.set_label(info_str)
4036 self.release_notes_url = info_str
4037
4038 self.w_ua_completed_dialog.set_title(_("Update All Complete"))
4039 self.w_ua_completed_dialog.show()
4040
4041 ###############################################################################
4042 #-----------------------------------------------------------------------------#
4043 # Main
4044 #-----------------------------------------------------------------------------#
4045
4046 def main():
4047 gtk.main()
4048 return 0
4049
4050 if __name__ == '__main__':
4051 debug = True
4052 debug_descriptions = False
4053 update_all_proceed = False
4054 ua_be_name = None
4055 app_path = None
4056 image_dir = None
4057 info_install_arg = None
4058 save_selected = _("Save selected...")
4059 save_selected_pkgs = _("Save selected packages...")
4060 reboot_needed = _("The installed package(s) require a reboot before "
4061 "installation can be completed.")
4062
4063 try:
4064 opts, args = getopt.getopt(sys.argv[1:], "hR:U:i:", \
4065 ["help", "image-dir=", "update-all=", "info-install="])
4066 except getopt.error, msg:
4067 print "%s, for help use --help" % msg
4068 sys.exit(2)
4069
4070 if os.path.isabs(sys.argv[0]):
4071 app_path = sys.argv[0]
4072 else:
4073 cmd = os.path.join(os.getcwd(), sys.argv[0])
4074 app_path = os.path.realpath(cmd)
4075
4076 for option, argument in opts:
4077 if option in ("-h", "--help"):
4078 print """\
4079 Use -R (--image-dir) to specify image directory.
4080 Use -U (--update-all) to proceed with Update All"""
4081 sys.exit(0)
4082 if option in ("-R", "--image-dir"):
4083 image_dir = argument
4084 if option in ("-U", "--update-all"):
4085 update_all_proceed = True
4086 ua_be_name = argument
4087 if option in ("-i", "--info-install"):
4088 info_install_arg = argument
4089
4090 if image_dir == None:
4091 try:
4092 image_dir = os.environ["PKG_IMAGE"]
4093 except KeyError:
4094 image_dir = os.getcwd()
4095 try:
4096 gtk.init_check()
4097 except RuntimeError, e:
4098 print _("Unable to initialize gtk")
4099 print str(e)
4100 sys.exit(1)
4101
4102 # Setup webinstall
4103 if info_install_arg or len(sys.argv) == 2:
4104 webinstall = webinstall.Webinstall(image_dir)
4105 if len(sys.argv) == 2:
4106 info_install_arg = sys.argv[1]
4107 webinstall.process_param(info_install_arg)
4108 main()
4109 sys.exit(0)
4110
4111 # Setup packagemanager
4112 packagemanager = PackageManager()
4113 packagemanager.application_path = app_path
4114 packagemanager.image_dir_arg = image_dir
4115 packagemanager.update_all_proceed = update_all_proceed
4116 packagemanager.ua_be_name = ua_be_name
4117
4118 while gtk.events_pending():
4119 gtk.main_iteration(False)
4120
4121 packagemanager.init_show_filter()
4122
4123 packagemanager.process_package_list_start(image_dir)
4124
4125 main()