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