~saurabhanandiit/gtg/exportFixed

« back to all changes in this revision

Viewing changes to GTG/gtk/backends_dialog/configurepanel.py

Merge of my work on liblarch newbase and all the backends ported to liblarch
(which mainly means porting the datastore).
One failing test, will check it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
# -----------------------------------------------------------------------------
 
3
# Getting Things Gnome! - a personal organizer for the GNOME desktop
 
4
# Copyright (c) 2008-2009 - Lionel Dricot & Bertrand Rousseau
 
5
#
 
6
# This program is free software: you can redistribute it and/or modify it under
 
7
# the terms of the GNU General Public License as published by the Free Software
 
8
# Foundation, either version 3 of the License, or (at your option) any later
 
9
# version.
 
10
#
 
11
# This program is distributed in the hope that it will be useful, but WITHOUT
 
12
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
13
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 
14
# details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License along with
 
17
# this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
# -----------------------------------------------------------------------------
 
19
 
 
20
import gtk
 
21
 
 
22
from GTG.gtk.colors                        import get_colored_tags_markup
 
23
from GTG                                   import _, ngettext
 
24
from GTG.backends.genericbackend           import GenericBackend
 
25
from GTG.gtk.backends_dialog.parameters_ui import ParametersUI
 
26
from GTG.backends.backendsignals           import BackendSignals
 
27
 
 
28
 
 
29
class ConfigurePanel(gtk.VBox):
 
30
    ''' 
 
31
    A VBox that lets you configure a backend
 
32
    '''
 
33
 
 
34
 
 
35
    def __init__(self, backends_dialog):
 
36
        '''
 
37
        Constructor, creating all the gtk widgets
 
38
 
 
39
        @param backends_dialog: a reference to the dialog in which this is
 
40
        loaded
 
41
        '''
 
42
        super(ConfigurePanel, self).__init__()
 
43
        self.dialog = backends_dialog
 
44
        self.should_spinner_be_shown = False
 
45
        self.task_deleted_handle = None
 
46
        self.task_added_handle = None
 
47
        self.req = backends_dialog.get_requester()
 
48
        self._create_widgets()
 
49
        self._connect_signals()
 
50
 
 
51
    def _connect_signals(self):
 
52
        ''' Connects the backends generated signals '''
 
53
        _signals = BackendSignals()
 
54
        _signals.connect(_signals.BACKEND_RENAMED, self.refresh_title)
 
55
        _signals.connect(_signals.BACKEND_STATE_TOGGLED, \
 
56
                         self.refresh_sync_status)
 
57
        _signals.connect(_signals.BACKEND_SYNC_STARTED, self.on_sync_started)
 
58
        _signals.connect(_signals.BACKEND_SYNC_ENDED, self.on_sync_ended)
 
59
    
 
60
    def _create_widgets(self):
 
61
        '''
 
62
        This function fills this Vbox with widgets
 
63
        '''
 
64
        #Division of the available space in three segments:
 
65
        # top, middle and bottom
 
66
        top = gtk.HBox()
 
67
        middle = gtk.HBox()
 
68
        self._fill_top_hbox(top)
 
69
        self._fill_middle_hbox(middle)
 
70
        self.pack_start(top, False)
 
71
        self.pack_start(middle, False)
 
72
        align = gtk.Alignment(xalign = 0, yalign = 0, xscale = 1)
 
73
        align.set_padding(10, 0, 0, 0)
 
74
        self.parameters_ui = ParametersUI(self.req)
 
75
        align.add(self.parameters_ui)
 
76
        self.pack_start(align, False)
 
77
 
 
78
    def _fill_top_hbox(self, hbox):
 
79
        '''
 
80
        Helper function to fill an hbox with an image, a spinner and 
 
81
        three labels
 
82
 
 
83
        @param hbox: the gtk.HBox to fill
 
84
        '''
 
85
        hbox.set_spacing(10)
 
86
        self.image_icon = gtk.Image()
 
87
        self.image_icon.set_size_request(100, 100)
 
88
        vbox = gtk.VBox()
 
89
        hbox_top = gtk.HBox()
 
90
        self.human_name_label = gtk.Label()
 
91
        self.human_name_label.set_alignment(xalign = 0, yalign = 0.5)
 
92
        try:
 
93
            self.spinner = gtk.Spinner()
 
94
        except AttributeError:
 
95
            #worarkound for archlinux: bug #624204
 
96
            self.spinner = gtk.HBox()
 
97
        self.spinner.connect("show", self.on_spinner_show)
 
98
        self.spinner.set_size_request(32, 32)
 
99
        align_spin = gtk.Alignment(xalign = 1, yalign = 0)
 
100
        align_spin.add(self.spinner)
 
101
        hbox_top.pack_start(self.human_name_label, True)
 
102
        hbox_top.pack_start(align_spin, False)
 
103
        self.sync_desc_label = gtk.Label()
 
104
        self.sync_desc_label.set_alignment(xalign = 0, yalign = 1)
 
105
        self.sync_desc_label.set_line_wrap(True)
 
106
        vbox.pack_start(hbox_top, True)
 
107
        vbox.pack_start(self.sync_desc_label, True)
 
108
        hbox.pack_start(self.image_icon, False)
 
109
        align_vbox = gtk.Alignment(xalign = 0, yalign = 0, xscale = 1)
 
110
        align_vbox.set_padding(10, 0, 20, 0)
 
111
        align_vbox.add(vbox)
 
112
        hbox.pack_start(align_vbox, True)
 
113
 
 
114
    def _fill_middle_hbox(self, hbox):
 
115
        '''
 
116
        Helper function to fill an hbox with a label and a button
 
117
 
 
118
        @param hbox: the gtk.HBox to fill
 
119
        '''
 
120
        self.sync_status_label = gtk.Label()
 
121
        self.sync_status_label.set_alignment(xalign = 0.8, yalign = 0.5)
 
122
        self.sync_button = gtk.Button()
 
123
        self.sync_button.connect("clicked", self.on_sync_button_clicked)
 
124
        hbox.pack_start(self.sync_status_label, True)
 
125
        hbox.pack_start(self.sync_button, True)
 
126
 
 
127
    def set_backend(self, backend_id):
 
128
        '''Changes the backend to configure, refreshing this view.
 
129
        
 
130
        @param backend_id: the id of the backend to configure
 
131
        '''
 
132
        self.backend = self.dialog.get_requester().get_backend(backend_id)
 
133
        self.refresh_title()
 
134
        self.refresh_sync_status()
 
135
        self.parameters_ui.refresh(self.backend)
 
136
        self.image_icon.set_from_pixbuf(self.dialog.get_pixbuf_from_icon_name(\
 
137
                                        self.backend.get_name(), 80, 80))
 
138
 
 
139
    def refresh_title(self, sender = None, data = None):
 
140
        '''
 
141
        Callback for the signal that notifies backends name changes. It changes
 
142
        the title of this view
 
143
 
 
144
        @param sender: not used, here only for signal callback compatibility
 
145
        @param data: not used, here only for signal callback compatibility
 
146
        '''
 
147
        markup = "<big><big><big><b>%s</b></big></big></big>" % \
 
148
                                        self.backend.get_human_name()
 
149
        self.human_name_label.set_markup(markup)
 
150
    
 
151
    def refresh_number_of_tasks(self):
 
152
        '''refreshes the number of synced tasks by this backend'''
 
153
        #FIXME: disabled for now. I'm not sure that this is nice because the
 
154
        # count is correct only after the backend has synced all the pending
 
155
        # tasks, and this is quite misleading (invernizzi)
 
156
        return
 
157
        #This will have to be changed for import/export..
 
158
        tags = self.backend.get_attached_tags()
 
159
        tasks_number = self.backend.get_number_of_tasks()
 
160
        if GenericBackend.ALLTASKS_TAG in tags:
 
161
            if tasks_number == 0:
 
162
                markup = _("Ready to start syncing")
 
163
            else:
 
164
                markup = ngettext("Syncing your only task", \
 
165
                    "Syncing all %d tasks" % tasks_number, tasks_number)
 
166
        else:
 
167
            tags_txt = get_colored_tags_markup(self.req, tags)
 
168
            if tasks_number == 0:
 
169
                markup = _("There's no task tagged %s") % tags_txt
 
170
            else:
 
171
                markup = ngettext("Syncing a task tagged %s" % tags_txt, \
 
172
                    "Syncing %d tasks tagged %s" % (tasks_number, tags_txt), \
 
173
                              tasks_number) 
 
174
        self.sync_desc_label.set_markup(markup)
 
175
 
 
176
    def refresh_sync_button(self):
 
177
        '''
 
178
        Refreshes the state of the button that enables the backend
 
179
        '''
 
180
        self.sync_button.set_sensitive(not self.backend.is_default())
 
181
        if self.backend.is_enabled():
 
182
            label = _("Disable syncing")
 
183
        else:
 
184
            label = _("Enable syncing")
 
185
        self.sync_button.set_label(label)
 
186
 
 
187
    def refresh_sync_status_label(self):
 
188
        '''
 
189
        Refreshes the gtk.Label that shows the current state of this backend
 
190
        '''
 
191
        if self.backend.is_default():
 
192
            label = _("This is the default backend")
 
193
        else:
 
194
            if self.backend.is_enabled():
 
195
                label = _("Syncing is enabled")
 
196
            else:
 
197
                label = _('Syncing is <span color="red">disabled</span>')
 
198
        self.sync_status_label.set_markup(label)
 
199
 
 
200
    def refresh_sync_status(self, sender = False, data = False):
 
201
        '''Signal callback function, called when a backend state
 
202
        (enabled/disabled) changes. Refreshes this view.
 
203
 
 
204
        @param sender: not used, here only for signal callback compatibility
 
205
        @param data: not used, here only for signal callback compatibility
 
206
        '''
 
207
        self.refresh_number_of_tasks()
 
208
        self.refresh_sync_button()
 
209
        self.refresh_sync_status_label()
 
210
    
 
211
    def set_hidden(self, is_hidden):
 
212
        '''
 
213
        Notifies this pane if it's hidden or not. We disconnect signals when
 
214
        hidden, since there is no need to keep the UI updated.
 
215
        Hopefully, this should make GTG faster :)
 
216
 
 
217
        @param is_hidden: boolean, True if the window is not visible
 
218
        '''
 
219
        #These is only needed to refresh the number of synced tasks.
 
220
        #since that is disabled for now, there is no need for this
 
221
 
 
222
#        if is_hidden:
 
223
#            if self.task_added_handle:
 
224
#                self.req.disconnect(self.task_added_handle)
 
225
#                self.task_added_handle = None
 
226
#            if self.task_deleted_handle:
 
227
#                self.req.disconnect(self.task_deleted_handle)
 
228
#                self.task_deleted_handle = None
 
229
#        else:
 
230
#            self.task_added_handle = self.req.connect("task-added", \
 
231
#                                            self.__on_task_changed)
 
232
#            self.task_added_handle = self.req.connect("task-modified", \
 
233
#                                            self.__on_task_changed)
 
234
#            self.task_deleted_handle = self.req.connect("task-deleted",  \
 
235
#                                            self.__on_task_changed)
 
236
#
 
237
#    def __on_task_changed(self, sender, task_id):
 
238
#        '''
 
239
#        If tasks are added, modified or removed, updates the number of
 
240
#        tasks of the current backend
 
241
#        '''
 
242
#        self.refresh_sync_status()
 
243
 
 
244
    def on_sync_button_clicked(self, sender):
 
245
        '''
 
246
        Signal callback when a backend is enabled/disabled via the UI button
 
247
 
 
248
        @param sender: not used, here only for signal callback compatibility
 
249
        '''
 
250
        self.parameters_ui.commit_changes()
 
251
        self.req.set_backend_enabled(self.backend.get_id(), \
 
252
                                     not self.backend.is_enabled())
 
253
 
 
254
    def on_sync_started(self, sender, backend_id):
 
255
        '''
 
256
        If the backend has started syncing tasks, update the state of the
 
257
        gtk.Spinner
 
258
 
 
259
        @param sender: not used, here only for signal callback compatibility
 
260
        @param backend_id: the id of the backend that emitted this signal
 
261
        '''
 
262
        if backend_id == self.backend.get_id():
 
263
            self.spinner_set_active(True)
 
264
 
 
265
    def on_sync_ended(self, sender, backend_id):
 
266
        '''
 
267
        If the backend has stopped syncing tasks, update the state of the
 
268
        gtk.Spinner
 
269
 
 
270
        @param sender: not used, here only for signal callback compatibility
 
271
        @param backend_id: the id of the backend that emitted this signal
 
272
        '''
 
273
 
 
274
        if backend_id == self.backend.get_id():
 
275
            self.spinner_set_active(False)
 
276
 
 
277
    def on_spinner_show(self, sender):
 
278
        '''This signal callback hides the spinner if it's not supposed to be
 
279
        seen. It's a workaround to let us call show_all on the whole window
 
280
        while keeping this hidden (it's the only widget that requires special
 
281
        attention)
 
282
 
 
283
        @param sender: not used, here only for signal callback compatibility
 
284
        '''
 
285
        if self.should_spinner_be_shown == False:
 
286
            self.spinner.hide()
 
287
 
 
288
    def spinner_set_active(self, active):
 
289
        '''
 
290
        Enables/disables the gtk.Spinner, while showing/hiding it at the same
 
291
        time
 
292
        
 
293
        @param active: True if the spinner should spin
 
294
        '''
 
295
        self.should_spinner_be_shown = active
 
296
        if active:
 
297
            if isinstance(self.spinner, gtk.Spinner):
 
298
                self.spinner.start()
 
299
            self.spinner.show()
 
300
        else:
 
301
            self.spinner.hide()
 
302
            if isinstance(self.spinner, gtk.Spinner):
 
303
                self.spinner.stop()
 
304