diff --git a/spritz/gfx/empty.png b/spritz/gfx/empty.png new file mode 100644 index 000000000..e0e257da2 Binary files /dev/null and b/spritz/gfx/empty.png differ diff --git a/spritz/src/dialogs.py b/spritz/src/dialogs.py index 7a5752003..b43179d23 100644 --- a/spritz/src/dialogs.py +++ b/spritz/src/dialogs.py @@ -31,10 +31,13 @@ class PkgInfoMenu: def __init__(self, Entropy, pkg, window): - self.pkg_pixmap = const.PIXMAPS_PATH+'/package-x-generic.png' - self.ugc_small_pixmap = const.PIXMAPS_PATH+'/ugc.png' - self.ugc_pixmap = const.PIXMAPS_PATH+'/ugc/icon.png' - self.refresh_pixmap = const.PIXMAPS_PATH+'/ugc/refresh.png' + self.pkg_pixmap = const.pkg_pixmap + self.ugc_small_pixmap = const.ugc_small_pixmap + self.ugc_pixmap = const.ugc_pixmap + self.refresh_pixmap = const.refresh_pixmap + self.star_normal_pixmap = const.star_normal_pixmap + self.star_selected_pixmap = const.star_selected_pixmap + self.star_empty_pixmap = const.star_empty_pixmap self.pkg = pkg self.vote = 0 @@ -47,6 +50,8 @@ class PkgInfoMenu: self.pkginfo_ui = UI( const.GLADE_FILE, 'pkgInfo', 'entropy' ) self.pkginfo_ui.signal_autoconnect(self._getAllMethods()) self.pkginfo_ui.pkgInfo.set_transient_for(self.window) + self.pkginfo_ui.pkgInfo.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.pkginfo_ui.pkgInfo.connect('button-press-event', self.on_button_press) self.setupPkgPropertiesView() def _getAllMethods(self): @@ -81,6 +86,13 @@ class PkgInfoMenu: except gobject.GError: pass + def on_button_press(self, widget, event): + self.pkginfo_ui.pkgInfo.begin_move_drag( + event.button, + int(event.x_root), + int(event.y_root), + event.time) + def on_showContentButton_clicked( self, widget ): content = self.pkg.contentExt for x in content: @@ -309,10 +321,10 @@ class PkgInfoMenu: def set_stars(self, count, hover = False): - pix_path = const.PIXMAPS_PATH+'/star.png' - if hover: - pix_path = const.PIXMAPS_PATH+'/star_selected.png' - pix_path_empty = const.PIXMAPS_PATH+'/star_empty.png' + pix_path = self.star_normal_pixmap + if hover: pix_path = self.star_selected_pixmap + pix_path_empty = self.star_empty_pixmap + widgets = [ self.pkginfo_ui.vote1, self.pkginfo_ui.vote2, @@ -1340,7 +1352,7 @@ class MessageDialog: defaultchoice = 0 dialog.set_title(title) - dialog.format_secondary_markup(cleanMarkupString(text)) + #dialog.format_secondary_markup(cleanMarkupString(text)) dialog.set_position (gtk.WIN_POS_CENTER) dialog.set_default_response(defaultchoice) dialog.show_all () diff --git a/spritz/src/etpgui/packages.py b/spritz/src/etpgui/packages.py index 5c3b76f0d..9a17d451c 100644 --- a/spritz/src/etpgui/packages.py +++ b/spritz/src/etpgui/packages.py @@ -29,6 +29,9 @@ class DummyEntropyPackage: self.namedesc = namedesc self.queued = None self.repoid = '' + self.name = '' + self.vote = -1 + self.voted = 0 self.color = None self.action = None self.dbconn = None @@ -47,6 +50,7 @@ class EntropyPackage: self.available = avail self.do_purge = False self.masked = None + self.voted = 0 self.color = SpritzConf.color_normal if matched_atom[1] == 0: @@ -91,15 +95,8 @@ class EntropyPackage: else: repo = self.matched_atom[1] key = self.entropyTools.dep_getkey(atom) - vote = EquoConnection.UGC.UGCCache.get_package_vote(repo,key) - ugc_string = "[" - if isinstance(vote,float): - vote = int(vote) - vote_stars = "★"*vote - vote_spaces = "☆"*(5-vote) - ugc_string += vote_stars+vote_spaces downloads = EquoConnection.UGC.UGCCache.get_package_downloads(repo,key) - ugc_string += ":%s] " % (downloads,) + ugc_string = "[%s] " % (downloads,) t = ugc_string+'/'.join(atom.split("/")[1:]) if self.masked: @@ -277,6 +274,9 @@ class EntropyPackage: if not atom: return None return EquoConnection.UGC.UGCCache.get_package_vote(self.matched_atom[1],self.entropyTools.dep_getkey(atom)) + def getUGCPackageVoted(self): + return self.voted + def getUGCPackageDownloads(self): if self.from_installed: return False atom = self.getName() @@ -380,4 +380,5 @@ class EntropyPackage: syspkg = property(fget=getSysPkg) install_status = property(fget=getInstallStatus) vote = property(fget=getUGCPackageVote) + voted = property(fget=getUGCPackageVoted) downloads = property(fget=getUGCPackageDownloads) diff --git a/spritz/src/etpgui/widgets.py b/spritz/src/etpgui/widgets.py index e7e0601c0..6f9561107 100644 --- a/spritz/src/etpgui/widgets.py +++ b/spritz/src/etpgui/widgets.py @@ -26,6 +26,220 @@ import sys, os from spritz_setup import const import vte +def hex2float(myhex): + ret = [] + for i in range(4): + ic = int(myhex[i]) + ret.append( ic / 255.0 ) + return ret + +class TransparentWindow(gtk.Window): + __gsignals__ = { + 'expose-event': 'override', + 'screen-changed': 'override', + } + + def __init__(self): + import cairo + self.cairo = cairo + gtk.Window.__init__(self) + self.set_app_paintable(True) + self.set_decorated(False) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.connect('button-press-event', self.on_button_press) + self.do_screen_changed() + + def on_button_press(self, widget, event): + self.begin_move_drag( + event.button, + int(event.x_root), + int(event.y_root), + event.time) + + def render_rect(self, cr, x, y, w, h, o): + # Crea un rettangolo con i bordi arrotondati + x0 = x + y0 = y + rect_width = w + rect_height = h + radius = 10 + o + + x1 = x0 + rect_width + y1 = y0 + rect_height + cr.move_to(x0, y0 + radius) + cr.curve_to(x0, y0, x0, y0, x0 + radius, y0) + cr.line_to(x1 - radius, y0) + cr.curve_to(x1, y0, x1, y0, x1, y0 + radius) + cr.line_to(x1 , y1) + cr.line_to (x0 , y1) + cr.close_path() + + def do_expose_event(self, event): + cr = self.window.cairo_create() + + if self.supports_alpha: + cr.set_source_rgba(1.0, 1.0, 1.0, 0.0) + else: + cr.set_source_rgb(1.0, 1.0, 1.0) + + cr.set_operator(self.cairo.OPERATOR_SOURCE) + cr.paint() + + + (width, height) = self.get_size() + cr.move_to(0, 0) + cr.set_line_width(1.0) + + cr.set_operator(self.cairo.OPERATOR_OVER) + + pat = self.cairo.LinearGradient(0.0, 0.0, 0.0, height) + + ex_list = [0xA1, 0xA8, 0xBB, 0xEC] + col = hex2float(ex_list) + pat.add_color_stop_rgba(0.0, col[0], col[1], col[2], col[3]) + + ex_list = [0x14, 0x1E, 0x3C, 0xF3] + col = hex2float(ex_list) + pat.add_color_stop_rgba(1.0, col[0], col[1], col[2], col[3]) + + self.render_rect(cr, 0, 0, width, height, 10) + cr.set_source(pat) + cr.fill() + + ex_list = [0xFF, 0xFF, 0xFF, 0x4e] + col = hex2float(ex_list) + cr.set_source_rgba(col[0], col[1], col[2], col[3]) + self.render_rect(cr, 1.5, 1.5, width - 3 , height - 3, 10) + cr.stroke() + + # border + ex_list = [0x00, 0x15, 0x1F, 0xe0] + col = hex2float(ex_list) + cr.set_source_rgba(col[0], col[1], col[2], col[3]) + self.render_rect(cr, 0.5, 0.5, width - 1 , height - 1, 10) + cr.stroke() + + ex_list = [0xFF, 0xFF, 0xFF, 0xFF] + col = hex2float(ex_list) + cr.set_source_rgba(col[0], col[1], col[2], col[3]) + self.render_rect(cr, 0, 0, width , height, 10) + cr.stroke() + + pat = self.cairo.LinearGradient(0.0, 0.0, 0.0, height) + cr.set_source(pat) + ex_list = [0xFF, 0xFF, 0xFF, 0xbb] + col = hex2float(ex_list) + pat.add_color_stop_rgba(0.0, col[0], col[1], col[2], col[3]) + + ex_list = [0x00, 0x00, 0x10, 0xaa] + col = hex2float(ex_list) + pat.add_color_stop_rgba(0.2, col[0], col[1], col[2], col[3]) + self.render_rect(cr, 0, 0, width, 20, 10) + cr.fill() + + children = self.get_children() + for c in children: self.propagate_expose(c, event) + + def do_screen_changed(self, old_screen=None): + screen = self.get_screen() + if self.is_composited(): + colormap = screen.get_rgba_colormap() + self.supports_alpha = True + else: + # no alpha support + colormap = screen.get_rgb_colormap() + self.supports_alpha = False + self.set_colormap(colormap) + +class CellRendererStars(gtk.GenericCellRenderer): + __gproperties__ = { + "custom": (gobject.TYPE_OBJECT, "Custom", + "Custom", gobject.PARAM_READWRITE), + } + + def __init__(self): + self.__gobject_init__() + self.value = -1 + self.value_voted = 0 + + def do_set_property(self, pspec, value): + setattr(self, pspec.name, value) + + def do_get_property(self, pspec): + return getattr(self, pspec.name) + + def on_render(self, window, widget, background_area, cell_area, expose_area, flags): + (x_offset, y_offset, width, height) = self.on_get_size(widget, cell_area) + widget.style.paint_box(window, + gtk.STATE_NORMAL, + gtk.SHADOW_IN, + None, widget, "trough", + cell_area.x+x_offset, + cell_area.y+y_offset, + width, height) + if ((self.value > -1) and (self.value < 6)) or (self.value_voted > 0): + + xt = widget.style.xthickness + empty = gtk.Image() + empty.set_from_file(const.empty_background) + empty_buf = empty.get_pixbuf() + + if self.value_voted: + star = gtk.Image() + star.set_from_file(const.star_selected_pixmap) + else: + star = gtk.Image() + star.set_from_file(const.star_normal_pixmap) + + star_empty = gtk.Image() + star_empty.set_from_file(const.star_empty_pixmap) + + star_buf = star.get_pixbuf() + star_empty_buf = star_empty.get_pixbuf() + + w, h = star_buf.get_width(),star_buf.get_height() + myval = self.value + if self.value_voted: + myval = self.value_voted + empty_buf = empty_buf.scale_simple(w*5,h+12,gtk.gdk.INTERP_BILINEAR) + myvals = [0,w,w*2,w*3,w*4] + cnt = 0 + while myval: + star_buf.copy_area(0, 0, w, h, empty_buf, myvals[cnt], 6) + myval -= 1 + cnt += 1 + myval = 5 - cnt + while myval: + star_empty_buf.copy_area(0, 0, w, h, empty_buf, myvals[cnt], 6) + myval -= 1 + cnt += 1 + + if empty_buf: window.draw_pixbuf(None, empty_buf, 0, 0, cell_area.x+x_offset+xt, cell_area.y+y_offset+xt, -1, -1) + + + def on_get_size(self, widget, cell_area): + xpad = self.get_property("xpad") + ypad = self.get_property("ypad") + if cell_area: + width = cell_area.width + height = cell_area.height + x_offset = xpad + y_offset = ypad + else: + width = self.get_property("width") + height = self.get_property("height") + if width == -1: width = 100 + if height == -1: height = 30 + width += xpad*2 + height += ypad*2 + x_offset = 0 + y_offset = 0 + return x_offset, y_offset, width, height + + +gobject.type_register(CellRendererStars) + + class SpritzConsole(vte.Terminal): def __init__(self, settings): diff --git a/spritz/src/spritz.glade b/spritz/src/spritz.glade index 66065d317..e7de24d55 100644 --- a/spritz/src/spritz.glade +++ b/spritz/src/spritz.glade @@ -807,7 +807,16 @@ - + + True + 0.99000000953674316 + 1 + <small>Powered by User Generated Content</small> + True + + + 1 + @@ -954,7 +963,7 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - Hello world + True False @@ -4445,10 +4454,10 @@ gz GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 8 Package Information - True - GTK_WIN_POS_CENTER_ON_PARENT + GTK_WIN_POS_CENTER_ALWAYS + 600 + False @@ -4648,6 +4657,35 @@ gz 1 + + + True + 1 + 0 + 0 + 0 + 10 + + + True + True + True + 0 + + + + + True + gtk-close + + + + + + + 2 + + False @@ -5849,31 +5887,6 @@ gz 1 - - - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - GTK_BUTTONBOX_END - - - True - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-close - True - 0 - - - - - - - False - False - 7 - 2 - - diff --git a/spritz/src/spritz.py b/spritz/src/spritz.py index 9459c6839..a94df4d70 100644 --- a/spritz/src/spritz.py +++ b/spritz/src/spritz.py @@ -1071,6 +1071,7 @@ class SpritzApplication(SpritzController,SpritzGUI): # init flags self.ugcTask = None + self.spawning_ugc = False self.Preferences = None self.skipMirrorNow = False self.abortQueueNow = False @@ -1117,29 +1118,33 @@ class SpritzApplication(SpritzController,SpritzGUI): fn = packages_install[0] self.on_installPackageItem_activate(None,fn) - def setupUgc(self): self.ugcTask = entropyTools.TimeScheduled(self.spawnUgcUpdate, 120) self.ugcTask.start() def spawnUgcUpdate(self): + try: + while (self.spawning_ugc or self.isWorking): + time.sleep(1) - if self.isWorking: return - self.isWorking = True - gtkEventThread.startProcessing() - connected = entropyTools.get_remote_data(etpConst['conntestlink']) - if (connected == False) or (self.Equo.UGC == None): - self.isWorking = False - gtkEventThread.endProcessing() - return - for repo in self.Equo.validRepositories: - try: + self.isWorking = True + self.spawning_ugc = True + gtkEventThread.startProcessing() + connected = entropyTools.get_remote_data(etpConst['conntestlink']) + if (connected == False) or (self.Equo.UGC == None): + self.isWorking = False + self.spawning_ugc = False + gtkEventThread.endProcessing() + return + for repo in self.Equo.validRepositories: self.Equo.update_ugc_cache(repo) - except: - pass - self.isWorking = False - gtkEventThread.endProcessing() + self.isWorking = False + self.spawning_ugc = False + gtkEventThread.endProcessing() + + except: + pass def setupPreferences(self): @@ -1526,6 +1531,9 @@ class SpritzApplication(SpritzController,SpritzGUI): self.progress.set_mainLabel(_('Errors updating repositories.')) self.progress.set_subLabel(_('Please check logs below for more info')) else: + t = entropyTools.parallelTask(self.spawnUgcUpdate) + t.parallel_wait() + t.start() if repoConn.alreadyUpdated == 0: self.progress.set_mainLabel(_('Repositories updated successfully')) else: diff --git a/spritz/src/spritz_setup.py b/spritz/src/spritz_setup.py index 8bc2694c6..e712084c4 100644 --- a/spritz/src/spritz_setup.py +++ b/spritz/src/spritz_setup.py @@ -39,6 +39,15 @@ class const: else: ICONS_PATH = MAIN_PATH+'/pixmaps' + pkg_pixmap = PIXMAPS_PATH+'/package-x-generic.png' + ugc_small_pixmap = PIXMAPS_PATH+'/ugc.png' + ugc_pixmap = PIXMAPS_PATH+'/ugc/icon.png' + refresh_pixmap = PIXMAPS_PATH+'/ugc/refresh.png' + star_normal_pixmap = PIXMAPS_PATH+'/star.png' + star_selected_pixmap = PIXMAPS_PATH+'/star_selected.png' + star_empty_pixmap = PIXMAPS_PATH+'/star_empty.png' + empty_background = PIXMAPS_PATH+'/empty.png' + # package categories PACKAGE_CATEGORIES = [ "None", diff --git a/spritz/src/views.py b/spritz/src/views.py index e9db6e18d..f80d83794 100644 --- a/spritz/src/views.py +++ b/spritz/src/views.py @@ -20,7 +20,7 @@ import gtk import gobject from spritz_setup import const, cleanMarkupString, SpritzConf -from etpgui.widgets import UI +from etpgui.widgets import UI,CellRendererStars from packages import DummyEntropyPackage from entropyapi import EquoConnection from etpgui import * @@ -62,6 +62,7 @@ class EntropyPackageView: self.Equo = EquoConnection self.pkgcolumn_text = _("Selection") + self.stars_col_size = 100 self.selection_width = 34 self.show_reinstall = True self.show_purge = True @@ -70,6 +71,10 @@ class EntropyPackageView: self.loaded_event = None self.main_window = main_window self.event_click_pos = 0,0 + # UGC pixmaps + self.star_normal_pixmap = const.star_normal_pixmap + self.star_selected_pixmap = const.star_selected_pixmap + self.star_empty_pixmap = const.star_empty_pixmap # default for installed packages self.pkg_install_ok = "package-installed-updated.png" self.pkg_install_updatable = "package-installed-outdated.png" @@ -115,6 +120,8 @@ class EntropyPackageView: treeview.set_fixed_height_mode(True) self.view = treeview self.view.connect("button-release-event", self.load_menu) + self.view.connect("enter-notify-event",self.treeview_enter_notify) + self.view.connect("leave-notify-event",self.treeview_leave_notify) #self.view.connect("button-press-event", self.load_properties_button) self.store = self.setupView() self.queue = qview.queue @@ -160,6 +167,12 @@ class EntropyPackageView: self.install_install.set_image(self.img_pkg_install) self.updates_undoupdate.set_image(self.img_pkg_undoinstall) + def treeview_enter_notify(self, widget, event): + self.main_window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.CROSSHAIR)) + + def treeview_leave_notify(self, widget, event): + self.main_window.window.set_cursor(None) + def reset_install_menu(self): self.install_install.show() self.install_undoinstall.hide() @@ -223,25 +236,33 @@ class EntropyPackageView: else: self.enable_properties_menu(None) - if event.x < 10: + event_x = event.x + if event_x < 10: return try: - row, column, x, y = widget.get_path_at_pos(int(event.x),int(event.y)) + row, column, x, y = widget.get_path_at_pos(int(event_x),int(event.y)) except TypeError: return self.event_click_pos = x,y - if column.get_title() != self.pkgcolumn_text: - return - - if obj: - if obj.action in ["r","rr"]: # installed packages listing - self.run_installed_menu_stuff(obj) - elif obj.action in ["u"]: # updatable packages listing - self.run_updates_menu_stuff(obj) - elif obj.action in ["i"]: - self.run_install_menu_stuff(obj) + if column.get_title() == self.pkgcolumn_text: + if obj: + if obj.action in ["r","rr"]: # installed packages listing + self.run_installed_menu_stuff(obj) + elif obj.action in ["u"]: # updatable packages listing + self.run_updates_menu_stuff(obj) + elif obj.action in ["i"]: + self.run_install_menu_stuff(obj) + else: + distance = 0 + for col in self.view.get_columns()[0:2]: + distance += col.get_width() + if (event_x > distance) and (event_x < (distance+self.stars_col_size)): + vote = int((event_x - distance)/self.stars_col_size*5)+1 + obj.voted = vote + # submit vote + self.spawn_vote_submit(obj) def reposition_menu(self, menu): # devo tradurre x=0,y=20 in posizioni assolute @@ -438,7 +459,7 @@ class EntropyPackageView: self.view.set_model( store ) myheight = 35 - # Setup resent column + # selection pixmap cell1 = gtk.CellRendererPixbuf() cell1.set_property('height', myheight) self.set_pixbuf_to_cell(cell1, self.pkg_install_ok ) @@ -451,10 +472,72 @@ class EntropyPackageView: column1.set_clickable( False ) self.create_text_column( _( "Package" ), 'namedesc' , size=300, expand = True, set_height = myheight) + + # vote event box + cell2 = CellRendererStars() + cell2.set_property('height', myheight) + column2 = gtk.TreeViewColumn( _("Rating"), cell2 ) + column2.set_resizable( True ) + column2.set_cell_data_func( cell2, self.get_stars_rating ) + column2.set_sizing( gtk.TREE_VIEW_COLUMN_FIXED ) + column2.set_fixed_width( self.stars_col_size ) + column2.set_expand(False) + column2.set_sort_column_id( -1 ) + self.view.append_column( column2 ) + self.create_text_column( _( "Repository" ), 'repoid', size = 130, set_height = myheight) return store + def get_stars_rating( self, column, cell, model, myiter ): + obj = model.get_value( myiter, 0 ) + if not obj: return + try: + voted = getattr( obj, 'voted' ) + except self.Equo.dbapi2.ProgrammingError: + return + #print obj.name,mydata + if voted: + cell.value_voted = int(voted) + return + try: + mydata = getattr( obj, 'vote' ) + except self.Equo.dbapi2.ProgrammingError: + return + cell.value = int(mydata) + cell.value_voted = int(voted) + self.queueView.refresh() + self.view.queue_draw() + + def spawn_vote_submit(self, obj): + if self.Equo.UGC == None: + obj.voted = 0 + return + repository = obj.repoid + if not self.Equo.UGC.is_repository_eapi3_aware(repository): + obj.voted = 0 + return + atom = obj.name + key = self.Equo.entropyTools.dep_getkey(atom) + + status, err_msg = self.Equo.UGC.add_vote(repository, key, obj.voted) + if status: + msg = "%s: %s" % (_("Vote registered successfully"),obj.voted,) + else: + msg = "%s: %s" % (_("Error registering vote"),err_msg,) + + self.ui.UGCMessageLabel.set_markup(msg) + t = self.Equo.entropyTools.parallelTask(self.refresh_vote_info, obj) + t.parallel_wait() + t.start() + + + def refresh_vote_info(self, obj): + time.sleep(5) + obj.voted = 0 + self.queueView.refresh() + self.view.queue_draw() + def clear(self): self.store.clear() @@ -511,8 +594,6 @@ class EntropyPackageView: self.view.expand_all() - - def atom_search(self, model, column, key, iterator): obj = model.get_value( iterator, 0 ) if obj: