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
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
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
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
# -----------------------------------------------------------------------------
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
29
class ConfigurePanel(gtk.VBox):
31
A VBox that lets you configure a backend
35
def __init__(self, backends_dialog):
37
Constructor, creating all the gtk widgets
39
@param backends_dialog: a reference to the dialog in which this is
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()
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)
60
def _create_widgets(self):
62
This function fills this Vbox with widgets
64
#Division of the available space in three segments:
65
# top, middle and bottom
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)
78
def _fill_top_hbox(self, hbox):
80
Helper function to fill an hbox with an image, a spinner and
83
@param hbox: the gtk.HBox to fill
86
self.image_icon = gtk.Image()
87
self.image_icon.set_size_request(100, 100)
90
self.human_name_label = gtk.Label()
91
self.human_name_label.set_alignment(xalign = 0, yalign = 0.5)
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)
112
hbox.pack_start(align_vbox, True)
114
def _fill_middle_hbox(self, hbox):
116
Helper function to fill an hbox with a label and a button
118
@param hbox: the gtk.HBox to fill
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)
127
def set_backend(self, backend_id):
128
'''Changes the backend to configure, refreshing this view.
130
@param backend_id: the id of the backend to configure
132
self.backend = self.dialog.get_requester().get_backend(backend_id)
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))
139
def refresh_title(self, sender = None, data = None):
141
Callback for the signal that notifies backends name changes. It changes
142
the title of this view
144
@param sender: not used, here only for signal callback compatibility
145
@param data: not used, here only for signal callback compatibility
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)
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)
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")
164
markup = ngettext("Syncing your only task", \
165
"Syncing all %d tasks" % tasks_number, tasks_number)
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
171
markup = ngettext("Syncing a task tagged %s" % tags_txt, \
172
"Syncing %d tasks tagged %s" % (tasks_number, tags_txt), \
174
self.sync_desc_label.set_markup(markup)
176
def refresh_sync_button(self):
178
Refreshes the state of the button that enables the backend
180
self.sync_button.set_sensitive(not self.backend.is_default())
181
if self.backend.is_enabled():
182
label = _("Disable syncing")
184
label = _("Enable syncing")
185
self.sync_button.set_label(label)
187
def refresh_sync_status_label(self):
189
Refreshes the gtk.Label that shows the current state of this backend
191
if self.backend.is_default():
192
label = _("This is the default backend")
194
if self.backend.is_enabled():
195
label = _("Syncing is enabled")
197
label = _('Syncing is <span color="red">disabled</span>')
198
self.sync_status_label.set_markup(label)
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.
204
@param sender: not used, here only for signal callback compatibility
205
@param data: not used, here only for signal callback compatibility
207
self.refresh_number_of_tasks()
208
self.refresh_sync_button()
209
self.refresh_sync_status_label()
211
def set_hidden(self, is_hidden):
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 :)
217
@param is_hidden: boolean, True if the window is not visible
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
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
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)
237
# def __on_task_changed(self, sender, task_id):
239
# If tasks are added, modified or removed, updates the number of
240
# tasks of the current backend
242
# self.refresh_sync_status()
244
def on_sync_button_clicked(self, sender):
246
Signal callback when a backend is enabled/disabled via the UI button
248
@param sender: not used, here only for signal callback compatibility
250
self.parameters_ui.commit_changes()
251
self.req.set_backend_enabled(self.backend.get_id(), \
252
not self.backend.is_enabled())
254
def on_sync_started(self, sender, backend_id):
256
If the backend has started syncing tasks, update the state of the
259
@param sender: not used, here only for signal callback compatibility
260
@param backend_id: the id of the backend that emitted this signal
262
if backend_id == self.backend.get_id():
263
self.spinner_set_active(True)
265
def on_sync_ended(self, sender, backend_id):
267
If the backend has stopped syncing tasks, update the state of the
270
@param sender: not used, here only for signal callback compatibility
271
@param backend_id: the id of the backend that emitted this signal
274
if backend_id == self.backend.get_id():
275
self.spinner_set_active(False)
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
283
@param sender: not used, here only for signal callback compatibility
285
if self.should_spinner_be_shown == False:
288
def spinner_set_active(self, active):
290
Enables/disables the gtk.Spinner, while showing/hiding it at the same
293
@param active: True if the spinner should spin
295
self.should_spinner_be_shown = active
297
if isinstance(self.spinner, gtk.Spinner):
302
if isinstance(self.spinner, gtk.Spinner):