class AppsStock(bigboard.stock.AbstractMugshotStock): STATIC_SET_SIZE = 7 DYNAMIC_SET_SIZE = 7 STATIFICATION_TIME_SEC = 60 * 60 #* 24 * 3; # 3 days __gsignals__ = { "all-apps-loaded" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()) } def __init__(self, *args, **kwargs): super(AppsStock, self).__init__(*args, **kwargs) self.__box = CanvasVBox(spacing=3) self.__message = hippo.CanvasText() self.__message_link = ActionLink() self.__message_link.connect("button-press-event", lambda link, event: self.__on_message_link()) self.__message_link_url = None self.__subtitle = hippo.CanvasText(font="Bold 12px") self.__static_set = CanvasVBox() self.__dynamic_set = CanvasVBox() self.__box.append(self.__message) self.__box.append(self.__message_link) self.__box.append(self.__subtitle) self.__box.append(self.__static_set) self.__box.append(self.__dynamic_set) self.__box.set_child_visible(self.__dynamic_set, False) self.__app_browser = None self._add_more_link(self.__on_more_link) self._mugshot.connect("all-apps-loaded", lambda mugshot: self.__merge_apps()) self._mugshot.connect("global-top-apps-changed", lambda mugshot, apps: self.__sync()) self._mugshot.connect("my-top-apps-changed", lambda mugshot, apps: self.__sync()) self._mugshot.connect("pinned-apps-changed", lambda mugshot, apps: self.__sync()) self._mugshot.connect("pref-changed", lambda mugshot, key, value: self.__handle_pref_change(key, value)) self.__usage_enabled = False self.__static_set_ids = {} self.__set_message('Loading...') self.__apps = {} # mugshot app -> app # apps installed locally and not known in Mugshot self.__local_apps = {} # desktop -> app ad = apps_directory.get_app_directory() for app in ad.get_apps(): self.get_local_app(app) def __on_more_link(self): self._logger.debug("more!") if self.__app_browser is None: self.__app_browser = appbrowser.AppBrowser(self) self.__app_browser.present() def __on_message_link(self): libbig.show_url(self.__message_link_url) def __set_message(self, text, link=None): self.__box.set_child_visible(self.__message, (not text is None) and link is None) self.__box.set_child_visible(self.__message_link, not (text is None or link is None)) if text: self.__message.set_property("text", text) self.__message_link.set_property("text", text) if link: self.__message_link_url = link def __set_subtitle(self, text): self.__box.set_child_visible(self.__subtitle, not text is None) if text: self.__subtitle.set_property("text", text) def _on_mugshot_initialized(self): super(AppsStock, self)._on_mugshot_initialized() self._mugshot.get_global_top_apps() self._mugshot.request_all_apps() def _on_mugshot_ready(self): super(AppsStock, self)._on_mugshot_ready() self._mugshot.get_pinned_apps() self._mugshot.get_my_top_apps() def get_authed_content(self, size): return self.__box def get_unauthed_content(self, size): return self.__box def __set_item_size(self, item, size): if size == bigboard.stock.Stock.SIZE_BULL: item.set_property('xalign', hippo.ALIGNMENT_FILL) else: item.set_property('xalign', hippo.ALIGNMENT_CENTER) item.set_size(size) def set_size(self, size): super(AppsStock, self).set_size(size) for child in self.__static_set.get_children() + self.__dynamic_set.get_children(): self.__set_item_size(child, size) def __handle_pref_change(self, key, value): if key != 'applicationUsageEnabled': return self._logger.debug("handling %s pref change: %s", key, value) self.__sync() def __on_pinned_apps_success(self, pinned_ids): self._logger.debug("app pin set succeeded") self._mugshot.get_pinned_apps(force=True) def __set_dynamic_set(self, mugshot_apps): self.__dynamic_set.remove_all() for i, mugshot_app in enumerate(mugshot_apps or []): app = self.get_app(mugshot_app) if self.__static_set_ids.has_key(app.get_id()): continue if app.get_is_excluded(): continue if i >= self.DYNAMIC_SET_SIZE: break if not app.is_installed(): continue self._logger.debug("setting dynamic app: %s", app) display = apps_widgets.AppDisplay(app) display.connect("button-press-event", lambda display, event: display.launch()) self.__dynamic_set.append(display) if mugshot_apps: self.__box.set_child_visible(self.__dynamic_set,True) def get_app(self, mugshot_app): if not self.__apps.has_key(mugshot_app.get_id()): ad = apps_directory.get_app_directory() for desktop_name in mugshot_app.get_desktop_names().split(';'): try: target_menuitem = ad.lookup(desktop_name) except KeyError, e: continue if self.__local_apps.has_key(target_menuitem.get_name()): self._logger.debug("moving app %s from local to apps", target_menuitem.get_name()) existing_app = self.__local_apps[target_menuitem.get_name()] del self.__local_apps[target_menuitem.get_name()] existing_app.set_app(mugshot_app) self.__apps[mugshot_app.get_id()] = existing_app return existing_app self._logger.debug("creating app %s", mugshot_app.get_id()) self.__apps[mugshot_app.get_id()] = Application(mugshot_app=mugshot_app) return self.__apps[mugshot_app.get_id()]
class PhotosStock(AbstractMugshotStock): SLIDE_TIMEOUT_SEC = 1 * 60 # 1 minute MAX_PREV_IMAGES = 5 """Cycles between photos from friends in Mugshot network""" def __init__(self, *args, **kwargs): super(PhotosStock,self).__init__(*args, **kwargs) self.__images = None self.__images_reverse = [] self.__images_forward = [] self.__current_image = None self.__box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, spacing=4) self.__successive_load_failures = 0 self.__photosize = 120 self.__idle_display_id = 0 self.__text = hippo.CanvasText(text="No thumbnails found") self.__displaymode = "uninitialized" # "none", "photo" self.__displaybox = CanvasVBox(spacing=4) self.__photo_header = CanvasHBox(spacing=4) self.__favicon = CanvasMugshotURLImage() self.__title = ActionLink(size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END) self.__title.connect("button-press-event", lambda photo, event: self.__visit_photo()) self.__photo_header.append(self.__favicon) self.__photo_header.append(self.__title) self.__displaybox.append(self.__photo_header) self.__photobox = CanvasHBox(spacing=6) self.__photo = TransitioningURLImage(self._logger, dimension=self.__photosize) self.__photo.connect("loaded", lambda photo, loaded: self.__on_image_load(loaded)) self.__photo.connect("button-press-event", lambda photo, event: self.__visit_photo()) self.__metabox = CanvasVBox() self.__metabox.append(hippo.CanvasText(text="from")) self.__fromphoto = CanvasMugshotURLImage() self.__fromphoto.set_clickable(True) self.__fromphoto.connect("button-press-event", lambda photo, event: self.__visit_person()) self.__metabox.append(self.__fromphoto) self.__fromname = ActionLink(size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END) self.__fromname.connect("button-press-event", lambda photo, event: self.__visit_person()) self.__metabox.append(self.__fromname) self.__photobox.append(self.__photo) self.__photobox.append(self.__metabox) self.__displaybox.append(self.__photobox) self.__controlbox = CanvasHBox() prev_link = ActionLink(text=u"\u00ab Prev", xalign=hippo.ALIGNMENT_START) prev_link.connect("button-press-event", lambda b,e: self.__do_prev()) self.__controlbox.append(prev_link) next_link = ActionLink(text=u"Next \u00bb", xalign=hippo.ALIGNMENT_END) next_link.connect("button-press-event", lambda b,e: self.__do_next()) self.__controlbox.append(next_link, hippo.PACK_EXPAND) self.__displaybox.append(self.__controlbox) self.__person_accts_len = {} # <Person,int> self.__bearbox = CanvasVBox() self.__bearphoto = TransitioningURLImage(self._logger, dimension=self.SIZE_BEAR_CONTENT_PX-6) self.__bearphoto.connect("button-press-event", lambda photo, event: self.__visit_photo()) self.__bearbox.append(self.__bearphoto) self._mugshot.connect("network-changed", lambda mugshot: self.__handle_network_change()) def _on_mugshot_ready(self): super(PhotosStock, self)._on_mugshot_ready() self._mugshot.get_network() def get_authed_content(self, size): return size == self.SIZE_BULL and self.__box or self.__bearbox def __visit_photo(self): self._logger.debug("visiting photo for %s", self.__current_image) if not self.__current_image: return libbig.show_url(self.__current_image[2].get_href()) def __visit_person(self): self._logger.debug("visiting person for %s", self.__current_image) if not self.__current_image: return libbig.show_url(mugshot.get_mugshot().get_baseurl() + self.__current_image[0].get_home_url()) def __thumbnails_generator(self): """The infinite photos function. Cool.""" found_one = False while True: for entity in self._mugshot.get_network(): accts = entity.get_external_accounts() if not accts: continue for acct in accts: if acct.get_thumbnails(): for thumbnail in acct.get_thumbnails(): found_one = True yield (entity, acct, thumbnail) if not found_one: return def __next_image(self): if self.__current_image: self.__images_reverse.append(self.__current_image) if len(self.__images_reverse) > self.MAX_PREV_IMAGES: self.__images_reverse.pop(0) if self.__images_forward: return self.__images_forward.pop() else: return self.__images.next() def __prev_image(self): if self.__current_image: self.__images_forward.append(self.__current_image) return self.__images_reverse.pop() def __set_image(self, imageinfo): self.__current_image = imageinfo (entity, acct, thumbnail) = imageinfo self._logger.debug("starting load of url %s" % (thumbnail.get_src(),)) self.__photo.set_url(thumbnail.get_src()) self.__bearphoto.set_url(thumbnail.get_src()) def __on_image_load(self, success): if self.__current_image is None: self._logger.debug("image load complete, but no current image") return if not success: self.__successive_load_failures = max(self.__successive_load_failures+1, 17) self._logger.debug("image load failed, queueing skip to next") gobject.timeout_add(8000 + (2 ** self.__successive_load_failures) * 1000, self.__do_next) else: self.__successive_load_failures = 0 self._logger.debug("image load success, syncing metadata") (entity, acct, thumbnail) = self.__current_image self.__favicon.set_url(acct.get_icon()) self.__title.set_property("text", thumbnail.get_title() or "(untitled)") self.__fromname.set_property("text", entity.get_name()) self.__fromphoto.set_url(entity.get_photo_url()) def __idle_display_image(self): self._logger.debug("in idle, doing next image") self.__idle_display_id = 0 self.__do_next() return False def __handle_person_change(self, person): need_reset = False accts = person.get_external_accounts() if not self.__person_accts_len.has_key(person): need_reset = True self.__person_accts_len[person] = -1 elif accts and self.__person_accts_len[person] != len(accts): self.__person_accts_len[person] = len(accts) need_reset = True if need_reset: self.__reset() def __handle_network_change(self): self._logger.debug("handling network change") for person in self._mugshot.get_network(): if not self.__person_accts_len.has_key(person): person.connect("changed", self.__handle_person_change) accts = person.get_external_accounts() self.__person_accts_len[person] = accts and len(accts) or 0 not_in_network = [] for person in self.__person_accts_len.iterkeys(): if not person in self._mugshot.get_network(): not_in_network.append(person) for person in not_in_network: self._logger.debug("removing not-in-network person %s", person.get_guid()) del self.__person_accts_len[person] self.__reset() def __do_direction(self, is_next): self._logger.debug("skipping to %s" % (is_next and "next" or "prev",)) try: self.__set_image(is_next and self.__next_image() or self.__prev_image()) if self.__displaymode == 'text': self.__box.remove(self.__text) if self.__displaymode != 'photo': self.__box.append(self.__displaybox) self.__displaymode = 'photo' except StopIteration: self._logger.debug("caught StopIteration, displaying no photos text") if self.__displaymode == 'photo': self.__box.remove(self.__displaybox) if self.__displaymode != 'text': self.__box.append(self.__text) self.__displaymode = 'text' if self.__idle_display_id > 0: gobject.source_remove(self.__idle_display_id) self.__idle_display_id = gobject.timeout_add(self.SLIDE_TIMEOUT_SEC * 1000, self.__idle_display_image) def __do_next(self): self.__do_direction(True) def __do_prev(self): self.__do_direction(False) def __reset(self): self._logger.debug("resetting") self.__images = self.__thumbnails_generator() self.__box.remove_all() self.__displaymode = 'uninitialized' self.__do_next()
class PhotosStock(AbstractMugshotStock): SLIDE_TIMEOUT_SEC = 1 * 60 # 1 minute MAX_PREV_IMAGES = 5 """Cycles between photos from friends in Mugshot network""" def __init__(self, *args, **kwargs): super(PhotosStock, self).__init__(*args, **kwargs) self.__images = None self.__images_reverse = [] self.__images_forward = [] self.__current_image = None self.__box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, spacing=4) self.__successive_load_failures = 0 self.__photosize = 120 self.__idle_display_id = 0 self.__text = hippo.CanvasText(text="No thumbnails found") self.__displaymode = "uninitialized" # "none", "photo" self.__displaybox = CanvasVBox(spacing=4) self.__photo_header = CanvasHBox(spacing=4) self.__favicon = CanvasMugshotURLImage() self.__title = ActionLink(size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END) self.__title.connect("button-press-event", lambda photo, event: self.__visit_photo()) self.__photo_header.append(self.__favicon) self.__photo_header.append(self.__title) self.__displaybox.append(self.__photo_header) self.__photobox = CanvasHBox(spacing=6) self.__photo = TransitioningURLImage(dimension=self.__photosize) self.__photo.connect("loaded", lambda photo, loaded: self.__on_image_load(loaded)) self.__photo.connect("button-press-event", lambda photo, event: self.__visit_photo()) self.__metabox = CanvasVBox() self.__metabox.append(hippo.CanvasText(text="from")) self.__fromphoto = CanvasMugshotURLImage() self.__fromphoto.set_clickable(True) self.__fromphoto.connect("button-press-event", lambda photo, event: self.__visit_person()) self.__metabox.append(self.__fromphoto) self.__fromname = ActionLink(size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END) self.__fromname.connect("button-press-event", lambda photo, event: self.__visit_person()) self.__metabox.append(self.__fromname) self.__photobox.append(self.__photo) self.__photobox.append(self.__metabox) self.__displaybox.append(self.__photobox) self.__controlbox = CanvasHBox() prev_link = ActionLink(text=u"\u00ab Prev", xalign=hippo.ALIGNMENT_START) prev_link.connect("button-press-event", lambda b, e: self.__do_prev()) self.__controlbox.append(prev_link) next_link = ActionLink(text=u"Next \u00bb", xalign=hippo.ALIGNMENT_END) next_link.connect("button-press-event", lambda b, e: self.__do_next()) self.__controlbox.append(next_link, hippo.PACK_EXPAND) self.__displaybox.append(self.__controlbox) self.__person_accts_len = {} # <Person,int> self.__bearbox = CanvasVBox() self.__bearphoto = TransitioningURLImage(dimension=self.SIZE_BEAR_CONTENT_PX - 6) self.__bearphoto.connect("button-press-event", lambda photo, event: self.__visit_photo()) self.__bearbox.append(self.__bearphoto) def _on_ready(self): if self._model.self_resource != None: query = self._model.query_resource( self._model.self_resource, "contacts user [+;lovedAccounts [+;thumbnails +]]" ) query.add_handler(self.__on_got_self) query.execute() def __on_got_self(self, myself): self.__reset() def get_authed_content(self, size): return size == self.SIZE_BULL and self.__box or self.__bearbox def __visit_photo(self): _logger.debug("visiting photo for %s", self.__current_image) if not self.__current_image: return libbig.show_url(self.__current_image[2].get_href()) def __visit_person(self): _logger.debug("visiting person for %s", self.__current_image) if not self.__current_image: return libbig.show_url(urlparse.urljoin(globals.get_baseurl(), self.__current_image[0].get_home_url())) def __thumbnails_generator(self): """The infinite photos function. Cool.""" while True: found_one = False # Iterate through all thumbnails for all "loved accounts" for all contacts. # We don't handle change notification ... if something changes we'll pick # it up next time around. Note the use of temporary copies of lists to avoid # problems if a list is mutated by a change notification while we are iterating it. if self._model.self_resource: for contact in list(getattr(self._model.self_resource, "contacts", [])): user = getattr(contact, "user", None) if user != None: lovedAccounts = getattr(user, "lovedAccounts", None) if lovedAccounts: for externalAccount in lovedAccounts: thumbnails = getattr(externalAccount, "thumbnails", None) if thumbnails: for thumbnail in thumbnails: yield (user, externalAccount, thumbnail) # If we didn't find any photos, we go into a "no photos" state; we'll keep on trying # to restart the iterator in the timeout, so when things appear we'll display them if not found_one: return def __next_image(self): if self.__current_image: self.__images_reverse.append(self.__current_image) if len(self.__images_reverse) > self.MAX_PREV_IMAGES: self.__images_reverse.pop(0) if self.__images_forward: return self.__images_forward.pop() else: return self.__images.next() def __prev_image(self): if self.__current_image: self.__images_forward.append(self.__current_image) return self.__images_reverse.pop() def __set_image(self, imageinfo): self.__current_image = imageinfo (user, account, thumbnail) = imageinfo _logger.debug("starting load of url %s" % (thumbnail.src,)) self.__photo.set_url(thumbnail.src) self.__bearphoto.set_url(thumbnail.src) def __on_image_load(self, success): if self.__current_image is None: _logger.debug("image load complete, but no current image") return if not success: self.__successive_load_failures = max(self.__successive_load_failures + 1, 17) _logger.debug("image load failed, queueing skip to next") gobject.timeout_add(8000 + (2 ** self.__successive_load_failures) * 1000, self.__do_next) else: self.__successive_load_failures = 0 _logger.debug("image load success, syncing metadata") (user, account, thumbnail) = self.__current_image self.__favicon.set_url(account.iconUrl) title = getattr(thumbnail, "title", None) if not title: title = "(untitled)" self.__title.set_property("text", title) self.__fromname.set_property("text", user.name) self.__fromphoto.set_url(user.photoUrl) def __idle_display_image(self): _logger.debug("in idle, doing next image") self.__idle_display_id = 0 self.__do_next() return False def __do_direction(self, is_next): _logger.debug("skipping to %s" % (is_next and "next" or "prev",)) try: self.__set_image(is_next and self.__next_image() or self.__prev_image()) if self.__displaymode == "text": self.__box.remove(self.__text) if self.__displaymode != "photo": self.__box.append(self.__displaybox) self.__displaymode = "photo" except StopIteration: _logger.debug("caught StopIteration, displaying no photos text") if self.__displaymode == "photo": self.__box.remove(self.__displaybox) if self.__displaymode != "text": self.__box.append(self.__text) self.__displaymode = "text" if self.__idle_display_id > 0: gobject.source_remove(self.__idle_display_id) self.__idle_display_id = gobject.timeout_add(self.SLIDE_TIMEOUT_SEC * 1000, self.__idle_display_image) def __do_next(self): self.__do_direction(True) def __do_prev(self): self.__do_direction(False) def __reset(self): _logger.debug("resetting") self.__images = self.__thumbnails_generator() self.__images_reverse = [] self.__box.remove_all() self.__displaymode = "uninitialized" self.__do_next()
class AppExtras(CanvasVBox): __gsignals__ = { "more-info" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)) } def __init__(self, filterfunc, **args): super(AppExtras, self).__init__(background_color=0xFFF9DDFF, color=0x3F3F3FFF, **args) self.__search = False self.__search_filter = filterfunc self.__catname = None self.__apps = None self.__have_search_hits = False self.__found_app_count = 0 self.__headerbox = CanvasHBox(padding=6) self.append(self.__headerbox) thing = self.__get_section_name() self.__left_title = hippo.CanvasText(text="New Popular %s" % (thing,), font="12px Bold", xalign=hippo.ALIGNMENT_START) self.__headerbox.append(self.__left_title) # TODO this won't underline properly until ActionLink->set_underline listens for text changes # underline=pango.UNDERLINE_LOW self.__right_title = ActionLink(font="12px", xalign=hippo.ALIGNMENT_END) self.__right_title.connect("activated", self.__on_more_popular) self.__headerbox.append(self.__right_title, hippo.PACK_EXPAND) self.__app_pair = CanvasHBox(box_height=120) self.__app_pair2 = CanvasHBox(box_height=120) self.append(self.__app_pair) self.append(self.__app_pair2) self.__repo = apps.get_apps_repo() def __get_section_name(self): return self.__catname or 'Apps' def have_apps(self): return not not self.__found_app_count def __on_search_results(self, applications, category, search_terms): _logger.debug("Got %d search results for category '%s' terms '%s'" % (len(applications), str(category), str(search_terms))) if search_terms != self.__search or category != self.__catname: _logger.debug("Search terms have changed since starting search, ignoring results") return if search_terms is not None: self.__have_search_hits = len(applications) > 0 else: self.__have_search_hits = False self.__apps = applications self.__sync() def set_search_parameters(self, catname, search): if self.__catname != catname or self.__search != search: self.__catname = catname self.__search = search self.__repo.search(self.__catname, self.__search, self.__on_search_results) def __on_more_popular(self, w): subquery = (self.__search and ("?q=" + urllib.quote(self.__search))) \ or self.__catname and ("?category=" + urllib.quote(self.__catname)) \ or '' libbig.show_url(urlparse.urljoin(globals.get_baseurl(), "applications%s" % (subquery,))) # more-info with None just means hide window self.emit("more-info", None) def __sync(self): thing = self.__get_section_name() if self.__apps: if self.__search: if self.__have_search_hits: self.__left_title.set_property('markup', "Searching for <b>%s</b>" % (gobject.markup_escape_text(self.__search),)) else: self.__left_title.set_property('markup', "No results for <b>%s</b>" % (gobject.markup_escape_text(self.__search),)) else: self.__left_title.set_property('text', "New Popular %s" % (thing,)) self.__right_title.set_property('text', u"More Popular %s" % (thing,)) elif self.__apps is None: self.__left_title.set_property('text', '') self.__right_title.set_property("text", "Loading popular %s..." % (thing,)) else: self.__left_title.set_property('text', '') self.__right_title.set_property("text", "No popular %s found" % (thing,)) self.__app_pair.clear() self.__app_pair2.clear() self.set_child_visible(self.__app_pair, not not self.__apps) found = 0 for i,app in enumerate(self.__apps or []): if app.is_installed(): continue if self.__search_filter and (not self.__search_filter(app)): continue app_view = apps_widgets.AppDisplay(apps_widgets.AppLocation.APP_BROWSER, app, color=0x3F3F3FFF) app_view.connect("title-clicked", self.__on_app_clicked) app_view.set_description_mode(True) if found > 1: self.__app_pair2.append(app_view, hippo.PACK_EXPAND) else: self.__app_pair.append(app_view, hippo.PACK_EXPAND) found += 1 if found > 3: break self.set_child_visible(self.__app_pair, found > 0) self.set_child_visible(self.__app_pair2, found > 2 and (not not self.__search)) self.__found_app_count = found def __on_app_clicked(self, app_view): self.emit("more-info", app_view.get_app())
class AppDisplay(PhotoContentItem): __gsignals__ = { "title-clicked" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__(self, app=None, **kwargs): PhotoContentItem.__init__(self, border_right=6, **kwargs) self.__app = None self.__description_mode = False self._logger = logging.getLogger('bigboard.AppDisplay') self.__photo = CanvasMugshotURLImage(scale_width=30, scale_height=30) self.set_photo(self.__photo) self.__box = CanvasVBox(spacing=2, border_right=4) sub_kwargs = {} if kwargs.has_key('color'): sub_kwargs['color'] = kwargs['color'] self.__title = ActionLink(font="14px",xalign=hippo.ALIGNMENT_START, size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END, **sub_kwargs) self.__title.connect("activated", lambda t: self.emit("title-clicked")) self.__subtitle = hippo.CanvasText(font="10px",xalign=hippo.ALIGNMENT_START, size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END) attrs = pango.AttrList() attrs.insert(pango.AttrForeground(0x6666, 0x6666, 0x6666, 0, 0xFFFF)) self.__subtitle.set_property("attributes", attrs) self.__box.append(self.__title) self.__box.append(self.__subtitle) self.set_child(self.__box) self.__description = hippo.CanvasText(size_mode=hippo.CANVAS_SIZE_WRAP_WORD, **sub_kwargs) self.__box.append(self.__description) if app: self.set_app(app) def set_description_mode(self, mode): self.__description_mode = mode self.__app_display_sync() def get_app(self): return self.__app def set_app(self, app): self.__app = app self.__app.connect("changed", lambda app: self.__app_display_sync()) self.__app_display_sync() def __get_name(self): if self.__app is None: return "unknown" return self.__app.get_name() def __str__(self): return '<AppDisplay name="%s">' % (self.__get_name()) # override def do_prelight(self): return self.__app.is_installed() def __app_display_sync(self): if not self.__app: return self.__box.set_child_visible(self.__subtitle, not self.__description_mode) self.__box.set_child_visible(self.__description, self.__description_mode) self.__photo.set_clickable(self.__app.is_installed()) self.__box.set_clickable(self.__app.is_installed()) self.__title.set_property("text", self.__app.get_name()) self.__subtitle.set_property("text", self.__app.get_generic_name() or self.__app.get_tooltip() or self.__app.get_comment()) self.__description.set_property("text", self.__app.get_description()) if self.__app.get_mugshot_app(): self.__photo.set_url(self.__app.get_mugshot_app().get_icon_url()) else: pixbuf = self.__app.get_local_pixbuf() if pixbuf: self.__photo.set_property("image", hippo.cairo_surface_from_gdk_pixbuf(pixbuf)) def launch(self): self._logger.debug("launching app %s", self) self.__app.launch()
class AppDisplay(PhotoContentItem): __gsignals__ = { "title-clicked" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__(self, app_location, app=None, **kwargs): if app_location == AppLocation.STOCK: kwargs['enable_theme'] = True PhotoContentItem.__init__(self, border_right=6, **kwargs) self.__app = None self.__description_mode = False self._logger = logging.getLogger('bigboard.AppDisplay') self.__photo = CanvasMugshotURLImage(scale_width=30, scale_height=30) self.set_photo(self.__photo) self.__box = CanvasVBox(spacing=2, border_right=4) sub_kwargs = {} if kwargs.has_key('color'): sub_kwargs['color'] = kwargs['color'] title_kwargs = dict(sub_kwargs) title_kwargs.update({'font': '14px', 'xalign': hippo.ALIGNMENT_START, 'size-mode': hippo.CANVAS_SIZE_ELLIPSIZE_END }) if app_location == AppLocation.STOCK: self.__title = ThemedLink(**title_kwargs) else: self.__title = ActionLink(**title_kwargs) self.__title.connect("activated", lambda t: self.emit("title-clicked")) subtitle_kwargs = {'font': '10px', 'xalign': hippo.ALIGNMENT_START, 'size-mode': hippo.CANVAS_SIZE_ELLIPSIZE_END } if app_location == AppLocation.STOCK: self.__subtitle = ThemedText(theme_hints=['subforeground'], **subtitle_kwargs) else: self.__subtitle = hippo.CanvasText(**subtitle_kwargs) self.__box.append(self.__title) self.__box.append(self.__subtitle) self.set_child(self.__box) self.__description = hippo.CanvasText(size_mode=hippo.CANVAS_SIZE_WRAP_WORD, **sub_kwargs) self.__box.append(self.__description) self.__photo.set_clickable(True) self.__box.set_clickable(True) self.__app_location = app_location if app: self.set_app(app) def set_description_mode(self, mode): self.__description_mode = mode self.__app_display_sync() def get_app(self): return self.__app def set_app(self, app): self.__app = app self.__app_display_sync() def __get_name(self): if self.__app is None: return "unknown" return self.__app.get_name() def __str__(self): return '<AppDisplay name="%s">' % (self.__get_name()) def __app_display_sync(self): if not self.__app: return self.__box.set_child_visible(self.__subtitle, not self.__description_mode) self.__box.set_child_visible(self.__description, self.__description_mode) self.__title.set_property("text", self.__app.get_name()) if self.__app.is_installed() or self.__app_location == AppLocation.DESCRIPTION_HEADER: self.__subtitle.set_property("text", self.__app.get_generic_name() or self.__app.get_tooltip() or self.__app.get_comment()) ## for now, install won't work if not connected elif self.__app_location == AppLocation.STOCK and globals.get_data_model().ready and globals.get_data_model().global_resource.online: self.__subtitle.set_property('text', "(Click to Install)") else: self.__subtitle.set_property('text', "(Not Installed)") self.__description.set_property("text", self.__app.get_description()) if self.__app.get_icon_url(): self.__photo.set_url(self.__app.get_icon_url()) else: pixbuf = self.__app.get_local_pixbuf() if pixbuf: self.__photo.set_property("image", hippo.cairo_surface_from_gdk_pixbuf(pixbuf)) def launch(self): self._logger.debug("launching app %s", self) self.__app.launch()
class AppsStock(bigboard.stock.AbstractMugshotStock): STATIC_SET_SIZE = 4 DYNAMIC_SET_SIZE = 0 STATIFICATION_TIME_SEC = 60 * 60 #* 24 * 3; # 3 days __gsignals__ = { } def __init__(self, *args, **kwargs): super(AppsStock, self).__init__(*args, **kwargs) search.enable_search_provider('apps') self.__box = CanvasVBox(spacing=3) self.__message = hippo.CanvasText() self.__message_link = ActionLink() self.__message_link.connect("button-press-event", lambda link, event: self.__on_message_link()) self.__message_link_url = None self.__subtitle = hippo.CanvasText(font="Bold 12px") self.__static_set = CanvasVBox() self.__dynamic_set = CanvasVBox() self.__box.append(self.__message) self.__box.append(self.__message_link) self.__box.append(self.__subtitle) self.__box.append(self.__static_set) self.__box.append(self.__dynamic_set) self.__box.set_child_visible(self.__dynamic_set, False) self.__app_browser = None self._add_more_button(self.__on_more_button) self.__static_set_ids = {} gconf.client_get_default().notify_add(GCONF_KEY_APP_SIZE, self.__on_app_size_changed) self.__set_message('Loading...') self.__repo = apps.get_apps_repo() self.__repo.connect('enabled-changed', self.__on_usage_enabled_changed) self.__repo.connect('all-apps-loaded', self.__on_all_apps_loaded) self.__repo.connect('my-pinned-apps-changed', self.__on_my_pinned_apps_changed) self.__repo.connect('my-top-apps-changed', self.__on_my_top_apps_changed) self.__repo.connect('global-top-apps-changed', self.__on_global_top_apps_changed) self.__repo.connect('app-launched', self.__on_app_launched) self.__sync() def _on_ready(self): # When we disconnect from the server we freeze existing content, then on reconnect # we clear everything and start over. _logger.debug("Connected to data model") def __on_query_error(self, where, error_code, message): _logger.warn("Query '" + where + "' failed, code " + str(error_code) + " message: " + str(message)) def __on_usage_enabled_changed(self, repo): _logger.debug("usage enabled changed") self.__sync() def __on_all_apps_loaded(self, repo): _logger.debug("all apps are loaded") self.__sync() def __on_my_pinned_apps_changed(self, repo, pinned_apps): _logger.debug("Pinned apps changed from apps repo: " + str(pinned_apps)) self.__sync() def __on_my_top_apps_changed(self, repo, my_top_apps): _logger.debug("My top apps changed from apps repo: " + str(my_top_apps)) self.__sync() def __on_global_top_apps_changed(self, repo, global_top_apps): _logger.debug("Global top apps changed from apps repo: " + str(global_top_apps)) self.__sync() def __on_app_size_changed(self, *args): _logger.debug("app size changed") self.__sync() def __on_app_launched(self, repo, app): self._panel.action_taken() def __on_more_button(self): _logger.debug("more!") if self.__app_browser is None: self.__app_browser = appbrowser.AppBrowser() if self.__app_browser.get_property('is-active'): self.__app_browser.hide() else: self.__app_browser.present() def __on_message_link(self): libbig.show_url(self.__message_link_url) def __set_message(self, text, link=None): self.__box.set_child_visible(self.__message, (not text is None) and link is None) self.__box.set_child_visible(self.__message_link, not (text is None or link is None)) if text: self.__message.set_property("text", text) self.__message_link.set_property("text", text) if link: self.__message_link_url = link def __set_subtitle(self, text): self.__box.set_child_visible(self.__subtitle, not text is None) if text: self.__subtitle.set_property("text", text) def get_authed_content(self, size): return self.__box def get_unauthed_content(self, size): return self.__box def __set_item_size(self, item, size): if size == bigboard.stock.Stock.SIZE_BULL: item.set_property('xalign', hippo.ALIGNMENT_FILL) else: item.set_property('xalign', hippo.ALIGNMENT_CENTER) item.set_size(size) def set_size(self, size): super(AppsStock, self).set_size(size) for child in self.__static_set.get_children() + self.__dynamic_set.get_children(): self.__set_item_size(child, size) def __fill_static_set(self): self.__static_set.remove_all() self.__static_set_ids = {} usage = self.__repo.get_app_usage_enabled() pinned_apps = self.__repo.get_pinned_apps() global_top_apps = self.__repo.get_global_top_apps() local_apps = self.__repo.get_local_apps() static_size = gconf.client_get_default().get_int(GCONF_KEY_APP_SIZE) or 7 self.__set_subtitle(None) apps_in_set = [] using_local_apps = False if usage: apps_in_set = pinned_apps if len(apps_in_set) == 0: if len(global_top_apps) > 0: apps_in_set = global_top_apps self.__set_subtitle("Popular Applications") elif len(local_apps) > 0: apps_in_set = local_apps using_local_apps = True if using_local_apps: apps_in_set = filter(lambda a: POPULAR_APPS.count(a.get_app_name_from_file_name()) > 0, apps_in_set) apps_in_set.sort(lambda a, b: cmp(POPULAR_APPS.index(a.get_app_name_from_file_name()), POPULAR_APPS.index(b.get_app_name_from_file_name()))) else: ## note the "-" in front of the cmp to sort descending apps_in_set.sort(lambda a, b: - cmp(a.get_usage_count(), b.get_usage_count())) for i, app in enumerate(apps_in_set): if i >= static_size: break # don't display apps that are not installed if the user is not logged in if not self._model.self_resource and not app.is_installed(): continue display = apps_widgets.AppDisplay(apps_widgets.AppLocation.STOCK, app) display.connect("button-press-event", lambda display, event: display.launch()) #_logger.debug("setting static set app: %s", app) self.__static_set.append(display) self.__static_set_ids[app.get_id()] = True @defer_idle_func(logger=_logger) def __sync(self): #_logger.debug("doing sync") self.__set_message(None) self.__box.set_child_visible(self.__dynamic_set, False) usage = self.__repo.get_app_usage_enabled() #_logger.debug("usage: %s", usage) if usage is False and self._model.ready and self._model.global_resource.online: self.__set_message("Enable application tracking", globals.get_baseurl() + "/account") self.__fill_static_set() self.__repo.pin_stuff_if_we_have_none()