~ubuntu-branches/ubuntu/quantal/software-center/quantal-updates

« back to all changes in this revision

Viewing changes to softwarecenter/ui/gtk3/widgets/reviews.py

  • Committer: Package Import Robot
  • Author(s): Michael Vogt, Michael Vogt, Anthony Lenton, Gary Lasker, Natalia Bidart
  • Date: 2012-03-09 08:55:46 UTC
  • Revision ID: package-import@ubuntu.com-20120309085546-p7p4ppu59ishighc
Tags: 5.1.12
[ Michael Vogt ]
* lp:~mvo/software-center/webcam-string-fix:
- small fix to support the debtagshw updated tags for hardware::camera
  (split into webcam,digicam now)
* lp:~mvo/software-center/trivial-nm-state-override:
  - support the SOFTWARE_CENTER_NET_CONNECTED environment value to
    force the connected state as a stopgap workaround for bug 911706
* lp:~mvo/software-center/lp941361:
  - adds a general make_string_from_list() helper to build (somewhat)
    i18n friendly human readable strings easily (LP: #941361)
* lp:~mvo/software-center/expunge-cache:
  - merge os.nice() addition
* lp:~mvo/software-center/lp789596:
  - better (less scary) string for updates from third-party
    venders (LP: #789596)
* lp:~mvo/software-center/fix-refresh-of-whats-new:
  - fix missing refresh of the what's new content on a database
    reopen so that new content from the agent appears as soon
    as it finishes the update
* lp:~mvo/software-center/review-fixes:
  - ensure ui is correctly updated after submitting/modifying review
* lp:~mvo/software-center/simulate-slow-network:
  - add a small helper to simulate a slow network connection to
    see if we have hidden latencies in the UI when the network
    is (really) slow
* lp:~mvo/software-center/top-rated-refresh:
  - ensure that the top-rated apps are refreshed when we have new data
* lp:~mvo/software-center/apptreeview-tweaks:
  - fix 'load_range" errors when navigating the installed view
    (LP: #913675), and do some nice needed refactoring/cleanup

[ Anthony Lenton ]
* lp:~elachuni/software-center/dont-store:
  - consolidate Ubunty SSO logins to use "Ubuntu Software Center", fixes
    inconsistent UI and storing of two keys in they keyring, as well as
    being prompted to log in twice the first time that you review an app
* lp:~elachuni/software-center/pep8-test,
  lp:~elachuni/software-center/pep8-test-part2,
  lp:~elachuni/software-center/pep8-test-part3,
  lp:~elachuni/software-center/pep8-test-part4:
  - add a unit test to check code statically for pep8 compliance and update
    more and more of the code to meet it

[ Gary Lasker ]
* lp:~gary-lasker/software-center/fix-crash-lp944875:
  - Fix an intermittent crash that can happen if the installed view pane
    has not been built yet when a call to show the spinner is made as a
    result of a refresh (LP: #944875)
* lp:~gary-lasker/software-center/list-view-icon-coordinates-lp947624:
  - This branch adds support for providing the correct icon size and 
    on-screen coordinates values in the Unity launcher integration dbus 
    call when an installation is initiated via the list view
    (LP: #947624)
* lp:~gary-lasker/software-center/enable-published-date-unit-test:
  - unit test change only, enable the published-date unit test that was
    disabled pending deployment of support on the staging server

[ Natalia Bidart ]
* lp:~nataliabidart/software-center/one-auth:
  - use consistently the same app name for every Ubuntu SSO call to
    ensure SSO tokens are handled properly

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
from stars import Star
33
33
from softwarecenter.utils import (
34
34
    get_person_from_config,
35
 
    get_nice_date_string, 
36
 
    upstream_version_compare, 
 
35
    get_nice_date_string,
 
36
    upstream_version_compare,
37
37
    upstream_version,
38
38
    utf8,
39
39
    )
40
40
 
41
41
 
42
 
from softwarecenter.i18n import get_languages, langcode_to_name
 
42
from softwarecenter.i18n import (
 
43
    get_languages,
 
44
    langcode_to_name,
 
45
    )
43
46
 
44
 
from softwarecenter.netstatus import network_state_is_connected, get_network_watcher
 
47
from softwarecenter.netstatus import (
 
48
    network_state_is_connected,
 
49
    get_network_watcher,
 
50
    )
45
51
from softwarecenter.enums import (
46
 
    PkgStates, 
 
52
    PkgStates,
47
53
    ReviewSortMethods,
48
54
    )
49
 
    
 
55
 
50
56
from softwarecenter.backend.reviews import UsefulnessCache
51
57
 
52
58
from softwarecenter.ui.gtk3.em import StockEms
58
64
(COL_LANGNAME,
59
65
 COL_LANGCODE) = range(2)
60
66
 
 
67
 
61
68
class UIReviewsList(Gtk.VBox):
62
69
 
63
70
    __gsignals__ = {
64
 
        'new-review':(GObject.SignalFlags.RUN_FIRST,
 
71
        'new-review': (GObject.SignalFlags.RUN_FIRST,
65
72
                    None,
66
73
                    ()),
67
 
        'report-abuse':(GObject.SignalFlags.RUN_FIRST,
 
74
        'report-abuse': (GObject.SignalFlags.RUN_FIRST,
68
75
                    None,
69
76
                    (GObject.TYPE_PYOBJECT,)),
70
 
        'submit-usefulness':(GObject.SignalFlags.RUN_FIRST,
 
77
        'submit-usefulness': (GObject.SignalFlags.RUN_FIRST,
71
78
                    None,
72
79
                    (GObject.TYPE_PYOBJECT, bool)),
73
 
        'modify-review':(GObject.SignalFlags.RUN_FIRST,
74
 
                    None,
75
 
                    (GObject.TYPE_PYOBJECT,)),
76
 
        'delete-review':(GObject.SignalFlags.RUN_FIRST,
77
 
                    None,
78
 
                    (GObject.TYPE_PYOBJECT,)),
79
 
        'more-reviews-clicked':(GObject.SignalFlags.RUN_FIRST,
 
80
        'modify-review': (GObject.SignalFlags.RUN_FIRST,
 
81
                    None,
 
82
                    (GObject.TYPE_PYOBJECT,)),
 
83
        'delete-review': (GObject.SignalFlags.RUN_FIRST,
 
84
                    None,
 
85
                    (GObject.TYPE_PYOBJECT,)),
 
86
        'more-reviews-clicked': (GObject.SignalFlags.RUN_FIRST,
80
87
                                None,
81
 
                                () ),
82
 
        'different-review-language-clicked':(GObject.SignalFlags.RUN_FIRST,
 
88
                                ()),
 
89
        'different-review-language-clicked': (GObject.SignalFlags.RUN_FIRST,
83
90
                                             None,
84
 
                                             (GObject.TYPE_STRING,) ),
85
 
        'review-sort-changed':(GObject.SignalFlags.RUN_FIRST,
 
91
                                             (GObject.TYPE_STRING,)),
 
92
        'review-sort-changed': (GObject.SignalFlags.RUN_FIRST,
86
93
                               None,
87
 
                               (GObject.TYPE_INT,) ),
 
94
                               (GObject.TYPE_INT,)),
88
95
    }
89
96
 
90
97
    def __init__(self, parent):
109
116
        label.set_alignment(0, 0.5)
110
117
        self.pack_start(label, False, False, 0)
111
118
 
112
 
        # header 
 
119
        # header
113
120
        self.header = Gtk.HBox()
114
121
        self.header.set_spacing(StockEms.MEDIUM)
115
122
 
136
143
        self.review_language.add_attribute(cell, "text", COL_LANGNAME)
137
144
        self.review_language_model = Gtk.ListStore(str, str)
138
145
        for lang in get_languages():
139
 
            self.review_language_model.append( (langcode_to_name(lang), lang) )
140
 
        self.review_language_model.append( (_('Any language'), 'any') )
 
146
            self.review_language_model.append((langcode_to_name(lang), lang))
 
147
        self.review_language_model.append((_('Any language'), 'any'))
141
148
        self.review_language.set_model(self.review_language_model)
142
149
        self.review_language.set_active(0)
143
150
        self.review_language.connect(
153
160
        self.no_network_msg = None
154
161
        watcher = get_network_watcher()
155
162
        watcher.connect(
156
 
            "changed", lambda w,s: self._on_network_state_change())
 
163
            "changed", lambda w, s: self._on_network_state_change())
157
164
 
158
165
        self.show_all()
159
 
        return
160
166
 
161
167
    def _on_network_state_change(self):
162
168
        is_connected = network_state_is_connected()
171
177
 
172
178
    def _on_button_new_clicked(self, button):
173
179
        self.emit("new-review")
174
 
    
 
180
 
175
181
    def _on_sort_method_changed(self, cb):
176
182
        selection = self.sort_combo.get_active()
177
183
        if selection == self._current_sort:
179
185
        else:
180
186
            self._current_sort = selection
181
187
            self.emit("review-sort-changed", selection)
182
 
    
 
188
 
183
189
    def update_useful_votes(self, my_votes):
184
190
        self.useful_votes = my_votes
185
191
 
191
197
        if self.reviews:
192
198
            for r in self.reviews:
193
199
                pkgversion = self._parent.app_details.version
194
 
                review = UIReview(r, pkgversion, self.logged_in_person, self.useful_votes)
 
200
                review = UIReview(r, pkgversion, self.logged_in_person,
 
201
                    self.useful_votes)
195
202
                self.vbox.pack_start(review, True, True, 0)
196
 
        return
197
203
 
198
204
    def _be_the_first_to_review(self):
199
205
        s = _('Be the first to review it')
200
206
        self.new_review.set_label(s)
201
207
        self.vbox.pack_start(NoReviewYetWriteOne(), True, True, 0)
202
208
        self.vbox.show_all()
203
 
        return
204
209
 
205
210
    def _install_to_review(self):
206
 
        s = '<small>%s</small>' % _("You need to install this before you can review it")
 
211
        s = ('<small>%s</small>' %
 
212
            _("You need to install this before you can review it"))
207
213
        self.install_first_label = Gtk.Label(label=s)
208
214
        self.install_first_label.set_use_markup(True)
209
215
        self.install_first_label.set_alignment(1.0, 0.5)
210
216
        self.header.pack_start(self.install_first_label, False, False, 0)
211
217
        self.install_first_label.show()
212
 
        return
213
 
    
 
218
 
214
219
    # FIXME: this needs to be smarter in the future as we will
215
220
    #        not allow multiple reviews for the same software version
216
221
    def _any_reviews_current_user(self):
225
230
        m = EmbeddedMessage(title, msg, 'network-offline')
226
231
        self.vbox.pack_start(m, True, True, 0)
227
232
        return m
228
 
        
 
233
 
229
234
    def _clear_vbox(self, vbox):
230
235
        children = vbox.get_children()
231
236
        for child in children:
232
237
            child.destroy()
233
238
 
234
 
    # FIXME: instead of clear/add_reviews/configure_reviews_ui we should provide
235
 
    #        a single show_reviews(reviews_data_list)
 
239
    # FIXME: instead of clear/add_reviews/configure_reviews_ui we should
 
240
    #        provide a single show_reviews(reviews_data_list)
236
241
    def configure_reviews_ui(self):
237
242
        """ this needs to be called after add_reviews, it will actually
238
243
            show the reviews
243
248
            self.install_first_label.hide()
244
249
        except AttributeError:
245
250
            pass
246
 
        
 
251
 
247
252
        self._clear_vbox(self.vbox)
248
253
 
249
254
        # network sensitive stuff, only show write_review if connected,
253
258
 
254
259
        # only show new_review for installed stuff
255
260
        is_installed = (self._parent.app_details and
256
 
                        self._parent.app_details.pkg_state == PkgStates.INSTALLED)
 
261
            self._parent.app_details.pkg_state == PkgStates.INSTALLED)
257
262
 
258
263
        # show/hide new review button
259
264
        if is_installed:
260
265
            self.new_review.show()
261
266
        else:
262
267
            self.new_review.hide()
263
 
            # if there are no reviews, the install to review text appears 
 
268
            # if there are no reviews, the install to review text appears
264
269
            # where the reviews usually are (LP #823255)
265
270
            if self.reviews:
266
271
                self._install_to_review()
301
306
        # always run this here to make update the current ui based on the
302
307
        # network state
303
308
        self._on_network_state_change()
304
 
        return
305
309
 
306
310
    def _on_more_reviews_clicked(self, button):
307
311
        # remove buttn and emit signal
323
327
        ids = []
324
328
        for review in self.reviews:
325
329
            ids.append(review.id)
326
 
        return ids 
 
330
        return ids
327
331
 
328
332
    def add_review(self, review):
329
333
        self.reviews.append(review)
330
 
        return
331
334
 
332
335
    def replace_review(self, review):
333
336
        for r in self.reviews:
336
339
                self.reviews.remove(r)
337
340
                self.reviews.insert(pos, review)
338
341
                break
339
 
        return
340
342
 
341
343
    def remove_review(self, review):
342
344
        for r in self.reviews:
343
345
            if r.id == review.id:
344
346
                self.reviews.remove(r)
345
347
                break
346
 
        return
347
348
 
348
349
    def clear(self):
349
350
        self.reviews = []
358
359
            self.install_first_label.hide()
359
360
        except AttributeError:
360
361
            pass
361
 
        
 
362
 
362
363
        a = Gtk.Alignment.new(0.5, 0.5, 1.0, 1.0)
363
364
        hb = Gtk.HBox(spacing=12)
364
365
        hb.show()
380
381
 
381
382
        self.vbox.pack_start(a, False, False, 0)
382
383
        self.vbox.show()
383
 
        return
384
384
 
385
385
    def hide_spinner(self):
386
386
        for child in self.vbox.get_children():
387
387
            if isinstance(child, Gtk.Alignment):
388
388
                child.destroy()
389
 
        return
390
389
 
391
390
    def draw(self, cr, a):
392
391
        for r in self.vbox:
393
392
            if isinstance(r, (UIReview)):
394
393
                r.draw(cr, r.get_allocation())
395
 
        return
396
394
 
397
395
 
398
396
class UIReview(Gtk.VBox):
399
397
    """ the UI for a individual review including all button to mark
400
398
        useful/inappropriate etc
401
399
    """
402
 
    def __init__(self, review_data=None, app_version=None, 
 
400
    def __init__(self, review_data=None, app_version=None,
403
401
                 logged_in_person=None, useful_votes=None):
404
402
        GObject.GObject.__init__(self)
405
403
        self.set_spacing(StockEms.SMALL)
420
418
        self.delete_error_img = Gtk.Image()
421
419
        self.delete_error_img.set_from_stock(
422
420
                                Gtk.STOCK_DIALOG_ERROR,
423
 
                                Gtk.IconSize.SMALL_TOOLBAR) 
 
421
                                Gtk.IconSize.SMALL_TOOLBAR)
424
422
        self.submit_error_img = Gtk.Image()
425
423
        self.submit_error_img.set_from_stock(
426
424
                                Gtk.STOCK_DIALOG_ERROR,
427
425
                                Gtk.IconSize.SMALL_TOOLBAR)
428
426
        self.submit_status_spinner = Gtk.Spinner()
429
 
        self.submit_status_spinner.set_size_request(12,12)
 
427
        self.submit_status_spinner.set_size_request(12, 12)
430
428
        self.delete_status_spinner = Gtk.Spinner()
431
 
        self.delete_status_spinner.set_size_request(12,12)
 
429
        self.delete_status_spinner.set_size_request(12, 12)
432
430
        self.acknowledge_error = Gtk.Button()
433
431
        label = Gtk.Label()
434
432
        label.set_markup('<small>%s</small>' % _("OK"))
465
463
 
466
464
    def _on_realize(self, widget, *content):
467
465
        self._build(*content)
468
 
        return
469
466
 
470
467
    def _on_report_abuse_clicked(self, button):
471
468
        reviews = self.get_ancestor(UIReviewsList)
476
473
        reviews = self.get_ancestor(UIReviewsList)
477
474
        if reviews:
478
475
            reviews.emit("modify-review", self.id)
479
 
    
 
476
 
480
477
    def _on_useful_clicked(self, btn, is_useful):
481
478
        reviews = self.get_ancestor(UIReviewsList)
482
479
        if reviews:
483
480
            self._usefulness_ui_update('progress')
484
481
            reviews.emit("submit-usefulness", self.id, is_useful)
485
 
            
486
 
    def _on_error_acknowledged(self, button, current_user_reviewer, useful_total, useful_favorable):
 
482
 
 
483
    def _on_error_acknowledged(self, button, current_user_reviewer,
 
484
        useful_total, useful_favorable):
487
485
        self.usefulness_error = False
488
 
        self._usefulness_ui_update('renew', current_user_reviewer, useful_total, useful_favorable)
489
 
    
490
 
    def _usefulness_ui_update(self, type, current_user_reviewer=False, useful_total=0, useful_favorable=0):
 
486
        self._usefulness_ui_update('renew', current_user_reviewer,
 
487
            useful_total, useful_favorable)
 
488
 
 
489
    def _usefulness_ui_update(self, type, current_user_reviewer=False,
 
490
        useful_total=0, useful_favorable=0):
491
491
        self._hide_usefulness_elements()
492
492
        #print "_usefulness_ui_update: %s" % type
493
493
        if type == 'renew':
494
 
            self._build_usefulness_ui(current_user_reviewer, useful_total, useful_favorable, self.useful_votes)
 
494
            self._build_usefulness_ui(current_user_reviewer, useful_total,
 
495
                useful_favorable, self.useful_votes)
495
496
            return
496
497
        if type == 'progress':
497
 
            self.status_label = Gtk.Label.new("<small>%s</small>" % _(u"Submitting now\u2026"))
 
498
            self.status_label = Gtk.Label.new(
 
499
                "<small>%s</small>" % _(u"Submitting now\u2026"))
498
500
            self.status_label.set_use_markup(True)
499
 
            self.status_box.pack_start(self.submit_status_spinner, False, False, 0)
 
501
            self.status_box.pack_start(self.submit_status_spinner, False,
 
502
                False, 0)
500
503
            self.submit_status_spinner.show()
501
504
            self.submit_status_spinner.start()
502
 
            self.status_label.set_padding(2,0)
 
505
            self.status_label.set_padding(2, 0)
503
506
            self.status_box.pack_start(self.status_label, False, False, 0)
504
507
            self.status_label.show()
505
508
        if type == 'error':
506
509
            self.submit_error_img.show()
507
 
            self.status_label = Gtk.Label.new("<small>%s</small>" % _("Error submitting usefulness"))
 
510
            self.status_label = Gtk.Label.new(
 
511
                "<small>%s</small>" % _("Error submitting usefulness"))
508
512
            self.status_label.set_use_markup(True)
509
513
            self.status_box.pack_start(self.submit_error_img, False, False, 0)
510
 
            self.status_label.set_padding(2,0)
 
514
            self.status_label.set_padding(2, 0)
511
515
            self.status_box.pack_start(self.status_label, False, False, 0)
512
516
            self.status_label.show()
513
517
            self.acknowledge_error.show()
514
518
            self.status_box.pack_start(self.acknowledge_error, False, False, 0)
515
 
            self.acknowledge_error.connect('clicked', self._on_error_acknowledged, current_user_reviewer, useful_total, useful_favorable)
 
519
            self.acknowledge_error.connect('clicked',
 
520
                self._on_error_acknowledged, current_user_reviewer,
 
521
                useful_total, useful_favorable)
516
522
        self.status_box.show()
517
523
        self.footer.pack_start(self.status_box, False, False, 0)
518
 
        return
519
524
 
520
525
    def _hide_usefulness_elements(self):
521
526
        """ hide all usefulness elements """
526
531
            widget = getattr(self, attr, None)
527
532
            if widget:
528
533
                widget.hide()
529
 
        return
530
534
 
531
535
    def _get_datetime_from_review_date(self, raw_date_str):
532
536
        # example raw_date str format: 2011-01-28 19:15:21
533
537
        return datetime.datetime.strptime(raw_date_str, '%Y-%m-%d %H:%M:%S')
534
538
 
535
 
    def _delete_ui_update(self, type, current_user_reviewer=False, action=None):
 
539
    def _delete_ui_update(self, type, current_user_reviewer=False,
 
540
        action=None):
536
541
        self._hide_delete_elements()
537
542
        if type == 'renew':
538
543
            self._build_delete_flag_ui(current_user_reviewer)
540
545
        if type == 'progress':
541
546
            self.delete_status_spinner.start()
542
547
            self.delete_status_spinner.show()
543
 
            self.delete_status_label = Gtk.Label("<small><b>%s</b></small>" % _(u"Deleting now\u2026"))
544
 
            self.delete_status_box.pack_start(self.delete_status_spinner, False, False, 0)
 
548
            self.delete_status_label = Gtk.Label(
 
549
                "<small><b>%s</b></small>" % _(u"Deleting now\u2026"))
 
550
            self.delete_status_box.pack_start(self.delete_status_spinner,
 
551
                False, False, 0)
545
552
            self.delete_status_label.set_use_markup(True)
546
 
            self.delete_status_label.set_padding(2,0)
547
 
            self.delete_status_box.pack_start(self.delete_status_label, False, False, 0)
 
553
            self.delete_status_label.set_padding(2, 0)
 
554
            self.delete_status_box.pack_start(self.delete_status_label, False,
 
555
                False, 0)
548
556
            self.delete_status_label.show()
549
557
        if type == 'error':
550
558
            self.delete_error_img.show()
551
 
            # build full strings for easier i18n 
 
559
            # build full strings for easier i18n
552
560
            if action == 'deleting':
553
561
                s = _("Error deleting review")
554
562
            elif action == 'modifying':
555
563
                s = _("Error modifying review")
556
564
            else:
557
 
                # or unknown error, but we are in string freeze, 
 
565
                # or unknown error, but we are in string freeze,
558
566
                # should never happen anyway
559
567
                s = _("Internal Error")
560
 
            self.delete_status_label = Gtk.Label("<small><b>%s</b></small>" % s)
561
 
            self.delete_status_box.pack_start(self.delete_error_img, False, False, 0)
 
568
            self.delete_status_label = Gtk.Label(
 
569
                "<small><b>%s</b></small>" % s)
 
570
            self.delete_status_box.pack_start(self.delete_error_img,
 
571
                False, False, 0)
562
572
            self.delete_status_label.set_use_markup(True)
563
 
            self.delete_status_label.set_padding(2,0)
564
 
            self.delete_status_box.pack_start(self.delete_status_label, False, False, 0)
 
573
            self.delete_status_label.set_padding(2, 0)
 
574
            self.delete_status_box.pack_start(self.delete_status_label,
 
575
                False, False, 0)
565
576
            self.delete_status_label.show()
566
577
            self.delete_acknowledge_error.show()
567
 
            self.delete_status_box.pack_start(self.delete_acknowledge_error, False, False, 0)
568
 
            self.delete_acknowledge_error.connect('clicked', self._on_delete_error_acknowledged, current_user_reviewer)
 
578
            self.delete_status_box.pack_start(self.delete_acknowledge_error,
 
579
                False, False, 0)
 
580
            self.delete_acknowledge_error.connect('clicked',
 
581
                self._on_delete_error_acknowledged, current_user_reviewer)
569
582
        self.delete_status_box.show()
570
583
        self.footer.pack_end(self.delete_status_box, False, False, 0)
571
 
        return
572
584
 
573
585
    def _on_delete_clicked(self, btn):
574
586
        reviews = self.get_ancestor(UIReviewsList)
583
595
    def _hide_delete_elements(self):
584
596
        """ hide all delete elements """
585
597
        for attr in ["complain", "edit", "delete", "delete_status_spinner",
586
 
                     "delete_error_img", "delete_status_box", "delete_status_label",
587
 
                     "delete_acknowledge_error", "flagbox"
 
598
                     "delete_error_img", "delete_status_box",
 
599
                     "delete_status_label", "delete_acknowledge_error",
 
600
                     "flagbox"
588
601
                     ]:
589
602
            o = getattr(self, attr, None)
590
603
            if o:
591
604
                o.hide()
592
 
        return
593
605
 
594
606
    def _build(self, review_data, app_version, logged_in_person, useful_votes):
595
 
        # all the attributes of review_data may need markup escape, 
 
607
        # all the attributes of review_data may need markup escape,
596
608
        # depening on if they are used as text or markup
597
609
        self.id = review_data.id
598
610
        self.person = review_data.reviewer_username
610
622
        # upstream version
611
623
        version = GObject.markup_escape_text(upstream_version(review_version))
612
624
        # default string
613
 
        version_string = _("For version %(version)s") % { 
614
 
            'version' : version,
 
625
        version_string = _("For version %(version)s") % {
 
626
            'version': version,
615
627
            }
616
628
        # If its for the same version, show it as such
617
629
        if (review_version and
618
630
            app_version and
619
631
            upstream_version_compare(review_version, app_version) == 0):
620
 
            version_string = _("For this version (%(version)s)") % { 
621
 
                    'version' : version,
 
632
            version_string = _("For this version (%(version)s)") % {
 
633
                    'version': version,
622
634
                    }
623
635
 
624
636
        m = '<small>%s</small>'
663
675
            current_user_reviewer = True
664
676
 
665
677
        self._build_usefulness_ui(current_user_reviewer, useful_total,
666
 
                                  useful_favorable, useful_votes, useful_submit_error)
 
678
                                  useful_favorable, useful_votes,
 
679
                                  useful_submit_error)
667
680
 
668
681
        self.flagbox = Gtk.HBox()
669
682
        self.flagbox.set_spacing(4)
670
 
        self._build_delete_flag_ui(current_user_reviewer, delete_error, modify_error)
 
683
        self._build_delete_flag_ui(current_user_reviewer, delete_error,
 
684
            modify_error)
671
685
        self.footer.pack_end(self.flagbox, False, False, 0)
672
686
 
673
687
        # connect network signals
674
688
        self.connect("realize", lambda w: self._on_network_state_change())
675
689
        watcher = get_network_watcher()
676
690
        watcher.connect(
677
 
            "changed", lambda w,s: self._on_network_state_change())
678
 
        return
679
 
    
680
 
    def _build_usefulness_ui(self, current_user_reviewer, useful_total, 
681
 
                             useful_favorable, useful_votes, usefulness_submit_error=False):
 
691
            "changed", lambda w, s: self._on_network_state_change())
 
692
 
 
693
    def _build_usefulness_ui(self, current_user_reviewer, useful_total,
 
694
                             useful_favorable, useful_votes,
 
695
                             usefulness_submit_error=False):
682
696
        if usefulness_submit_error:
683
 
            self._usefulness_ui_update('error', current_user_reviewer, 
 
697
            self._usefulness_ui_update('error', current_user_reviewer,
684
698
                                       useful_total, useful_favorable)
685
699
        else:
686
700
            already_voted = useful_votes.check_for_usefulness(self.id)
687
 
            #get correct label based on retrieved usefulness totals and 
 
701
            #get correct label based on retrieved usefulness totals and
688
702
            # if user is reviewer
689
703
            self.useful = self._get_usefulness_label(
690
 
                current_user_reviewer, useful_total, useful_favorable, already_voted)
 
704
                current_user_reviewer, useful_total, useful_favorable,
 
705
                already_voted)
691
706
            self.useful.set_use_markup(True)
692
707
            #vertically centre so it lines up with the Yes and No buttons
693
708
            self.useful.set_alignment(0, 0.5)
714
729
                self.likebox.pack_start(self.yes_no_separator, False, False, 0)
715
730
                self.likebox.pack_start(self.no_like, False, False, 0)
716
731
                self.footer.pack_start(self.likebox, False, False, 0)
717
 
        return
718
732
 
719
733
    def _on_network_state_change(self):
720
734
        """ show/hide widgets based on network connection state """
732
746
            # actually submit anything without network
733
747
            self.useful.hide()
734
748
            self.complain.hide()
735
 
    
736
 
    def _get_usefulness_label(self, current_user_reviewer, 
737
 
                              useful_total,  useful_favorable, already_voted):
738
 
        '''returns Gtk.Label() to be used as usefulness label depending 
 
749
 
 
750
    def _get_usefulness_label(self, current_user_reviewer,
 
751
                              useful_total, useful_favorable, already_voted):
 
752
        '''returns Gtk.Label() to be used as usefulness label depending
739
753
           on passed in parameters
740
754
        '''
741
755
        if already_voted == None:
751
765
                    "found this review helpful.",
752
766
                    "%(useful_favorable)s of %(useful_total)s people "
753
767
                    "found this review helpful.",
754
 
                    useful_total) % { 'useful_total' : useful_total,
755
 
                                    'useful_favorable' : useful_favorable,
756
 
                                    }
 
768
                    useful_total) % {
 
769
                        'useful_total': useful_total,
 
770
                        'useful_favorable': useful_favorable,
 
771
                        }
757
772
            else:
758
773
                # user has not already voted for the review
759
774
                s = gettext.ngettext(
761
776
                    "found this review helpful. Did you?",
762
777
                    "%(useful_favorable)s of %(useful_total)s people "
763
778
                    "found this review helpful. Did you?",
764
 
                    useful_total) % { 'useful_total' : useful_total,
765
 
                                    'useful_favorable' : useful_favorable,
766
 
                                    }
 
779
                    useful_total) % {
 
780
                        'useful_total': useful_total,
 
781
                        'useful_favorable': useful_favorable,
 
782
                        }
767
783
        else:
768
784
        #only display these special strings if the user voted either way
769
785
            if already_voted:
775
791
                        "found this review helpful, including you",
776
792
                        "%(useful_favorable)s of %(useful_total)s people "
777
793
                        "found this review helpful, including you.",
778
 
                        useful_total) % { 'useful_total' : useful_total,
779
 
                                    'useful_favorable' : useful_favorable,
780
 
                                    }
 
794
                        useful_total) % {
 
795
                            'useful_total': useful_total,
 
796
                            'useful_favorable': useful_favorable,
 
797
                            }
781
798
            else:
782
799
                if useful_total == 1:
783
800
                    s = _("You found this review unhelpful.")
787
804
                        "found this review helpful; you did not.",
788
805
                        "%(useful_favorable)s of %(useful_total)s people "
789
806
                        "found this review helpful; you did not.",
790
 
                        useful_total) % { 'useful_total' : useful_total,
791
 
                                    'useful_favorable' : useful_favorable,
792
 
                                    }
 
807
                        useful_total) % {
 
808
                            'useful_total': useful_total,
 
809
                            'useful_favorable': useful_favorable,
 
810
                            }
793
811
 
794
812
        m = '<small>%s</small>'
795
813
        label = Gtk.Label()
796
814
        label.set_name("subtle-label")
797
815
        label.set_markup(m % s)
798
816
        return label
799
 
        
800
 
    def _build_delete_flag_ui(self, current_user_reviewer, delete_error=False, modify_error=False):
 
817
 
 
818
    def _build_delete_flag_ui(self, current_user_reviewer, delete_error=False,
 
819
        modify_error=False):
801
820
        if delete_error:
802
821
            self._delete_ui_update('error', current_user_reviewer, 'deleting')
803
822
        elif modify_error:
814
833
                self.edit.connect('clicked', self._on_modify_clicked)
815
834
                self.delete.connect('clicked', self._on_delete_clicked)
816
835
            else:
817
 
                # Translators: This link is for flagging a review as inappropriate.
818
 
                # To minimize repetition, if at all possible, keep it to a single word.
819
 
                # If your language has an obvious verb, it won't need a question mark.
 
836
                # Translators: This link is for flagging a review as
 
837
                # inappropriate.  To minimize repetition, if at all possible,
 
838
                # keep it to a single word.  If your language has an obvious
 
839
                # verb, it won't need a question mark.
820
840
                self.complain = Link(m % _('Inappropriate?'))
821
841
                self.complain.set_name("subtle-label")
822
842
                self.complain.set_sensitive(network_state_is_connected())
823
843
                self.flagbox.pack_start(self.complain, False, False, 0)
824
844
                self.complain.connect('clicked', self._on_report_abuse_clicked)
825
845
            self.flagbox.show_all()
826
 
            return
827
846
 
828
847
    def _whom_when_markup(self, person, displayname, cur_t):
829
848
        nice_date = get_nice_date_string(cur_t)
851
870
        return m
852
871
 
853
872
    def draw(self, widget, cr):
854
 
        return
 
873
        pass
855
874
 
856
875
 
857
876
class EmbeddedMessage(UIReview):
860
879
        UIReview.__init__(self)
861
880
        self.label = None
862
881
        self.image = None
863
 
        
 
882
 
864
883
        a = Gtk.Alignment.new(0.5, 0.5, 1.0, 1.0)
865
884
        self.body.pack_start(a, False, False, 0)
866
885
 
878
897
        self.label.set_alignment(0, 0.5)
879
898
 
880
899
        if title:
881
 
            self.label.set_markup('<b><big>%s</big></b>\n%s' % (title, message))
 
900
            self.label.set_markup('<b><big>%s</big></b>\n%s' %
 
901
                (title, message))
882
902
        else:
883
903
            self.label.set_markup(message)
884
904
 
885
905
        hb.pack_start(self.label, True, True, 0)
886
906
        self.show_all()
887
 
        return
888
907
 
889
908
    def draw(self, cr, a):
890
 
        return
 
909
        pass
891
910
 
892
911
 
893
912
class NoReviewRelaxLanguage(EmbeddedMessage):
922
941
        msg = _('Be the first to contribute a review for this application')
923
942
 
924
943
        EmbeddedMessage.__init__(self, title, msg, 'text-editor')
925
 
        return
926
944
 
927
945
 
928
946
def get_test_reviews_window():