/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-02 12:42:35 UTC
  • Revision ID: jelmer@samba.org-20110402124235-h9cvzbawsk79fol3
RemoveĀ unusedĀ import.

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
 
from gi.repository import Gtk
18
 
from gi.repository import GdkPixbuf
 
17
import gtk
19
18
 
20
19
from bzrlib.config import parse_username
21
20
 
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):
 
21
from bzrlib.plugins.gtk import _i18n
 
22
from bzrlib.plugins.gtk.avatarproviders import AvatarProviderGravatar, AvatarDownloaderWorker
 
23
 
 
24
 
 
25
class Avatar(gtk.Box):
30
26
    """ Author or committer avatar """
31
 
 
 
27
    
32
28
    def __init__(self, apparent_username):
33
29
        """ Constructor """
34
 
        super(Avatar, self).__init__()
35
 
 
 
30
        gtk.Box.__init__(self)
 
31
        
36
32
        self.apparent_username = apparent_username
37
33
        self.username, self.email = parse_username(apparent_username)
38
34
        self.image = None
39
 
 
 
35
    
40
36
    def __eq__(self, other):
41
 
        return (self.apparent_username == other.apparent_username and
42
 
                self.username == other.username and
43
 
                self.email == other.email)
44
 
 
 
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 ~~~~~
45
46
    def show_spinner(self):
46
47
        """
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
 
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
49
50
        the translatable 'No email' text.
50
51
        """
51
 
        if self.email:
52
 
            tooltip = _i18n("Retrieving avatar for %s...") % self.email
53
 
            spinner = Gtk.Spinner()
 
52
        if not self.email is "":
 
53
            spinner = gtk.Spinner()
54
54
            spinner.start()
55
 
            self.pack_start(spinner, False, True, 0)
56
 
            spinner.set_tooltip_text(tooltip)
 
55
            self.pack_start(spinner, False)
 
56
            spinner.set_tooltip_text(_i18n("Retrieving avatar for %s...") % self.email)
57
57
            spinner.set_size_request(20, 20)
58
58
            spinner.show()
59
59
        else:
60
 
            no_email = Gtk.Label(label=_i18n("No email"))
61
 
            self.pack_start(no_email, True, True, 0)
 
60
            no_email = gtk.Label(_i18n("No email"))
 
61
            self.pack_start(no_email)
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 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)
 
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)
72
70
            self.image.set_tooltip_text(self.apparent_username)
73
71
            self.image.show()
74
72
 
75
73
 
76
 
class AvatarBox(Gtk.HBox):
77
 
    """HBox showing an avatar."""
78
 
 
 
74
class AvatarBox(gtk.HBox):
 
75
    """ Improved gtk.HBox """
 
76
    
79
77
    def __init__(self, homogeneous=False, spacing=0):
80
 
        super(AvatarBox, self).__init__(
81
 
            homogeneous=homogeneous, spacing=spacing)
 
78
        """ Constructor """
 
79
        gtk.HBox.__init__(self, homogeneous, spacing)
82
80
        self.__avatars = {}
83
81
        self.avatar = None
84
82
        self.__displaying = None
85
 
 
 
83
    
 
84
    
 
85
    # ~~~~~ Public methods ~~~~~
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
 
        """Return True if this box has the specified avatar.
 
93
        """
 
94
        Return True if this box has registered given avatar,
 
95
        otherwise return False
94
96
        """
95
97
        return avatar.email in self.__avatars
96
 
 
 
98
    
97
99
    def showing(self, avatar):
98
 
        """Return True if the displaying avatar matches the specified one.
 
100
        """
 
101
        Return True if the displaying avatar is the same
 
102
        as the given one otherwise return False
99
103
        """
100
104
        return self.__displaying and self.__displaying == avatar
101
 
 
 
105
    
102
106
    def append_avatars_with(self, avatar):
103
107
        """
104
108
        Append avatars collection with the given one if not already registed
106
110
        When an avatar is added this method True, otherwise, if the avatar
107
111
        was in the collection, return False.
108
112
        """
109
 
        if avatar.email and not avatar.email in self.__avatars:
 
113
        if not avatar.email is "" and not avatar.email in self.__avatars:
110
114
            self.__avatars[avatar.email] = avatar
111
115
            self._new_cell_for_avatar(avatar)
112
116
            return True
113
117
        else:
114
118
            self.and_avatar_email(avatar.email).come_back_to_gui()
115
119
        return False
116
 
 
 
120
    
117
121
    def and_avatar_email(self, email):
118
122
        """
119
123
        Select the avatar from avatars collection
120
124
        in order to apply future actions
121
125
        """
122
126
        self.avatar = None
123
 
        if email and email in self.__avatars:
 
127
        if not email is "" and email in self.__avatars:
124
128
            self.avatar = self.__avatars[email]
125
129
        else:
126
130
            self.avatar = None
127
131
        return self
128
 
 
 
132
    
129
133
    def update_avatar_image_from_pixbuf_loader(self, loader):
130
 
        """Replace the Gtk.Spinner with the given loader."""
 
134
        """ Replace the gtk.Spinner with the given loader """
131
135
        if self.avatar:
132
 
            self.avatar.image = Gtk.Image()
 
136
            self.avatar.image = gtk.Image()
133
137
            self.avatar.image.set_from_pixbuf(loader.get_pixbuf())
134
138
            self.avatar.show_image()
135
139
            self.__displaying = self.avatar
136
 
 
 
140
    
137
141
    def come_back_to_gui(self):
138
 
        """Render back avatar in the GUI."""
 
142
        """ Render back avatar in the GUI """
139
143
        if self.avatar:
140
 
            self.pack_start(self.avatar, True, True, 0)
 
144
            self.pack_start(self.avatar)
141
145
            self.__displaying = self.avatar
142
146
        else:
143
147
            self._show_no_email()
144
 
 
 
148
    
 
149
    
 
150
    # ~~~~~ Private methods ~~~~~~
145
151
    def _new_cell_for_avatar(self, avatar):
146
 
        """Create a new cell in this box with a Gtk.Spinner."""
 
152
        """ Create a new cell in this box with a gtk.Spinner """
147
153
        avatar.show_spinner()
148
 
        self.pack_start(avatar, True, True, 0)
 
154
        self.pack_start(avatar)
149
155
        avatar.show()
150
156
        self.__displaying = avatar
151
 
 
 
157
    
152
158
    def _show_no_email(self):
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)
 
159
        """ Show a gtk.Label with test 'No email' """
 
160
        no_email = gtk.Label(_i18n("No email"))
 
161
        self.pack_start(no_email)
156
162
        no_email.show()
157
163
 
158
164
 
159
 
class AvatarsBox(Gtk.HBox):
160
 
    """GTK container for author and committer avatars."""
161
 
 
 
165
class AvatarsBox(gtk.HBox):
 
166
    """ GTK container for authors and committers avatars """
 
167
    
162
168
    def __init__(self):
163
 
        super(AvatarsBox, self).__init__(homogeneous=False, spacing=10)
 
169
        """ Constructor """
 
170
        gtk.HBox.__init__(self, False, 10)
 
171
        
164
172
        self.__committer_box = None
165
173
        self.__authors_box = None
166
174
        self._init_gui()
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
 
 
 
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.
173
180
        # For example, with Gravatar, the method return the complete url
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).
 
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).
177
183
        provider = AvatarProviderGravatar()
178
184
        self.__worker = AvatarDownloaderWorker(
179
185
            provider.gravatar_id_for_email
180
186
        )
181
 
        # This callback method should be fired by all workers when a request
182
 
        # is done.
 
187
        # This callback method should be fired bt all workers when a request is done.
183
188
        self.__worker.set_callback_method(self._update_avatar_from_response)
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
 
 
 
189
        self.__worker.start()
 
190
    
 
191
    
 
192
    # ~~~~~ Public methods ~~~~~
191
193
    def add(self, username, role):
192
 
        """Add the given username in the role box and add in the worker queue.
 
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.
193
198
        """
194
199
        avatar = Avatar(username)
195
 
        is_shown = self._role_box_for("committer").showing(avatar)
196
 
        if (role == "author" and not is_shown) or role == "committer":
 
200
        if role is "author" and not self._role_box_for("committer").showing(avatar) or role is "committer":
197
201
            if self._role_box_for(role).append_avatars_with(avatar):
198
202
                self.__worker.queue(avatar.email)
199
 
 
 
203
    
200
204
    def merge(self, usernames, role):
201
 
        """Add avatars from a list"""
 
205
        """ Add avatars from a list """
202
206
        for username in usernames:
203
207
            self.add(username, role)
204
 
 
 
208
    
205
209
    def reset(self):
206
210
        """
207
211
        Request a reset view for all boxes in order to show only avatars
208
212
        of the selected line in the revision view screen.
209
213
        """
210
 
        for role in ("committer", "author"):
211
 
            self._role_box_for(role).reset_view()
212
 
 
 
214
        [self._role_box_for(role).reset_view() for role in ["committer", "author"]]
 
215
    
 
216
    
 
217
    # ~~~~~ Private methods ~~~~~
213
218
    def _init_gui(self):
214
 
        """Create boxes where avatars will be displayed."""
215
 
        # 2 Gtk.HBox: One for the committer and one for authors
 
219
        """ Create boxes where avatars will be displayed """
 
220
        # 2 gtk.HBox: One for the committer and one for authors
216
221
        # Committer
217
222
        self.__committer_box = AvatarBox()
218
223
        self.__committer_box.set_size_request(80, 80)
219
 
        self.pack_end(self.__committer_box, False, True, 0)
 
224
        self.pack_end(self.__committer_box, False)
220
225
        self.__committer_box.show()
221
226
        # Authors
222
227
        self.__authors_box = AvatarBox()
223
 
        self.pack_end(self.__authors_box, False, True, 0)
 
228
        self.pack_end(self.__authors_box, False)
224
229
        self.__authors_box.set_spacing(10)
225
230
        self.__authors_box.show()
226
 
 
 
231
    
227
232
    def _update_avatar_from_response(self, response, email):
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()
 
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()
236
242
            loader.write(response.read())
237
243
            loader.close()
238
 
 
 
244
            
239
245
            for role in ["committer", "author"]:
240
 
                self._role_box_for(role).and_avatar_email(
241
 
                    email).update_avatar_image_from_pixbuf_loader(loader)
242
 
 
 
246
                self._role_box_for(role).and_avatar_email(email).update_avatar_image_from_pixbuf_loader(loader)
 
247
    
243
248
    def _role_box_for(self, role):
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
 
249
        """ Return the gtk.HBox for the given role """
 
250
        return self.__committer_box if role is "committer" else self.__authors_box