~tualatrix/ubuntu-tweak/arb-packaging

« back to all changes in this revision

Viewing changes to ubuntutweak/module/desktoprecovery.py

  • Committer: Tualatrix Chou
  • Date: 2011-03-15 11:44:46 UTC
  • Revision ID: git-v1:638ab7e8545fbb783e490b588d058cbf9acd75da
Rename the modules to module temporarily

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Ubuntu Tweak - magic tool to configure Ubuntu
 
2
#
 
3
# Copyright (C) 2010 TualatriX <tualatrix@gmail.com>
 
4
#
 
5
# Ubuntu Tweak is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation; either version 2 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# Ubuntu Tweak is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with Ubuntu Tweak; if not, write to the Free Software Foundation, Inc.,
 
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 
18
 
 
19
import os
 
20
import gtk
 
21
import glob
 
22
import time
 
23
import dbus
 
24
import gobject
 
25
import logging
 
26
 
 
27
from subprocess import Popen, PIPE
 
28
 
 
29
from ubuntutweak.utils import icon
 
30
from ubuntutweak.common.consts import CONFIG_ROOT
 
31
from ubuntutweak.common.gui import GuiWorker
 
32
from ubuntutweak.modules  import TweakModule
 
33
from ubuntutweak.ui.dialogs import InfoDialog, QuestionDialog, ErrorDialog
 
34
from ubuntutweak.ui.dialogs import ProcessDialog
 
35
 
 
36
log = logging.getLogger('DesktopRecovery')
 
37
 
 
38
def build_backup_prefix(dir):
 
39
    name_prefix = os.path.join(CONFIG_ROOT, 'desktoprecovery', dir[1:]) + '/'
 
40
 
 
41
    log.debug("build_backup_prefix: %s" % name_prefix)
 
42
 
 
43
    if not os.path.exists(name_prefix):
 
44
        os.makedirs(name_prefix)
 
45
    return name_prefix
 
46
 
 
47
def build_backup_path(dir, name):
 
48
    name_prefix = build_backup_prefix(dir)
 
49
    return name_prefix + name + '.xml'
 
50
 
 
51
def do_backup_task(dir, name):
 
52
    backup_name = build_backup_path(dir, name)
 
53
    log.debug("the backup path is %s" % backup_name)
 
54
    backup_file = open(backup_name, 'w')
 
55
    process = Popen(['gconftool-2', '--dump', dir], stdout=backup_file)
 
56
    return process.communicate()
 
57
 
 
58
def do_recover_task(path):
 
59
    process = Popen(['gconftool-2', '--load', path])
 
60
    log.debug('Start setting recovery: %s' % path)
 
61
    return process.communicate()
 
62
 
 
63
def do_reset_task(dir):
 
64
    process = Popen(['gconftool-2', '--recursive-unset', dir])
 
65
    log.debug('Start setting reset: %s' % dir)
 
66
    return process.communicate()
 
67
 
 
68
class CateView(gtk.TreeView):
 
69
    (COLUMN_ICON,
 
70
     COLUMN_DIR,
 
71
     COLUMN_TITLE
 
72
    ) = range(3)
 
73
 
 
74
    path_dict = {
 
75
        '/apps': _('Applications'),
 
76
        '/desktop': _('Desktop'),
 
77
        '/system': _('System'),
 
78
    }
 
79
 
 
80
    def __init__(self):
 
81
        gtk.TreeView.__init__(self)
 
82
 
 
83
        self.set_rules_hint(True)
 
84
        self.model = self.__create_model()
 
85
        self.set_model(self.model)
 
86
        self.__add_columns()
 
87
        self.update_model()
 
88
 
 
89
        selection = self.get_selection()
 
90
        selection.select_iter(self.model.get_iter_first())
 
91
 
 
92
    def __create_model(self):
 
93
        '''The model is icon, title and the list reference'''
 
94
        model = gtk.ListStore(
 
95
                    gtk.gdk.Pixbuf,
 
96
                    gobject.TYPE_STRING,
 
97
                    gobject.TYPE_STRING)
 
98
 
 
99
        return model
 
100
 
 
101
    def __add_columns(self):
 
102
        column = gtk.TreeViewColumn(_('Category'))
 
103
 
 
104
        renderer = gtk.CellRendererPixbuf()
 
105
        column.pack_start(renderer, False)
 
106
        column.set_attributes(renderer, pixbuf=self.COLUMN_ICON)
 
107
 
 
108
        renderer = gtk.CellRendererText()
 
109
        column.pack_start(renderer, True)
 
110
        column.set_sort_column_id(self.COLUMN_TITLE)
 
111
        column.set_attributes(renderer, text=self.COLUMN_TITLE)
 
112
 
 
113
        self.append_column(column)
 
114
 
 
115
    def update_model(self):
 
116
        for path, title in self.path_dict.items():
 
117
            pixbuf = icon.get_from_name('folder')
 
118
            iter = self.model.append(None)
 
119
            self.model.set(iter,
 
120
                           self.COLUMN_ICON, pixbuf,
 
121
                           self.COLUMN_DIR, path,
 
122
                           self.COLUMN_TITLE, title)
 
123
 
 
124
class SettingView(gtk.TreeView):
 
125
    (COLUMN_ICON,
 
126
     COLUMN_DIR,
 
127
     COLUMN_TITLE
 
128
    ) = range(3)
 
129
 
 
130
    def __init__(self):
 
131
        gtk.TreeView.__init__(self)
 
132
 
 
133
        self.model = self.__create_model()
 
134
        self.set_model(self.model)
 
135
        self.__add_columns()
 
136
 
 
137
    def __create_model(self):
 
138
        ''' The first is for icon, second is for real path, second is for title (if available)'''
 
139
        model = gtk.ListStore(gtk.gdk.Pixbuf,
 
140
                              gobject.TYPE_STRING,
 
141
                              gobject.TYPE_STRING)
 
142
 
 
143
        return model
 
144
 
 
145
    def __add_columns(self):
 
146
        column = gtk.TreeViewColumn(_('Setting'))
 
147
 
 
148
        renderer = gtk.CellRendererPixbuf()
 
149
        column.pack_start(renderer, False)
 
150
        column.set_attributes(renderer, pixbuf=self.COLUMN_ICON)
 
151
 
 
152
        renderer = gtk.CellRendererText()
 
153
        column.pack_start(renderer, True)
 
154
        column.set_sort_column_id(self.COLUMN_TITLE)
 
155
        column.set_attributes(renderer, text=self.COLUMN_TITLE)
 
156
 
 
157
        self.append_column(column)
 
158
 
 
159
    def update_model(self, dir):
 
160
        self.model.clear()
 
161
        
 
162
        process = Popen(['gconftool-2', '--all-dirs', dir], stdout=PIPE)
 
163
        stdout, stderr = process.communicate()
 
164
        if stderr:
 
165
            log.error(stderr)
 
166
            #TODO raise error or others
 
167
            return
 
168
 
 
169
        dirlist = stdout.split()
 
170
        dirlist.sort()
 
171
        for dir in dirlist:
 
172
            title = dir.split('/')[-1]
 
173
 
 
174
            pixbuf = icon.get_from_name(title, alter='folder')
 
175
            iter = self.model.append(None)
 
176
            self.model.set(iter,
 
177
                           self.COLUMN_ICON, pixbuf,
 
178
                           self.COLUMN_DIR, dir,
 
179
                           self.COLUMN_TITLE, title)
 
180
 
 
181
class GetTextDialog(QuestionDialog):
 
182
    def __init__(self, title='', message='', text=''):
 
183
        super(GetTextDialog, self).__init__(title=title, message=message)
 
184
 
 
185
        self.text = text
 
186
 
 
187
        vbox = self.vbox
 
188
 
 
189
        hbox = gtk.HBox(False, 12)
 
190
        label = gtk.Label(_('Backup Name:'))
 
191
        hbox.pack_start(label, False, False, 0)
 
192
 
 
193
        self.entry = gtk.Entry()
 
194
        if text:
 
195
            self.entry.set_text(text)
 
196
        hbox.pack_start(self.entry)
 
197
 
 
198
        vbox.pack_start(hbox)
 
199
        vbox.show_all()
 
200
 
 
201
    def destroy(self):
 
202
        self.text = self.entry.get_text()
 
203
        super(GetTextDialog, self).destroy()
 
204
 
 
205
    def set_text(self, text):
 
206
        self.entry.set_text(text)
 
207
 
 
208
    def get_text(self):
 
209
        return self.text
 
210
 
 
211
class BackupProgressDialog(ProcessDialog):
 
212
    def __init__(self, parent, name, dir):
 
213
        self.file_name = name
 
214
        self.dir = dir
 
215
        self.done = False
 
216
        self.error = False
 
217
 
 
218
        super(BackupProgressDialog, self).__init__(parent=parent)
 
219
 
 
220
    def process_data(self):
 
221
        dir = self.dir
 
222
        name = self.file_name
 
223
 
 
224
        process = Popen(['gconftool-2', '--all-dirs', dir], stdout=PIPE)
 
225
        stdout, stderr = process.communicate()
 
226
        if stderr:
 
227
            log.error(stderr)
 
228
            #TODO raise error or others
 
229
            self.error = True
 
230
            self.done = True
 
231
            return
 
232
 
 
233
        dirlist = stdout.split()
 
234
        dirlist.sort()
 
235
        totol_backuped = []
 
236
 
 
237
        for subdir in dirlist:
 
238
            self.set_progress_text(_('Backing up...%s') % subdir)
 
239
            stdout, stderr = do_backup_task(subdir, name)
 
240
            if stderr is not None:
 
241
                log.error(stderr)
 
242
                self.error = True
 
243
                break
 
244
            else:
 
245
                totol_backuped.append(build_backup_path(subdir, name))
 
246
 
 
247
        if stderr is None:
 
248
            backup_name = build_backup_path(dir, name)
 
249
            sum_file = open(backup_name, 'w')
 
250
            sum_file.write('\n'.join(totol_backuped))
 
251
            sum_file.close()
 
252
 
 
253
        self.done = True
 
254
 
 
255
    def on_timeout(self):
 
256
        self.pulse()
 
257
 
 
258
        if not self.done:
 
259
            return True
 
260
        else:
 
261
            self.destroy()
 
262
 
 
263
class DesktopRecovery(TweakModule):
 
264
    __title__ = _('Desktop Recovery')
 
265
    __desc__ = _('Backup and recover your desktop and application settings with ease.\n'
 
266
                 'You can also use "Reset" to reset to the system default settings.')
 
267
    __icon__ = 'gnome-control-center'
 
268
    __category__ = 'desktop'
 
269
    __desktop__ = ['gnome']
 
270
 
 
271
    def __init__(self):
 
272
        TweakModule.__init__(self, 'desktoprecovery.ui')
 
273
 
 
274
        self.setup_backup_model()
 
275
 
 
276
        hbox = gtk.HBox(False, 5)
 
277
        self.add_start(hbox)
 
278
 
 
279
        self.cateview = CateView()
 
280
        #FIXME it will cause two callback for cateview changed
 
281
        self.cateview.connect('button_press_event', self.on_cateview_button_press_event)
 
282
        self.cate_selection = self.cateview.get_selection()
 
283
        self.cate_selection.connect('changed', self.on_cateview_changed)
 
284
        hbox.pack_start(self.cateview, False, False, 0)
 
285
 
 
286
        vpaned = gtk.VPaned()
 
287
        hbox.pack_start(vpaned)
 
288
 
 
289
        self.settingview = SettingView()
 
290
        self.setting_selection = self.settingview.get_selection()
 
291
        self.setting_selection.connect('changed', self.on_settingview_changed)
 
292
        sw = gtk.ScrolledWindow()
 
293
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
294
        sw.add(self.settingview)
 
295
        vpaned.pack1(sw, True, False)
 
296
 
 
297
        self.window1.remove(self.recover_box)
 
298
        vpaned.pack2(self.recover_box, False, False)
 
299
 
 
300
        self.on_cateview_changed(self.cate_selection)
 
301
        self.show_all()
 
302
 
 
303
    def setup_backup_model(self):
 
304
        model = gtk.ListStore(gobject.TYPE_STRING,
 
305
                              gobject.TYPE_STRING)
 
306
 
 
307
        self.backup_combobox.set_model(model)
 
308
 
 
309
        cell = gtk.CellRendererText()
 
310
        self.backup_combobox.pack_start(cell, True)
 
311
        self.backup_combobox.add_attribute(cell, 'text', 0)
 
312
 
 
313
    def update_backup_model(self, dir):
 
314
        def file_cmp(f1, f2):
 
315
            return cmp(os.stat(f1).st_ctime, os.stat(f2).st_ctime)
 
316
 
 
317
        model = self.backup_combobox.get_model()
 
318
        model.clear()
 
319
 
 
320
        name_prefix = build_backup_prefix(dir)
 
321
 
 
322
        file_lsit = glob.glob(name_prefix + '*.xml')
 
323
        file_lsit.sort(cmp=file_cmp, reverse=True)
 
324
        log.debug('Use glob to find the name_prefix: %s with result: %s' % (name_prefix, str(file_lsit)))
 
325
        if file_lsit:
 
326
            first_iter = None
 
327
            for file in file_lsit:
 
328
                iter = model.append(None)
 
329
                if first_iter == None:
 
330
                    first_iter = iter
 
331
                model.set(iter,
 
332
                        0, os.path.basename(file)[:-4],
 
333
                          1, file)
 
334
            self.backup_combobox.set_active_iter(first_iter)
 
335
            self.delete_button.set_sensitive(True)
 
336
            self.edit_button.set_sensitive(True)
 
337
            self.recover_button.set_sensitive(True)
 
338
        else:
 
339
            iter = model.append(None)
 
340
            model.set(iter, 0, _('No Backup Yet'), 1, '')
 
341
            self.backup_combobox.set_active_iter(iter)
 
342
            self.delete_button.set_sensitive(False)
 
343
            self.edit_button.set_sensitive(False)
 
344
            self.recover_button.set_sensitive(False)
 
345
 
 
346
    def on_cateview_changed(self, widget):
 
347
        model, iter = widget.get_selected()
 
348
        if iter:
 
349
            dir = model.get_value(iter, self.cateview.COLUMN_DIR)
 
350
            self.settingview.update_model(dir)
 
351
 
 
352
            self.dir_label.set_text(dir)
 
353
            self.update_backup_model(dir)
 
354
 
 
355
    def on_cateview_button_press_event(self, widget, event):
 
356
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
 
357
            self.on_cateview_changed(self.cate_selection)
 
358
 
 
359
    def on_settingview_changed(self, widget):
 
360
        model, iter = widget.get_selected()
 
361
        if iter:
 
362
            dir = model.get_value(iter, self.settingview.COLUMN_DIR)
 
363
            self.dir_label.set_text(dir)
 
364
            self.update_backup_model(dir)
 
365
 
 
366
    def on_backup_button_clicked(self, widget):
 
367
        def get_time_stamp():
 
368
            return time.strftime('%Y-%m-%d-%H-%M', time.localtime(time.time()))
 
369
 
 
370
        dir = self.dir_label.get_text()
 
371
        log.debug("Start backing up the dir: %s" % dir)
 
372
 
 
373
        # if 1, then root dir
 
374
        if dir.count('/') == 1:
 
375
            dialog = GetTextDialog(message=_('Backup all settings under: <b>%s</b>\nWould you like to continue?') % dir,
 
376
                                   text=get_time_stamp())
 
377
 
 
378
            response = dialog.run()
 
379
            dialog.destroy()
 
380
            name = dialog.get_text()
 
381
 
 
382
            if response == gtk.RESPONSE_YES and name:
 
383
                dialog = BackupProgressDialog(self.get_toplevel(), name, dir)
 
384
 
 
385
                dialog.run()
 
386
                dialog.destroy()
 
387
 
 
388
                if dialog.error == False:
 
389
                    self.show_backup_successful_dialog()
 
390
                    self.update_backup_model(dir)
 
391
                else:
 
392
                    self.show_backup_failed_dialog()
 
393
        else:
 
394
            dialog = GetTextDialog(message=_('Backup settings under: <b>%s</b>\nWould you like to continue?') % dir,
 
395
                                   text=get_time_stamp())
 
396
            response = dialog.run()
 
397
            dialog.destroy()
 
398
            name = dialog.get_text()
 
399
 
 
400
            if response == gtk.RESPONSE_YES and name:
 
401
                stdout, stderr = do_backup_task(dir, name)
 
402
 
 
403
                if stderr is None:
 
404
                    self.show_backup_successful_dialog()
 
405
                    self.update_backup_model(dir)
 
406
                else:
 
407
                    self.show_backup_failed_dialog()
 
408
                    log.debug("Backup error: %s" % stderr)
 
409
 
 
410
    def on_delete_button_clicked(self, widget):
 
411
        def try_remove_record_in_root_backup(dir, path):
 
412
            rootpath = build_backup_prefix('/'.join(dir.split('/')[:2])) + os.path.basename(path)
 
413
            if os.path.exists(rootpath):
 
414
                lines = open(rootpath).read().split()
 
415
                lines.remove(path)
 
416
 
 
417
                if len(lines) == 0:
 
418
                    os.remove(rootpath)
 
419
                else:
 
420
                    new = open(rootpath, 'w')
 
421
                    new.write('\n'.join(lines))
 
422
                    new.close()
 
423
 
 
424
        def try_remove_all_subback(path):
 
425
            for line in open(path):
 
426
                os.remove(line.strip())
 
427
 
 
428
        iter = self.backup_combobox.get_active_iter()
 
429
        model = self.backup_combobox.get_model()
 
430
 
 
431
        dir = self.dir_label.get_text()
 
432
 
 
433
        path = model.get_value(iter, 1)
 
434
        if dir.count('/') == 2:
 
435
            dialog = QuestionDialog(_('Would you like to delete the backup: <b>%s/%s</b>?') % (dir, os.path.basename(path)[:-4]))
 
436
        else:
 
437
            dialog = QuestionDialog(_('Would you like to delete the backup of all <b>%s</b> settings named <b>%s</b>?') % (dir, os.path.basename(path)[:-4]))
 
438
        response = dialog.run()
 
439
        dialog.destroy()
 
440
        if response == gtk.RESPONSE_YES:
 
441
            if dir.count('/') == 2:
 
442
                try_remove_record_in_root_backup(dir, path)
 
443
            else:
 
444
                try_remove_all_subback(path)
 
445
 
 
446
            os.remove(path)
 
447
            self.update_backup_model(dir)
 
448
 
 
449
    def on_recover_button_clicked(self, widget):
 
450
        iter = self.backup_combobox.get_active_iter()
 
451
        model = self.backup_combobox.get_model()
 
452
        dir = self.dir_label.get_text()
 
453
        path = model.get_value(iter, 1)
 
454
 
 
455
        if dir.count('/') == 2:
 
456
            message = _('Would you like to recover the backup: <b>%s/%s</b>?') % (dir, os.path.basename(path)[:-4])
 
457
        else:
 
458
            message = _('Would you like to recover the backup of all <b>%s</b> settings named <b>%s</b>?') % (dir, os.path.basename(path)[:-4])
 
459
 
 
460
        addon_message = _('<b>NOTES</b>: While recovering, your desktop may be unresponsive for a moment.')
 
461
 
 
462
        dialog = QuestionDialog(message + '\n\n' + addon_message)
 
463
        response = dialog.run()
 
464
        dialog.destroy()
 
465
 
 
466
        if response == gtk.RESPONSE_YES:
 
467
            if dir.count('/') == 1:
 
468
                for line in open(path):
 
469
                    stdout, stderr = do_recover_task(line.strip())
 
470
            else:
 
471
                stdout, stderr = do_recover_task(path)
 
472
 
 
473
            if stderr:
 
474
                log.error(stderr)
 
475
                #TODO raise error or others
 
476
                return
 
477
            self.__show_successful_with_logout_button(_('Recovery Successful!\nYou may need to restart your desktop for changes to take effect'))
 
478
 
 
479
    def __show_successful_with_logout_button(self, message):
 
480
        dialog = InfoDialog(message)
 
481
 
 
482
        button = gtk.Button(_('_Logout'))
 
483
        button.connect('clicked', self.on_logout_button_clicked, dialog)
 
484
        dialog.add_option_button(button)
 
485
 
 
486
        dialog.launch()
 
487
 
 
488
    def on_reset_button_clicked(self, widget):
 
489
        iter = self.backup_combobox.get_active_iter()
 
490
        model = self.backup_combobox.get_model()
 
491
        dir = self.dir_label.get_text()
 
492
 
 
493
        if dir.count('/') == 2:
 
494
            message = _('Would you like to reset settings for: <b>%s</b>?') % dir
 
495
        else:
 
496
            message = _('Would you like to reset all settings under: <b>%s</b>?') % dir
 
497
 
 
498
        addon_message = _('<b>NOTES</b>: Whilst resetting, your desktop may be unresponsive for a moment.')
 
499
 
 
500
        dialog = QuestionDialog(message + '\n\n' + addon_message)
 
501
        response = dialog.run()
 
502
        dialog.destroy()
 
503
 
 
504
        if response == gtk.RESPONSE_YES:
 
505
            stdout, stderr = do_reset_task(dir)
 
506
 
 
507
            if stderr:
 
508
                log.error(stderr)
 
509
                #TODO raise error or others
 
510
                return
 
511
            self.__show_successful_with_logout_button(_('Reset Successful!\nYou may need to restart your desktop for changes to take effect'))
 
512
 
 
513
    def on_logout_button_clicked(self, widget, dialog):
 
514
        bus = dbus.SessionBus()
 
515
        object = bus.get_object('org.gnome.SessionManager', '/org/gnome/SessionManager')
 
516
        object.get_dbus_method('Logout', 'org.gnome.SessionManager')(True)
 
517
        dialog.destroy()
 
518
        self.emit('call', 'mainwindow', 'destroy', {})
 
519
 
 
520
    def on_edit_button_clicked(self, widget):
 
521
        def try_rename_record_in_root_backup(dir, old_path, new_path):
 
522
            rootpath = build_backup_prefix('/'.join(dir.split('/')[:2])) + os.path.basename(path)
 
523
            if os.path.exists(rootpath):
 
524
                lines = open(rootpath).read().split()
 
525
                lines.remove(old_path)
 
526
                lines.append(new_path)
 
527
 
 
528
                new = open(rootpath, 'w')
 
529
                new.write('\n'.join(lines))
 
530
                new.close()
 
531
        iter = self.backup_combobox.get_active_iter()
 
532
        model = self.backup_combobox.get_model()
 
533
        dir = self.dir_label.get_text()
 
534
        path = model.get_value(iter, 1)
 
535
 
 
536
        dialog = GetTextDialog(message=_('Please enter a new name for your backup:'))
 
537
 
 
538
        dialog.set_text(os.path.basename(path)[:-4])
 
539
        res = dialog.run()
 
540
        dialog.destroy()
 
541
        new_name = dialog.get_text()
 
542
        log.debug('Get the new backup name: %s' % new_name)
 
543
 
 
544
        if res == gtk.RESPONSE_YES and new_name:
 
545
            # If is root, try to rename all the subdir, then rename itself
 
546
            if dir.count('/') == 1:
 
547
                totol_renamed = []
 
548
                for line in open(path):
 
549
                    line = line.strip()
 
550
                    dirname = os.path.dirname(line)
 
551
                    new_path = os.path.join(dirname, new_name + '.xml')
 
552
                    log.debug('Rename backup file from "%s" to "%s"' % (line, new_path))
 
553
                    os.rename(line, new_path)
 
554
                    totol_renamed.append(new_path)
 
555
                sum_file = open(path, 'w')
 
556
                sum_file.write('\n'.join(totol_renamed))
 
557
                sum_file.close()
 
558
 
 
559
            dirname = os.path.dirname(path)
 
560
            new_path = os.path.join(dirname, new_name + '.xml')
 
561
            log.debug('Rename backup file from "%s" to "%s"' % (path, new_path))
 
562
            os.rename(path, new_path)
 
563
            try_rename_record_in_root_backup(dir, path, new_path)
 
564
 
 
565
        self.update_backup_model(dir)
 
566
 
 
567
    def show_backup_successful_dialog(self):
 
568
        InfoDialog(_("Backup Successful!")).launch()
 
569
 
 
570
    def show_backup_failed_dialog(self):
 
571
        ErrorDialog(_("Backup Failed!")).launch()