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 MAIN TREEVIEW
1019 if application_list_filter == None:
1020 application_list_filter = application_list.filter_new()
1021 if application_list_sort == None:
1022 application_list_sort = \
1023 gtk.TreeModelSort(application_list_filter)
1024 application_list_sort.set_sort_column_id(
1025 enumerations.NAME_COLUMN, gtk.SORT_ASCENDING)
1026 application_list_sort.set_sort_func(
1027 enumerations.STATUS_ICON_COLUMN, self.__status_sort_func)
1028 toggle_renderer = gtk.CellRendererToggle()
1029
1030 column = gtk.TreeViewColumn("", toggle_renderer, \
1031 active = enumerations.MARK_COLUMN)
1032 column.set_sort_column_id(enumerations.MARK_COLUMN)
1033 column.set_sort_indicator(True)
1034 column.set_cell_data_func(toggle_renderer, self.cell_data_function, None)
1035 column.connect_after('clicked',
1036 self.__application_treeview_column_sorted, None)
1037 self.w_application_treeview.append_column(column)
1038 name_renderer = gtk.CellRendererText()
1039 column = gtk.TreeViewColumn(_("Name"), name_renderer,
1040 text = enumerations.NAME_COLUMN)
1041 column.set_resizable(True)
1042 column.set_min_width(150)
1043 column.set_sort_column_id(enumerations.NAME_COLUMN)
1044 column.set_sort_indicator(True)
1045 column.set_cell_data_func(name_renderer, self.cell_data_function, None)
1046 column.connect_after('clicked',
1047 self.__application_treeview_column_sorted, None)
1048 self.w_application_treeview.append_column(column)
1049 column = self.__create_icon_column(_("Status"), True,
1050 enumerations.STATUS_ICON_COLUMN, True)
1051 column.set_sort_column_id(enumerations.STATUS_ICON_COLUMN)
1052 column.set_sort_indicator(True)
1053 column.connect_after('clicked',
1054 self.__application_treeview_column_sorted, None)
1055 self.w_application_treeview.append_column(column)
1056 if self.is_search_all:
1057 repository_renderer = gtk.CellRendererText()
1058 column = gtk.TreeViewColumn(_('Repository'),
1059 repository_renderer,
1060 text = enumerations.AUTHORITY_COLUMN)
1061 column.set_sort_column_id(enumerations.AUTHORITY_COLUMN)
1062 column.set_resizable(True)
1063 column.set_sort_indicator(True)
1064 column.set_cell_data_func(repository_renderer,
1065 self.cell_data_function, None)
1066 column.connect_after('clicked',
1067 self.__application_treeview_column_sorted, None)
1068 self.w_application_treeview.append_column(column)
1069 description_renderer = gtk.CellRendererText()
1070 column = gtk.TreeViewColumn(_('Description'),
1071 description_renderer,
1072 text = enumerations.DESCRIPTION_COLUMN)
1073 column.set_sort_column_id(enumerations.DESCRIPTION_COLUMN)
1074 column.set_resizable(True)
1075 column.set_sort_indicator(True)
1076 column.set_cell_data_func(description_renderer,
1077 self.cell_data_function, None)
1078 column.connect_after('clicked',
1079 self.__application_treeview_column_sorted, None)
1080 self.w_application_treeview.append_column(column)
1081 #Added selection listener
1082 self.package_selection = self.w_application_treeview.get_selection()
1083 self.application_list = application_list
1084 self.application_list_filter = application_list_filter
1085 self.application_list_sort = application_list_sort
1086 toggle_renderer.connect('toggled', self.__active_pane_toggle,
1087 application_list_sort)
1088
1089 def __init_tree_views(self, application_list, category_list,
1090 section_list, application_list_filter = None,
1091 application_list_sort = None):
1092 '''This function connects treeviews with their models and also applies
1093 filters'''
1094 if category_list == None:
1095 self.w_application_treeview.set_model(None)
1096 self.__remove_treeview_columns(self.w_application_treeview)
1097 elif application_list == None:
1098 self.w_categories_treeview.set_model(None)
1099 self.__remove_treeview_columns(self.w_categories_treeview)
1100 else:
1101 self.__disconnect_models()
1102 self.__remove_treeview_columns(self.w_application_treeview)
1103 self.__remove_treeview_columns(self.w_categories_treeview)
1104 # The logic for set section needs to be here as some sections
1105 # might be not enabled. In such situation we are setting the set
1106 # section to "All Categories" one.
1107 if section_list != None:
1108 row = section_list[self.set_section]
1109 if row[enumerations.SECTION_ENABLED] and \
1110 self.set_section >= 0 and \
1111 self.set_section < len(section_list):
1112 if row[enumerations.SECTION_ID] != self.set_section:
1113 self.set_section = 0
1114 else:
1115 self.set_section = 0
1116
1117 if application_list != None:
1118 self.__init_application_tree_view(application_list,
1119 application_list_filter, application_list_sort)
1120
1121 if self.first_run:
1122 # When vadj changes we need to set image descriptions
1123 # on visible status icons. This catches moving the scroll bars
1124 # and scrolling up and down using keyboard.
1125 vadj = self.w_application_treeview.get_vadjustment()
1126 vadj.connect('value-changed',
1127 self.__application_treeview_vadjustment_changed, None)
1128 vadj = self.w_categories_treeview.get_vadjustment()
1129 vadj.connect('value-changed',
1130 self.__categories_treeview_vadjustment_changed, None)
1131
1132 # When the size of the application_treeview changes
1133 # we need to set image descriptions on visible status icons.
1134 self.w_application_treeview.connect('size-allocate',
1135 self.__application_treeview_size_allocate, None)
1136 self.w_categories_treeview.connect('size-allocate',
1137 self.__categories_treeview_size_allocate, None)
1138
1139 if category_list != None:
1140 ##CATEGORIES TREEVIEW
1141 #enumerations.CATEGORY_NAME
1142 category_list_filter = category_list.filter_new()
1143 column = self.__create_icon_column("", False,
1144 enumerations.CATEGORY_ICON, False)
1145 self.w_categories_treeview.append_column(column)
1146 enumerations.CATEGORY_NAME_renderer = gtk.CellRendererText()
1147 column = gtk.TreeViewColumn(_('Name'),
1148 enumerations.CATEGORY_NAME_renderer,
1149 text = enumerations.CATEGORY_NAME)
1150 self.w_categories_treeview.append_column(column)
1151 #Added selection listener
1152 category_selection = self.w_categories_treeview.get_selection()
1153 category_selection.set_mode(gtk.SELECTION_SINGLE)
1154
1155 if self.first_run:
1156 ##SECTION COMBOBOX
1157 #enumerations.SECTION_NAME
1158 cell = gtk.CellRendererText()
1159 self.w_sections_combobox.pack_start(cell, True)
1160 self.w_sections_combobox.add_attribute(cell, 'text',
1161 enumerations.SECTION_NAME)
1162 self.w_sections_combobox.set_row_separator_func(
1163 self.combobox_id_separator)
1164 self.w_sections_combobox.add_attribute( cell,
1165 'sensitive', enumerations.SECTION_ENABLED )
1166 ##FILTER COMBOBOX
1167 #enumerations.FILTER_NAME
1168 cell = gtk.CellRendererText()
1169 self.w_filter_combobox.pack_start(cell, True)
1170 self.w_filter_combobox.add_attribute(cell, 'text',
1171 enumerations.FILTER_NAME)
1172 self.w_filter_combobox.set_row_separator_func(
1173 self.combobox_id_separator)
1174
1175 if section_list != None:
1176 self.section_list = section_list
1177 if category_list != None:
1178 self.category_list = category_list
1179 self.category_list_filter = category_list_filter
1180 self.w_categories_treeview.set_model(category_list_filter)
1181 if not self.is_search_all:
1182 category_list_filter.set_visible_func(
1183 self.category_filter)
1184 self.__set_categories_visibility(self.set_section)
1185 self.a11y_categories_treeview = \
1186 self.w_categories_treeview.get_accessible()
1187 if application_list != None:
1188 if category_list != None:
1189 self.w_sections_combobox.set_model(section_list)
1190 self.w_sections_combobox.set_active(self.set_section)
1191 self.w_filter_combobox.set_model(self.filter_list)
1192 self.w_filter_combobox.set_active(self.set_show_filter)
1193 self.w_application_treeview.set_model(
1194 self.application_list_sort)
1195 if not self.in_search_mode:
1196 if application_list_filter == None:
1197 self.application_list_filter.set_visible_func(
1198 self.__application_filter)
1199
1200 category_selection = self.w_categories_treeview.get_selection()
1201 category_model, category_iter = category_selection.get_selected()
1202 self.pylintstub = category_model
1203 if not category_iter and not self.in_search_mode:
1204 #no category was selected, so select "All"
1205 category_selection.select_path(0)
1206 category_model, category_iter = category_selection.get_selected()
1207 if self.first_run:
1208 category_selection.connect("changed",
1209 self.__on_category_selection_changed, None)
1210 self.w_categories_treeview.connect("row-activated",
1211 self.__on_category_row_activated, None)
1212 self.w_categories_treeview.connect("focus-in-event",
1213 self.__on_category_focus_in, None)
1214 self.package_selection.set_mode(gtk.SELECTION_SINGLE)
1215 self.package_selection.connect("changed",
1216 self.__on_package_selection_changed, None)
1217
1218 self.a11y_application_treeview = \
1219 self.w_application_treeview.get_accessible()
1220 self.process_package_list_end()
1221
1222 def __categories_treeview_size_allocate(self, widget, allocation, user_data):
1223 # We ignore any changes in the size during initialization.
1224 if self.categories_treeview_initialized:
1225 if self.categories_status_id == 0:
1226 self.categories_status_id = gobject.idle_add(
1227 self.__set_accessible_categories_visible_status)
1228
1229 def __categories_treeview_vadjustment_changed(self, widget, user_data):
1230 self.__set_accessible_categories_visible_status()
1231
1232 def __set_accessible_categories_status(self, model, itr):
1233 status = model.get_value(itr, enumerations.CATEGORY_ICON)
1234 if status != None:
1235 desc = _("Updates Available")
1236 else:
1237 desc = None
1238 if desc != None:
1239 obj = self.a11y_categories_treeview.ref_at(
1240 int(model.get_string_from_iter(itr)),
1241 CATEGORIES_STATUS_COLUMN_INDEX)
1242 obj.set_image_description(desc)
1243
1244 def __set_accessible_categories_visible_status(self):
1245 self.categories_status_id = 0
1246 if self.a11y_categories_treeview.get_n_accessible_children() == 0:
1247 # accessibility is not enabled
1248 return
1249
1250 visible_range = self.w_categories_treeview.get_visible_range()
1251 if visible_range == None:
1252 return
1253 start = visible_range[0][0]
1254 end = visible_range[1][0]
1255 # We try to minimize the range of accessible objects
1256 # on which we set image descriptions
1257 if self.categories_treeview_range != None:
1258 old_start = self.categories_treeview_range[0][0]
1259 old_end = self.categories_treeview_range[1][0]
1260 # Old range is the same or smaller than new range
1261 # so do nothing
1262 if start >= old_start and end <= old_end:
1263 return
1264 if start < old_end:
1265 if end < old_end:
1266 if end >= old_start:
1267 end = old_start
1268 else:
1269 start = old_end
1270 self.categories_treeview_range = visible_range
1271 model = self.category_list_filter
1272 itr = model.get_iter_from_string(str(start))
1273 while start <= end:
1274 start += 1
1275 self.__set_accessible_categories_status(model, itr)
1276 itr = model.iter_next(itr)
1277
1278 def __application_treeview_column_sorted(self, widget, user_data):
1279 self.__set_visible_status(False)
1280
1281 def __init_repository_tree_view(self):
1282 cell = gtk.CellRendererText()
1283 self.w_repository_combobox.pack_start(cell, True)
1284 self.w_repository_combobox.add_attribute(cell, 'text',
1285 enumerations.REPOSITORY_NAME)
1286 self.w_repository_combobox.set_row_separator_func(
1287 self.combobox_id_separator)
1288
1289 def __application_treeview_size_allocate(self, widget, allocation, user_data):
1290 # We ignore any changes in the size during initialization.
1291 if self.visible_status_id == 0:
1292 self.visible_status_id = gobject.idle_add(
1293 self.__set_visible_status)
1294
1295 def __application_treeview_vadjustment_changed(self, widget, user_data):
1296 self.__set_visible_status()
1297
1298 def __set_accessible_status(self, model, itr):
1299 status = model.get_value(itr, enumerations.STATUS_COLUMN)
1300 if status == enumerations.INSTALLED:
1301 desc = _("Installed")
1302 elif status == enumerations.NOT_INSTALLED:
1303 desc = _("Not Installed")
1304 elif status == enumerations.UPDATABLE:
1305 desc = _("Updates Available")
1306 else:
1307 desc = None
1308 if desc != None:
1309 obj = self.a11y_application_treeview.ref_at(
1310 int(model.get_string_from_iter(itr)),
1311 STATUS_COLUMN_INDEX)
1312 obj.set_image_description(desc)
1313
1314 def __set_visible_status(self, check_range = True):
1315 self.visible_status_id = 0
1316 if self.w_main_view_notebook.get_current_page() != \
1317 NOTEBOOK_PACKAGE_LIST_PAGE:
1318 return
1319 if self.__doing_search():
1320 return
1321
1322 a11y_enabled = False
1323 if self.a11y_application_treeview.get_n_accessible_children() != 0:
1324 a11y_enabled = True
1325
1326 visible_range = self.w_application_treeview.get_visible_range()
1327 if visible_range == None:
1328 return
1329 start = visible_range[0][0]
1330 end = visible_range[1][0]
1331 if debug_descriptions:
1332 print "Range Start: %d End: %d" % (start, end)
1333
1334 # Switching Publishers need to use default range
1335 active_pub = self.__get_active_publisher()
1336 if self.last_active_publisher != active_pub:
1337 check_range = False
1338 self.last_active_publisher = active_pub
1339 if self.in_search_mode:
1340 check_range = False
1341
1342 if self.application_treeview_range != None:
1343 if check_range:
1344 old_start = self.application_treeview_range[0][0]
1345 old_end = self.application_treeview_range[1][0]
1346 # Old range is the same or smaller than new range
1347 # so do nothing
1348 if start >= old_start and end <= old_end:
1349 return
1350 if start < old_end:
1351 if end < old_end:
1352 if end >= old_start:
1353 end = old_start
1354 else:
1355 start = old_end
1356 if debug_descriptions:
1357 print "Adjusted Range Start: %d End: %d" % (start, end)
1358 self.application_treeview_range = visible_range
1359
1360 sort_filt_model = \
1361 self.w_application_treeview.get_model() #gtk.TreeModelSort
1362 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1363 model = filt_model.get_model() #gtk.ListStore
1364 sf_itr = sort_filt_model.get_iter_from_string(str(start))
1365 pkg_stems_and_itr_to_fetch = {}
1366 while start <= end:
1367 filtered_itr = sort_filt_model.convert_iter_to_child_iter(None,
1368 sf_itr)
1369 app_itr = filt_model.convert_iter_to_child_iter(filtered_itr)
1370
1371 desc = sort_filt_model.get_value(sf_itr,
1372 enumerations.DESCRIPTION_COLUMN)
1373 # Only Fetch description for packages without a
1374 # description
1375 if desc == '...':
1376 fmri = sort_filt_model.get_value(sf_itr,
1377 enumerations.FMRI_COLUMN)
1378 if fmri != None:
1379 pkg_stem = fmri.get_pkg_stem(
1380 include_scheme = True)
1381 pkg_stems_and_itr_to_fetch[pkg_stem] = \
1382 model.get_string_from_iter(app_itr)
1383 if a11y_enabled:
1384 self.__set_accessible_status(sort_filt_model, sf_itr)
1385 start += 1
1386 sf_itr = sort_filt_model.iter_next(sf_itr)
1387
1388 if debug_descriptions:
1389 print "PKGS to FETCH: \n%s" % pkg_stems_and_itr_to_fetch
1390 if len(pkg_stems_and_itr_to_fetch) > 0:
1391 Thread(target = self.__get_pkg_descriptions,
1392 args = [pkg_stems_and_itr_to_fetch, model]).start()
1393
1394 def __doing_search(self):
1395 return self.search_start > 0
1396
1397 def __get_pkg_descriptions(self, pkg_stems_and_itr_to_fetch, orig_model):
1398 # Note: no need to aquire lock even though this can be called from
1399 # multiple threads, it is just creating an update job and dispatching it
1400 # to the idle handler, not modifying any global state
1401 info = None
1402 if not self.__doing_search():
1403 gobject.idle_add(self.__update_statusbar_message,
1404 _("Fetching descriptions..."))
1405 try:
1406 info = self.api_o.info(pkg_stems_and_itr_to_fetch.keys(), False,
1407 frozenset([api.PackageInfo.IDENTITY,
1408 api.PackageInfo.SUMMARY]))
1409 except api_errors.TransportError:
1410 self.update_statusbar()
1411 return
1412 if info and len(info.get(0)) == 0:
1413 self.update_statusbar()
1414 return
1415 pkg_infos = info.get(0)
1416 pkg_descriptions_for_update = []
1417 for pkg_info in pkg_infos:
1418 short_fmri = fmri.PkgFmri(pkg_info.fmri).get_pkg_stem(
1419 include_scheme = True)
1420 pkg_descriptions_for_update.append((short_fmri,
1421 pkg_stems_and_itr_to_fetch[short_fmri],
1422 pkg_info.summary))
1423 if debug_descriptions:
1424 print "FETCHED PKGS: \n%s" % pkg_descriptions_for_update
1425 gobject.idle_add(self.__update_description_from_iter,
1426 pkg_descriptions_for_update, orig_model)
1427
1428 def __update_description_from_iter(self, pkg_descriptions_for_update, orig_model):
1429 #If doing a search abandon description updates
1430 if self.__doing_search():
1431 return
1432
1433 sort_filt_model = \
1434 self.w_application_treeview.get_model() #gtk.TreeModelSort
1435 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1436 model = filt_model.get_model() #gtk.ListStore
1437
1438 #If model has changed
1439 if orig_model != model:
1440 return
1441
1442 if debug_descriptions:
1443 print "UPDATE DESCRIPTIONS: \n%s" % pkg_descriptions_for_update
1444 for pkg_stem, path, summary in pkg_descriptions_for_update:
1445 itr = model.get_iter_from_string(path)
1446 stored_pkg_fmri = model.get_value(itr, enumerations.FMRI_COLUMN)
1447 stored_pkg_stem = stored_pkg_fmri.get_pkg_stem(
1448 include_scheme = True)
1449
1450 if pkg_stem != stored_pkg_stem:
1451 if debug:
1452 print ("__update_description_from_iter(): "
1453 "model not consistent so abandoning "
1454 "these description updates.")
1455 self.update_statusbar()
1456 return
1457 model.set_value(itr, enumerations.DESCRIPTION_COLUMN, summary)
1458 if not self.__doing_search():
1459 self.update_statusbar()
1460
1461 def __create_icon_column(self, name, expand_pixbuf, enum_value, set_data_func):
1462 column = gtk.TreeViewColumn()
1463 column.set_title(name)
1464 #Commented, since there was funny jumping of the icons
1465 #column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
1466 render_pixbuf = gtk.CellRendererPixbuf()
1467 column.pack_start(render_pixbuf, expand = expand_pixbuf)
1468 column.add_attribute(render_pixbuf, "pixbuf", enum_value)
1469 column.set_fixed_width(32)
1470 if set_data_func:
1471 column.set_cell_data_func(render_pixbuf,
1472 self.cell_data_function, None)
1473 return column
1474
1475 def __disconnect_models(self):
1476 self.w_application_treeview.set_model(None)
1477 self.w_categories_treeview.set_model(None)
1478 self.w_sections_combobox.set_model(None)
1479 self.w_filter_combobox.set_model(None)
1480
1481 def __disconnect_repository_model(self):
1482 self.w_repository_combobox.set_model(None)
1483
1484 @staticmethod
1485 def __status_sort_func(treemodel, iter1, iter2, user_data=None):
1486 get_val = treemodel.get_value
1487 status1 = get_val(iter1, enumerations.STATUS_COLUMN)
1488 status2 = get_val(iter2, enumerations.STATUS_COLUMN)
1489 return cmp(status1, status2)
1490
1491 @staticmethod
1492 def __remove_treeview_columns(treeview):
1493 columns = treeview.get_columns()
1494 if columns:
1495 for column in columns:
1496 treeview.remove_column(column)
1497
1498 @staticmethod
1499 def __init_sections(section_list):
1500 '''This function is for initializing sections combo box, also adds "All"
1501 Category. It sets active section combobox entry "All"'''
1502 cat_path = None
1503 enabled = True
1504 # We enable only first section and later we might enable the rest,
1505 # depending if there are some packages connected with them
1506 section_list.append([0, _('All Categories'), cat_path, enabled ])
1507 section_list.append([-1, "", cat_path, enabled ])
1508 enabled = False
1509 section_list.append([2, _('Meta Packages'), cat_path, enabled ])
1510 section_list.append([3, _('Applications'), cat_path, enabled ])
1511 section_list.append([4, _('Desktop (GNOME)'), cat_path, enabled ])
1512 section_list.append([5, _('Development'), cat_path, enabled ])
1513 section_list.append([6, _('Distributions'), cat_path, enabled ])
1514 section_list.append([7, _('Drivers'), cat_path, enabled ])
1515 section_list.append([8, _('System'), cat_path, enabled ])
1516 section_list.append([9, _('Web Services'), cat_path, enabled ])
1517
1518 def __init_show_filter(self):
1519 self.filter_list.append([enumerations.FILTER_ALL, _('All Packages'), ])
1520 self.filter_list.append([enumerations.FILTER_INSTALLED,
1521 _('Installed Packages'), ])
1522 self.filter_list.append([enumerations.FILTER_UPDATES,
1523 _('Updates'), ])
1524 self.filter_list.append([enumerations.FILTER_NOT_INSTALLED,
1525 _('Non-installed Packages'), ])
1526 self.filter_list.append([-1, "", ])
1527 self.filter_list.append([enumerations.FILTER_SELECTED,
1528 _('Selected Packages'), ])
1529 if self.initial_show_filter >= enumerations.FILTER_ALL and \
1530 self.initial_show_filter < len(self.filter_list):
1531 row = self.filter_list[self.initial_show_filter]
1532 if row[enumerations.SECTION_ID] != self.initial_show_filter:
1533 self.initial_show_filter = enumerations.FILTER_ALL
1534 else:
1535 self.initial_show_filter = enumerations.FILTER_ALL
1536
1537
1538 def __on_cancel_progressdialog_clicked(self, widget):
1539 self.progress_canceled = True
1540 self.progress_stop_timer_thread = True
1541
1542 def __on_mainwindow_delete_event(self, widget, event):
1543 ''' handler for delete event of the main window '''
1544 if self.__check_if_something_was_changed() == True:
1545 # XXX Change this to not quit and show dialog
1546 # XXX if some changes were applied:
1547 self.__main_application_quit()
1548 return True
1549 else:
1550 self.__main_application_quit()
1551
1552 def __on_api_search_error_delete_event(self, widget, event):
1553 self.__on_api_search_button_clicked(None)
1554
1555 def __on_api_search_button_clicked(self, widget):
1556 self.api_search_error_dialog.hide()
1557
1558 def __on_file_quit_activate(self, widget):
1559 ''' handler for quit menu event '''
1560 self.__on_mainwindow_delete_event(None, None)
1561
1562 def __on_ua_completed_close(self, widget):
1563 self.w_ua_completed_dialog.hide()
1564 self.__on_mainwindow_delete_event(None, None)
1565
1566 def __on_edit_repositories_activate(self, widget):
1567 ''' handler for repository menu event '''
1568 repository.Repository(self)
1569
1570 def __on_file_be_activate(self, widget):
1571 ''' handler for be menu event '''
1572 beadm.Beadmin(self)
1573
1574 def __on_searchentry_changed(self, widget):
1575 if widget.get_text_length() > 0:
1576 self.w_clear_search_button.set_sensitive(True)
1577 else:
1578 self.w_clear_search_button.set_sensitive(False)
1579 self.__enable_disable_entry_selection(widget)
1580 if self.is_search_all and not self.changing_search_option:
1581 if self.w_searchentry.get_text() == "":
1582 self.w_infosearch_frame.hide()
1583 self.__link_load_blank()
1584 self.w_main_view_notebook.set_current_page(
1585 NOTEBOOK_START_PAGE)
1586 self.__update_statusbar_for_search()
1587 self.w_searchentry.grab_focus()
1588
1589 def __update_statusbar_for_search(self):
1590 self.__update_statusbar_message(
1591 self.search_options[self.current_search_option][3])
1592
1593 def __update_statusbar_message(self, message):
1594 if self.statusbar_message_id > 0:
1595 self.w_main_statusbar.remove(0, self.statusbar_message_id)
1596 self.statusbar_message_id = 0
1597 self.statusbar_message_id = self.w_main_statusbar.push(0, message)
1598
1599 def __setup_before_search_all_mode(self):
1600 self.is_search_all = True
1601 self.w_infosearch_frame.hide()
1602
1603 self.__save_setup_before_search()
1604 self.w_repository_combobox.set_active(0)
1605 self.__link_load_blank()
1606 self.w_main_view_notebook.set_current_page(
1607 NOTEBOOK_START_PAGE)
1608 self.__update_statusbar_for_search()
1609 self.w_searchentry.grab_focus()
1610 if len(self.w_searchentry.get_text()) > 0:
1611 start, end = self.w_searchentry.get_selection_bounds()
1612 self.w_searchentry.select_region(end, end)
1613 self.__unselect_category()
1614
1615 def __clear_before_search(self):
1616 self.in_setup = True
1617 application_list = self.__get_new_application_liststore()
1618 self.__set_empty_details_panel()
1619 self.__set_main_view_package_list()
1620 self.__init_tree_views(application_list, None, None)
1621 self.__unselect_category()
1622
1623 def __restore_setup_for_browse(self):
1624 self.in_search_mode = False
1625 self.is_search_all = False
1626 self.w_infosearch_frame.hide()
1627
1628 self.set_busy_cursor()
1629 self.w_repository_combobox.set_active(
1630 self.saved_repository_combobox_active)
1631 self.set_section = self.saved_sections_combobox_active
1632 self.set_show_filter = self.saved_filter_combobox_active
1633 if self.saved_category_list == self.category_list:
1634 self.__init_tree_views(self.saved_application_list,
1635 None, None,
1636 self.saved_application_list_filter,
1637 self.saved_application_list_sort)
1638 else:
1639 self.__init_tree_views(self.saved_application_list,
1640 self.saved_category_list, self.saved_section_list,
1641 self.saved_application_list_filter,
1642 self.saved_application_list_sort)
1643
1644 self.__set_main_view_package_list()
1645
1646 def __save_setup_before_search(self, single_search=False):
1647 #Do not save search data models
1648 if self.in_search_mode:
1649 return
1650 self.saved_sections_combobox_active = \
1651 self.w_sections_combobox.get_active()
1652 self.saved_filter_combobox_active = \
1653 self.w_filter_combobox.get_active()
1654 self.saved_application_list = self.application_list
1655 self.saved_application_list_sort = \
1656 self.application_list_sort
1657 self.saved_application_list_filter = \
1658 self.application_list_filter
1659 self.saved_category_list = self.category_list
1660 self.saved_section_list = self.section_list
1661 if single_search:
1662 self.saved_repository_combobox_active = \
1663 self.w_repository_combobox.get_active()
1664 self.w_filter_combobox.set_active(0)
1665
1666 def __do_search(self):
1667 self.search_start = 0
1668 if self.changing_search_option:
1669 return
1670 active = self.w_filter_combobox.get_active()
1671 if active != enumerations.FILTER_SELECTED:
1672 self.saved_filter_combobox_active = active
1673 if len(self.w_searchentry.get_text()) == 0:
1674 return
1675 if not self.is_search_all:
1676 self.__save_setup_before_search(single_search=True)
1677 self.__clear_before_search()
1678 self.set_busy_cursor()
1679 self.in_search_mode = True
1680
1681 self.w_infosearch_frame.hide()
1682 self.__update_statusbar_message(_("Searching..."))
1683 if not self.is_search_all:
1684 Thread(target = self.__do_api_search,
1685 args = (self.is_search_all, )).start()
1686 else:
1687 Thread(target = self.__do_api_search,
1688 args = ()).start()
1689
1690 def __unselect_category(self):
1691 selection = self.w_categories_treeview.get_selection()
1692 model, itr = selection.get_selected()
1693 if itr:
1694 cat_path = model.get_string_from_iter(itr)
1695 selected_section = self.w_sections_combobox.get_active()
1696 section_row = self.section_list[selected_section]
1697 section_row[enumerations.SECTION_SUBCATEGORY] = cat_path
1698 selection.unselect_all()
1699
1700 def __process_after_search_failure(self):
1701 self.search_start = 0
1702 self.search_time_sec = 0
1703 self.application_list = []
1704 self.update_statusbar()
1705 self.unset_busy_cursor()
1706 self.in_setup = False
1707
1708 def __get_origin_uri(self, repo):
1709 if repo == None:
1710 return None
1711 origin_uri = repo.origins[0]
1712 ret_uri = None
1713 if isinstance(origin_uri, str):
1714 if len(origin_uri) > 0:
1715 ret_uri = origin_uri.strip("/")
1716 elif isinstance(origin_uri, publisher.RepositoryURI):
1717 uri = origin_uri.uri
1718 if uri != None and len(uri) > 0:
1719 ret_uri = uri.strip("/")
1720 return ret_uri
1721
1722
1723 def __do_api_search(self, search_all = True):
1724 self.search_start = time.time()
1725 self.search_time_sec = 0
1726 text = self.w_searchentry.get_text()
1727 # Here we call the search API to get the results
1728 searches = []
1729 servers = []
1730 result = []
1731 pargs = []
1732 search_str = SEARCH_STR_FORMAT % text
1733 pargs.append(search_str)
1734 if search_all:
1735 servers = None
1736 else:
1737 pub_prefix = self.__get_active_publisher()
1738 if pub_prefix != None:
1739 pub = self.api_o.get_publisher(prefix=pub_prefix)
1740 else:
1741 pub = self.api_o.get_preferred_publisher()
1742 origin_uri = self.__get_origin_uri(pub.selected_repository)
1743 servers.append({"origin": origin_uri})
1744 if debug:
1745 print "Search: pargs %s servers: %s" % (pargs, servers)
1746
1747 #TBD If we ever search just Installed pkgs should allow for a local search
1748 case_sensitive = False
1749 return_actions = True
1750 searches.append(self.api_o.remote_search(
1751 [api.Query(" ".join(pargs), case_sensitive, return_actions)],
1752 servers=servers))
1753 if debug:
1754 print "Search Args: %s : cs: %s : retact: %s" % \
1755 ("".join(pargs), case_sensitive, return_actions)
1756
1757 last_name = ""
1758 self.current_search_publisher = None
1759 try:
1760 for query_num, publisher, (v, return_type, tmp) in \
1761 itertools.chain(*searches):
1762 if v < 1 or return_type != api.Query.RETURN_PACKAGES:
1763 gobject.idle_add(self.w_progress_dialog.hide)
1764 self.__process_after_search_failure()
1765 return
1766
1767 pub = None
1768 if publisher is not None \
1769 and "prefix" in publisher:
1770 pub = publisher["prefix"]
1771 name = fmri.PkgFmri(str(tmp)).get_name()
1772 if last_name != name:
1773 if debug:
1774 print "Result Name: %s (%s)" % (name, pub)
1775 a_res = name, pub
1776 result.append(a_res)
1777 #Ignore Status when fetching
1778 application_list = \
1779 self.__get_min_list_from_search(result)
1780 self.current_search_publisher = pub
1781 self.in_setup = True
1782 gobject.idle_add(self.__init_tree_views,
1783 application_list, None, None)
1784 last_name = name
1785 self.pylintstub = query_num
1786 except api_errors.ProblematicSearchServers, ex:
1787 self.__process_api_search_error(ex)
1788 gobject.idle_add(self.w_progress_dialog.hide)
1789 gobject.idle_add(self.__handle_api_search_error)
1790 if len(result) == 0:
1791 self.__process_after_search_with_zero_results()
1792 return
1793 except Exception, ex:
1794 # We are not interested in this error
1795 gobject.idle_add(self.w_progress_dialog.hide)
1796 self.__process_after_search_failure()
1797 return
1798 if debug:
1799 print "Number of search results:", len(result)
1800 if len(result) == 0:
1801 if debug:
1802 print "No search results"
1803 self.__process_after_search_with_zero_results()
1804 return
1805 # We cannot get status of the packages if catalogs have not
1806 # been loaded so we pause for up to 5 seconds here to
1807 # allow catalogs to be loaded
1808 times = 5
1809 while self.catalog_loaded == False:
1810 if times == 0:
1811 break
1812 time.sleep(1)
1813 times -= 1
1814
1815 #Now fetch full result set with Status
1816 self.in_setup = True
1817 application_list = self.__get_full_list_from_search(result)
1818 gobject.idle_add(self.__init_tree_views, application_list, None, None)
1819
1820 if self.search_start > 0:
1821 self.search_time_sec = int(time.time() - self.search_start)
1822 if debug:
1823 print "Search time: %d (sec)" % self.search_time_sec
1824 self.search_start = 0
1825
1826 def __process_after_search_with_zero_results(self):
1827 if self.search_start > 0:
1828 self.search_time_sec = int(time.time() - self.search_start)
1829 self.search_start = 0
1830 self.in_setup = True
1831 application_list = self.__get_new_application_liststore()
1832 gobject.idle_add(self.__set_empty_details_panel)
1833 gobject.idle_add(self.__set_main_view_package_list)
1834 gobject.idle_add(self.__init_tree_views, application_list, None, None)
1835
1836 def __get_min_list_from_search(self, search_result):
1837 application_list = self.__get_new_application_liststore()
1838 for name, publisher in search_result:
1839 application_list.append(
1840 [False, None, name, '...', enumerations.NOT_INSTALLED, None,
1841 "pkg://" + publisher + "/" + name, None, True, None,
1842 publisher])
1843 return application_list
1844
1845 def __get_full_list_from_search(self, search_result):
1846 application_list = self.__get_new_application_liststore()
1847 self.__add_pkgs_to_list_from_search(search_result,
1848 application_list)
1849 return application_list
1850
1851 def __add_pkgs_to_list_from_search(self, search_result,
1852 application_list):
1853 pargs = []
1854 default_pub = self.api_o.get_preferred_publisher().prefix
1855 for name, publisher in search_result:
1856 pargs.append("pkg://" + publisher + "/" + name)
1857 # We now need to get the status for each package
1858 if debug_descriptions:
1859 print "pargs:", pargs
1860 try:
1861 pkgs_known = self.__get_inventory_list(pargs,
1862 True, True)
1863 except api_errors.InventoryException:
1864 # This can happen if load_catalogs has not been run
1865 err = _("Unable to get status for search results.\n"
1866 "The catalogs have not been loaded.\n"
1867 "Please try after few seconds.\n")
1868 gobject.idle_add(self.w_progress_dialog.hide)
1869 gobject.idle_add(self.error_occurred, err)
1870 return
1871 return self.__add_pkgs_to_lists(pkgs_known, application_list,
1872 None, None)
1873
1874 def __application_refilter(self):
1875 ''' Disconnecting the model from the treeview improves
1876 performance when assistive technologies are enabled'''
1877 if self.in_setup:
1878 return
1879 self.application_refilter_id = 0
1880 self.application_refilter_idle_id = 0
1881 if not self.in_search_mode:
1882 model = self.w_application_treeview.get_model()
1883 self.w_application_treeview.set_model(None)
1884 self.application_list_filter.refilter()
1885 self.w_application_treeview.set_model(model)
1886 gobject.idle_add(self.__set_empty_details_panel)
1887 gobject.idle_add(self.__enable_disable_selection_menus)
1888 gobject.idle_add(self.__enable_disable_install_remove)
1889 self.application_treeview_initialized = True
1890 self.application_treeview_range = None
1891 if self.visible_status_id == 0:
1892 self.visible_status_id = gobject.idle_add(
1893 self.__set_visible_status)
1894 self.categories_treeview_initialized = True
1895 self.categories_treeview_range = None
1896 if self.categories_status_id == 0:
1897 self.categories_status_id = gobject.idle_add(
1898 self.__set_accessible_categories_visible_status)
1899 return False
1900
1901 def __on_edit_paste(self, widget):
1902 self.w_searchentry.paste_clipboard()
1903
1904 def __on_clear_paste(self, widget):
1905 bounds = self.w_searchentry.get_selection_bounds()
1906 self.w_searchentry.delete_text(bounds[0], bounds[1])
1907 return
1908
1909 def __on_copy(self, widget):
1910 focus_widget = self.w_main_window.get_focus()
1911 if focus_widget == self.w_searchentry:
1912 self.w_searchentry.copy_clipboard()
1913 self.w_paste_menuitem.set_sensitive(True)
1914 elif self.__is_a_textview(focus_widget):
1915 focus_widget.get_buffer().copy_clipboard(
1916 self.w_main_clipboard)
1917
1918 def __on_cut(self, widget):
1919 self.w_searchentry.cut_clipboard()
1920 self.w_paste_menuitem.set_sensitive(True)
1921
1922 def __popup_position_func(self, menu):
1923 ''' Position popup menu immediately below search button'''
1924 root = self.w_main_window.window.get_origin()
1925 alloc = self.search_button.get_allocation()
1926 return (root[0] + alloc.x, root[1] + alloc.y + alloc.height, False)
1927
1928 def __on_set_search(self, widget, event):
1929 if event.type == gtk.gdk.BUTTON_PRESS:
1930 self.searchmenu.popup(None, None, self.__popup_position_func,
1931 event.button, event.time)
1932 return True
1933 return False
1934
1935 def __on_set_search_clicked(self, widget):
1936 self.searchmenu.popup(None, None, self.__popup_position_func,
1937 0, 0)
1938 return True
1939
1940 def __on_edit_search_clicked(self, widget):
1941 self.w_searchentry.grab_focus()
1942
1943 def __on_clear_search(self, widget):
1944 self.w_searchentry.delete_text(0, -1)
1945 self.__do_search()
1946 return
1947
1948 def __on_startpage(self, widget):
1949 self.__load_startpage()
1950 self.w_main_view_notebook.set_current_page(NOTEBOOK_START_PAGE)
1951
1952 def __on_notebook_change(self, widget, event, pagenum):
1953 if (pagenum == INFO_NOTEBOOK_LICENSE_PAGE and
1954 not self.showing_empty_details):
1955 licbuffer = self.w_license_textview.get_buffer()
1956 leg_txt = _("Fetching legal information...")
1957 licbuffer.set_text(leg_txt)
1958 if self.show_licenses_id != 0:
1959 gobject.source_remove(self.show_licenses_id)
1960 self.show_licenses_id = 0
1961 self.last_show_licenses_id = self.show_licenses_id = \
1962 gobject.timeout_add(SHOW_LICENSE_DELAY,
1963 self.__show_licenses)
1964
1965 def __is_a_textview(self, widget):
1966 if (widget == self.w_generalinfo_textview or
1967 widget == self.w_installedfiles_textview or
1968 widget == self.w_dependencies_textview or
1969 widget == self.w_license_textview):
1970 return True
1971 else:
1972 return False
1973
1974
1975 def __on_select_all(self, widget):
1976 focus_widget = self.w_main_window.get_focus()
1977 if self.__is_a_textview(focus_widget):
1978 focus_widget.emit('select-all', True)
1979 self.w_selectall_menuitem.set_sensitive(False)
1980 self.w_deselect_menuitem.set_sensitive(True)
1981 return
1982 elif focus_widget == self.w_searchentry:
1983 focus_widget.select_region(0, -1)
1984 self.w_selectall_menuitem.set_sensitive(False)
1985 self.w_deselect_menuitem.set_sensitive(True)
1986 return
1987
1988 sort_filt_model = \
1989 self.w_application_treeview.get_model() #gtk.TreeModelSort
1990 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1991 model = filt_model.get_model() #gtk.ListStore
1992 iter_next = sort_filt_model.get_iter_first()
1993 list_of_paths = []
1994 while iter_next != None:
1995 sorted_path = sort_filt_model.get_path(iter_next)
1996 filtered_path = \
1997 sort_filt_model.convert_path_to_child_path(sorted_path)
1998 path = filt_model.convert_path_to_child_path(filtered_path)
1999 list_of_paths.append(path)
2000 iter_next = sort_filt_model.iter_next(iter_next)
2001 for path in list_of_paths:
2002 itr = model.get_iter(path)
2003 already_marked = model.get_value(itr, enumerations.MARK_COLUMN)
2004 if not already_marked:
2005 model.set_value(itr, enumerations.MARK_COLUMN, True)
2006 pkg_stem = model.get_value(itr,
2007 enumerations.STEM_COLUMN)
2008 pkg_status = model.get_value(itr,
2009 enumerations.STATUS_COLUMN)
2010 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2011 self.w_selectall_menuitem.set_sensitive(False)
2012 self.w_deselect_menuitem.set_sensitive(True)
2013 self.__enable_disable_selection_menus()
2014 self.update_statusbar()
2015 self.__enable_disable_install_remove()
2016
2017 def __on_select_updates(self, widget):
2018 sort_filt_model = \
2019 self.w_application_treeview.get_model() #gtk.TreeModelSort
2020 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
2021 model = filt_model.get_model() #gtk.ListStore
2022 iter_next = sort_filt_model.get_iter_first()
2023 list_of_paths = []
2024 while iter_next != None:
2025 sorted_path = sort_filt_model.get_path(iter_next)
2026 filtered_iter = sort_filt_model.convert_iter_to_child_iter(None, \
2027 iter_next)
2028 app_iter = filt_model.convert_iter_to_child_iter(filtered_iter)
2029
2030 filtered_path = \
2031 sort_filt_model.convert_path_to_child_path(sorted_path)
2032 path = filt_model.convert_path_to_child_path(filtered_path)
2033 if model.get_value(app_iter, \
2034 enumerations.STATUS_COLUMN) == enumerations.UPDATABLE:
2035 list_of_paths.append(path)
2036 iter_next = sort_filt_model.iter_next(iter_next)
2037 for path in list_of_paths:
2038 itr = model.get_iter(path)
2039 model.set_value(itr, enumerations.MARK_COLUMN, True)
2040 pkg_stem = model.get_value(itr, enumerations.STEM_COLUMN)
2041 pkg_status = model.get_value(itr, enumerations.STATUS_COLUMN)
2042 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2043 self.__enable_disable_selection_menus()
2044 self.update_statusbar()
2045 self.__enable_disable_install_remove()
2046
2047 def __on_deselect(self, widget):
2048 focus_widget = self.w_main_window.get_focus()
2049 if self.__is_a_textview(focus_widget):
2050 focus_widget.emit('select-all', False)
2051 self.w_deselect_menuitem.set_sensitive(False)
2052 self.w_selectall_menuitem.set_sensitive(True)
2053 return
2054 elif focus_widget == self.w_searchentry:
2055 focus_widget.select_region(0, 0)
2056 self.w_deselect_menuitem.set_sensitive(False)
2057 self.w_selectall_menuitem.set_sensitive(True)
2058 return
2059
2060 sort_filt_model = \
2061 self.w_application_treeview.get_model() #gtk.TreeModelSort
2062 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
2063 model = filt_model.get_model() #gtk.ListStore
2064 iter_next = sort_filt_model.get_iter_first()
2065 list_of_paths = []
2066 while iter_next != None:
2067 sorted_path = sort_filt_model.get_path(iter_next)
2068 filtered_iter = sort_filt_model.convert_iter_to_child_iter(None, \
2069 iter_next)
2070 app_iter = filt_model.convert_iter_to_child_iter(filtered_iter)
2071 filtered_path = \
2072 sort_filt_model.convert_path_to_child_path(sorted_path)
2073 path = filt_model.convert_path_to_child_path(filtered_path)
2074 if model.get_value(app_iter, enumerations.MARK_COLUMN):
2075 list_of_paths.append(path)
2076 iter_next = sort_filt_model.iter_next(iter_next)
2077 for path in list_of_paths:
2078 itr = model.get_iter(path)
2079 already_deselected = not model.get_value(itr,
2080 enumerations.MARK_COLUMN)
2081 if not already_deselected:
2082 model.set_value(itr, enumerations.MARK_COLUMN, False)
2083 self.__remove_pkg_stem_from_list(model.get_value(itr,
2084 enumerations.STEM_COLUMN))
2085 self.w_selectall_menuitem.set_sensitive(True)
2086 self.w_deselect_menuitem.set_sensitive(False)
2087 self.__enable_disable_selection_menus()
2088 self.update_statusbar()
2089 self.__enable_disable_install_remove()
2090
2091 def __on_preferences(self, widget):
2092 self.w_startpage_checkbutton.set_active(self.show_startpage)
2093 self.w_preferencesdialog.show()
2094
2095 def __on_preferencesclose_clicked(self, widget):
2096 self.w_preferencesdialog.hide()
2097
2098 def __on_preferenceshelp_clicked(self, widget):
2099 gui_misc.display_help(self.application_dir, "pm_win")
2100
2101 def __on_startpage_checkbutton_toggled(self, widget):
2102 self.show_startpage = self.w_startpage_checkbutton.get_active()
2103 try:
2104 self.client.set_bool(SHOW_STARTPAGE_PREFERENCES,
2105 self.show_startpage)
2106 except GError:
2107 pass
2108
2109 def __on_api_search_checkbox_toggled(self, widget):
2110 active = self.api_search_checkbox.get_active()
2111 if len(self.current_repos_with_search_errors) > 0:
2112 if active:
2113 for pub, err_type, err_str in \
2114 self.current_repos_with_search_errors:
2115 if pub not in self.gconf_not_show_repos:
2116 self.gconf_not_show_repos += pub + ","
2117 else:
2118 for pub, err_type, err_str in \
2119 self.current_repos_with_search_errors:
2120 self.gconf_not_show_repos = \
2121 self.gconf_not_show_repos.replace(
2122 pub + ",", "")
2123 try:
2124 self.client.set_string(API_SEARCH_ERROR_PREFERENCES,
2125 self.gconf_not_show_repos)
2126 except GError:
2127 pass
2128
2129 def __on_searchentry_focus_in(self, widget, event):
2130 if self.w_main_clipboard.wait_is_text_available():
2131 self.w_paste_menuitem.set_sensitive(True)
2132 char_count = widget.get_text_length()
2133 if char_count > 0:
2134 self.w_selectall_menuitem.set_sensitive(True)
2135 else:
2136 self.w_selectall_menuitem.set_sensitive(False)
2137 bounds = widget.get_selection_bounds()
2138 if bounds:
2139 offset1 = bounds[0]
2140 offset2 = bounds[1]
2141 if abs(offset2 - offset1) == char_count:
2142 self.w_selectall_menuitem.set_sensitive(False)
2143 self.w_deselect_menuitem.set_sensitive(True)
2144 self.w_copy_menuitem.set_sensitive(True)
2145 else:
2146 self.w_deselect_menuitem.set_sensitive(False)
2147
2148 def __on_searchentry_focus_out(self, widget, event):
2149 self.w_paste_menuitem.set_sensitive(False)
2150 self.__enable_disable_select_all()
2151 self.__enable_disable_deselect()
2152 self.w_cut_menuitem.set_sensitive(False)
2153 self.w_copy_menuitem.set_sensitive(False)
2154 self.w_clear_menuitem.set_sensitive(False)
2155 return False
2156
2157 def __on_searchentry_activate(self, widget):
2158 self.__do_search()
2159
2160 def __on_searchentry_selection(self, widget, pspec):
2161 self.__enable_disable_entry_selection(widget)
2162
2163 def __enable_disable_entry_selection(self, widget):
2164 char_count = widget.get_text_length()
2165 bounds = widget.get_selection_bounds()
2166 if bounds:
2167 #enable selection functions
2168 self.w_cut_menuitem.set_sensitive(True)
2169 self.w_copy_menuitem.set_sensitive(True)
2170 self.w_clear_menuitem.set_sensitive(True)
2171 if char_count == abs(bounds[1] - bounds[0]):
2172 self.w_selectall_menuitem.set_sensitive(False)
2173 else:
2174 self.w_selectall_menuitem.set_sensitive(True)
2175 self.w_deselect_menuitem.set_sensitive(True)
2176 else:
2177 self.w_cut_menuitem.set_sensitive(False)
2178 self.w_copy_menuitem.set_sensitive(False)
2179 self.w_clear_menuitem.set_sensitive(False)
2180 self.w_deselect_menuitem.set_sensitive(False)
2181 if char_count == 0:
2182 self.w_selectall_menuitem.set_sensitive(False)
2183 else:
2184 self.w_selectall_menuitem.set_sensitive(True)
2185
2186 def __refilter_on_idle(self):
2187 if self.application_refilter_id != 0:
2188 gobject.source_remove(self.application_refilter_id)
2189 self.application_refilter_id = 0
2190 if self.application_refilter_idle_id == 0:
2191 self.application_refilter_idle_id = gobject.idle_add(
2192 self.__application_refilter)
2193
2194 def __on_category_focus_in(self, widget, event, user):
2195 self.__on_category_row_activated(None, None, None, user)
2196
2197 def __on_category_row_activated(self, view, path, col, user):
2198 '''This function is for handling category double click activations'''
2199 if self.w_filter_combobox.get_model():
2200 self.w_filter_combobox.set_active(self.saved_filter_combobox_active)
2201 self.w_searchentry.delete_text(0, -1)
2202 if self.in_search_mode or self.is_search_all:
2203 self.__unset_search(True)
2204 if self.selected == 0:
2205 gobject.idle_add(self.__enable_disable_install_remove)
2206 return
2207 self.__set_main_view_package_list()
2208 self.set_busy_cursor()
2209 self.__refilter_on_idle()
2210 if self.selected == 0:
2211 gobject.idle_add(self.__enable_disable_install_remove)
2212
2213 def __set_main_view_package_list(self):
2214 # Only switch from Start Page View to List view if we are not in startup
2215 if not self.in_startpage_startup:
2216 self.w_main_view_notebook.set_current_page(
2217 NOTEBOOK_PACKAGE_LIST_PAGE)
2218
2219 def __on_category_selection_changed(self, selection, widget):
2220 '''This function is for handling category selection changes'''
2221 if self.in_setup or self.changing_search_option:
2222 return
2223 model, itr = selection.get_selected()
2224 if itr:
2225 cat_path = model.get_string_from_iter(itr)
2226 if self.is_search_all:
2227 selected_section = self.set_section
2228 else:
2229 selected_section = self.w_sections_combobox.get_active()
2230 section_row = self.section_list[selected_section]
2231 section_row[enumerations.SECTION_SUBCATEGORY] = cat_path
2232
2233 if self.in_search_mode or self.is_search_all:
2234 return
2235
2236 if self.saved_filter_combobox_active != None:
2237 self.w_filter_combobox.set_active(self.saved_filter_combobox_active)
2238 self.__set_main_view_package_list()
2239
2240 self.set_busy_cursor()
2241 self.__refilter_on_idle()
2242 if self.selected == 0:
2243 gobject.idle_add(self.__enable_disable_install_remove)
2244
2245 def __process_package_selection(self):
2246 model, itr = self.package_selection.get_selected()
2247 if self.show_info_id != 0:
2248 gobject.source_remove(self.show_info_id)
2249 self.show_info_id = 0
2250 if itr:
2251 self.__enable_disable_install_remove()
2252 self.selected_pkgstem = \
2253 model.get_value(itr, enumerations.STEM_COLUMN)
2254 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
2255 gobject.idle_add(self.__show_fetching_package_info, pkg)
2256 self.showing_empty_details = False
2257 self.last_show_info_id = self.show_info_id = \
2258 gobject.timeout_add(SHOW_INFO_DELAY,
2259 self.__show_info, model, model.get_path(itr))
2260 if (self.w_info_notebook.get_current_page() ==
2261 INFO_NOTEBOOK_LICENSE_PAGE):
2262 self.__on_notebook_change(None, None,
2263 INFO_NOTEBOOK_LICENSE_PAGE)
2264 else:
2265 self.selected_model = None
2266 self.selected_path = None
2267 self.selected_pkgstem = None
2268
2269 def __on_package_selection_changed(self, selection, widget):
2270 '''This function is for handling package selection changes'''
2271 if self.in_setup:
2272 return
2273 self.__process_package_selection()
2274
2275 def __on_filtercombobox_changed(self, widget):
2276 '''On filter combobox changed'''
2277 if self.in_setup or self.changing_search_option:
2278 return
2279 active = self.w_filter_combobox.get_active()
2280 if active != enumerations.FILTER_SELECTED:
2281 self.saved_filter_combobox_active = active
2282 self.__set_main_view_package_list()
2283 if self.in_search_mode or self.is_search_all:
2284 self.set_busy_cursor()
2285 self.saved_filter_combobox_active = \
2286 self.w_filter_combobox.get_active()
2287 self.__unset_search(True)
2288 return
2289 self.set_busy_cursor()
2290 self.__refilter_on_idle()
2291 if self.selected == 0:
2292 gobject.idle_add(self.__enable_disable_install_remove)
2293
2294 def __set_categories_visibility(self, selected_section):
2295 self.category_list[0][enumerations.CATEGORY_ICON] = None
2296 if selected_section == 0:
2297 for category in self.category_list:
2298 category[enumerations.CATEGORY_VISIBLE] = True
2299 else:
2300 for category in self.category_list:
2301 if category[enumerations.CATEGORY_ID] == 0:
2302 category[enumerations.CATEGORY_VISIBLE] = True
2303 else:
2304 category_list = \
2305 category[enumerations.SECTION_LIST_OBJECT]
2306 if not category_list:
2307 category[enumerations.CATEGORY_VISIBLE] \
2308 = False
2309 else:
2310 for section in category_list:
2311 if section == selected_section:
2312 category[enumerations. \
2313 CATEGORY_VISIBLE] = \
2314 True
2315 else:
2316 category[enumerations. \
2317 CATEGORY_VISIBLE] = \
2318 False
2319
2320 # Set category icon for All if a visible category has it
2321 for category in self.category_list:
2322 if category[enumerations.CATEGORY_ICON] != None:
2323 self.category_list[0][enumerations.CATEGORY_ICON] = \
2324 category[enumerations.CATEGORY_ICON]
2325 break
2326
2327 section_row = self.section_list[selected_section]
2328 cat_path = section_row[enumerations.SECTION_SUBCATEGORY]
2329 if cat_path != None:
2330 itr = self.category_list_filter.get_iter_from_string(cat_path)
2331 path = self.category_list_filter.get_path(itr)
2332 self.w_categories_treeview.set_cursor(path,
2333 None, start_editing=False)
2334
2335 def __on_sectionscombobox_changed(self, widget):
2336 '''On section combobox changed'''
2337 if self.in_setup:
2338 return
2339 if self.changing_search_option:
2340 return
2341 self.__set_main_view_package_list()
2342 self.set_busy_cursor()
2343 self.__set_first_category_text()
2344 self.__set_categories_visibility(widget.get_active())
2345 self.category_list_filter.refilter()
2346 if self.in_search_mode or self.is_search_all:
2347 self.saved_sections_combobox_active = \
2348 self.w_sections_combobox.get_active()
2349 self.__unset_search(True)
2350 return
2351 self.__refilter_on_idle()
2352 if self.selected == 0:
2353 gobject.idle_add(self.__enable_disable_install_remove)
2354
2355 def __set_first_category_text(self):
2356 active_section = self.w_sections_combobox.get_active()
2357 all_cat_text = _("All")
2358 if active_section != 0:
2359 all_cat_text += " " + self.section_list[active_section][1]
2360 category_model = self.w_categories_treeview.get_model()
2361 if category_model:
2362 list_store = category_model.get_model()
2363 list_store[0][1] = all_cat_text
2364
2365 def __unset_search(self, same_repo):
2366 self.w_infosearch_frame.hide()
2367 self.changing_search_option = True
2368 self.current_search_option = 0
2369 visible_repository = self.__get_visible_repository_name()
2370 if visible_repository in self.selected_pkgs:
2371 self.selected_pkgs.pop(visible_repository)
2372 if visible_repository in self.to_install_update:
2373 self.to_install_update.pop(visible_repository)
2374 if visible_repository in self.to_remove:
2375 self.to_remove.pop(visible_repository)
2376 self.__update_tooltips()
2377 if self.is_search_all:
2378 self.__update_repository_combobox_for_search(False)
2379 pixbuf = self.search_options[0][1]
2380 self.search_image.set_from_pixbuf(pixbuf)
2381 self.in_search_mode = False
2382 self.is_search_all = False
2383 if same_repo:
2384 self.__restore_setup_for_browse()
2385 self.changing_search_option = False
2386
2387 def __on_repositorycombobox_changed(self, widget):
2388 '''On repository combobox changed'''
2389 if self.changing_search_option:
2390 return
2391 self.changing_search_option = True
2392 active_publisher = self.__get_active_publisher()
2393 if self.is_search_all:
2394 same_repo = False
2395 active = self.w_repository_combobox.get_active() - 1
2396 if active == -1:
2397 # We get here is we choose "Add ..." when
2398 # doing api search
2399 self.changing_search_option = False
2400 return
2401 if not active_publisher == _("Add..."):
2402 if self.saved_repository_combobox_active == active:
2403 same_repo = True
2404 self.__unset_search(same_repo)
2405 self.w_repository_combobox.set_active(active)
2406
2407 if same_repo:
2408 self.changing_search_option = False
2409 return
2410 active_publisher = self.__get_active_publisher()
2411 self.changing_search_option = False
2412 if self.visible_repository == active_publisher:
2413 # If we are coming back to the same repository, we do
2414 # not want to setup publishers. This is the case when
2415 # we are calling Add... then we are firing the event for
2416 # Add... case and immediately coming back to the
2417 # previously selected repository.
2418 return
2419 # Checking for Add... is fine enough, as the repository
2420 # name cannot contain "..." in the name.
2421 if active_publisher == _("Add..."):
2422 index = -1
2423 if self.is_search_all:
2424 index = 0
2425 else:
2426 model = self.w_repository_combobox.get_model()
2427 for entry in model:
2428 if entry[1] == self.visible_repository:
2429 index = entry[0]
2430 break
2431 # We do not want to switch permanently to the Add...
2432 self.w_repository_combobox.set_active(index)
2433 self.__on_edit_repositories_activate(None)
2434 return
2435 self.cancelled = True
2436 self.in_setup = True
2437 self.set_busy_cursor()
2438 self.__set_empty_details_panel()
2439 if self.in_search_mode:
2440 self.__unset_search(False)
2441 self.w_searchentry.grab_focus()
2442 if len(self.w_searchentry.get_text()) > 0:
2443 start, end = self.w_searchentry.get_selection_bounds()
2444 self.w_searchentry.select_region(end, end)
2445
2446 pub = [active_publisher, ]
2447 self.set_show_filter = self.initial_show_filter
2448 self.set_section = self.initial_section
2449 Thread(target = self.__setup_publisher, args = [pub]).start()
2450 self.__set_main_view_package_list()
2451
2452 def __get_active_publisher(self):
2453 pub_iter = self.w_repository_combobox.get_active_iter()
2454 if pub_iter == None:
2455 return None
2456 return self.repositories_list.get_value(pub_iter, \
2457 enumerations.REPOSITORY_NAME)
2458
2459 def __setup_publisher(self, publishers=[]):
2460 self.saved_filter_combobox_active = self.initial_show_filter
2461 application_list, category_list , section_list = \
2462 self.__get_application_categories_lists(publishers)
2463 self.__unset_saved()
2464 gobject.idle_add(self.__init_tree_views, application_list,
2465 category_list, section_list)
2466
2467 def __unset_saved(self):
2468 self.saved_application_list = None
2469 self.saved_application_list_filter = None
2470 self.saved_application_list_sort = None
2471 self.saved_category_list = None
2472 self.saved_section_list = None
2473
2474 def __get_application_categories_lists(self, publishers=[]):
2475 if not self.visible_repository:
2476 self.visible_repository = self.__get_active_publisher()
2477 application_list = self.__get_new_application_liststore()
2478 category_list = self.__get_new_category_liststore()
2479 section_list = self.__get_new_section_liststore()
2480 first_loop = True
2481 for publisher in publishers:
2482 uptodate = False
2483 try:
2484 uptodate = self.__check_if_cache_uptodate(publisher)
2485 if uptodate:
2486 self.__add_pkgs_to_lists_from_cache(publisher,
2487 application_list, category_list,
2488 section_list)
2489 except (UnpicklingError, EOFError, IOError):
2490 #Most likely cache is corrupted, silently load list
2491 #from api.
2492 application_list = self.__get_new_application_liststore()
2493 category_list = self.__get_new_category_liststore()
2494 uptodate = False
2495 if not uptodate:
2496 if first_loop == True:
2497 first_loop = False
2498 gobject.idle_add(self.setup_progressdialog_show)
2499 self.api_o.refresh(pubs=[publisher])
2500 self.__add_pkgs_to_lists_from_api(publisher,
2501 application_list, category_list, section_list)
2502 category_list.prepend([0, _('All'), None, None, False,
2503 True, None])
2504 if self.application_list and self.category_list and \
2505 not self.visible_repository_uptodate:
2506 if self.visible_repository:
2507 dump_list = self.application_list
2508 if self.saved_application_list != None:
2509 dump_list = \
2510 self.saved_application_list
2511 self.__dump_datamodels(self.visible_repository,
2512 dump_list, self.category_list,
2513 self.section_list)
2514 self.visible_repository = self.__get_active_publisher()
2515 self.visible_repository_uptodate = uptodate
2516 return application_list, category_list, section_list
2517
2518 def __check_if_cache_uptodate(self, publisher):
2519 if self.cache_o:
2520 return self.cache_o.check_if_cache_uptodate(publisher)
2521 return False
2522
2523 def __dump_datamodels(self, publisher, application_list, category_list,
2524 section_list):
2525 #Consistency check - only dump models if publisher passed in matches
2526 #publisher in application list
2527 if application_list == None:
2528 return
2529 app_pub = application_list[0][enumerations.AUTHORITY_COLUMN]
2530 if publisher != app_pub:
2531 if debug:
2532 print "ERROR: __dump_data_models(): INCONSISTENT " \
2533 "pub %s != app_list_pub %s" % \
2534 (publisher, app_pub)
2535 return
2536
2537 if self.cache_o:
2538 if self.img_timestamp == \
2539 self.cache_o.get_index_timestamp():
2540 Thread(target = self.cache_o.dump_datamodels,
2541 args = (publisher, application_list, category_list,
2542 section_list)).start()
2543 else:
2544 self.__remove_cache()
2545
2546 def __remove_cache(self):
2547 model = self.w_repository_combobox.get_model()
2548 for publisher in model:
2549 pub_name = publisher[1]
2550 if pub_name and pub_name != _("Add..."):
2551 Thread(target = self.cache_o.remove_datamodel,
2552 args = [publisher[1]]).start()
2553
2554 def __on_install_update(self, widget):
2555 self.api_o.reset()
2556 install_update = []
2557 if self.selected == 0:
2558 model, itr = self.package_selection.get_selected()
2559 if itr:
2560 install_update.append(
2561 model.get_value(itr, enumerations.STEM_COLUMN))
2562 else:
2563 visible_repository = self.__get_visible_repository_name()
2564 pkgs = self.selected_pkgs.get(visible_repository)
2565 if pkgs:
2566 for pkg_stem in pkgs:
2567 status = pkgs.get(pkg_stem)
2568 if status == enumerations.NOT_INSTALLED or \
2569 status == enumerations.UPDATABLE:
2570 install_update.append(pkg_stem)
2571
2572 if self.img_timestamp != self.cache_o.get_index_timestamp():
2573 self.img_timestamp = None
2574 self.__remove_cache()
2575
2576 installupdate.InstallUpdate(install_update, self, \
2577 self.api_o, ips_update = False, \
2578 action = enumerations.INSTALL_UPDATE)
2579
2580 def __on_update_all(self, widget):
2581 self.api_o.reset()
2582 installupdate.InstallUpdate([], self,
2583 self.api_o, ips_update = False,
2584 action = enumerations.IMAGE_UPDATE, be_name = self.ua_be_name,
2585 parent_name = _("Package Manager"),
2586 pkg_list = ["SUNWipkg", "SUNWipkg-gui"],
2587 main_window = self.w_main_window)
2588 return
2589
2590 def __on_ua_completed_linkbutton_clicked(self, widget):
2591 try:
2592 gnome.url_show(self.release_notes_url)
2593 except gobject.GError:
2594 self.error_occurred(_("Unable to navigate to:\n\t%s") %
2595 self.release_notes_url)
2596
2597 def __on_help_about(self, widget):
2598 wTreePlan = gtk.glade.XML(self.gladefile, "aboutdialog")
2599 aboutdialog = wTreePlan.get_widget("aboutdialog")
2600 aboutdialog.connect("response", lambda x = None, \
2601 y = None: aboutdialog.destroy())
2602 aboutdialog.run()
2603
2604 def __on_help_help(self, widget):
2605 gui_misc.display_help(self.application_dir)
2606
2607 def __on_remove(self, widget):
2608 self.api_o.reset()
2609 remove_list = []
2610 if self.selected == 0:
2611 model, itr = self.package_selection.get_selected()
2612 if itr:
2613 remove_list.append(
2614 model.get_value(itr, enumerations.STEM_COLUMN))
2615 else:
2616 visible_repository = self.__get_visible_repository_name()
2617 pkgs = self.selected_pkgs.get(visible_repository)
2618 if pkgs:
2619 for pkg_stem in pkgs:
2620 status = pkgs.get(pkg_stem)
2621 if status == enumerations.INSTALLED or \
2622 status == enumerations.UPDATABLE:
2623 remove_list.append(pkg_stem)
2624
2625 if self.img_timestamp != self.cache_o.get_index_timestamp():
2626 self.img_timestamp = None
2627 self.__remove_cache()
2628
2629 installupdate.InstallUpdate(remove_list, self,
2630 self.api_o, ips_update = False,
2631 action = enumerations.REMOVE)
2632
2633 def __on_reload(self, widget):
2634 if self.description_thread_running:
2635 self.cancelled = True
2636 if self.in_search_mode or self.is_search_all:
2637 self.__unset_search(False)
2638 self.__set_empty_details_panel()
2639 self.in_setup = True
2640 self.visible_repository = None
2641 if widget != None:
2642 self.__remove_cache()
2643 self.w_progress_dialog.set_title(_("Refreshing catalogs"))
2644 self.w_progressinfo_label.set_text(_("Refreshing catalogs..."))
2645 self.progress_stop_timer_thread = False
2646 Thread(target = self.__progressdialog_progress_pulse).start()
2647 self.w_progress_dialog.show()
2648 self.w_progress_cancel.hide()
2649 self.__disconnect_models()
2650 self.in_reload = True
2651 Thread(target = self.__catalog_refresh).start()
2652
2653 def __catalog_refresh_done(self):
2654 self.progress_stop_timer_thread = True
2655 #Let the progress_pulse finish. This should be done other way, but at
2656 #The moment this works fine
2657 time.sleep(0.2)
2658 gobject.idle_add(self.w_progress_cancel.show)
2659 gobject.idle_add(self.process_package_list_start,
2660 self.image_directory)
2661
2662 def __main_application_quit(self, be_name = None):
2663 '''quits the main gtk loop'''
2664 self.cancelled = True
2665 if self.in_setup:
2666 return
2667
2668 if be_name:
2669 if self.image_dir_arg:
2670 gobject.spawn_async([self.application_path, "-R",
2671 self.image_dir_arg, "-U", be_name])
2672 else:
2673 gobject.spawn_async([self.application_path,
2674 "-U", be_name])
2675 elif not self.in_search_mode:
2676 visible_repository = self.__get_visible_repository_name()
2677 self.__dump_datamodels(visible_repository,
2678 self.application_list, self.category_list,
2679 self.section_list)
2680
2681 width, height = self.w_main_window.get_size()
2682 hpos = self.w_main_hpaned.get_position()
2683 vpos = self.w_main_vpaned.get_position()
2684 try:
2685 self.client.set_int(INITIAL_APP_WIDTH_PREFERENCES, width)
2686 self.client.set_int(INITIAL_APP_HEIGHT_PREFERENCES, height)
2687 self.client.set_int(INITIAL_APP_HPOS_PREFERENCES, hpos)
2688 self.client.set_int(INITIAL_APP_VPOS_PREFERENCES, vpos)
2689 except GError:
2690 pass
2691
2692 self.w_main_window.hide()
2693 while gtk.events_pending():
2694 gtk.main_iteration(False)
2695 gtk.main_quit()
2696 sys.exit(0)
2697 return True
2698
2699 def __check_if_something_was_changed(self):
2700 ''' Returns True if any of the check boxes for package was changed, false
2701 if not'''
2702 if self.application_list:
2703 for pkg in self.application_list:
2704 if pkg[enumerations.MARK_COLUMN] == True:
2705 return True
2706 return False
2707
2708 def __setup_repositories_combobox(self, api_o, repositories_list):
2709 self.__disconnect_repository_model()
2710 default_pub = api_o.get_preferred_publisher().prefix
2711 if self.default_publisher != default_pub:
2712 self.__clear_pkg_selections()
2713 self.default_publisher = default_pub
2714 selected_repos = []
2715 enabled_repos = []
2716 for repo in self.selected_pkgs:
2717 selected_repos.append(repo)
2718 i = 0
2719 active = 0
2720 for pub in api_o.get_publishers():
2721 if pub.disabled:
2722 continue
2723 prefix = pub.prefix
2724 if cmp(prefix, self.default_publisher) == 0:
2725 active = i
2726 repositories_list.append([i, prefix, ])
2727 enabled_repos.append(prefix)
2728 i = i + 1
2729 repositories_list.append([-1, "", ])
2730 repositories_list.append([-1, _("Add..."), ])
2731 pkgs_to_remove = []
2732 for repo_name in selected_repos:
2733 if repo_name not in enabled_repos:
2734 pkg_stems = self.selected_pkgs.get(repo_name)
2735 for pkg_stem in pkg_stems:
2736 pkgs_to_remove.append(pkg_stem)
2737 for pkg_stem in pkgs_to_remove:
2738 self.__remove_pkg_stem_from_list(pkg_stem)
2739 self.w_repository_combobox.set_model(repositories_list)
2740 if self.default_publisher:
2741 self.w_repository_combobox.set_active(active)
2742 else:
2743 self.w_repository_combobox.set_active(0)
2744
2745 def __active_pane_toggle(self, cell, path, model_sort):
2746 '''Toggle function for column enumerations.MARK_COLUMN'''
2747 applicationModel = model_sort.get_model()
2748 applicationPath = model_sort.convert_path_to_child_path(path)
2749 filterModel = applicationModel.get_model()
2750 child_path = applicationModel.convert_path_to_child_path(applicationPath)
2751 itr = filterModel.get_iter(child_path)
2752 if itr:
2753 modified = filterModel.get_value(itr, enumerations.MARK_COLUMN)
2754 filterModel.set_value(itr, enumerations.MARK_COLUMN,
2755 not modified)
2756 pkg_status = filterModel.get_value(itr,
2757 enumerations.STATUS_COLUMN)
2758 pkg_stem = filterModel.get_value(itr, enumerations.STEM_COLUMN)
2759 if modified:
2760 self.__remove_pkg_stem_from_list(pkg_stem)
2761 else:
2762 self.__add_pkg_stem_to_list(pkg_stem, pkg_status)
2763 self.update_statusbar()
2764 self.__enable_disable_selection_menus()
2765
2766 def __update_reload_button(self):
2767 if self.user_rights:
2768 self.w_reload_button.set_sensitive(True)
2769 else:
2770 self.w_reload_button.set_sensitive(False)
2771
2772 def __add_pkg_stem_to_list(self, stem, status):
2773 publisher = self.__get_active_publisher()
2774 if self.selected_pkgs.get(publisher) == None:
2775 self.selected_pkgs[publisher] = {}
2776 self.selected_pkgs.get(publisher)[stem] = status
2777 if status == enumerations.NOT_INSTALLED or \
2778 status == enumerations.UPDATABLE:
2779 if self.to_install_update.get(publisher) == None:
2780 self.to_install_update[publisher] = 1
2781 else:
2782 self.to_install_update[publisher] += 1
2783 if status == enumerations.UPDATABLE or status == enumerations.INSTALLED:
2784 if self.to_remove.get(publisher) == None:
2785 self.to_remove[publisher] = 1
2786 else:
2787 self.to_remove[publisher] += 1
2788 self.__update_tooltips()
2789
2790 def __update_tooltips(self):
2791 to_remove = None
2792 to_install = None
2793 no_iter = 0
2794 for publisher in self.to_remove:
2795 packages = self.to_remove.get(publisher)
2796 if packages > 0:
2797 if no_iter == 0:
2798 to_remove = _("Selected for Removal:")
2799 to_remove += "\n %s: %d" % (publisher, packages)
2800 no_iter += 1
2801 no_iter = 0
2802 for publisher in self.to_install_update:
2803 packages = self.to_install_update.get(publisher)
2804 if packages > 0:
2805 if no_iter == 0:
2806 to_install = _("Selected for Install/Update:")
2807 to_install += "\n %s: %d" % (publisher, packages)
2808 no_iter += 1
2809 if not to_install:
2810 to_install = _("Select packages by marking the checkbox "
2811 "and click to Install/Update.")
2812 self.w_installupdate_button.set_tooltip(self.install_button_tooltip,
2813 to_install)
2814 if not to_remove:
2815 to_remove = _("Select packages by marking the checkbox "
2816 "and click to Remove selected.")
2817 self.w_remove_button.set_tooltip(self.remove_button_tooltip, to_remove)
2818
2819 def __remove_pkg_stem_from_list(self, stem):
2820 remove_pub = []
2821 for publisher in self.selected_pkgs:
2822 pkgs = self.selected_pkgs.get(publisher)
2823 status = None
2824 if stem in pkgs:
2825 status = pkgs.pop(stem)
2826 if status == enumerations.NOT_INSTALLED or \
2827 status == enumerations.UPDATABLE:
2828 if self.to_install_update.get(publisher) == None:
2829 self.to_install_update[publisher] = 0
2830 else:
2831 self.to_install_update[publisher] -= 1
2832 if status == enumerations.UPDATABLE or \
2833 status == enumerations.INSTALLED:
2834 if self.to_remove.get(publisher) == None:
2835 self.to_remove[publisher] = 0
2836 else:
2837 self.to_remove[publisher] -= 1
2838 if len(pkgs) == 0:
2839 remove_pub.append(publisher)
2840 for publisher in remove_pub:
2841 self.selected_pkgs.pop(publisher)
2842 self.__update_tooltips()
2843
2844 def __clear_pkg_selections(self):
2845 # We clear the selections as the preffered repository was changed
2846 # and pkg stems are not valid.
2847 remove_pub = []
2848 for publisher in self.selected_pkgs:
2849 stems = self.selected_pkgs.get(publisher)
2850 for pkg_stem in stems:
2851 remove_pub.append(pkg_stem)
2852 for pkg_stem in remove_pub:
2853 self.__remove_pkg_stem_from_list(pkg_stem)
2854
2855 def __set_empty_details_panel(self):
2856 self.showing_empty_details = True
2857 if self.show_info_id != 0:
2858 gobject.source_remove(self.show_info_id)
2859 self.show_info_id = 0
2860 if self.show_licenses_id != 0:
2861 gobject.source_remove(self.show_licenses_id)
2862 self.show_licenses_id = 0
2863 pkg_name = _("Package Name")
2864 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2865 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2866 self.w_installedfiles_textview.get_buffer().set_text("")
2867 self.w_dependencies_textview.get_buffer().set_text("")
2868 self.w_generalinfo_textview.get_buffer().set_text("")
2869 self.w_license_textview.get_buffer().set_text("")
2870 return
2871
2872 def __show_fetching_package_info(self, pkg):
2873 pkg_name = pkg.get_name()
2874 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2875 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2876
2877 pkg_stem = pkg.get_pkg_stem()
2878 if self.__setting_from_cache(pkg_stem):
2879 return
2880
2881 self.w_shortdescription_label.set_text(
2882 _("Fetching description..."))
2883 instbuffer = self.w_installedfiles_textview.get_buffer()
2884 depbuffer = self.w_dependencies_textview.get_buffer()
2885 infobuffer = self.w_generalinfo_textview.get_buffer()
2886 fetching_text = _("Fetching information...")
2887 instbuffer.set_text(fetching_text)
2888 depbuffer.set_text(fetching_text)
2889 infobuffer.set_text(fetching_text)
2890 return
2891
2892 def __setting_from_cache(self, pkg_stem):
2893 if len(self.info_cache) > MAX_INFO_CACHE_LIMIT:
2894 self.info_cache = {}
2895
2896 if self.info_cache.has_key(pkg_stem):
2897 self.w_shortdescription_label.set_text(
2898 self.info_cache[pkg_stem][0])
2899 instbuffer = self.w_installedfiles_textview.get_buffer()
2900 depbuffer = self.w_dependencies_textview.get_buffer()
2901 infobuffer = self.w_generalinfo_textview.get_buffer()
2902 infobuffer.set_text(self.info_cache[pkg_stem][1])
2903 instbuffer.set_text(self.info_cache[pkg_stem][2])
2904 depbuffer.set_text(self.info_cache[pkg_stem][3])
2905 return True
2906 else:
2907 return False
2908
2909 def __update_package_info(self, pkg, local_info, remote_info, info_id):
2910 if self.showing_empty_details or (info_id !=
2911 self.last_show_info_id):
2912 return
2913 pkg_name = pkg.get_name()
2914 pkg_stem = pkg.get_pkg_stem()
2915 self.w_packagename_label.set_markup("<b>" + pkg_name + "</b>")
2916 self.w_general_info_label.set_markup("<b>" + pkg_name + "</b>")
2917 installed = True
2918
2919 if self.__setting_from_cache(pkg_stem):
2920 return
2921
2922 instbuffer = self.w_installedfiles_textview.get_buffer()
2923 depbuffer = self.w_dependencies_textview.get_buffer()
2924 infobuffer = self.w_generalinfo_textview.get_buffer()
2925
2926 if not local_info and not remote_info:
2927 network_str = \
2928 _("\nThis might be caused by network problem "
2929 "while accessing the repository.")
2930 self.w_shortdescription_label.set_text(
2931 _("Description not available for this package...") +
2932 network_str)
2933 instbuffer.set_text( \
2934 _("Files Details not available for this package...") +
2935 network_str)
2936 depbuffer.set_text(_(
2937 "Dependencies info not available for this package...") +
2938 network_str)
2939 infobuffer.set_text(
2940 _("Information not available for this package...") +
2941 network_str)
2942 return
2943
2944 if not local_info:
2945 # Package is not installed
2946 local_info = remote_info
2947 installed = False
2948
2949 if not remote_info:
2950 remote_info = local_info
2951 installed = True
2952
2953 description = local_info.summary
2954 #XXX long term need to have something more robust here for multi byte
2955 if len(description) > MAX_DESC_LEN:
2956 description = description[:MAX_DESC_LEN] + " ..."
2957 self.w_shortdescription_label.set_text(description)
2958 inst_str = _("Root: %s\n") % self.api_o.img.get_root()
2959 dep_str = _("Dependencies:\n")
2960
2961 if local_info.dependencies:
2962 dep_str += ''.join(
2963 ["\t%s\n" % x for x in local_info.dependencies])
2964 if local_info.dirs:
2965 inst_str += ''.join(["\t%s\n" % x for x in local_info.dirs])
2966 if local_info.files:
2967 inst_str += ''.join(["\t%s\n" % x for x in local_info.files])
2968 if local_info.hardlinks:
2969 inst_str += ''.join(["\t%s\n" % x for x in local_info.hardlinks])
2970 if local_info.links:
2971 inst_str += ''.join(["\t%s\n" % x for x in local_info.links])
2972 info_str = ""
2973 labs = {}
2974 labs["sum"] = _("Summary:\t\t")
2975 labs["size"] = _("Size:\t\t\t")
2976 labs["cat"] = _("Category:\t\t")
2977 labs["ins"] = _("Installed Version:\t")
2978 labs["lat"] = _("Latest Version:\t")
2979 labs["pkg_date"] = _("Packaging Date:\t")
2980 labs["fmri"] = _("FMRI:\t\t\t")
2981 labs["repository"] = _("Repository:\t\t")
2982 max_len = 0
2983 for lab in labs:
2984 if len(labs[lab]) > max_len:
2985 max_len = len(labs[lab])
2986 categories = _("None")
2987 if local_info.category_info_list:
2988 verbose = len(local_info.category_info_list) > 1
2989 categories = ""
2990 categories += local_info.category_info_list[0].__str__(verbose)
2991 if len(local_info.category_info_list) > 1:
2992 for ci in local_info.category_info_list[1:]:
2993 categories += ", " + ci.__str__(verbose)
2994 summary = _("None")
2995 if local_info.summary:
2996 summary = local_info.summary
2997 info_str += " %s %s" % (labs["sum"], summary)
2998 info_str += "\n %s %s" % (labs["size"],
2999 misc.bytes_to_str(local_info.size))
3000 info_str += "\n %s %s" % (labs["cat"], categories)
3001 if installed:
3002 info_str += "\n %s %s,%s-%s" % (labs["ins"], local_info.version,
3003 local_info.build_release, local_info.branch)
3004 info_str += "\n %s %s,%s-%s" % (labs["lat"], remote_info.version,
3005 remote_info.build_release, remote_info.branch)
3006 info_str += "\n %s %s" % (labs["pkg_date"], local_info.packaging_date)
3007 info_str += "\n %s %s" % (labs["fmri"], local_info.fmri)
3008 info_str += "\n %s %s" % (labs["repository"], local_info.publisher)
3009 infobuffer.set_text(info_str)
3010 instbuffer.set_text(inst_str)
3011 depbuffer.set_text(dep_str)
3012 self.info_cache[pkg_stem] = \
3013 (description, info_str, inst_str, dep_str)
3014
3015 def __update_package_license(self, licenses, license_id):
3016 if self.showing_empty_details or (license_id !=
3017 self.last_show_licenses_id):
3018 return
3019 lic = ""
3020 lic_u = ""
3021 if licenses == None:
3022 lic_u = _("Not available")
3023 else:
3024 for licens in licenses:
3025 lic += licens.get_text()
3026 lic += "\n"
3027 try:
3028 lic_u = unicode(lic, "utf-8")
3029 except UnicodeDecodeError:
3030 lic_u += ""
3031 licbuffer = self.w_license_textview.get_buffer()
3032 licbuffer.set_text(lic_u)
3033
3034 def __show_licenses(self):
3035 self.show_licenses_id = 0
3036 if self.catalog_loaded == False:
3037 return
3038 Thread(target = self.__show_package_licenses,
3039 args = (self.selected_pkgstem, self.last_show_licenses_id,)).start()
3040
3041 def __show_package_licenses(self, selected_pkgstem, license_id):
3042 if selected_pkgstem == None:
3043 gobject.idle_add(self.__update_package_license, None,
3044 self.last_show_licenses_id)
3045 return
3046 info = None
3047 try:
3048 info = self.api_o.info([selected_pkgstem],
3049 True, frozenset([api.PackageInfo.LICENSES]))
3050 except (api_errors.TransportError):
3051 pass
3052 if self.showing_empty_details or (license_id !=
3053 self.last_show_licenses_id):
3054 return
3055 if not info or (info and len(info.get(0)) == 0):
3056 try:
3057 # Get license from remote
3058 info = self.api_o.info([selected_pkgstem],
3059 False, frozenset([api.PackageInfo.LICENSES]))
3060 except (api_errors.TransportError):
3061 pass
3062 if self.showing_empty_details or (license_id !=
3063 self.last_show_licenses_id):
3064 return
3065 pkgs_info = None
3066 package_info = None
3067 no_licenses = 0
3068 if info:
3069 pkgs_info = info[0]
3070 if pkgs_info:
3071 package_info = pkgs_info[0]
3072 if package_info:
3073 no_licenses = len(package_info.licenses)
3074 if no_licenses == 0:
3075 gobject.idle_add(self.__update_package_license, None,
3076 license_id)
3077 return
3078 else:
3079 gobject.idle_add(self.__update_package_license,
3080 package_info.licenses, license_id)
3081
3082 def __get_pkg_info(self, pkg_stem, local):
3083 info = None
3084 try:
3085 info = self.api_o.info([pkg_stem], local,
3086 api.PackageInfo.ALL_OPTIONS -
3087 frozenset([api.PackageInfo.LICENSES]))
3088 except (api_errors.TransportError):
3089 return info
3090 pkgs_info = None
3091 package_info = None
3092 if info:
3093 pkgs_info = info[0]
3094 if pkgs_info:
3095 package_info = pkgs_info[0]
3096 if package_info:
3097 return package_info
3098 else:
3099 return None
3100
3101 def __show_info(self, model, path):
3102 self.show_info_id = 0
3103 if self.catalog_loaded == False:
3104 self.selected_model = model
3105 self.selected_path = path
3106 return
3107 if not (model and path):
3108 return
3109 if self.selected_model != None:
3110 if (self.selected_model != model or
3111 self.selected_path != path):
3112 # This can happen after catalogs are loaded in
3113 # enable_disable_update_all and a different
3114 # package is selected before enable_disable_update_all
3115 # calls __show_info. We set these variable to None
3116 # so that when __show_info is called it does nothing.
3117 self.selected_model = None
3118 self.selected_path = None
3119
3120 itr = model.get_iter(path)
3121 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
3122 pkg_stem = model.get_value(itr, enumerations.STEM_COLUMN)
3123 pkg_status = model.get_value(itr, enumerations.STATUS_COLUMN)
3124 if self.info_cache.has_key(pkg_stem):
3125 return
3126 Thread(target = self.__show_package_info,
3127 args = (pkg, pkg_stem, pkg_status, self.last_show_info_id)).start()
3128
3129 def __show_package_info(self, pkg, pkg_stem, pkg_status, info_id):
3130 self.api_o.log_operation_start("info")
3131 local_info = None
3132 remote_info = None
3133 if not self.showing_empty_details and (info_id ==
3134 self.last_show_info_id) and (pkg_status ==
3135 enumerations.INSTALLED or pkg_status ==
3136 enumerations.UPDATABLE):
3137 local_info = self.__get_pkg_info(pkg_stem, True)
3138 if not self.showing_empty_details and (info_id ==
3139 self.last_show_info_id) and (pkg_status ==
3140 enumerations.NOT_INSTALLED or pkg_status ==
3141 enumerations.UPDATABLE):
3142 remote_info = self.__get_pkg_info(pkg_stem, False)
3143 if not self.showing_empty_details and (info_id ==
3144 self.last_show_info_id):
3145 gobject.idle_add(self.__update_package_info, pkg,
3146 local_info, remote_info, info_id)
3147 self.api_o.log_operation_end()
3148 return
3149
3150 # This function is ported from pkg.actions.generic.distinguished_name()
3151 @staticmethod
3152 def __locale_distinguished_name(action):
3153 if action.key_attr == None:
3154 return str(action)
3155 return "%s: %s" % \
3156 (_(action.name), action.attrs.get(action.key_attr, "???"))
3157
3158 def __application_filter(self, model, itr):
3159 '''This function is used to filter content in the main
3160 application view'''
3161 if self.in_setup or self.cancelled:
3162 return False
3163 filter_id = self.w_filter_combobox.get_active()
3164 if filter_id == enumerations.FILTER_SELECTED:
3165 return model.get_value(itr, enumerations.MARK_COLUMN)
3166 # XXX Show filter, chenge text to integers
3167 selected_category = 0
3168 category_selection = self.w_categories_treeview.get_selection()
3169 category_model, category_iter = category_selection.get_selected()
3170 if category_iter:
3171 selected_category = category_model.get_value(category_iter,
3172 enumerations.CATEGORY_ID)
3173 category_list = model.get_value(itr, enumerations.CATEGORY_LIST_COLUMN)
3174 selected_section = self.w_sections_combobox.get_active()
3175 category = False
3176 if selected_section == 0 and selected_category == 0:
3177 #For section "All" and category "All" always true
3178 category = True
3179 elif selected_category != 0:
3180 if category_list and selected_category in category_list:
3181 category = True
3182 elif category_list:
3183 #The selected category is "All" so we need to check
3184 #If the package belongs to one of the visible categories
3185 for visible_category in category_model:
3186 visible_id = visible_category[enumerations.CATEGORY_ID]
3187 if visible_id in category_list:
3188 category = True
3189 break
3190 if (model.get_value(itr, enumerations.IS_VISIBLE_COLUMN) == False):
3191 return False
3192 return (category &
3193 self.__is_package_filtered(model, itr, filter_id))
3194
3195 @staticmethod
3196 def __is_package_filtered(model, itr, filter_id):
3197 '''Function for filtercombobox'''
3198 if filter_id == enumerations.FILTER_ALL:
3199 return True
3200 status = model.get_value(itr, enumerations.STATUS_COLUMN)
3201 if filter_id == enumerations.FILTER_INSTALLED:
3202 return (status == enumerations.INSTALLED or status == \
3203 enumerations.UPDATABLE)
3204 elif filter_id == enumerations.FILTER_UPDATES:
3205 return status == enumerations.UPDATABLE
3206 elif filter_id == enumerations.FILTER_NOT_INSTALLED:
3207 return status == enumerations.NOT_INSTALLED
3208
3209 def __is_pkg_repository_visible(self, model, itr):
3210 if len(self.repositories_list) <= 1:
3211 return True
3212 else:
3213 visible_repository = self.__get_visible_repository_name()
3214 pkg = model.get_value(itr, enumerations.FMRI_COLUMN)
3215 if not pkg:
3216 return False
3217 if cmp(pkg.get_publisher(), visible_repository) == 0:
3218 return True
3219 else:
3220 return False
3221
3222 def __get_visible_repository_name(self):
3223 pub_iter = self.w_repository_combobox.get_active_iter()
3224 if pub_iter == None:
3225 return None
3226 visible = self.repositories_list.get_value(pub_iter, \
3227 enumerations.REPOSITORY_NAME)
3228 return visible
3229
3230 def __enable_disable_selection_menus(self):
3231 if self.in_setup:
3232 return
3233 self.__enable_disable_select_updates()
3234 if not self.__doing_search():
3235 self.unset_busy_cursor()
3236
3237 def __enable_disable_select_all(self):
3238 if self.in_setup:
3239 return
3240 if len(self.w_application_treeview.get_model()) > 0:
3241 for row in self.w_application_treeview.get_model():
3242 if not row[enumerations.MARK_COLUMN]:
3243 self.w_selectall_menuitem.set_sensitive(True)
3244 return
3245 self.w_selectall_menuitem.set_sensitive(False)
3246 else:
3247 self.w_selectall_menuitem.set_sensitive(False)
3248
3249 def __enable_disable_install_remove(self):
3250 if not self.user_rights:
3251 self.w_installupdate_button.set_sensitive(False)
3252 self.w_installupdate_menuitem.set_sensitive(False)
3253 self.w_remove_button.set_sensitive(False)
3254 self.w_remove_menuitem.set_sensitive(False)
3255 return
3256 selected_removal = self.__enable_if_selected_for_removal()
3257 selected_install_update = self.__enable_if_selected_for_install_update()
3258 if selected_removal or selected_install_update:
3259 return
3260 remove = False
3261 install = False
3262 if self.selected == 0:
3263 model, itr = self.package_selection.get_selected()
3264 if itr:
3265 status = \
3266 model.get_value(itr, enumerations.STATUS_COLUMN)
3267 if status == enumerations.NOT_INSTALLED:
3268 remove = False
3269 install = True
3270 elif status == enumerations.UPDATABLE:
3271 remove = True
3272 install = True
3273 elif status == enumerations.INSTALLED:
3274 remove = True
3275 install = False
3276 self.w_installupdate_button.set_sensitive(install)
3277 self.w_installupdate_menuitem.set_sensitive(install)
3278 self.w_remove_button.set_sensitive(remove)
3279 self.w_remove_menuitem.set_sensitive(remove)
3280
3281 def __enable_if_selected_for_removal(self):
3282 sensitive = False
3283 visible_repository = self.__get_visible_repository_name()
3284 selected = self.to_remove.get(visible_repository)
3285 if selected > 0:
3286 sensitive = True
3287 self.w_remove_button.set_sensitive(sensitive)
3288 self.w_remove_menuitem.set_sensitive(sensitive)
3289 return sensitive
3290
3291 def __enable_if_selected_for_install_update(self):
3292 sensitive = False
3293 visible_repository = self.__get_visible_repository_name()
3294 selected = self.to_install_update.get(visible_repository)
3295 if selected > 0:
3296 sensitive = True
3297 self.w_installupdate_button.set_sensitive(sensitive)
3298 self.w_installupdate_menuitem.set_sensitive(sensitive)
3299 return sensitive
3300
3301 def __enable_disable_select_updates(self):
3302 for row in self.w_application_treeview.get_model():
3303 if row[enumerations.STATUS_COLUMN] == enumerations.UPDATABLE:
3304 if not row[enumerations.MARK_COLUMN]:
3305 self.w_selectupdates_menuitem. \
3306 set_sensitive(True)
3307 return
3308 self.w_selectupdates_menuitem.set_sensitive(False)
3309 return
3310
3311 def __get_inventory_list(self, pargs, all_known, all_versions):
3312 self.__image_activity_lock.acquire()
3313 try:
3314 res = misc.get_inventory_list(self.api_o.img,
3315 pargs, all_known, all_versions)
3316 finally:
3317 self.__image_activity_lock.release()
3318 return res
3319
3320 def __enable_disable_update_all(self):
3321 #XXX Api to provide fast information if there are some updates
3322 #available within image
3323 gobject.idle_add(self.w_updateall_button.set_sensitive, False)
3324 gobject.idle_add(self.w_updateall_menuitem.set_sensitive, False)
3325 update_available = self.__check_if_updates_available()
3326 gobject.idle_add(self.__g_enable_disable_update_all, update_available)
3327 gobject.idle_add(self.__show_info_after_catalog_load)
3328 return False
3329
3330 def __show_info_after_catalog_load(self):
3331 self.__show_info(self.selected_model, self.selected_path)
3332 self.selected_model = None
3333 self.selected_path = None
3334 if (self.w_info_notebook.get_current_page() ==
3335 INFO_NOTEBOOK_LICENSE_PAGE and
3336 not self.showing_empty_details):
3337 self.__show_licenses()
3338
3339 def __check_if_updates_available(self):
3340 try:
3341 self.catalog_loaded = False
3342 self.api_o.refresh()
3343 self.catalog_loaded = True
3344 res = self.__get_inventory_list([], False, False)
3345 for pfmri, state in res:
3346 if state["upgradable"]:
3347 self.pylintstub = pfmri
3348 return True
3349
3350 except api_errors.InventoryException:
3351 gobject.idle_add(self.__set_empty_details_panel)
3352 return False
3353 return False
3354
3355 def __g_enable_disable_update_all(self, update_available):
3356 self.w_updateall_button.set_sensitive(update_available)
3357 self.w_updateall_menuitem.set_sensitive(update_available)
3358 self.__enable_disable_install_remove()
3359
3360 def __enable_disable_deselect(self):
3361 if self.w_application_treeview.get_model():
3362 for row in self.w_application_treeview.get_model():
3363 if row[enumerations.MARK_COLUMN]:
3364 self.w_deselect_menuitem.set_sensitive(True)
3365 return
3366 self.w_deselect_menuitem.set_sensitive(False)
3367 return
3368
3369 def __catalog_refresh(self, reload_gui=True):
3370 """Update image's catalogs."""
3371 try:
3372 # Since the user requested the refresh, perform it
3373 # immediately for all publishers.
3374 self.api_o.refresh(immediate=True)
3375 # Refresh will load the catalogs.
3376 self.catalog_loaded = True
3377 except api_errors.PublisherError:
3378 # In current implementation, this will never happen
3379 # We are not refreshing specific publisher
3380 self.__catalog_refresh_done()
3381 raise
3382 except api_errors.PermissionsException:
3383 #Error will already have been reported in
3384 #Manage Repository dialog
3385 self.__catalog_refresh_done()
3386 return -1
3387 except api_errors.CatalogRefreshException, cre:
3388 total = cre.total
3389 succeeded = cre.succeeded
3390 ermsg = _("Network problem.\n\n")
3391 ermsg += _("Details:\n")
3392 ermsg += "%s/%s" % (succeeded, total)
3393 ermsg += _(" catalogs successfully updated:\n")
3394 for pub, err in cre.failed:
3395 if isinstance(err, HTTPError):
3396 ermsg += " %s: %s - %s\n" % \
3397 (err.filename, err.code, err.msg)
3398 elif isinstance(err, URLError):
3399 if err.args[0][0] == 8:
3400 ermsg += " %s: %s\n" % \
3401 (urlparse.urlsplit(
3402 pub["origin"])[1].split(":")[0],
3403 err.args[0][1])
3404 else:
3405 if isinstance(err.args[0], \
3406 socket.timeout):
3407 ermsg += " %s: %s\n" % \
3408 (pub["origin"], "timeout")
3409 else:
3410 ermsg += " %s: %s\n" % \
3411 (pub["origin"], \
3412 err.args[0][1])
3413 elif "data" in err.__dict__ and err.data:
3414 ermsg += err.data
3415 else:
3416 ermsg += _("Unknown error")
3417 ermsg += "\n"
3418
3419 gobject.idle_add(self.error_occurred, ermsg,
3420 None, gtk.MESSAGE_INFO)
3421 self.__catalog_refresh_done()
3422 return -1
3423 except api_errors.InvalidDepotResponseException, idrex:
3424 err = str(idrex)
3425 gobject.idle_add(self.error_occurred, err,
3426 None, gtk.MESSAGE_INFO)
3427 self.__catalog_refresh_done()
3428 return -1
3429 except api_errors.PublisherError:
3430 self.__catalog_refresh_done()
3431 raise
3432 except Exception:
3433 self.__catalog_refresh_done()
3434 raise
3435 if reload_gui:
3436 self.__catalog_refresh_done()
3437 return 0
3438
3439 def __add_pkgs_to_lists_from_cache(self, publisher, application_list,
3440 category_list, section_list):
3441 if self.cache_o:
3442 self.cache_o.load_application_list(publisher, application_list,
3443 self.selected_pkgs)
3444 self.cache_o.load_category_list(publisher, category_list)
3445 self.cache_o.load_section_list(publisher, section_list)
3446
3447 def __add_pkgs_to_lists_from_api(self, publisher, application_list,
3448 category_list, section_list):
3449 """ This method set up image from the given directory and
3450 returns the image object or None"""
3451 pargs = []
3452 pargs.append("pkg://" + publisher + "/*")
3453 try:
3454 pkgs_known = self.__get_inventory_list(pargs,
3455 True, True)
3456 except api_errors.InventoryException:
3457 # This can happen if the repository does not
3458 # contain any packages
3459 err = _("Selected repository does not contain any packages.")
3460 gobject.idle_add(self.w_progress_dialog.hide)
3461 gobject.idle_add(self.error_occurred, err, None,
3462 gtk.MESSAGE_INFO)
3463 self.unset_busy_cursor()
3464 pkgs_known = []
3465
3466 return self.__add_pkgs_to_lists(pkgs_known, application_list,
3467 category_list, section_list)
3468
3469 def __add_pkgs_to_lists(self, pkgs_known, application_list,
3470 category_list, section_list):
3471 if section_list != None:
3472 self.__init_sections(section_list)
3473 #Only one instance of those icons should be in memory
3474 update_available_icon = gui_misc.get_icon(self.icon_theme,
3475 "status_newupdate")
3476 installed_icon = gui_misc.get_icon(self.icon_theme,
3477 "status_installed")
3478 update_for_category_icon = \
3479 self.get_icon_pixbuf_from_glade_dir("legend_newupdate")
3480 #Imageinfo for categories
3481 imginfo = imageinfo.ImageInfo()
3482 sectioninfo = imageinfo.ImageInfo()
3483 pubs = [p.prefix for p in self.api_o.get_publishers()]
3484 categories = {}
3485 sections = {}
3486 share_path = "/usr/share/package-manager/data/"
3487 for pub in pubs:
3488 category = imginfo.read(self.application_dir +
3489 share_path + pub)
3490 if len(category) == 0:
3491 category = imginfo.read(self.application_dir +
3492 share_path + "opensolaris.org")
3493 categories[pub] = category
3494 section = sectioninfo.read(self.application_dir +
3495 share_path + pub + ".sections")
3496 if len(section) == 0:
3497 section = sectioninfo.read(self.application_dir +
3498 share_path + "opensolaris.org.sections")
3499 sections[pub] = section
3500 pkg_count = 0
3501 pkg_add = 0
3502 progress_percent = INITIAL_PROGRESS_TOTAL_PERCENTAGE
3503 total_pkg_count = len(pkgs_known)
3504 progress_increment = \
3505 total_pkg_count / PACKAGE_PROGRESS_TOTAL_INCREMENTS
3506 self.progress_stop_timer_thread = True
3507 while gtk.events_pending():
3508 gtk.main_iteration(False)
3509 prev_stem = ""
3510 prev_pfmri_str = ""
3511 next_app = None
3512 pkg_name = None
3513 pkg_publisher = None
3514 prev_state = None
3515 category_icon = None
3516 for pkg, state in pkgs_known:
3517 if prev_pfmri_str and \
3518 prev_pfmri_str == pkg.get_short_fmri() and \
3519 prev_state == state:
3520 pkg_count += 1
3521 continue
3522 if prev_stem and \
3523 prev_stem == pkg.get_pkg_stem() and \
3524 prev_state["state"] == "known" and \
3525 state["state"] == "installed":
3526 pass
3527 elif next_app != None:
3528 self.__add_package_to_list(next_app,
3529 application_list,
3530 pkg_add, pkg_name,
3531 category_icon,
3532 categories, category_list, pkg_publisher)
3533 pkg_add += 1
3534 prev_stem = pkg.get_pkg_stem()
3535 prev_pfmri_str = pkg.get_short_fmri()
3536 prev_state = state
3537
3538 if progress_increment > 0 and pkg_count % progress_increment == 0:
3539 progress_percent += PACKAGE_PROGRESS_PERCENT_INCREMENT
3540 if progress_percent <= PACKAGE_PROGRESS_PERCENT_TOTAL:
3541 self.__progressdialog_progress_percent(
3542 progress_percent, pkg_count, total_pkg_count)
3543 while gtk.events_pending():
3544 gtk.main_iteration(False)
3545
3546 status_icon = None
3547 category_icon = None
3548 pkg_name = pkg.get_name()
3549 pkg_name = gui_misc.get_pkg_name(pkg_name)
3550 pkg_stem = pkg.get_pkg_stem()
3551 pkg_publisher = pkg.get_publisher()
3552 pkg_state = enumerations.NOT_INSTALLED
3553 if state["state"] == "installed":
3554 pkg_state = enumerations.INSTALLED
3555 if state["upgradable"] == True:
3556 status_icon = update_available_icon
3557 category_icon = update_for_category_icon
3558 pkg_state = enumerations.UPDATABLE
3559 else:
3560 status_icon = installed_icon
3561 marked = False
3562 if not self.is_search_all:
3563 pkgs = self.selected_pkgs.get(pkg_publisher)
3564 if pkgs != None:
3565 if pkg_stem in pkgs:
3566 marked = True
3567 next_app = \
3568 [
3569 marked, status_icon, pkg_name, '...', pkg_state,
3570 pkg, pkg_stem, None, True, None, pkg_publisher
3571 ]
3572 pkg_count += 1
3573
3574 if next_app:
3575 self.__add_package_to_list(next_app, application_list,
3576 pkg_add, pkg_name, category_icon, categories,
3577 category_list, pkg_publisher)
3578 pkg_add += 1
3579 if category_list != None:
3580 self.__add_categories_to_sections(sections,
3581 category_list, section_list)
3582 self.__progressdialog_progress_percent(PACKAGE_PROGRESS_PERCENT_TOTAL,
3583 total_pkg_count, total_pkg_count)
3584 return
3585
3586 def __add_categories_to_sections(self, sections, category_list, section_list):
3587 for publisher in sections:
3588 for section in sections[publisher]:
3589 for category in sections[publisher][section].split(","):
3590 self.__add_category_to_section(_(category),
3591 _(section), category_list, section_list)
3592
3593 #1915 Sort the Categories into alphabetical order and prepend All Category
3594 if len(category_list) > 0:
3595 rows = [tuple(r) + (i,) for i, r in enumerate(category_list)]
3596 rows.sort(self.__sort)
3597 r = []
3598 category_list.reorder([r[-1] for r in rows])
3599 return
3600
3601 def __add_package_to_list(self, app, application_list, pkg_add,
3602 pkg_name, category_icon, categories, category_list, publisher):
3603 row_iter = application_list.insert(pkg_add, app)
3604 if category_list == None:
3605 return
3606 cat_pub = categories.get(publisher)
3607 if pkg_name in cat_pub:
3608 pkg_categories = cat_pub.get(pkg_name)
3609 for pcat in pkg_categories.split(","):
3610 self.__add_package_to_category(_(pcat), None,
3611 category_icon, row_iter, application_list,
3612 category_list)
3613
3614 @staticmethod
3615 def __add_package_to_category(category_name, category_description,
3616 category_icon, package, application_list, category_list):
3617 if not package or category_name == _('All'):
3618 return
3619 if not category_name:
3620 return
3621 category_id = None
3622 icon_visible = False
3623 if category_icon:
3624 icon_visible = True
3625 for category in category_list:
3626 if category[enumerations.CATEGORY_NAME] == category_name:
3627 category_id = category[enumerations.CATEGORY_ID]
3628 if category_icon:
3629 category[enumerations.CATEGORY_ICON] = \
3630 category_icon
3631 category[enumerations.CATEGORY_ICON_VISIBLE] = \
3632 icon_visible
3633 break
3634 if not category_id: # Category not exists
3635 category_id = len(category_list) + 1
3636 category_list.append([category_id, category_name,
3637 category_description, category_icon, icon_visible,
3638 True, None])
3639 if application_list.get_value(package,
3640 enumerations.CATEGORY_LIST_COLUMN):
3641 a = application_list.get_value(package,
3642 enumerations.CATEGORY_LIST_COLUMN)
3643 a.append(category_id)
3644 else:
3645 category_list = []
3646 category_list.append(category_id)
3647 application_list.set(package,
3648 enumerations.CATEGORY_LIST_COLUMN, category_list)
3649
3650 @staticmethod
3651 def __add_category_to_section(category_name, section_name, category_list,
3652 section_list):
3653 '''Adds the section to section list in category. If there is no such
3654 section, than it is not added. If there was already section than it
3655 is skipped. Sections must be case sensitive'''
3656 if not category_name:
3657 return
3658 for section in section_list:
3659 if section[enumerations.SECTION_NAME] == section_name:
3660 section_id = section[enumerations.SECTION_ID]
3661 for category in category_list:
3662 if category[enumerations.CATEGORY_NAME] == \
3663 category_name:
3664 section_lst = category[ \
3665 enumerations.SECTION_LIST_OBJECT]
3666 section[enumerations.SECTION_ENABLED] = \
3667 True
3668 if not section_lst:
3669 category[ \
3670 enumerations.SECTION_LIST_OBJECT] = \
3671 [section_id, ]
3672 else:
3673 if not section_name in \
3674 section_lst:
3675 section_lst.append(
3676 section_id)
3677
3678 def __progressdialog_progress_pulse(self):
3679 while not self.progress_stop_timer_thread:
3680 gobject.idle_add(self.w_progressbar.pulse)
3681 time.sleep(0.1)
3682 gobject.idle_add(self.w_progress_dialog.hide)
3683 self.progress_stop_timer_thread = False
3684
3685 # For initial setup before loading package entries allow 5% of progress bar
3686 # update it on a time base as we have no other way to judge progress at this point
3687 def __progressdialog_progress_time(self):
3688 while not self.progress_stop_timer_thread and \
3689 self.progress_fraction_time_count <= \
3690 INITIAL_PROGRESS_TOTAL_PERCENTAGE:
3691
3692 gobject.idle_add(self.w_progressbar.set_fraction,
3693 self.progress_fraction_time_count)
3694 self.progress_fraction_time_count += \
3695 INITIAL_PROGRESS_TIME_PERCENTAGE
3696 time.sleep(INITIAL_PROGRESS_TIME_INTERVAL)
3697 self.progress_stop_timer_thread = False
3698 self.progress_fraction_time_count = 0
3699
3700 def __progressdialog_progress_percent(self, fraction, count, total):
3701 gobject.idle_add(self.w_progressinfo_label.set_text, _(
3702 "Processing package entries: %d of %d") % (count, total) )
3703 gobject.idle_add(self.w_progressbar.set_fraction, fraction)
3704
3705 def error_occurred(self, error_msg, msg_title=None, msg_type=gtk.MESSAGE_ERROR):
3706 if msg_title:
3707 title = msg_title
3708 else:
3709 title = _("Package Manager")
3710 gui_misc.error_occurred(self.w_main_window, error_msg,
3711 title, msg_type, use_markup=True)
3712
3713
3714 msgbox = gtk.MessageDialog(parent =
3715 self.w_main_window,
3716 buttons = gtk.BUTTONS_CLOSE,
3717 flags = gtk.DIALOG_MODAL,
3718 type = msg_type,
3719 message_format = None)
3720 msgbox.set_property('text', error_msg)
3721 title = None
3722 if msg_title:
3723 title = msg_title
3724 else:
3725 title = _("Package Manager")
3726 msgbox.set_title(title)
3727 msgbox.run()
3728 msgbox.destroy()
3729
3730 #-----------------------------------------------------------------------------#
3731 # Static Methods
3732 #-----------------------------------------------------------------------------#
3733
3734 #@staticmethod
3735 #def N_(message):
3736 # return message
3737
3738 @staticmethod
3739 def __sort(a, b):
3740 return cmp(a[1], b[1])
3741
3742 @staticmethod
3743 def cell_data_function(column, renderer, model, itr, data):
3744 '''Function which sets the background colour to black if package is
3745 selected'''
3746 if itr:
3747 if model.get_value(itr, enumerations.MARK_COLUMN):
3748 renderer.set_property("cell-background", "#ffe5cc")
3749 renderer.set_property("cell-background-set", True)
3750 else:
3751 renderer.set_property("cell-background-set", False)
3752
3753 @staticmethod
3754 def combobox_separator(model, itr):
3755 return model.get_value(itr, enumerations.FILTER_NAME) == ""
3756
3757 @staticmethod
3758 def combobox_id_separator(model, itr):
3759 return model.get_value(itr, 0) == -1 and \
3760 model.get_value(itr, 1) == ""
3761
3762 @staticmethod
3763 def category_filter(model, itr):
3764 '''This function filters category in the main application view'''
3765 return model.get_value(itr, enumerations.CATEGORY_VISIBLE)
3766
3767 @staticmethod
3768 def get_datetime(version):
3769 dt = None
3770 try:
3771 dt = version.get_datetime()
3772 except AttributeError:
3773 dt = version.get_timestamp()
3774 return dt
3775
3776 @staticmethod
3777 def get_installed_version(api_o, pkg):
3778 info = api_o.info([pkg], False, frozenset(
3779 [api.PackageInfo.STATE, api.PackageInfo.IDENTITY]))
3780 found = info[api.ImageInterface.INFO_FOUND]
3781 try:
3782 version = found[0]
3783 except IndexError:
3784 version = None
3785 return version
3786
3787 #-----------------------------------------------------------------------------#
3788 # Public Methods
3789 #-----------------------------------------------------------------------------#
3790 def setup_progressdialog_show(self):
3791 self.w_progress_dialog.set_title(_("Loading Repository Information"))
3792 self.w_progressinfo_label.set_text(
3793 _( "Fetching package entries ..."))
3794 self.w_progress_cancel.hide()
3795 self.w_progress_dialog.show()
3796 Thread(target = self.__progressdialog_progress_time).start()
3797
3798 def setup_progressdialog_hide(self):
3799 self.progress_stop_timer_thread = True
3800 self.w_progress_dialog.hide()
3801
3802 def init_show_filter(self):
3803 self.__init_show_filter() #Initiates filter
3804
3805 def reload_packages(self):
3806 self.api_o = gui_misc.get_api_object(self.image_directory,
3807 self.pr, self.w_main_window)
3808 self.cache_o = self.__get_cache_obj(self.icon_theme,
3809 self.application_dir, self.api_o)
3810 self.__on_reload(None)
3811
3812 def set_busy_cursor(self):
3813 self.gdk_window.show()
3814
3815 def unset_busy_cursor(self):
3816 self.gdk_window.hide()
3817
3818 def process_package_list_start(self, image_directory):
3819 self.image_directory = image_directory
3820 if not self.api_o:
3821 self.api_o = gui_misc.get_api_object(image_directory,
3822 self.pr, self.w_main_window)
3823 self.cache_o = self.__get_cache_obj(self.icon_theme,
3824 self.application_dir, self.api_o)
3825 self.img_timestamp = self.cache_o.get_index_timestamp()
3826 self.repositories_list = self.__get_new_repositories_liststore()
3827 self.__setup_repositories_combobox(self.api_o, self.repositories_list)
3828
3829 @staticmethod
3830 def __get_cache_obj(icon_theme, application_dir, api_o):
3831 cache_o = cache.CacheListStores(icon_theme, application_dir,
3832 api_o)
3833 return cache_o
3834
3835 def process_package_list_end(self):
3836 self.__set_first_category_text()
3837 self.in_startpage_startup = False
3838 if self.update_all_proceed:
3839 # TODO: Handle situation where only SUNWipkg/SUNWipg-gui have been updated
3840 # in update all: bug 6357
3841 self.__on_update_all(None)
3842 self.update_all_proceed = False
3843 self.setup_progressdialog_hide()
3844 self.__enable_disable_install_remove()
3845 self.update_statusbar()
3846 self.in_setup = False
3847 self.cancelled = False
3848 if self.set_section != 0 or \
3849 self.set_show_filter != enumerations.FILTER_ALL:
3850 self.__application_refilter()
3851 else:
3852 self.unset_busy_cursor()
3853
3854 if self.first_run or self.in_reload:
3855 Thread(target = self.__enable_disable_update_all).start()
3856 self.first_run = False
3857 self.in_reload = False
3858
3859 def get_icon_pixbuf_from_glade_dir(self, icon_name):
3860 return gui_misc.get_pixbuf_from_path(self.application_dir +
3861 "/usr/share/package-manager/", icon_name)
3862
3863 def update_statusbar(self):
3864 '''Function which updates statusbar'''
3865 if self.statusbar_message_id > 0:
3866 self.w_main_statusbar.remove(0, self.statusbar_message_id)
3867 self.statusbar_message_id = 0
3868 search_text = self.w_searchentry.get_text()
3869
3870 if not self.in_search_mode:
3871 installed = 0
3872 self.selected = 0
3873 sel = 0
3874 if self.application_list == None:
3875 return
3876 visible_repository = self.__get_visible_repository_name()
3877 pkgs = self.selected_pkgs.get(visible_repository)
3878 if pkgs:
3879 self.selected = len(pkgs)
3880 for pkg_row in self.application_list:
3881 if pkg_row[enumerations.STATUS_COLUMN] == \
3882 enumerations.INSTALLED \
3883 or pkg_row[enumerations.STATUS_COLUMN] == \
3884 enumerations.UPDATABLE:
3885 installed = installed + 1
3886 if pkg_row[enumerations.MARK_COLUMN]:
3887 sel = sel + 1
3888 listed_str = _('%d listed') % len(self.application_list)
3889 sel_str = _('%d selected') % sel
3890 inst_str = _('%d installed') % installed
3891 status_str = _("%s: %s , %s, %s.") % (visible_repository,
3892 listed_str, inst_str, sel_str)
3893 self.w_main_statusbar.push(0, status_str)
3894 return
3895
3896 # In Search Mode
3897 active = ""
3898 if self.is_search_all:
3899 if self.__doing_search():
3900 if self.current_search_publisher != None:
3901 active = "(" + self.current_search_publisher + \
3902 ") "
3903 opt_str = _('Searching... '
3904 '%(active)sfor "%(search_text)s"') % \
3905 {"active": active, "search_text": search_text}
3906 else:
3907 opt_str = _('Searched All for "%s"') % (search_text)
3908 else:
3909 search_str = _("Searched")
3910 if self.__doing_search():
3911 search_str = _("Searching...")
3912 if self.last_active_publisher != None:
3913 active = "(" + self.last_active_publisher + ") "
3914 opt_str = \
3915 _('%(search)s %(last_active)sfor "%(search_text)s"') \
3916 % {"search": search_str, "last_active" : active,
3917 "search_text" : search_text}
3918 fmt_str = _("%(option_str)s: %(number)d found %(time)s")
3919 time_str = ""
3920 if self.search_time_sec > 0:
3921 time_str = _("in %d seconds") % self.search_time_sec
3922 status_str = fmt_str % {"option_str" : opt_str, "number" :
3923 len(self.application_list), "time" : time_str}
3924 self.w_main_statusbar.push(0, status_str)
3925
3926 def update_package_list(self, update_list):
3927 if update_list == None and self.img_timestamp:
3928 return
3929 visible_repository = self.__get_visible_repository_name()
3930 default_publisher = self.default_publisher
3931 self.api_o.refresh()
3932 if not self.img_timestamp:
3933 self.img_timestamp = self.cache_o.get_index_timestamp()
3934 self.__on_reload(None)
3935 return
3936 self.img_timestamp = self.cache_o.get_index_timestamp()
3937 installed_icon = gui_misc.get_icon(self.icon_theme,
3938 "status_installed")
3939 visible_list = update_list.get(visible_repository)
3940 if visible_list:
3941 i = 0
3942 while i < len(visible_list):
3943 visible_list[i] = gui_misc.get_pkg_name(
3944 visible_list[i])
3945 i += 1
3946 for row in self.application_list:
3947 if row[enumerations.NAME_COLUMN] in visible_list:
3948 pkg = row[enumerations.FMRI_COLUMN]
3949 pkg_stem = row[enumerations.STEM_COLUMN]
3950 self.__remove_pkg_stem_from_list(pkg_stem)
3951 if self.info_cache.has_key(pkg_stem):
3952 del self.info_cache[pkg_stem]
3953 package_info = self.get_installed_version(
3954 self.api_o, pkg_stem)
3955 package_installed = (package_info.state
3956 == api.PackageInfo.INSTALLED)
3957 print pkg_stem, package_installed
3958 if package_installed:
3959 row[enumerations.STATUS_COLUMN] = \
3960 enumerations.INSTALLED
3961 row[enumerations.STATUS_ICON_COLUMN] = \
3962 installed_icon
3963 else:
3964 row[enumerations.STATUS_COLUMN] = \
3965 enumerations.NOT_INSTALLED
3966 row[enumerations.STATUS_ICON_COLUMN] = \
3967 None
3968 row[enumerations.MARK_COLUMN] = False
3969 self.__dump_datamodels(visible_repository,
3970 self.application_list, self.category_list,
3971 self.section_list)
3972 for publisher in update_list:
3973 if publisher != visible_repository:
3974 pkg_list = update_list.get(publisher)
3975 for pkg in pkg_list:
3976 pkg_stem = None
3977 if publisher != default_publisher:
3978 pkg_stem = "pkg://%s/%s" % \
3979 (publisher, pkg)
3980 else:
3981 pkg_stem = "pkg:/%s" % pkg
3982 if pkg_stem:
3983 if self.info_cache.has_key(pkg_stem):
3984 del self.info_cache[pkg_stem]
3985 self.__remove_pkg_stem_from_list(pkg_stem)
3986 self.__process_package_selection()
3987 self.__enable_disable_selection_menus()
3988 self.__enable_disable_install_remove()
3989 self.update_statusbar()
3990 Thread(target = self.__enable_disable_update_all).start()
3991
3992 @staticmethod
3993 def __find_root_home_dir():
3994 return_str = '/var/tmp'
3995
3996 try:
3997 lines = pwd.getpwnam('root')
3998 except KeyError:
3999 if debug:
4000 print "Error getting passwd database entry for root"
4001 return return_str
4002 try:
4003 return_str = lines[5]
4004 except IndexError:
4005 if debug:
4006 print "Error getting home directory for root"
4007 return return_str
4008
4009 def restart_after_ips_update(self, be_name):
4010 self.__main_application_quit(be_name)
4011
4012 def shutdown_after_image_update(self):
4013 info_str = _("The Update All action is now complete and "
4014 "Package Manager will close.\n\nReview the posted release notes "
4015 "before rebooting your system:\n\n"
4016 )
4017 self.w_ua_completed_release_label.set_text(info_str.strip('\n'))
4018
4019 info_str = misc.get_release_notes_url()
4020 self.w_ua_completed_linkbutton.set_uri(info_str)
4021 self.w_ua_completed_linkbutton.set_label(info_str)
4022 self.release_notes_url = info_str
4023
4024 self.w_ua_completed_dialog.set_title(_("Update All Complete"))
4025 self.w_ua_completed_dialog.show()
4026
4027 ###############################################################################
4028 #-----------------------------------------------------------------------------#
4029 # Main
4030 #-----------------------------------------------------------------------------#
4031
4032 def main():
4033 gtk.main()
4034 return 0
4035
4036 if __name__ == '__main__':
4037 debug = False
4038 debug_descriptions = False
4039 update_all_proceed = False
4040 ua_be_name = None
4041 app_path = None
4042 image_dir = None
4043 info_install_arg = None
4044 save_selected = _("Save selected...")
4045 save_selected_pkgs = _("Save selected packages...")
4046 reboot_needed = _("The installed package(s) require a reboot before "
4047 "installation can be completed.")
4048
4049 try:
4050 opts, args = getopt.getopt(sys.argv[1:], "hR:U:i:", \
4051 ["help", "image-dir=", "update-all=", "info-install="])
4052 except getopt.error, msg:
4053 print "%s, for help use --help" % msg
4054 sys.exit(2)
4055
4056 if os.path.isabs(sys.argv[0]):
4057 app_path = sys.argv[0]
4058 else:
4059 cmd = os.path.join(os.getcwd(), sys.argv[0])
4060 app_path = os.path.realpath(cmd)
4061
4062 for option, argument in opts:
4063 if option in ("-h", "--help"):
4064 print """\
4065 Use -R (--image-dir) to specify image directory.
4066 Use -U (--update-all) to proceed with Update All"""
4067 sys.exit(0)
4068 if option in ("-R", "--image-dir"):
4069 image_dir = argument
4070 if option in ("-U", "--update-all"):
4071 update_all_proceed = True
4072 ua_be_name = argument
4073 if option in ("-i", "--info-install"):
4074 info_install_arg = argument
4075
4076 if image_dir == None:
4077 try:
4078 image_dir = os.environ["PKG_IMAGE"]
4079 except KeyError:
4080 image_dir = os.getcwd()
4081 try:
4082 gtk.init_check()
4083 except RuntimeError, e:
4084 print _("Unable to initialize gtk")
4085 print str(e)
4086 sys.exit(1)
4087
4088 # Setup webinstall
4089 if info_install_arg or len(sys.argv) == 2:
4090 webinstall = webinstall.Webinstall(image_dir)
4091 if len(sys.argv) == 2:
4092 info_install_arg = sys.argv[1]
4093 webinstall.process_param(info_install_arg)
4094 main()
4095 sys.exit(0)
4096
4097 # Setup packagemanager
4098 packagemanager = PackageManager()
4099 packagemanager.application_path = app_path
4100 packagemanager.image_dir_arg = image_dir
4101 packagemanager.update_all_proceed = update_all_proceed
4102 packagemanager.ua_be_name = ua_be_name
4103
4104 while gtk.events_pending():
4105 gtk.main_iteration(False)
4106
4107 packagemanager.init_show_filter()
4108
4109 packagemanager.process_package_list_start(image_dir)
4110
4111 main()