/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to avatarsbox.py

  • Committer: Jelmer Vernooij
  • Date: 2012-07-09 15:23:26 UTC
  • mto: This revision was merged to the branch mainline in revision 794.
  • Revision ID: jelmer@samba.org-20120709152326-dzxb8zoz0btull7n
Remove bzr-notify.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
import gtk
 
17
from gi.repository import Gtk
 
18
from gi.repository import GdkPixbuf
18
19
 
19
20
from bzrlib.config import parse_username
20
21
 
21
 
from bzrlib.plugins.gtk import _i18n
22
 
from bzrlib.plugins.gtk.avatarproviders import AvatarProviderGravatar, AvatarDownloaderWorker
23
 
 
24
 
 
25
 
class Avatar(gtk.Box):
 
22
from bzrlib.plugins.gtk.i18n import _i18n
 
23
from bzrlib.plugins.gtk.avatarproviders import (
 
24
    AvatarProviderGravatar,
 
25
    AvatarDownloaderWorker,
 
26
    )
 
27
 
 
28
 
 
29
class Avatar(Gtk.HBox):
26
30
    """ Author or committer avatar """
27
 
    
 
31
 
28
32
    def __init__(self, apparent_username):
29
33
        """ Constructor """
30
 
        gtk.Box.__init__(self)
31
 
        
 
34
        super(Avatar, self).__init__()
 
35
 
32
36
        self.apparent_username = apparent_username
33
37
        self.username, self.email = parse_username(apparent_username)
34
38
        self.image = None
35
 
    
 
39
 
36
40
    def __eq__(self, other):
37
 
        """
38
 
        Return True if attributes of the given avatar
39
 
        match to current object attributes otherwise return False
40
 
        """
41
 
        return self.apparent_username == other.apparent_username and \
42
 
               self.name == other.name and \
43
 
               self.email == other.email
44
 
    
45
 
    # ~~~~~ Public methods ~~~~~
 
41
        return (self.apparent_username == other.apparent_username and
 
42
                self.username == other.username and
 
43
                self.email == other.email)
 
44
 
46
45
    def show_spinner(self):
47
46
        """
48
 
        Replace the current content of the Avatar with a gtk.Spinner
49
 
        if an email address has been parsed. If not, show an gtk.Label with
 
47
        Replace the current content of the Avatar with a Gtk.Spinner
 
48
        if an email address has been parsed. If not, show an Gtk.Label with
50
49
        the translatable 'No email' text.
51
50
        """
52
 
        if not self.email is "":
53
 
            spinner = gtk.Spinner()
 
51
        if self.email:
 
52
            tooltip = _i18n("Retrieving avatar for %s...") % self.email
 
53
            spinner = Gtk.Spinner()
54
54
            spinner.start()
55
 
            self.pack_start(spinner, False)
56
 
            spinner.set_tooltip_text(_i18n("Retrieving avatar for %s...") % self.email)
 
55
            self.pack_start(spinner, False, True, 0)
 
56
            spinner.set_tooltip_text(tooltip)
57
57
            spinner.set_size_request(20, 20)
58
58
            spinner.show()
59
59
        else:
60
 
            no_email = gtk.Label(_i18n("No email"))
61
 
            self.pack_start(no_email)
 
60
            no_email = Gtk.Label(label=_i18n("No email"))
 
61
            self.pack_start(no_email, True, True, 0)
62
62
            self.set_tooltip_text(self.apparent_username)
63
63
            no_email.show()
64
 
    
 
64
 
65
65
    def show_image(self):
66
 
        """ Replace the current content of the Avatar with the gtk.Image """
67
 
        if not self.email is "" and self.image:
68
 
            self.remove(self.get_children()[0])
69
 
            self.pack_start(self.image)
 
66
        """Replace the current content of the Avatar with the Gtk.Image """
 
67
        if self.email and self.image:
 
68
            children = self.get_children()
 
69
            if children != []:
 
70
                self.remove(children[0])
 
71
            self.pack_start(self.image, True, True, 0)
70
72
            self.image.set_tooltip_text(self.apparent_username)
71
73
            self.image.show()
72
74
 
73
75
 
74
 
class AvatarBox(gtk.HBox):
75
 
    """ Improved gtk.HBox """
76
 
    
 
76
class AvatarBox(Gtk.HBox):
 
77
    """HBox showing an avatar."""
 
78
 
77
79
    def __init__(self, homogeneous=False, spacing=0):
78
 
        """ Constructor """
79
 
        gtk.HBox.__init__(self, homogeneous, spacing)
 
80
        super(AvatarBox, self).__init__(
 
81
            homogeneous=homogeneous, spacing=spacing)
80
82
        self.__avatars = {}
81
83
        self.avatar = None
82
84
        self.__displaying = None
83
 
    
84
 
    
85
 
    # ~~~~~ Public methods ~~~~~
 
85
 
86
86
    def reset_view(self):
87
 
        """ Remove current avatars from the gtk box """
 
87
        """Remove current avatars from the gtk box."""
88
88
        for child in self.get_children():
89
89
            self.remove(child)
90
90
        self.__displaying = None
91
 
    
 
91
 
92
92
    def have_avatar(self, avatar):
93
 
        """
94
 
        Return True if this box has registered given avatar,
95
 
        otherwise return False
 
93
        """Return True if this box has the specified avatar.
96
94
        """
97
95
        return avatar.email in self.__avatars
98
 
    
 
96
 
99
97
    def showing(self, avatar):
100
 
        """
101
 
        Return True if the displaying avatar is the same
102
 
        as the given one otherwise return False
 
98
        """Return True if the displaying avatar matches the specified one.
103
99
        """
104
100
        return self.__displaying and self.__displaying == avatar
105
 
    
 
101
 
106
102
    def append_avatars_with(self, avatar):
107
103
        """
108
104
        Append avatars collection with the given one if not already registed
110
106
        When an avatar is added this method True, otherwise, if the avatar
111
107
        was in the collection, return False.
112
108
        """
113
 
        if not avatar.email is "" and not avatar.email in self.__avatars:
 
109
        if avatar.email and not avatar.email in self.__avatars:
114
110
            self.__avatars[avatar.email] = avatar
115
111
            self._new_cell_for_avatar(avatar)
116
112
            return True
117
113
        else:
118
114
            self.and_avatar_email(avatar.email).come_back_to_gui()
119
115
        return False
120
 
    
 
116
 
121
117
    def and_avatar_email(self, email):
122
118
        """
123
119
        Select the avatar from avatars collection
124
120
        in order to apply future actions
125
121
        """
126
122
        self.avatar = None
127
 
        if not email is "" and email in self.__avatars:
 
123
        if email and email in self.__avatars:
128
124
            self.avatar = self.__avatars[email]
129
125
        else:
130
126
            self.avatar = None
131
127
        return self
132
 
    
 
128
 
133
129
    def update_avatar_image_from_pixbuf_loader(self, loader):
134
 
        """ Replace the gtk.Spinner with the given loader """
 
130
        """Replace the Gtk.Spinner with the given loader."""
135
131
        if self.avatar:
136
 
            self.avatar.image = gtk.Image()
 
132
            self.avatar.image = Gtk.Image()
137
133
            self.avatar.image.set_from_pixbuf(loader.get_pixbuf())
138
134
            self.avatar.show_image()
139
135
            self.__displaying = self.avatar
140
 
    
 
136
 
141
137
    def come_back_to_gui(self):
142
 
        """ Render back avatar in the GUI """
 
138
        """Render back avatar in the GUI."""
143
139
        if self.avatar:
144
 
            self.pack_start(self.avatar)
 
140
            self.pack_start(self.avatar, True, True, 0)
145
141
            self.__displaying = self.avatar
146
142
        else:
147
143
            self._show_no_email()
148
 
    
149
 
    
150
 
    # ~~~~~ Private methods ~~~~~~
 
144
 
151
145
    def _new_cell_for_avatar(self, avatar):
152
 
        """ Create a new cell in this box with a gtk.Spinner """
 
146
        """Create a new cell in this box with a Gtk.Spinner."""
153
147
        avatar.show_spinner()
154
 
        self.pack_start(avatar)
 
148
        self.pack_start(avatar, True, True, 0)
155
149
        avatar.show()
156
150
        self.__displaying = avatar
157
 
    
 
151
 
158
152
    def _show_no_email(self):
159
 
        """ Show a gtk.Label with test 'No email' """
160
 
        no_email = gtk.Label(_i18n("No email"))
161
 
        self.pack_start(no_email)
 
153
        """Show a Gtk.Label with text 'No email'."""
 
154
        no_email = Gtk.Label(label=_i18n("No email"))
 
155
        self.pack_start(no_email, True, True, 0)
162
156
        no_email.show()
163
157
 
164
158
 
165
 
class AvatarsBox(gtk.HBox):
166
 
    """ GTK container for authors and committers avatars """
167
 
    
 
159
class AvatarsBox(Gtk.HBox):
 
160
    """GTK container for author and committer avatars."""
 
161
 
168
162
    def __init__(self):
169
 
        """ Constructor """
170
 
        gtk.HBox.__init__(self, False, 10)
171
 
        
 
163
        super(AvatarsBox, self).__init__(homogeneous=False, spacing=10)
172
164
        self.__committer_box = None
173
165
        self.__authors_box = None
174
166
        self._init_gui()
175
 
        
176
 
        # If more later you want to implement more avatar providers, to it like this:
177
 
        # Create a new class named AvatarProvider + provider_name that inherit from
178
 
        # the AvatarProvider class.
179
 
        # Implement a method that return url to use in the request.
 
167
 
 
168
        # If more later you want to implement more avatar providers:
 
169
        # * Create a new class named AvatarProvider + provider_name that
 
170
        #   inherit from the AvatarProvider class.
 
171
        # * Implement a method that return url to use in the request.
 
172
 
180
173
        # For example, with Gravatar, the method return the complete url
181
 
        # with MD5 hash of the email address and put the value in a gravatar_id field.
182
 
        # Then create a new worker (manage them in a python dictionnary).
 
174
        # with MD5 hash of the email address and put the value in a
 
175
        # gravatar_id field.
 
176
        # Then create a new worker (manage them in a python dictionary).
183
177
        provider = AvatarProviderGravatar()
184
178
        self.__worker = AvatarDownloaderWorker(
185
179
            provider.gravatar_id_for_email
186
180
        )
187
 
        # This callback method should be fired bt all workers when a request is done.
 
181
        # This callback method should be fired by all workers when a request
 
182
        # is done.
188
183
        self.__worker.set_callback_method(self._update_avatar_from_response)
189
 
        self.__worker.start()
190
 
    
191
 
    
192
 
    # ~~~~~ Public methods ~~~~~
 
184
        self.connect('destroy', self.on_destroy)
 
185
 
 
186
    def on_destroy(self, widget):
 
187
        self.__worker.stop()
 
188
        if self.__worker.is_alive():
 
189
            self.__worker.join()
 
190
 
193
191
    def add(self, username, role):
194
 
        """
195
 
        Add the given username in the right role box and add in the worker queue.
196
 
        Here again: If you want to implement more providers, you should add the
197
 
        avatar request in all workers queue.
 
192
        """Add the given username in the role box and add in the worker queue.
198
193
        """
199
194
        avatar = Avatar(username)
200
 
        if role is "author" and not self._role_box_for("committer").showing(avatar) or role is "committer":
 
195
        is_shown = self._role_box_for("committer").showing(avatar)
 
196
        if (role == "author" and not is_shown) or role == "committer":
201
197
            if self._role_box_for(role).append_avatars_with(avatar):
202
198
                self.__worker.queue(avatar.email)
203
 
    
 
199
 
204
200
    def merge(self, usernames, role):
205
 
        """ Add avatars from a list """
 
201
        """Add avatars from a list"""
206
202
        for username in usernames:
207
203
            self.add(username, role)
208
 
    
 
204
 
209
205
    def reset(self):
210
206
        """
211
207
        Request a reset view for all boxes in order to show only avatars
212
208
        of the selected line in the revision view screen.
213
209
        """
214
 
        [self._role_box_for(role).reset_view() for role in ["committer", "author"]]
215
 
    
216
 
    
217
 
    # ~~~~~ Private methods ~~~~~
 
210
        for role in ("committer", "author"):
 
211
            self._role_box_for(role).reset_view()
 
212
 
218
213
    def _init_gui(self):
219
 
        """ Create boxes where avatars will be displayed """
220
 
        # 2 gtk.HBox: One for the committer and one for authors
 
214
        """Create boxes where avatars will be displayed."""
 
215
        # 2 Gtk.HBox: One for the committer and one for authors
221
216
        # Committer
222
217
        self.__committer_box = AvatarBox()
223
218
        self.__committer_box.set_size_request(80, 80)
224
 
        self.pack_end(self.__committer_box, False)
 
219
        self.pack_end(self.__committer_box, False, True, 0)
225
220
        self.__committer_box.show()
226
221
        # Authors
227
222
        self.__authors_box = AvatarBox()
228
 
        self.pack_end(self.__authors_box, False)
 
223
        self.pack_end(self.__authors_box, False, True, 0)
229
224
        self.__authors_box.set_spacing(10)
230
225
        self.__authors_box.show()
231
 
    
 
226
 
232
227
    def _update_avatar_from_response(self, response, email):
233
 
        """
234
 
        Callback method fired by avatar worker when finished.
235
 
        
236
 
        response is a urllib2.urlopen() return value.
237
 
        email is used to identify item from self.__avatars.
238
 
        """
239
 
        if not email is "":
240
 
            # Convert downloaded image from provider to gtk.Image
241
 
            loader = gtk.gdk.PixbufLoader()
 
228
        """Callback method fired by avatar worker when finished.
 
229
 
 
230
        :param response: a urllib2.urlopen() return value.
 
231
        :param email: used to identify item from self.__avatars.
 
232
        """
 
233
        if email:
 
234
            # Convert downloaded image from provider to Gtk.Image
 
235
            loader = GdkPixbuf.PixbufLoader()
242
236
            loader.write(response.read())
243
237
            loader.close()
244
 
            
 
238
 
245
239
            for role in ["committer", "author"]:
246
 
                self._role_box_for(role).and_avatar_email(email).update_avatar_image_from_pixbuf_loader(loader)
247
 
    
 
240
                self._role_box_for(role).and_avatar_email(
 
241
                    email).update_avatar_image_from_pixbuf_loader(loader)
 
242
 
248
243
    def _role_box_for(self, role):
249
 
        """ Return the gtk.HBox for the given role """
250
 
        return self.__committer_box if role is "committer" else self.__authors_box
 
244
        """ Return the Gtk.HBox for the given role """
 
245
        if role == "committer":
 
246
            return self.__committer_box
 
247
        else:
 
248
            return self.__authors_box