/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-03-23 09:49:44 UTC
  • mfrom: (723.1.1 isearch)
  • Revision ID: jelmer@samba.org-20110323094944-7n5h1vif3xpbze3p
Merge support for interactive substring search in bzr viz and annotate.

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