/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: 2011-04-06 14:50:40 UTC
  • Revision ID: jelmer@samba.org-20110406145040-7u37k9hxkhleiqx2
Cleanups, fix compatibility with older versions of pygtk.

Show diffs side-by-side

added added

removed removed

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