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"
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")
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
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:
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)
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:
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
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
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(): "
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"
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)
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 = []
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()
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
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]
|
28 # - Start phase:
29 # The start phase should be fairly constant at around a few seconds and so is given 5%
30 # of the total progress bar.
31 # - Package entry loading phase:
32 # The package entry loading phase is given the remaining 95% of the bar for progress.
33
34 INITIAL_PROGRESS_TIME_INTERVAL = 0.5 # Time to update progress during start phase
35 INITIAL_PROGRESS_TIME_PERCENTAGE = 0.005 # Amount to update progress during start phase
36 INITIAL_PROGRESS_TOTAL_PERCENTAGE = 0.05 # Total progress for start phase
37 PACKAGE_PROGRESS_TOTAL_INCREMENTS = 95 # Total increments for loading phase
38 PACKAGE_PROGRESS_PERCENT_INCREMENT = 0.01 # Amount to update progress during loading phase
39 PACKAGE_PROGRESS_PERCENT_TOTAL = 1.0 # Total progress for loading phase
40 MAX_DESC_LEN = 60 # Max length of the description
41 MAX_INFO_CACHE_LIMIT = 100 # Max number of package descriptions to cache
42 NOTEBOOK_PACKAGE_LIST_PAGE = 0 # Main Package List page index
43 NOTEBOOK_START_PAGE = 1 # Main View Start page index
44 INFO_NOTEBOOK_LICENSE_PAGE = 3 # License Tab index
45 SHOW_INFO_DELAY = 600 # Delay before showing selected pacakge information
46 SHOW_LICENSE_DELAY = 600 # Delay before showing license information
47 SEARCH_STR_FORMAT = "<%s>"
48 MIN_APP_WIDTH = 750 # Minimum application width
49 MIN_APP_HEIGHT = 500 # Minimum application height
50 INITIAL_APP_WIDTH_PREFERENCES = "/apps/packagemanager/preferences/initial_app_width"
51 INITIAL_APP_HEIGHT_PREFERENCES = "/apps/packagemanager/preferences/initial_app_height"
52 INITIAL_APP_HPOS_PREFERENCES = "/apps/packagemanager/preferences/initial_app_hposition"
53 INITIAL_APP_VPOS_PREFERENCES = "/apps/packagemanager/preferences/initial_app_vposition"
54 INITIAL_SHOW_FILTER_PREFERENCES = "/apps/packagemanager/preferences/initial_show_filter"
55 INITIAL_SECTION_PREFERENCES = "/apps/packagemanager/preferences/initial_section"
56 SHOW_STARTPAGE_PREFERENCES = "/apps/packagemanager/preferences/show_startpage"
57 API_SEARCH_ERROR_PREFERENCES = "/apps/packagemanager/preferences/api_search_error"
58 CATEGORIES_STATUS_COLUMN_INDEX = 0 # Index of Status Column in Categories TreeView
59
60 STATUS_COLUMN_INDEX = 2 # Index of Status Column in Application TreeView
61
62 PKG_CLIENT_NAME = "packagemanager"
63
64 # Location for themable icons
65 ICON_LOCATION = "usr/share/package-manager/icons"
66 # Load Start Page from lang dir if available
67 START_PAGE_CACHE_LANG_BASE = "var/pkg/gui_cache/startpagebase/%s/%s"
251 self.categories_status_id = 0
252 self.icon_theme = gtk.IconTheme()
253 icon_location = os.path.join(self.application_dir, ICON_LOCATION)
254 self.icon_theme.append_search_path(icon_location)
255 self.search_options = [
256 ('ips-search',
257 gui_misc.get_icon(self.icon_theme, 'search', 20),
258 _("_Current Repository"),
259 _("Search Current Repository")),
260 ('ips-search-all',
261 gui_misc.get_icon(self.icon_theme, 'search_all', 20),
262 _("_All Repositories"),
263 _("Search All Repositories"))
264 ]
265 self.__register_iconsets(self.search_options)
266 self.visible_repository = None
267 self.visible_repository_uptodate = False
268 self.last_active_publisher = None
269 self.search_start = 0
270 self.search_time_sec = 0
271 self.current_search_publisher = None
272 self.section_list = None
273 self.filter_list = self.__get_new_filter_liststore()
274 self.application_list = None
275 self.a11y_application_treeview = None
276 self.a11y_categories_treeview = None
277 self.application_treeview_range = None
278 self.application_treeview_initialized = False
279 self.categories_treeview_range = None
280 self.categories_treeview_initialized = False
281 self.category_list = None
282 self.repositories_list = None
283 self.pr = progress.NullProgressTracker()
284 self.pylintstub = None
285 self.release_notes_url = "http://www.opensolaris.org"
286 self.__image_activity_lock = Lock()
287
288 # Create Widgets and show gui
289 self.gladefile = os.path.join(self.application_dir,
290 "usr/share/package-manager/packagemanager.glade")
291 w_tree_main = gtk.glade.XML(self.gladefile, "mainwindow")
657 is_search_all = (i != 0)
658 self.__update_repository_combobox_for_search(is_search_all)
659 if is_search_all:
660 self.__setup_before_search_all_mode()
661 else:
662 self.__restore_setup_for_browse()
663 self.changing_search_option = False
664
665 def __update_repository_combobox_for_search(self, is_search_all):
666 if is_search_all:
667 self.saved_repository_combobox_active = \
668 self.w_repository_combobox.get_active()
669 self.__disconnect_repository_model()
670 if is_search_all:
671 self.repositories_list.prepend(
672 [-1, _("All Repositories Search Results"), ])
673 else:
674 self.repositories_list.remove(
675 self.repositories_list.get_iter_first())
676 self.w_repository_combobox.set_model(self.repositories_list)
677 #self.visible_repository = None
678
679
680 def __link_load_blank(self):
681 self.document.clear()
682 self.document.open_stream('text/html')
683 self.document.write_stream(_(
684 "<html><head></head><body></body></html>"))
685 self.document.close_stream()
686
687 def __search_menu_item_activate(self, widget):
688 name = widget.get_name()
689 i = 0
690 for stock_id, pixbuf, label, description in self.search_options:
691 if stock_id == name:
692 self.__set_search_option(i)
693 self.search_image.set_from_pixbuf(pixbuf)
694 self.a11y_search_button.set_description(description)
695 break
696 i += 1
697 self.pylintstub = label
744 start_page_lang_base % (self.lang_root, START_PAGE_HOME))
745 if self.__load_uri(self.document, start_page_url):
746 return True
747
748 start_page_url = os.path.join(self.application_dir,
749 start_page_lang_base % ("C", START_PAGE_HOME))
750 if self.__load_uri(self.document, start_page_url):
751 return True
752 return False
753
754 def __handle_startpage_load_error(self, start_page_url):
755 self.document.open_stream('text/html')
756 self.document.write_stream(_(
757 "<html><head></head><body><H2>Welcome to"
758 "PackageManager!</H2><br>"
759 "<font color='#0000FF'>Warning: Unable to "
760 "load Start Page:<br>%s</font></body></html>"
761 % (start_page_url)))
762 self.document.close_stream()
763
764 def __process_api_search_error(self, error):
765 self.current_repos_with_search_errors = []
766
767 for pub, err in error.failed_servers:
768 self.current_repos_with_search_errors.append(
769 (pub, _("failed to respond"), err))
770 for pub in error.invalid_servers:
771 self.current_repos_with_search_errors.append(
772 (pub, _("invalid response"),
773 _("A valid response was not returned.")))
774 for pub, err in error.unsupported_servers:
775 self.current_repos_with_search_errors.append(
776 (pub, _("unsupported search"), err))
777
778 def __on_infosearch_button_clicked(self, widget):
779 self.__handle_api_search_error(True)
780
781 def __handle_api_search_error(self, show_all=False):
782 if len(self.current_repos_with_search_errors) == 0:
783 self.w_infosearch_frame.hide()
784 return
785
786 repo_count = 0
787 for pub, err_type, err_str in self.current_repos_with_search_errors:
788 if show_all or (pub not in self.gconf_not_show_repos):
789 repo_count += 1
790 if repo_count == 0:
791 self.w_infosearch_frame.hide()
792 return
793
794 self.w_infosearch_button.set_size_request(26, 22)
795 self.w_infosearch_frame.show()
796 infobuffer = self.api_search_error_textview.get_buffer()
797 infobuffer.set_text("")
798 textiter = infobuffer.get_end_iter()
799 for pub, err_type, err_str in self.current_repos_with_search_errors:
800
801 if show_all or (pub not in self.gconf_not_show_repos):
802 infobuffer.insert_with_tags_by_name(textiter,
803 "%(pub)s (%(err_type)s)\n" % {"pub": pub,
804 "err_type": err_type}, "bold")
805 infobuffer.insert(textiter, "%s\n" % (err_str))
806
807 self.api_search_checkbox.set_active(False)
808 self.api_search_error_dialog.show()
809 self.api_search_button.grab_focus()
810
811 def __get_repo_publishers(self):
812 repo_pub_dict = {}
813 pubs = self.api_o.get_publishers()
814 for pub in pubs:
815 repo = pub.selected_repository
816 origin = repo.origins[0]
817 repo_pub_dict[origin.uri] = pub.prefix
818 return repo_pub_dict
819
820 def __on_url(self, view, link):
821 # Handle mouse over events on links and reset when not on link
822 if link == None or link == "":
823 self.update_statusbar()
824 else:
825 display_link = self.__handle_link(None, link, DISPLAY_LINK)
826 if display_link != None:
997 gobject.TYPE_STRING, # enumerations.SECTION_NAME
998 gobject.TYPE_STRING, # enumerations.SECTION_SUBCATEGORY
999 gobject.TYPE_BOOLEAN, # enumerations.SECTION_ENABLED
1000 )
1001
1002 @staticmethod
1003 def __get_new_filter_liststore():
1004 return gtk.ListStore(
1005 gobject.TYPE_INT, # enumerations.FILTER_ID
1006 gobject.TYPE_STRING, # enumerations.FILTER_NAME
1007 )
1008
1009 @staticmethod
1010 def __get_new_repositories_liststore():
1011 return gtk.ListStore(
1012 gobject.TYPE_INT, # enumerations.REPOSITORY_ID
1013 gobject.TYPE_STRING, # enumerations.REPOSITORY_NAME
1014 )
1015
1016 def __init_application_tree_view(self, application_list,
1017 application_list_filter, application_list_sort,
1018 application_sort_column):
1019 ##APPLICATION MAIN TREEVIEW
1020 if application_list_filter == None:
1021 application_list_filter = application_list.filter_new()
1022 if application_list_sort == None:
1023 application_list_sort = \
1024 gtk.TreeModelSort(application_list_filter)
1025 application_list_sort.set_sort_column_id(
1026 application_sort_column, gtk.SORT_ASCENDING)
1027 application_list_sort.set_sort_func(
1028 enumerations.STATUS_ICON_COLUMN, self.__status_sort_func)
1029 toggle_renderer = gtk.CellRendererToggle()
1030
1031 column = gtk.TreeViewColumn("", toggle_renderer, \
1032 active = enumerations.MARK_COLUMN)
1033 column.set_sort_column_id(enumerations.MARK_COLUMN)
1034 column.set_sort_indicator(True)
1035 column.set_cell_data_func(toggle_renderer, self.cell_data_function, None)
1036 column.connect_after('clicked',
1037 self.__application_treeview_column_sorted, None)
1038 self.w_application_treeview.append_column(column)
1039 name_renderer = gtk.CellRendererText()
1040 column = gtk.TreeViewColumn(_("Name"), name_renderer,
1041 text = enumerations.NAME_COLUMN)
1042 column.set_resizable(True)
1043 column.set_min_width(150)
1044 column.set_sort_column_id(enumerations.NAME_COLUMN)
1045 column.set_sort_indicator(True)
1046 column.set_cell_data_func(name_renderer, self.cell_data_function, None)
1047 column.connect_after('clicked',
1048 self.__application_treeview_column_sorted, None)
1049 self.w_application_treeview.append_column(column)
1050 column = self.__create_icon_column(_("Status"), True,
1051 enumerations.STATUS_ICON_COLUMN, True)
1052 column.set_sort_column_id(enumerations.STATUS_ICON_COLUMN)
1053 column.set_sort_indicator(True)
1054 column.connect_after('clicked',
1055 self.__application_treeview_column_sorted, None)
1056 self.w_application_treeview.append_column(column)
1057 if self.is_search_all:
1058 repository_renderer = gtk.CellRendererText()
1059 column = gtk.TreeViewColumn(_('Repository'),
1060 repository_renderer,
1061 text = enumerations.AUTHORITY_COLUMN)
1062 column.set_sort_column_id(enumerations.AUTHORITY_COLUMN)
1063 column.set_resizable(True)
1072 description_renderer,
1073 text = enumerations.DESCRIPTION_COLUMN)
1074 column.set_sort_column_id(enumerations.DESCRIPTION_COLUMN)
1075 column.set_resizable(True)
1076 column.set_sort_indicator(True)
1077 column.set_cell_data_func(description_renderer,
1078 self.cell_data_function, None)
1079 column.connect_after('clicked',
1080 self.__application_treeview_column_sorted, None)
1081 self.w_application_treeview.append_column(column)
1082 #Added selection listener
1083 self.package_selection = self.w_application_treeview.get_selection()
1084 self.application_list = application_list
1085 self.application_list_filter = application_list_filter
1086 self.application_list_sort = application_list_sort
1087 toggle_renderer.connect('toggled', self.__active_pane_toggle,
1088 application_list_sort)
1089
1090 def __init_tree_views(self, application_list, category_list,
1091 section_list, application_list_filter = None,
1092 application_list_sort = None,
1093 application_sort_column = enumerations.NAME_COLUMN):
1094 '''This function connects treeviews with their models and also applies
1095 filters'''
1096 if category_list == None:
1097 self.w_application_treeview.set_model(None)
1098 self.__remove_treeview_columns(self.w_application_treeview)
1099 elif application_list == None:
1100 self.w_categories_treeview.set_model(None)
1101 self.__remove_treeview_columns(self.w_categories_treeview)
1102 else:
1103 self.__disconnect_models()
1104 self.__remove_treeview_columns(self.w_application_treeview)
1105 self.__remove_treeview_columns(self.w_categories_treeview)
1106 # The logic for set section needs to be here as some sections
1107 # might be not enabled. In such situation we are setting the set
1108 # section to "All Categories" one.
1109 if section_list != None:
1110 row = section_list[self.set_section]
1111 if row[enumerations.SECTION_ENABLED] and \
1112 self.set_section >= 0 and \
1113 self.set_section < len(section_list):
1114 if row[enumerations.SECTION_ID] != self.set_section:
1115 self.set_section = 0
1116 else:
1117 self.set_section = 0
1118
1119 if application_list != None:
1120 self.__init_application_tree_view(application_list,
1121 application_list_filter, application_list_sort,
1122 application_sort_column)
1123
1124 if self.first_run:
1125 # When vadj changes we need to set image descriptions
1126 # on visible status icons. This catches moving the scroll bars
1127 # and scrolling up and down using keyboard.
1128 vadj = self.w_application_treeview.get_vadjustment()
1129 vadj.connect('value-changed',
1130 self.__application_treeview_vadjustment_changed, None)
1131 vadj = self.w_categories_treeview.get_vadjustment()
1132 vadj.connect('value-changed',
1133 self.__categories_treeview_vadjustment_changed, None)
1134
1135 # When the size of the application_treeview changes
1136 # we need to set image descriptions on visible status icons.
1137 self.w_application_treeview.connect('size-allocate',
1138 self.__application_treeview_size_allocate, None)
1139 self.w_categories_treeview.connect('size-allocate',
1140 self.__categories_treeview_size_allocate, None)
1141
1142 if category_list != None:
1302 status = model.get_value(itr, enumerations.STATUS_COLUMN)
1303 if status == enumerations.INSTALLED:
1304 desc = _("Installed")
1305 elif status == enumerations.NOT_INSTALLED:
1306 desc = _("Not Installed")
1307 elif status == enumerations.UPDATABLE:
1308 desc = _("Updates Available")
1309 else:
1310 desc = None
1311 if desc != None:
1312 obj = self.a11y_application_treeview.ref_at(
1313 int(model.get_string_from_iter(itr)),
1314 STATUS_COLUMN_INDEX)
1315 obj.set_image_description(desc)
1316
1317 def __set_visible_status(self, check_range = True):
1318 self.visible_status_id = 0
1319 if self.w_main_view_notebook.get_current_page() != \
1320 NOTEBOOK_PACKAGE_LIST_PAGE:
1321 return
1322 if self.__doing_search():
1323 return
1324
1325 a11y_enabled = False
1326 if self.a11y_application_treeview.get_n_accessible_children() != 0:
1327 a11y_enabled = True
1328
1329 visible_range = self.w_application_treeview.get_visible_range()
1330 if visible_range == None:
1331 return
1332 start = visible_range[0][0]
1333 end = visible_range[1][0]
1334 if debug_descriptions:
1335 print "Range Start: %d End: %d" % (start, end)
1336
1337 # Switching Publishers need to use default range
1338 active_pub = self.__get_active_publisher()
1339 if self.last_active_publisher != active_pub:
1340 check_range = False
1341 self.last_active_publisher = active_pub
1342 if self.in_search_mode:
1343 check_range = False
1344
1361 self.application_treeview_range = visible_range
1362
1363 sort_filt_model = \
1364 self.w_application_treeview.get_model() #gtk.TreeModelSort
1365 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1366 model = filt_model.get_model() #gtk.ListStore
1367 sf_itr = sort_filt_model.get_iter_from_string(str(start))
1368 pkg_stems_and_itr_to_fetch = {}
1369 while start <= end:
1370 filtered_itr = sort_filt_model.convert_iter_to_child_iter(None,
1371 sf_itr)
1372 app_itr = filt_model.convert_iter_to_child_iter(filtered_itr)
1373
1374 desc = sort_filt_model.get_value(sf_itr,
1375 enumerations.DESCRIPTION_COLUMN)
1376 # Only Fetch description for packages without a
1377 # description
1378 if desc == '...':
1379 fmri = sort_filt_model.get_value(sf_itr,
1380 enumerations.FMRI_COLUMN)
1381 if fmri != None:
1382 pkg_stem = fmri.get_pkg_stem(
1383 include_scheme = True)
1384 pkg_stems_and_itr_to_fetch[pkg_stem] = \
1385 model.get_string_from_iter(app_itr)
1386 if a11y_enabled:
1387 self.__set_accessible_status(sort_filt_model, sf_itr)
1388 start += 1
1389 sf_itr = sort_filt_model.iter_next(sf_itr)
1390
1391 if debug_descriptions:
1392 print "PKGS to FETCH: \n%s" % pkg_stems_and_itr_to_fetch
1393 if len(pkg_stems_and_itr_to_fetch) > 0:
1394 Thread(target = self.__get_pkg_descriptions,
1395 args = [pkg_stems_and_itr_to_fetch, model]).start()
1396
1397 def __doing_search(self):
1398 return self.search_start > 0
1399
1400 def __get_pkg_descriptions(self, pkg_stems_and_itr_to_fetch, orig_model):
1401 # Note: no need to aquire lock even though this can be called from
1412 except api_errors.TransportError:
1413 self.update_statusbar()
1414 return
1415 if info and len(info.get(0)) == 0:
1416 self.update_statusbar()
1417 return
1418 pkg_infos = info.get(0)
1419 pkg_descriptions_for_update = []
1420 for pkg_info in pkg_infos:
1421 short_fmri = fmri.PkgFmri(pkg_info.fmri).get_pkg_stem(
1422 include_scheme = True)
1423 pkg_descriptions_for_update.append((short_fmri,
1424 pkg_stems_and_itr_to_fetch[short_fmri],
1425 pkg_info.summary))
1426 if debug_descriptions:
1427 print "FETCHED PKGS: \n%s" % pkg_descriptions_for_update
1428 gobject.idle_add(self.__update_description_from_iter,
1429 pkg_descriptions_for_update, orig_model)
1430
1431 def __update_description_from_iter(self, pkg_descriptions_for_update, orig_model):
1432 #If doing a search abandon description updates
1433 if self.__doing_search():
1434 return
1435
1436 sort_filt_model = \
1437 self.w_application_treeview.get_model() #gtk.TreeModelSort
1438 filt_model = sort_filt_model.get_model() #gtk.TreeModelFilter
1439 model = filt_model.get_model() #gtk.ListStore
1440
1441 #If model has changed abandon description updates
1442 if orig_model != model:
1443 return
1444
1445 if debug_descriptions:
1446 print "UPDATE DESCRIPTIONS: \n%s" % pkg_descriptions_for_update
1447 for pkg_stem, path, summary in pkg_descriptions_for_update:
1448 itr = model.get_iter_from_string(path)
1449 stored_pkg_fmri = model.get_value(itr, enumerations.FMRI_COLUMN)
1450 stored_pkg_stem = stored_pkg_fmri.get_pkg_stem(
1451 include_scheme = True)
1452
1453 if pkg_stem != stored_pkg_stem:
1454 if debug:
1455 print ("__update_description_from_iter(): "
1728 self.search_time_sec = 0
1729 text = self.w_searchentry.get_text()
1730 # Here we call the search API to get the results
1731 searches = []
1732 servers = []
1733 result = []
1734 pargs = []
1735 search_str = SEARCH_STR_FORMAT % text
1736 pargs.append(search_str)
1737 if search_all:
1738 servers = None
1739 else:
1740 pub_prefix = self.__get_active_publisher()
1741 if pub_prefix != None:
1742 pub = self.api_o.get_publisher(prefix=pub_prefix)
1743 else:
1744 pub = self.api_o.get_preferred_publisher()
1745 origin_uri = self.__get_origin_uri(pub.selected_repository)
1746 servers.append({"origin": origin_uri})
1747 if debug:
1748 print "Search: pargs %s servers: %s" % (pargs, servers)
1749
1750 #TBD If we ever search just Installed pkgs should allow for a local search
1751 case_sensitive = False
1752 return_actions = True
1753 searches.append(self.api_o.remote_search(
1754 [api.Query(" ".join(pargs), case_sensitive, return_actions)],
1755 servers=servers))
1756 if debug:
1757 print "Search Args: %s : cs: %s : retact: %s" % \
1758 ("".join(pargs), case_sensitive, return_actions)
1759
1760 last_name = ""
1761 self.current_search_publisher = None
1762
1763 # Sorting results by Name gives best overall appearance and flow
1764 sort_col = enumerations.NAME_COLUMN
1765 try:
1766 for query_num, publisher, (v, return_type, tmp) in \
1767 itertools.chain(*searches):
1768 if v < 1 or return_type != api.Query.RETURN_PACKAGES:
1769 gobject.idle_add(self.w_progress_dialog.hide)
1770 self.__process_after_search_failure()
1771 return
1772
1773 pub = None
1774 if publisher is not None \
1775 and "prefix" in publisher:
1776 pub = publisher["prefix"]
1777 name = fmri.PkgFmri(str(tmp)).get_name()
1778 if last_name != name:
1779 if debug:
1780 print "Result Name: %s (%s)" % (name, pub)
1781 a_res = name, pub
1782 result.append(a_res)
1783 #Ignore Status when fetching
1784 application_list = \
1785 self.__get_min_list_from_search(result)
1786 self.current_search_publisher = pub
1787 self.in_setup = True
1788 gobject.idle_add(self.__init_tree_views,
1789 application_list, None, None, None, None,
1790 sort_col)
1791 last_name = name
1792 self.pylintstub = query_num
1793 except api_errors.ProblematicSearchServers, ex:
1794 self.__process_api_search_error(ex)
1795 gobject.idle_add(self.w_progress_dialog.hide)
1796 gobject.idle_add(self.__handle_api_search_error)
1797 if len(result) == 0:
1798 self.__process_after_search_with_zero_results()
1799 return
1800 except Exception, ex:
1801 # We are not interested in this error
1802 gobject.idle_add(self.w_progress_dialog.hide)
1803 self.__process_after_search_failure()
1804 return
1805 if debug:
1806 print "Number of search results:", len(result)
1807 if len(result) == 0:
1808 if debug:
1809 print "No search results"
1810 self.__process_after_search_with_zero_results()
1811 return
1812 # We cannot get status of the packages if catalogs have not
1813 # been loaded so we pause for up to 5 seconds here to
1814 # allow catalogs to be loaded
1815 times = 5
1816 while self.catalog_loaded == False:
1817 if times == 0:
1818 break
1819 time.sleep(1)
1820 times -= 1
1821
1822 #Now fetch full result set with Status
1823 self.in_setup = True
1824 application_list = self.__get_full_list_from_search(result)
1825 gobject.idle_add(self.__init_tree_views, application_list, None, None, \
1826 None, None, sort_col)
1827
1828 if self.search_start > 0:
1829 self.search_time_sec = int(time.time() - self.search_start)
1830 if debug:
1831 print "Search time: %d (sec)" % self.search_time_sec
1832 self.search_start = 0
1833
1834 def __process_after_search_with_zero_results(self):
1835 if self.search_start > 0:
1836 self.search_time_sec = int(time.time() - self.search_start)
1837 self.search_start = 0
1838 self.in_setup = True
1839 application_list = self.__get_new_application_liststore()
1840 gobject.idle_add(self.__set_empty_details_panel)
1841 gobject.idle_add(self.__set_main_view_package_list)
1842 gobject.idle_add(self.__init_tree_views, application_list, None, None)
1843
1844 def __get_min_list_from_search(self, search_result):
1845 application_list = self.__get_new_application_liststore()
1846 for name, publisher in search_result:
1847 application_list.append(
1848 [False, None, name, '...', enumerations.NOT_INSTALLED, None,
1849 "pkg://" + publisher + "/" + name, None, True, None,
1850 publisher])
1851 return application_list
1852
1853 def __get_full_list_from_search(self, search_result):
1854 application_list = self.__get_new_application_liststore()
1855 self.__add_pkgs_to_list_from_search(search_result,
1856 application_list)
1857 return application_list
1858
1859 def __add_pkgs_to_list_from_search(self, search_result,
1860 application_list):
1861 pargs = []
1862 default_pub = self.api_o.get_preferred_publisher().prefix
1863 for name, publisher in search_result:
1864 pargs.append("pkg://" + publisher + "/" + name)
1865 # We now need to get the status for each package
1866 if debug_descriptions:
1867 print "pargs:", pargs
1868 try:
1869 pkgs_known = self.__get_inventory_list(pargs,
1870 True, True)
1871 except api_errors.InventoryException:
1872 # This can happen if load_catalogs has not been run
1873 err = _("Unable to get status for search results.\n"
1874 "The catalogs have not been loaded.\n"
2101 self.w_preferencesdialog.show()
2102
2103 def __on_preferencesclose_clicked(self, widget):
2104 self.w_preferencesdialog.hide()
2105
2106 def __on_preferenceshelp_clicked(self, widget):
2107 gui_misc.display_help(self.application_dir, "pm_win")
2108
2109 def __on_startpage_checkbutton_toggled(self, widget):
2110 self.show_startpage = self.w_startpage_checkbutton.get_active()
2111 try:
2112 self.client.set_bool(SHOW_STARTPAGE_PREFERENCES,
2113 self.show_startpage)
2114 except GError:
2115 pass
2116
2117 def __on_api_search_checkbox_toggled(self, widget):
2118 active = self.api_search_checkbox.get_active()
2119 if len(self.current_repos_with_search_errors) > 0:
2120 if active:
2121 for pub, err_type, err_str in \
2122 self.current_repos_with_search_errors:
2123 if pub not in self.gconf_not_show_repos:
2124 self.gconf_not_show_repos += pub + ","
2125 else:
2126 for pub, err_type, err_str in \
2127 self.current_repos_with_search_errors:
2128 self.gconf_not_show_repos = \
2129 self.gconf_not_show_repos.replace(
2130 pub + ",", "")
2131 try:
2132 self.client.set_string(API_SEARCH_ERROR_PREFERENCES,
2133 self.gconf_not_show_repos)
2134 except GError:
2135 pass
2136
2137 def __on_searchentry_focus_in(self, widget, event):
2138 if self.w_main_clipboard.wait_is_text_available():
2139 self.w_paste_menuitem.set_sensitive(True)
2140 char_count = widget.get_text_length()
2141 if char_count > 0:
2142 self.w_selectall_menuitem.set_sensitive(True)
2143 else:
2144 self.w_selectall_menuitem.set_sensitive(False)
2145 bounds = widget.get_selection_bounds()
2146 if bounds:
2147 offset1 = bounds[0]
2148 offset2 = bounds[1]
2149 if abs(offset2 - offset1) == char_count:
2150 self.w_selectall_menuitem.set_sensitive(False)
2451 start, end = self.w_searchentry.get_selection_bounds()
2452 self.w_searchentry.select_region(end, end)
2453
2454 pub = [active_publisher, ]
2455 self.set_show_filter = self.initial_show_filter
2456 self.set_section = self.initial_section
2457 Thread(target = self.__setup_publisher, args = [pub]).start()
2458 self.__set_main_view_package_list()
2459
2460 def __get_active_publisher(self):
2461 pub_iter = self.w_repository_combobox.get_active_iter()
2462 if pub_iter == None:
2463 return None
2464 return self.repositories_list.get_value(pub_iter, \
2465 enumerations.REPOSITORY_NAME)
2466
2467 def __setup_publisher(self, publishers=[]):
2468 self.saved_filter_combobox_active = self.initial_show_filter
2469 application_list, category_list , section_list = \
2470 self.__get_application_categories_lists(publishers)
2471 self.__unset_saved()
2472 gobject.idle_add(self.__init_tree_views, application_list,
2473 category_list, section_list)
2474
2475 def __unset_saved(self):
2476 self.saved_application_list = None
2477 self.saved_application_list_filter = None
2478 self.saved_application_list_sort = None
2479 self.saved_category_list = None
2480 self.saved_section_list = None
2481
2482 def __get_application_categories_lists(self, publishers=[]):
2483 print "__get_application_categories_lists: ", self.visible_repository
2484 if not self.visible_repository:
2485 self.visible_repository = self.__get_active_publisher()
2486 print "** __get_application_categories_lists: ", self.visible_repository
2487 application_list = self.__get_new_application_liststore()
2488 category_list = self.__get_new_category_liststore()
2489 section_list = self.__get_new_section_liststore()
2490 first_loop = True
2491 for publisher in publishers:
2492 uptodate = False
2493 try:
2494 uptodate = self.__check_if_cache_uptodate(publisher)
2495 if uptodate:
2496 self.__add_pkgs_to_lists_from_cache(publisher,
2497 application_list, category_list,
2498 section_list)
2499 except (UnpicklingError, EOFError, IOError):
2500 #Most likely cache is corrupted, silently load list
2501 #from api.
2502 application_list = self.__get_new_application_liststore()
2503 category_list = self.__get_new_category_liststore()
2504 uptodate = False
2505 if not uptodate:
2506 if first_loop == True:
2507 first_loop = False
2508 gobject.idle_add(self.setup_progressdialog_show)
2509 self.api_o.refresh(pubs=[publisher])
2510 self.__add_pkgs_to_lists_from_api(publisher,
2511 application_list, category_list, section_list)
2512 category_list.prepend([0, _('All'), None, None, False,
2513 True, None])
2514 if self.application_list and self.category_list and \
2515 not self.visible_repository_uptodate:
2516 if self.visible_repository:
2517 dump_list = self.application_list
2518 if self.saved_application_list != None:
2519 dump_list = \
2520 self.saved_application_list
2521 self.__dump_datamodels(self.visible_repository,
2522 dump_list, self.category_list,
2523 self.section_list)
2524 self.visible_repository = self.__get_active_publisher()
2525 self.visible_repository_uptodate = uptodate
2526 return application_list, category_list, section_list
2527
2528 def __check_if_cache_uptodate(self, publisher):
2529 if self.cache_o:
2530 return self.cache_o.check_if_cache_uptodate(publisher)
2531 return False
2532
2533 def __dump_datamodels(self, publisher, application_list, category_list,
2534 section_list):
2535 #Consistency check - only dump models if publisher passed in matches
2536 #publisher in application list
2537 if application_list == None:
2538 return
2539 app_pub = application_list[0][enumerations.AUTHORITY_COLUMN]
2540 if publisher != app_pub:
2541 if debug:
2542 print "ERROR: __dump_data_models(): INCONSISTENT " \
2543 "pub %s != app_list_pub %s" % \
2544 (publisher, app_pub)
2545 return
2546
2547 if self.cache_o:
2548 if self.img_timestamp == \
2549 self.cache_o.get_index_timestamp():
2550 Thread(target = self.cache_o.dump_datamodels,
2551 args = (publisher, application_list, category_list,
2552 section_list)).start()
2553 else:
2554 self.__remove_cache()
2555
2556 def __remove_cache(self):
2557 model = self.w_repository_combobox.get_model()
2558 for publisher in model:
2559 pub_name = publisher[1]
2560 if pub_name and pub_name != _("Add..."):
2561 Thread(target = self.cache_o.remove_datamodel,
2562 args = [publisher[1]]).start()
2563
2564 def __on_install_update(self, widget):
2565 self.api_o.reset()
2566 install_update = []
2665 #Let the progress_pulse finish. This should be done other way, but at
2666 #The moment this works fine
2667 time.sleep(0.2)
2668 gobject.idle_add(self.w_progress_cancel.show)
2669 gobject.idle_add(self.process_package_list_start,
2670 self.image_directory)
2671
2672 def __main_application_quit(self, be_name = None):
2673 '''quits the main gtk loop'''
2674 self.cancelled = True
2675 if self.in_setup:
2676 return
2677
2678 if be_name:
2679 if self.image_dir_arg:
2680 gobject.spawn_async([self.application_path, "-R",
2681 self.image_dir_arg, "-U", be_name])
2682 else:
2683 gobject.spawn_async([self.application_path,
2684 "-U", be_name])
2685 elif self.in_search_mode:
2686 self.__dump_datamodels(self.visible_repository,
2687 self.saved_application_list, self.category_list,
2688 self.section_list)
2689 else:
2690 visible_repository = self.__get_visible_repository_name()
2691 self.__dump_datamodels(visible_repository,
2692 self.application_list, self.category_list,
2693 self.section_list)
2694
2695 width, height = self.w_main_window.get_size()
2696 hpos = self.w_main_hpaned.get_position()
2697 vpos = self.w_main_vpaned.get_position()
2698 try:
2699 self.client.set_int(INITIAL_APP_WIDTH_PREFERENCES, width)
2700 self.client.set_int(INITIAL_APP_HEIGHT_PREFERENCES, height)
2701 self.client.set_int(INITIAL_APP_HPOS_PREFERENCES, hpos)
2702 self.client.set_int(INITIAL_APP_VPOS_PREFERENCES, vpos)
2703 except GError:
2704 pass
2705
2706 self.w_main_window.hide()
2707 while gtk.events_pending():
2708 gtk.main_iteration(False)
2709 gtk.main_quit()
3863 self.set_show_filter != enumerations.FILTER_ALL:
3864 self.__application_refilter()
3865 else:
3866 self.unset_busy_cursor()
3867
3868 if self.first_run or self.in_reload:
3869 Thread(target = self.__enable_disable_update_all).start()
3870 self.first_run = False
3871 self.in_reload = False
3872
3873 def get_icon_pixbuf_from_glade_dir(self, icon_name):
3874 return gui_misc.get_pixbuf_from_path(self.application_dir +
3875 "/usr/share/package-manager/", icon_name)
3876
3877 def update_statusbar(self):
3878 '''Function which updates statusbar'''
3879 if self.statusbar_message_id > 0:
3880 self.w_main_statusbar.remove(0, self.statusbar_message_id)
3881 self.statusbar_message_id = 0
3882 search_text = self.w_searchentry.get_text()
3883
3884 if not self.in_search_mode:
3885 installed = 0
3886 self.selected = 0
3887 sel = 0
3888 if self.application_list == None:
3889 return
3890 visible_repository = self.__get_visible_repository_name()
3891 pkgs = self.selected_pkgs.get(visible_repository)
3892 if pkgs:
3893 self.selected = len(pkgs)
3894 for pkg_row in self.application_list:
3895 if pkg_row[enumerations.STATUS_COLUMN] == \
3896 enumerations.INSTALLED \
3897 or pkg_row[enumerations.STATUS_COLUMN] == \
3898 enumerations.UPDATABLE:
3899 installed = installed + 1
3900 if pkg_row[enumerations.MARK_COLUMN]:
3901 sel = sel + 1
3902 listed_str = _('%d listed') % len(self.application_list)
3903 sel_str = _('%d selected') % sel
3904 inst_str = _('%d installed') % installed
3905 status_str = _("%s: %s , %s, %s.") % (visible_repository,
3906 listed_str, inst_str, sel_str)
3907 self.w_main_statusbar.push(0, status_str)
3908 return
3909
3910 # In Search Mode
3911 active = ""
3912 if self.is_search_all:
3913 if self.__doing_search():
3914 if self.current_search_publisher != None:
3915 active = "(" + self.current_search_publisher + \
3916 ") "
3917 opt_str = _('Searching... '
3918 '%(active)sfor "%(search_text)s"') % \
3919 {"active": active, "search_text": search_text}
3920 else:
3921 opt_str = _('Searched All for "%s"') % (search_text)
3922 else:
3923 search_str = _("Searched")
3924 if self.__doing_search():
3925 search_str = _("Searching...")
3926 if self.last_active_publisher != None:
3927 active = "(" + self.last_active_publisher + ") "
3928 opt_str = \
3929 _('%(search)s %(last_active)sfor "%(search_text)s"') \
3930 % {"search": search_str, "last_active" : active,
3931 "search_text" : search_text}
3932 fmt_str = _("%(option_str)s: %(number)d found %(time)s")
3933 time_str = ""
3934 if self.search_time_sec > 0:
3935 time_str = _("in %d seconds") % self.search_time_sec
3936 status_str = fmt_str % {"option_str" : opt_str, "number" :
3937 len(self.application_list), "time" : time_str}
3938 self.w_main_statusbar.push(0, status_str)
3939
3940 def update_package_list(self, update_list):
3941 if update_list == None and self.img_timestamp:
3942 return
3943 visible_repository = self.__get_visible_repository_name()
3944 default_publisher = self.default_publisher
3945 self.api_o.refresh()
3946 if not self.img_timestamp:
3947 self.img_timestamp = self.cache_o.get_index_timestamp()
3948 self.__on_reload(None)
3949 return
3950 self.img_timestamp = self.cache_o.get_index_timestamp()
3951 installed_icon = gui_misc.get_icon(self.icon_theme,
3952 "status_installed")
3953 visible_list = update_list.get(visible_repository)
3954 if visible_list:
3955 i = 0
3956 while i < len(visible_list):
3957 visible_list[i] = gui_misc.get_pkg_name(
3958 visible_list[i])
3959 i += 1
4031 self.w_ua_completed_release_label.set_text(info_str.strip('\n'))
4032
4033 info_str = misc.get_release_notes_url()
4034 self.w_ua_completed_linkbutton.set_uri(info_str)
4035 self.w_ua_completed_linkbutton.set_label(info_str)
4036 self.release_notes_url = info_str
4037
4038 self.w_ua_completed_dialog.set_title(_("Update All Complete"))
4039 self.w_ua_completed_dialog.show()
4040
4041 ###############################################################################
4042 #-----------------------------------------------------------------------------#
4043 # Main
4044 #-----------------------------------------------------------------------------#
4045
4046 def main():
4047 gtk.main()
4048 return 0
4049
4050 if __name__ == '__main__':
4051 debug = True
4052 debug_descriptions = False
4053 update_all_proceed = False
4054 ua_be_name = None
4055 app_path = None
4056 image_dir = None
4057 info_install_arg = None
4058 save_selected = _("Save selected...")
4059 save_selected_pkgs = _("Save selected packages...")
4060 reboot_needed = _("The installed package(s) require a reboot before "
4061 "installation can be completed.")
4062
4063 try:
4064 opts, args = getopt.getopt(sys.argv[1:], "hR:U:i:", \
4065 ["help", "image-dir=", "update-all=", "info-install="])
4066 except getopt.error, msg:
4067 print "%s, for help use --help" % msg
4068 sys.exit(2)
4069
4070 if os.path.isabs(sys.argv[0]):
4071 app_path = sys.argv[0]
|