/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 olive/__init__.py

  • Committer: Jelmer Vernooij
  • Date: 2012-07-09 15:23:26 UTC
  • mto: This revision was merged to the branch mainline in revision 794.
  • Revision ID: jelmer@samba.org-20120709152326-dzxb8zoz0btull7n
Remove bzr-notify.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
import os
18
 
import sys
19
 
import time
20
 
 
21
 
# gettext support
22
 
import gettext
23
 
gettext.install('olive-gtk')
24
 
 
25
 
try:
26
 
    import pygtk
27
 
    pygtk.require("2.0")
28
 
except:
29
 
    pass
30
 
 
31
 
import gobject
32
 
import gtk
33
 
import gtk.gdk
34
 
import gtk.glade
35
 
 
36
 
from bzrlib.branch import Branch
37
 
import bzrlib.errors as bzrerrors
38
 
from bzrlib.lazy_import import lazy_import
39
 
from bzrlib.ui import ui_factory
40
 
from bzrlib.workingtree import WorkingTree
41
 
 
42
 
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
43
 
from bzrlib.plugins.gtk.errors import show_bzr_error
44
 
from guifiles import GLADEFILENAME
45
 
 
46
 
from bzrlib.plugins.gtk.diff import DiffWindow
47
 
lazy_import(globals(), """
48
 
from bzrlib.plugins.gtk.viz import branchwin
49
 
""")
50
 
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
51
 
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
52
 
from bzrlib.plugins.gtk.commit import CommitDialog
53
 
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
54
 
from bzrlib.plugins.gtk.initialize import InitDialog
55
 
from bzrlib.plugins.gtk.push import PushDialog
56
 
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
57
 
 
58
 
def about():
59
 
    """ Display the AboutDialog. """
60
 
    from bzrlib.plugins.gtk import __version__
61
 
    from bzrlib.plugins.gtk.olive.guifiles import GLADEFILENAME
62
 
 
63
 
    # Load AboutDialog description
64
 
    dglade = gtk.glade.XML(GLADEFILENAME, 'aboutdialog')
65
 
    dialog = dglade.get_widget('aboutdialog')
66
 
 
67
 
    # Set version
68
 
    dialog.set_version(__version__)
69
 
    dialog.set_authors([ _("Lead Developer:"),
70
 
                         "Szilveszter Farkas <szilveszter.farkas@gmail.com>",
71
 
                         _("Contributors:"),
72
 
                         "Jelmer Vernooij <jelmer@samba.org>",
73
 
                         "Mateusz Korniak <mateusz.korniak@ant.gliwice.pl>",
74
 
                         "Gary van der Merwe <garyvdm@gmail.com>" ])
75
 
    dialog.set_artists([ "Simon Pascal Klein <klepas@klepas.org>",
76
 
                         "Jakub Steiner <jimmac@novell.com>" ])
77
 
 
78
 
    dialog.run()
79
 
    # Destroy the dialog
80
 
    dialog.destroy()
81
 
 
82
 
class OliveGtk:
83
 
    """ The main Olive GTK frontend class. This is called when launching the
84
 
    program. """
85
 
    
86
 
    def __init__(self):
87
 
        self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
88
 
        self.window = self.toplevel.get_widget('window_main')
89
 
        self.pref = Preferences()
90
 
        self.path = None
91
 
 
92
 
        # Initialize the statusbar
93
 
        self.statusbar = self.toplevel.get_widget('statusbar')
94
 
        self.context_id = self.statusbar.get_context_id('olive')
95
 
        
96
 
        # Get the main window
97
 
        self.window_main = self.toplevel.get_widget('window_main')
98
 
        # Get the HPaned
99
 
        self.hpaned_main = self.toplevel.get_widget('hpaned_main')
100
 
        # Get the TreeViews
101
 
        self.treeview_left = self.toplevel.get_widget('treeview_left')
102
 
        self.treeview_right = self.toplevel.get_widget('treeview_right')
103
 
        # Get some important menu items
104
 
        self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
105
 
        self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
106
 
        self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
107
 
        self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
108
 
        self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
109
 
        self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
110
 
        self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
111
 
        self.menuitem_view_show_ignored_files = self.toplevel.get_widget('menuitem_view_show_ignored_files')
112
 
        self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
113
 
        self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
114
 
        self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
115
 
        self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
116
 
        self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
117
 
        self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
118
 
        self.menuitem_branch_update = self.toplevel.get_widget('menuitem_branch_update')
119
 
        self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
120
 
        self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
121
 
        self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
122
 
        self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
123
 
        self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
124
 
        self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
125
 
        self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
126
 
        self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
127
 
        self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
128
 
        self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
129
 
        # Get some toolbuttons
130
 
        #self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
131
 
        self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
132
 
        self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
133
 
        self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
134
 
        self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
135
 
        self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
136
 
        self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
137
 
        # Get the drive selector
138
 
        self.combobox_drive = gtk.combo_box_new_text()
139
 
        self.combobox_drive.connect("changed", self._refresh_drives)
140
 
        
141
 
        # Get the navigation widgets
142
 
        self.hbox_location = self.toplevel.get_widget('hbox_location')
143
 
        self.button_location_up = self.toplevel.get_widget('button_location_up')
144
 
        self.button_location_jump = self.toplevel.get_widget('button_location_jump')
145
 
        self.entry_location = self.toplevel.get_widget('entry_location')
146
 
        self.image_location_error = self.toplevel.get_widget('image_location_error')
147
 
        
148
 
        # Get the History widgets
149
 
        self.check_history = self.toplevel.get_widget('checkbutton_history')
150
 
        self.entry_history = self.toplevel.get_widget('entry_history_revno')
151
 
        self.button_history = self.toplevel.get_widget('button_history_browse')
152
 
        
153
 
        self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
154
 
        
155
 
        # Dictionary for signal_autoconnect
156
 
        dic = { "on_window_main_destroy": gtk.main_quit,
157
 
                "on_window_main_delete_event": self.on_window_main_delete_event,
158
 
                "on_quit_activate": self.on_window_main_delete_event,
159
 
                "on_about_activate": self.on_about_activate,
160
 
                "on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
161
 
                "on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
162
 
                "on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
163
 
                "on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
164
 
                "on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
165
 
                "on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
166
 
                "on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
167
 
                "on_menuitem_view_show_ignored_files_activate": self.on_menuitem_view_show_ignored_files_activate,
168
 
                "on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
169
 
                "on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
170
 
                "on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
171
 
                "on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
172
 
                "on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
173
 
                "on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
174
 
                "on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
175
 
                "on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
176
 
                "on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
177
 
                "on_menuitem_branch_update_activate": self.on_menuitem_branch_update_activate,                
178
 
                "on_menuitem_branch_tags_activate": self.on_menuitem_branch_tags_activate,
179
 
                "on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
180
 
                "on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
181
 
                "on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
182
 
                "on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
183
 
                "on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
184
 
                "on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
185
 
                "on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
186
 
                "on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
187
 
                #"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
188
 
                "on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
189
 
                "on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
190
 
                "on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
191
 
                "on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
192
 
                "on_toolbutton_update_clicked": self.on_menuitem_branch_update_activate,
193
 
                "on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
194
 
                "on_treeview_right_row_activated": self.on_treeview_right_row_activated,
195
 
                "on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
196
 
                "on_treeview_left_row_activated": self.on_treeview_left_row_activated,
197
 
                "on_button_location_up_clicked": self.on_button_location_up_clicked,
198
 
                "on_button_location_jump_clicked": self.on_button_location_jump_clicked,
199
 
                "on_entry_location_key_press_event": self.on_entry_location_key_press_event,
200
 
                "on_checkbutton_history_toggled": self.on_checkbutton_history_toggled,
201
 
                "on_entry_history_revno_key_press_event": self.on_entry_history_revno_key_press_event,
202
 
                "on_button_history_browse_clicked": self.on_button_history_browse_clicked
203
 
            }
204
 
        
205
 
        # Connect the signals to the handlers
206
 
        self.toplevel.signal_autoconnect(dic)
207
 
        
208
 
        self._just_started = True
209
 
        
210
 
        # Apply window size and position
211
 
        width = self.pref.get_preference('window_width', 'int')
212
 
        height = self.pref.get_preference('window_height', 'int')
213
 
        self.window.resize(width, height)
214
 
        x = self.pref.get_preference('window_x', 'int')
215
 
        y = self.pref.get_preference('window_y', 'int')
216
 
        self.window.move(x, y)
217
 
        # Apply paned position
218
 
        pos = self.pref.get_preference('paned_position', 'int')
219
 
        self.hpaned_main.set_position(pos)
220
 
        
221
 
        # Apply menu to the toolbutton
222
 
        #menubutton = self.toplevel.get_widget('menutoolbutton_diff')
223
 
        #menubutton.set_menu(handler.menu.toolbar_diff)
224
 
        
225
 
        # Now we can show the window
226
 
        self.window.show()
227
 
        
228
 
        # Show drive selector if under Win32
229
 
        if sys.platform == 'win32':
230
 
            self.hbox_location.pack_start(self.combobox_drive, False, False, 0)
231
 
            self.hbox_location.reorder_child(self.combobox_drive, 1)
232
 
            self.combobox_drive.show()
233
 
            self.gen_hard_selector()
234
 
        
235
 
        self._load_left()
236
 
 
237
 
        # Apply menu state
238
 
        self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
239
 
        self.menuitem_view_show_ignored_files.set_active(self.pref.get_preference('ignored_files', 'bool'))
240
 
 
241
 
        # We're starting local
242
 
        self.remote = False
243
 
        self.remote_branch = None
244
 
        self.remote_path = None
245
 
        self.remote_revision = None
246
 
        
247
 
        self.set_path(os.getcwd())
248
 
        self._load_right()
249
 
        
250
 
        self._just_started = False
251
 
 
252
 
    def set_path(self, path, force_remote=False):
253
 
        self.notbranch = False
254
 
        
255
 
        if force_remote:
256
 
            # Forcing remote mode (reading data from inventory)
257
 
            self._show_stock_image(gtk.STOCK_DISCONNECT)
258
 
            try:
259
 
                br = Branch.open_containing(path)[0]
260
 
            except bzrerrors.NotBranchError:
261
 
                self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
262
 
                self.check_history.set_active(False)
263
 
                self.check_history.set_sensitive(False)
264
 
                return False
265
 
            except bzrerrors.UnsupportedProtocol:
266
 
                self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
267
 
                self.check_history.set_active(False)
268
 
                self.check_history.set_sensitive(False)
269
 
                return False
270
 
            
271
 
            self._show_stock_image(gtk.STOCK_CONNECT)
272
 
            
273
 
            self.remote = True
274
 
           
275
 
            # We're remote
276
 
            self.remote_branch, self.remote_path = Branch.open_containing(path)
277
 
            
278
 
            if self.remote_revision is None:
279
 
                self.remote_revision = self.remote_branch.last_revision()
280
 
            
281
 
            self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
282
 
            
283
 
            if len(self.remote_path) == 0:
284
 
                self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
285
 
            else:
286
 
                for (name, type) in self.remote_entries:
287
 
                    if name == self.remote_path:
288
 
                        self.remote_parent = type.file_id
289
 
                        break
290
 
            
291
 
            if not path.endswith('/'):
292
 
                path += '/'
293
 
            
294
 
            if self.remote_branch.base == path:
295
 
                self.button_location_up.set_sensitive(False)
296
 
            else:
297
 
                self.button_location_up.set_sensitive(True)
298
 
        else:
299
 
            if os.path.isdir(path):
300
 
                self.image_location_error.destroy()
301
 
                self.remote = False
302
 
                
303
 
                # We're local
304
 
                try:
305
 
                    self.wt, self.wtpath = WorkingTree.open_containing(path)
306
 
                except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
307
 
                    self.notbranch = True
308
 
                
309
 
                # If we're in the root, we cannot go up anymore
310
 
                if sys.platform == 'win32':
311
 
                    drive, tail = os.path.splitdrive(path)
312
 
                    if tail in ('', '/', '\\'):
313
 
                        self.button_location_up.set_sensitive(False)
314
 
                    else:
315
 
                        self.button_location_up.set_sensitive(True)
316
 
                else:
317
 
                    if self.path == '/':
318
 
                        self.button_location_up.set_sensitive(False)
319
 
                    else:
320
 
                        self.button_location_up.set_sensitive(True)
321
 
            elif not os.path.isfile(path):
322
 
                # Doesn't seem to be a file nor a directory, trying to open a
323
 
                # remote location
324
 
                self._show_stock_image(gtk.STOCK_DISCONNECT)
325
 
                try:
326
 
                    br = Branch.open_containing(path)[0]
327
 
                except bzrerrors.NotBranchError:
328
 
                    self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
329
 
                    self.check_history.set_active(False)
330
 
                    self.check_history.set_sensitive(False)
331
 
                    return False
332
 
                except bzrerrors.UnsupportedProtocol:
333
 
                    self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
334
 
                    self.check_history.set_active(False)
335
 
                    self.check_history.set_sensitive(False)
336
 
                    return False
337
 
                
338
 
                self._show_stock_image(gtk.STOCK_CONNECT)
339
 
                
340
 
                self.remote = True
341
 
               
342
 
                # We're remote
343
 
                self.remote_branch, self.remote_path = Branch.open_containing(path)
344
 
                
345
 
                if self.remote_revision is None:
346
 
                    self.remote_revision = self.remote_branch.last_revision()
347
 
                
348
 
                self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
349
 
                
350
 
                if len(self.remote_path) == 0:
351
 
                    self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
352
 
                else:
353
 
                    for (name, type) in self.remote_entries:
354
 
                        if name == self.remote_path:
355
 
                            self.remote_parent = type.file_id
356
 
                            break
357
 
                
358
 
                if not path.endswith('/'):
359
 
                    path += '/'
360
 
                
361
 
                if self.remote_branch.base == path:
362
 
                    self.button_location_up.set_sensitive(False)
363
 
                else:
364
 
                    self.button_location_up.set_sensitive(True)
365
 
        
366
 
        if self.notbranch:
367
 
            self.check_history.set_active(False)
368
 
            self.check_history.set_sensitive(False)
369
 
        else:
370
 
            self.check_history.set_sensitive(True)
371
 
        
372
 
        self.statusbar.push(self.context_id, path)
373
 
        self.entry_location.set_text(path)
374
 
        self.path = path
375
 
        return True
376
 
 
377
 
    def get_path(self):
378
 
        if not self.remote:
379
 
            return self.path
380
 
        else:
381
 
            # Remote mode
382
 
            if len(self.remote_path) > 0:
383
 
                return self.remote_branch.base + self.remote_path + '/'
384
 
            else:
385
 
                return self.remote_branch.base
386
 
   
387
 
    def on_about_activate(self, widget):
388
 
        about()
389
 
    
390
 
    def on_button_history_browse_clicked(self, widget):
391
 
        """ Browse for revision button handler. """
392
 
        if self.remote:
393
 
            br = self.remote_branch
394
 
        else:
395
 
            br = self.wt.branch
396
 
            
397
 
        revb = RevisionBrowser(br, self.window)
398
 
        response = revb.run()
399
 
        if response != gtk.RESPONSE_NONE:
400
 
            revb.hide()
401
 
        
402
 
            if response == gtk.RESPONSE_OK:
403
 
                if revb.selected_revno is not None:
404
 
                    self.entry_history.set_text(revb.selected_revno)
405
 
            
406
 
            revb.destroy()
407
 
    
408
 
    def on_button_location_jump_clicked(self, widget):
409
 
        """ Location Jump button handler. """
410
 
        location = self.entry_location.get_text()
411
 
        
412
 
        if self.set_path(location):
413
 
            self.refresh_right()
414
 
    
415
 
    def on_button_location_up_clicked(self, widget):
416
 
        """ Location Up button handler. """
417
 
        if not self.remote:
418
 
            # Local mode
419
 
            self.set_path(os.path.split(self.get_path())[0])
420
 
        else:
421
 
            # Remote mode
422
 
            delim = '/'
423
 
            newpath = delim.join(self.get_path().split(delim)[:-2])
424
 
            newpath += '/'
425
 
            self.set_path(newpath)
426
 
 
427
 
        self.refresh_right()
428
 
    
429
 
    def on_checkbutton_history_toggled(self, widget):
430
 
        """ History Mode toggle handler. """
431
 
        if self.check_history.get_active():
432
 
            # History Mode activated
433
 
            self.entry_history.set_sensitive(True)
434
 
            self.button_history.set_sensitive(True)
435
 
        else:
436
 
            # History Mode deactivated
437
 
            self.entry_history.set_sensitive(False)
438
 
            self.button_history.set_sensitive(False)
439
 
    
440
 
    @show_bzr_error
441
 
    def on_entry_history_revno_key_press_event(self, widget, event):
442
 
        """ Key pressed handler for the history entry. """
443
 
        if event.keyval == 65293:
444
 
            # Return was hit, so we have to load that specific revision
445
 
            # Emulate being remote, so inventory should be used
446
 
            path = self.get_path()
447
 
            if not self.remote:
448
 
                self.remote = True
449
 
                self.remote_branch = self.wt.branch
450
 
            
451
 
            revno = int(self.entry_history.get_text())
452
 
            self.remote_revision = self.remote_branch.get_rev_id(revno)
453
 
            if self.set_path(path, True):
454
 
                self.refresh_right()
455
 
    
456
 
    def on_entry_location_key_press_event(self, widget, event):
457
 
        """ Key pressed handler for the location entry. """
458
 
        if event.keyval == 65293:
459
 
            # Return was hit, so we have to jump
460
 
            self.on_button_location_jump_clicked(widget)
461
 
    
462
 
    def on_menuitem_add_files_activate(self, widget):
463
 
        """ Add file(s)... menu handler. """
464
 
        from add import OliveAdd
465
 
        add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
466
 
        add.display()
467
 
    
468
 
    def on_menuitem_branch_get_activate(self, widget):
469
 
        """ Branch/Get... menu handler. """
470
 
        from bzrlib.plugins.gtk.branch import BranchDialog
471
 
        
472
 
        if self.remote:
473
 
            branch = BranchDialog(os.getcwd(), self.window, self.remote_branch.base)
474
 
        else:
475
 
            branch = BranchDialog(self.get_path(), self.window)
476
 
        response = branch.run()
477
 
        if response != gtk.RESPONSE_NONE:
478
 
            branch.hide()
479
 
            
480
 
            if response == gtk.RESPONSE_OK:
481
 
                self.refresh_right()
482
 
            
483
 
            branch.destroy()
484
 
    
485
 
    def on_menuitem_branch_checkout_activate(self, widget):
486
 
        """ Branch/Checkout... menu handler. """
487
 
        from bzrlib.plugins.gtk.checkout import CheckoutDialog
488
 
        
489
 
        if self.remote:
490
 
            checkout = CheckoutDialog(os.getcwd(), self.window, self.remote_branch.base)
491
 
        else:
492
 
            checkout = CheckoutDialog(self.get_path(), self.window)
493
 
        response = checkout.run()
494
 
        if response != gtk.RESPONSE_NONE:
495
 
            checkout.hide()
496
 
        
497
 
            if response == gtk.RESPONSE_OK:
498
 
                self.refresh_right()
499
 
            
500
 
            checkout.destroy()
501
 
    
502
 
    @show_bzr_error
503
 
    def on_menuitem_branch_commit_activate(self, widget):
504
 
        """ Branch/Commit... menu handler. """
505
 
        commit = CommitDialog(self.wt, self.wtpath, self.notbranch, self.get_selected_right(), self.window)
506
 
        response = commit.run()
507
 
        if response != gtk.RESPONSE_NONE:
508
 
            commit.hide()
509
 
        
510
 
            if response == gtk.RESPONSE_OK:
511
 
                self.refresh_right()
512
 
            
513
 
            commit.destroy()
514
 
    
515
 
    def on_menuitem_branch_conflicts_activate(self, widget):
516
 
        """ Branch/Conflicts... menu handler. """
517
 
        conflicts = ConflictsDialog(self.wt, self.window)
518
 
        response = conflicts.run()
519
 
        if response != gtk.RESPONSE_NONE:
520
 
            conflicts.destroy()
521
 
    
522
 
    def on_menuitem_branch_merge_activate(self, widget):
523
 
        """ Branch/Merge... menu handler. """
524
 
        from bzrlib.plugins.gtk.merge import MergeDialog
525
 
        
526
 
        if self.check_for_changes():
527
 
            error_dialog(_('There are local changes in the branch'),
528
 
                         _('Please commit or revert the changes before merging.'))
529
 
        else:
530
 
            merge = MergeDialog(self.wt, self.wtpath)
531
 
            merge.display()
532
 
 
533
 
    @show_bzr_error
534
 
    def on_menuitem_branch_missing_revisions_activate(self, widget):
535
 
        """ Branch/Missing revisions menu handler. """
536
 
        local_branch = self.wt.branch
537
 
        
538
 
        other_branch = local_branch.get_parent()
539
 
        if other_branch is None:
540
 
            error_dialog(_('Parent location is unknown'),
541
 
                         _('Cannot determine missing revisions if no parent location is known.'))
542
 
            return
543
 
        
544
 
        remote_branch = Branch.open(other_branch)
545
 
        
546
 
        if remote_branch.base == local_branch.base:
547
 
            remote_branch = local_branch
548
 
 
549
 
        ret = len(local_branch.missing_revisions(remote_branch))
550
 
 
551
 
        if ret > 0:
552
 
            info_dialog(_('There are missing revisions'),
553
 
                        _('%d revision(s) missing.') % ret)
554
 
        else:
555
 
            info_dialog(_('Local branch up to date'),
556
 
                        _('There are no missing revisions.'))
557
 
 
558
 
    @show_bzr_error
559
 
    def on_menuitem_branch_pull_activate(self, widget):
560
 
        """ Branch/Pull menu handler. """
561
 
        branch_to = self.wt.branch
562
 
 
563
 
        location = branch_to.get_parent()
564
 
        if location is None:
565
 
            error_dialog(_('Parent location is unknown'),
566
 
                                     _('Pulling is not possible until there is a parent location.'))
567
 
            return
568
 
 
569
 
        branch_from = Branch.open(location)
570
 
 
571
 
        if branch_to.get_parent() is None:
572
 
            branch_to.set_parent(branch_from.base)
573
 
 
574
 
        ret = branch_to.pull(branch_from)
575
 
        
576
 
        info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
577
 
        
578
 
    @show_bzr_error
579
 
    def on_menuitem_branch_update_activate(self, widget):
580
 
        """ Brranch/checkout update menu handler. """
581
 
        
582
 
        ret = self.wt.update()
583
 
        conflicts = self.wt.conflicts()
584
 
        if conflicts:
585
 
            info_dialog(_('Update successful but conflicts generated'), _('Number of conflicts generated: %d.') % (len(conflicts),) )
586
 
        else:
587
 
            info_dialog(_('Update successful'), _('No conflicts generated.') )
588
 
    
589
 
    def on_menuitem_branch_push_activate(self, widget):
590
 
        """ Branch/Push... menu handler. """
591
 
        push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
592
 
        response = push.run()
593
 
        if response != gtk.RESPONSE_NONE:
594
 
            push.destroy()
595
 
    
596
 
    @show_bzr_error
597
 
    def on_menuitem_branch_revert_activate(self, widget):
598
 
        """ Branch/Revert all changes menu handler. """
599
 
        ret = self.wt.revert([])
600
 
        if ret:
601
 
            warning_dialog(_('Conflicts detected'),
602
 
                           _('Please have a look at the working tree before continuing.'))
603
 
        else:
604
 
            info_dialog(_('Revert successful'),
605
 
                        _('All files reverted to last revision.'))
606
 
        self.refresh_right()
607
 
    
608
 
    def on_menuitem_branch_status_activate(self, widget):
609
 
        """ Branch/Status... menu handler. """
610
 
        from bzrlib.plugins.gtk.status import StatusDialog
611
 
        status = StatusDialog(self.wt, self.wtpath)
612
 
        response = status.run()
613
 
        if response != gtk.RESPONSE_NONE:
614
 
            status.destroy()
615
 
    
616
 
    def on_menuitem_branch_initialize_activate(self, widget):
617
 
        """ Initialize current directory. """
618
 
        init = InitDialog(self.path, self.window)
619
 
        response = init.run()
620
 
        if response != gtk.RESPONSE_NONE:
621
 
            init.hide()
622
 
        
623
 
            if response == gtk.RESPONSE_OK:
624
 
                self.refresh_right()
625
 
            
626
 
            init.destroy()
627
 
        
628
 
    def on_menuitem_branch_tags_activate(self, widget):
629
 
        """ Branch/Tags... menu handler. """
630
 
        from bzrlib.plugins.gtk.tags import TagsWindow
631
 
        if not self.remote:
632
 
            window = TagsWindow(self.wt.branch, self.window)
633
 
        else:
634
 
            window = TagsWindow(self.remote_branch, self.window)
635
 
        window.show()
636
 
    
637
 
    def on_menuitem_file_annotate_activate(self, widget):
638
 
        """ File/Annotate... menu handler. """
639
 
        if self.get_selected_right() is None:
640
 
            error_dialog(_('No file was selected'),
641
 
                         _('Please select a file from the list.'))
642
 
            return
643
 
        
644
 
        branch = self.wt.branch
645
 
        file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
646
 
        
647
 
        window = GAnnotateWindow(all=False, plain=False)
648
 
        window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
649
 
        config = GAnnotateConfig(window)
650
 
        window.show()
651
 
        branch.lock_read()
652
 
        try:
653
 
            window.annotate(self.wt, branch, file_id)
654
 
        finally:
655
 
            branch.unlock()
656
 
    
657
 
    def on_menuitem_file_make_directory_activate(self, widget):
658
 
        """ File/Make directory... menu handler. """
659
 
        from mkdir import OliveMkdir
660
 
        mkdir = OliveMkdir(self.wt, self.wtpath)
661
 
        mkdir.display()
662
 
    
663
 
    def on_menuitem_file_move_activate(self, widget):
664
 
        """ File/Move... menu handler. """
665
 
        from move import OliveMove
666
 
        move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
667
 
        move.display()
668
 
    
669
 
    def on_menuitem_file_rename_activate(self, widget):
670
 
        """ File/Rename... menu handler. """
671
 
        from rename import OliveRename
672
 
        rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
673
 
        rename.display()
674
 
 
675
 
    def on_menuitem_remove_file_activate(self, widget):
676
 
        """ Remove (unversion) selected file. """
677
 
        from remove import OliveRemoveDialog
678
 
        remove = OliveRemoveDialog(self.wt, self.wtpath,
679
 
                                   selected=self.get_selected_right(),
680
 
                                   parent=self.window)
681
 
        response = remove.run()
682
 
        
683
 
        if response != gtk.RESPONSE_NONE:
684
 
            remove.hide()
685
 
        
686
 
            if response == gtk.RESPONSE_OK:
687
 
                self.set_path(self.path)
688
 
                self.refresh_right()
689
 
            
690
 
            remove.destroy()
691
 
    
692
 
    def on_menuitem_stats_diff_activate(self, widget):
693
 
        """ Statistics/Differences... menu handler. """
694
 
        window = DiffWindow()
695
 
        parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
696
 
        window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
697
 
        window.show()
698
 
    
699
 
    def on_menuitem_stats_infos_activate(self, widget):
700
 
        """ Statistics/Informations... menu handler. """
701
 
        from info import OliveInfo
702
 
        if self.remote:
703
 
            info = OliveInfo(self.remote_branch)
704
 
        else:
705
 
            info = OliveInfo(self.wt.branch)
706
 
        info.display()
707
 
    
708
 
    def on_menuitem_stats_log_activate(self, widget):
709
 
        """ Statistics/Log... menu handler. """
710
 
        window = branchwin.BranchWindow()
711
 
        if not self.remote:
712
 
            window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
713
 
        else:
714
 
            window.set_branch(self.remote_branch, self.remote_branch.last_revision(), None)
715
 
        window.show()
716
 
    
717
 
    def on_menuitem_view_refresh_activate(self, widget):
718
 
        """ View/Refresh menu handler. """
719
 
        # Refresh the left pane
720
 
        self.refresh_left()
721
 
        # Refresh the right pane
722
 
        self.refresh_right()
723
 
   
724
 
    def on_menuitem_view_show_hidden_files_activate(self, widget):
725
 
        """ View/Show hidden files menu handler. """
726
 
        self.pref.set_preference('dotted_files', widget.get_active())
727
 
        if self.path is not None:
728
 
            self.refresh_right()
729
 
 
730
 
    def on_menuitem_view_show_ignored_files_activate(self, widget):
731
 
        """ Hide/Show ignored files menu handler. """
732
 
        self.pref.set_preference('ignored_files', widget.get_active())
733
 
        if self.path is not None:
734
 
            self.refresh_right()
735
 
            
736
 
    def on_treeview_left_button_press_event(self, widget, event):
737
 
        """ Occurs when somebody right-clicks in the bookmark list. """
738
 
        if event.button == 3:
739
 
            # Don't show context with nothing selected
740
 
            if self.get_selected_left() == None:
741
 
                return
742
 
 
743
 
            # Create a menu
744
 
            from menu import OliveMenu
745
 
            menu = OliveMenu(path=self.get_path(),
746
 
                             selected=self.get_selected_left(),
747
 
                             app=self)
748
 
            
749
 
            menu.left_context_menu().popup(None, None, None, 0,
750
 
                                           event.time)
751
 
 
752
 
    def on_treeview_left_row_activated(self, treeview, path, view_column):
753
 
        """ Occurs when somebody double-clicks or enters an item in the
754
 
        bookmark list. """
755
 
 
756
 
        newdir = self.get_selected_left()
757
 
        if newdir == None:
758
 
            return
759
 
 
760
 
        if self.set_path(newdir):
761
 
            self.refresh_right()
762
 
 
763
 
    def on_treeview_right_button_press_event(self, widget, event):
764
 
        """ Occurs when somebody right-clicks in the file list. """
765
 
        if event.button == 3:
766
 
            # Create a menu
767
 
            from menu import OliveMenu
768
 
            menu = OliveMenu(path=self.get_path(),
769
 
                             selected=self.get_selected_right(),
770
 
                             app=self)
771
 
            # get the menu items
772
 
            m_open = menu.ui.get_widget('/context_right/open')
773
 
            m_add = menu.ui.get_widget('/context_right/add')
774
 
            m_remove = menu.ui.get_widget('/context_right/remove')
775
 
            m_rename = menu.ui.get_widget('/context_right/rename')
776
 
            m_revert = menu.ui.get_widget('/context_right/revert')
777
 
            m_commit = menu.ui.get_widget('/context_right/commit')
778
 
            m_annotate = menu.ui.get_widget('/context_right/annotate')
779
 
            m_diff = menu.ui.get_widget('/context_right/diff')
780
 
            # check if we're in a branch
781
 
            try:
782
 
                from bzrlib.branch import Branch
783
 
                Branch.open_containing(self.get_path())
784
 
                if self.remote:
785
 
                    m_open.set_sensitive(False)
786
 
                    m_add.set_sensitive(False)
787
 
                    m_remove.set_sensitive(False)
788
 
                    m_rename.set_sensitive(False)
789
 
                    m_revert.set_sensitive(False)
790
 
                    m_commit.set_sensitive(False)
791
 
                    m_annotate.set_sensitive(False)
792
 
                    m_diff.set_sensitive(False)
793
 
                else:
794
 
                    m_open.set_sensitive(True)
795
 
                    m_add.set_sensitive(True)
796
 
                    m_remove.set_sensitive(True)
797
 
                    m_rename.set_sensitive(True)
798
 
                    m_revert.set_sensitive(True)
799
 
                    m_commit.set_sensitive(True)
800
 
                    m_annotate.set_sensitive(True)
801
 
                    m_diff.set_sensitive(True)
802
 
            except bzrerrors.NotBranchError:
803
 
                m_open.set_sensitive(True)
804
 
                m_add.set_sensitive(False)
805
 
                m_remove.set_sensitive(False)
806
 
                m_rename.set_sensitive(False)
807
 
                m_revert.set_sensitive(False)
808
 
                m_commit.set_sensitive(False)
809
 
                m_annotate.set_sensitive(False)
810
 
                m_diff.set_sensitive(False)
811
 
 
812
 
            if not self.remote:
813
 
                menu.right_context_menu().popup(None, None, None, 0,
814
 
                                                event.time)
815
 
            else:
816
 
                menu.remote_context_menu().popup(None, None, None, 0,
817
 
                                                 event.time)
818
 
        
819
 
    def on_treeview_right_row_activated(self, treeview, path, view_column):
820
 
        """ Occurs when somebody double-clicks or enters an item in the
821
 
        file list. """
822
 
        from launch import launch
823
 
        
824
 
        newdir = self.get_selected_right()
825
 
        
826
 
        if not self.remote:
827
 
            # We're local
828
 
            if newdir == '..':
829
 
                self.set_path(os.path.split(self.get_path())[0])
830
 
            else:
831
 
                fullpath = os.path.join(self.get_path(), newdir)
832
 
                if os.path.isdir(fullpath):
833
 
                    # selected item is an existant directory
834
 
                    self.set_path(fullpath)
835
 
                else:
836
 
                    launch(fullpath)
837
 
        else:
838
 
            # We're remote
839
 
            if self._is_remote_dir(self.get_path() + newdir):
840
 
                self.set_path(self.get_path() + newdir)
841
 
        
842
 
        self.refresh_right()
843
 
    
844
 
    def on_window_main_delete_event(self, widget, event=None):
845
 
        """ Do some stuff before exiting. """
846
 
        width, height = self.window_main.get_size()
847
 
        self.pref.set_preference('window_width', width)
848
 
        self.pref.set_preference('window_height', height)
849
 
        x, y = self.window_main.get_position()
850
 
        self.pref.set_preference('window_x', x)
851
 
        self.pref.set_preference('window_y', y)
852
 
        self.pref.set_preference('paned_position',
853
 
                                 self.hpaned_main.get_position())
854
 
        
855
 
        self.pref.write()
856
 
        self.window_main.destroy()
857
 
        
858
 
    def _load_left(self):
859
 
        """ Load data into the left panel. (Bookmarks) """
860
 
        # Create TreeStore
861
 
        treestore = gtk.TreeStore(str, str)
862
 
        
863
 
        # Get bookmarks
864
 
        bookmarks = self.pref.get_bookmarks()
865
 
        
866
 
        # Add them to the TreeStore
867
 
        titer = treestore.append(None, [_('Bookmarks'), None])
868
 
        for item in bookmarks:
869
 
            title = self.pref.get_bookmark_title(item)
870
 
            treestore.append(titer, [title, item])
871
 
        
872
 
        # Create the column and add it to the TreeView
873
 
        self.treeview_left.set_model(treestore)
874
 
        tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
875
 
        self.treeview_left.append_column(tvcolumn_bookmark)
876
 
        
877
 
        # Set up the cells
878
 
        cell = gtk.CellRendererText()
879
 
        tvcolumn_bookmark.pack_start(cell, True)
880
 
        tvcolumn_bookmark.add_attribute(cell, 'text', 0)
881
 
        
882
 
        # Expand the tree
883
 
        self.treeview_left.expand_all()
884
 
 
885
 
    def _load_right(self):
886
 
        """ Load data into the right panel. (Filelist) """
887
 
        # Create ListStore
888
 
        # Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
889
 
        liststore = gtk.ListStore(gobject.TYPE_STRING,
890
 
                                  gobject.TYPE_BOOLEAN,
891
 
                                  gobject.TYPE_STRING,
892
 
                                  gobject.TYPE_STRING,
893
 
                                  gobject.TYPE_STRING,
894
 
                                  gobject.TYPE_STRING,
895
 
                                  gobject.TYPE_STRING,
896
 
                                  gobject.TYPE_INT,
897
 
                                  gobject.TYPE_STRING,
898
 
                                  gobject.TYPE_STRING)
899
 
        
900
 
        dirs = []
901
 
        files = []
902
 
        
903
 
        # Fill the appropriate lists
904
 
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
905
 
        for item in os.listdir(self.path):
906
 
            if not dotted_files and item[0] == '.':
907
 
                continue
908
 
            if os.path.isdir(self.path + os.sep + item):
909
 
                dirs.append(item)
910
 
            else:
911
 
                files.append(item)
912
 
        
913
 
        if not self.notbranch:
914
 
            branch = self.wt.branch
915
 
            tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
916
 
        
917
 
            delta = self.wt.changes_from(tree2, want_unchanged=True)
918
 
        
919
 
        # Add'em to the ListStore
920
 
        for item in dirs:
921
 
            statinfo = os.stat(self.path + os.sep + item)
922
 
            liststore.append([ gtk.STOCK_DIRECTORY,
923
 
                               True,
924
 
                               item,
925
 
                               '',
926
 
                               '',
927
 
                               "<DIR>",
928
 
                               "<DIR>",
929
 
                               statinfo.st_mtime,
930
 
                               self._format_date(statinfo.st_mtime),
931
 
                               ''])
932
 
        for item in files:
933
 
            status = 'unknown'
934
 
            fileid = ''
935
 
            if not self.notbranch:
936
 
                filename = self.wt.relpath(self.path + os.sep + item)
937
 
                
938
 
                try:
939
 
                    self.wt.lock_read()
940
 
                    
941
 
                    for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
942
 
                        if rpathnew == filename:
943
 
                            status = 'renamed'
944
 
                            fileid = id
945
 
                    for rpath, id, kind in delta.added:
946
 
                        if rpath == filename:
947
 
                            status = 'added'
948
 
                            fileid = id
949
 
                    for rpath, id, kind in delta.removed:
950
 
                        if rpath == filename:
951
 
                            status = 'removed'
952
 
                            fileid = id
953
 
                    for rpath, id, kind, text_modified, meta_modified in delta.modified:
954
 
                        if rpath == filename:
955
 
                            status = 'modified'
956
 
                            fileid = id
957
 
                    for rpath, id, kind in delta.unchanged:
958
 
                        if rpath == filename:
959
 
                            status = 'unchanged'
960
 
                            fileid = id
961
 
                    for rpath, file_class, kind, id, entry in self.wt.list_files():
962
 
                        if rpath == filename and file_class == 'I':
963
 
                            status = 'ignored'
964
 
                finally:
965
 
                    self.wt.unlock()
966
 
            
967
 
            if status == 'renamed':
968
 
                st = _('renamed')
969
 
            elif status == 'removed':
970
 
                st = _('removed')
971
 
            elif status == 'added':
972
 
                st = _('added')
973
 
            elif status == 'modified':
974
 
                st = _('modified')
975
 
            elif status == 'unchanged':
976
 
                st = _('unchanged')
977
 
            elif status == 'ignored':
978
 
                st = _('ignored')
979
 
            else:
980
 
                st = _('unknown')
981
 
            
982
 
            statinfo = os.stat(self.path + os.sep + item)
983
 
            liststore.append([gtk.STOCK_FILE,
984
 
                              False,
985
 
                              item,
986
 
                              st,
987
 
                              status,
988
 
                              str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
989
 
                              self._format_size(statinfo.st_size),
990
 
                              statinfo.st_mtime,
991
 
                              self._format_date(statinfo.st_mtime),
992
 
                              fileid])
993
 
        
994
 
        # Create the columns and add them to the TreeView
995
 
        self.treeview_right.set_model(liststore)
996
 
        self._tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
997
 
        self._tvcolumn_status = gtk.TreeViewColumn(_('Status'))
998
 
        self._tvcolumn_size = gtk.TreeViewColumn(_('Size'))
999
 
        self._tvcolumn_mtime = gtk.TreeViewColumn(_('Last modified'))
1000
 
        self.treeview_right.append_column(self._tvcolumn_filename)
1001
 
        self.treeview_right.append_column(self._tvcolumn_status)
1002
 
        self.treeview_right.append_column(self._tvcolumn_size)
1003
 
        self.treeview_right.append_column(self._tvcolumn_mtime)
1004
 
        
1005
 
        # Set up the cells
1006
 
        cellpb = gtk.CellRendererPixbuf()
1007
 
        cell = gtk.CellRendererText()
1008
 
        self._tvcolumn_filename.pack_start(cellpb, False)
1009
 
        self._tvcolumn_filename.pack_start(cell, True)
1010
 
        self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
1011
 
        self._tvcolumn_filename.add_attribute(cell, 'text', 2)
1012
 
        self._tvcolumn_status.pack_start(cell, True)
1013
 
        self._tvcolumn_status.add_attribute(cell, 'text', 3)
1014
 
        self._tvcolumn_size.pack_start(cell, True)
1015
 
        self._tvcolumn_size.add_attribute(cell, 'text', 6)
1016
 
        self._tvcolumn_mtime.pack_start(cell, True)
1017
 
        self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
1018
 
        
1019
 
        # Set up the properties of the TreeView
1020
 
        self.treeview_right.set_headers_visible(True)
1021
 
        self.treeview_right.set_headers_clickable(True)
1022
 
        self.treeview_right.set_search_column(1)
1023
 
        self._tvcolumn_filename.set_resizable(True)
1024
 
        self._tvcolumn_status.set_resizable(True)
1025
 
        self._tvcolumn_size.set_resizable(True)
1026
 
        self._tvcolumn_mtime.set_resizable(True)
1027
 
        # Set up sorting
1028
 
        liststore.set_sort_func(13, self._sort_filelist_callback, None)
1029
 
        liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
1030
 
        self._tvcolumn_filename.set_sort_column_id(13)
1031
 
        self._tvcolumn_status.set_sort_column_id(3)
1032
 
        self._tvcolumn_size.set_sort_column_id(5)
1033
 
        self._tvcolumn_mtime.set_sort_column_id(7)
1034
 
        
1035
 
        # Set sensitivity
1036
 
        self.set_sensitivity()
1037
 
        
1038
 
    def get_selected_fileid(self):
1039
 
        """ Get the file_id of the selected file. """
1040
 
        treeselection = self.treeview_right.get_selection()
1041
 
        (model, iter) = treeselection.get_selected()
1042
 
        
1043
 
        if iter is None:
1044
 
            return None
1045
 
        else:
1046
 
            return model.get_value(iter, 9)
1047
 
    
1048
 
    def get_selected_right(self):
1049
 
        """ Get the selected filename. """
1050
 
        treeselection = self.treeview_right.get_selection()
1051
 
        (model, iter) = treeselection.get_selected()
1052
 
        
1053
 
        if iter is None:
1054
 
            return None
1055
 
        else:
1056
 
            return model.get_value(iter, 2)
1057
 
    
1058
 
    def get_selected_left(self):
1059
 
        """ Get the selected bookmark. """
1060
 
        treeselection = self.treeview_left.get_selection()
1061
 
        (model, iter) = treeselection.get_selected()
1062
 
        
1063
 
        if iter is None:
1064
 
            return None
1065
 
        else:
1066
 
            return model.get_value(iter, 1)
1067
 
 
1068
 
    def set_statusbar(self, message):
1069
 
        """ Set the statusbar message. """
1070
 
        self.statusbar.push(self.context_id, message)
1071
 
    
1072
 
    def clear_statusbar(self):
1073
 
        """ Clean the last message from the statusbar. """
1074
 
        self.statusbar.pop(self.context_id)
1075
 
    
1076
 
    def set_sensitivity(self):
1077
 
        """ Set menu and toolbar sensitivity. """
1078
 
        if not self.remote:
1079
 
            # We're local
1080
 
            self.menuitem_branch_init.set_sensitive(self.notbranch)
1081
 
            self.menuitem_branch_get.set_sensitive(self.notbranch)
1082
 
            self.menuitem_branch_checkout.set_sensitive(self.notbranch)
1083
 
            self.menuitem_branch_pull.set_sensitive(not self.notbranch)
1084
 
            self.menuitem_branch_push.set_sensitive(not self.notbranch)
1085
 
            self.menuitem_branch_update.set_sensitive(not self.notbranch)
1086
 
            self.menuitem_branch_revert.set_sensitive(not self.notbranch)
1087
 
            self.menuitem_branch_merge.set_sensitive(not self.notbranch)
1088
 
            self.menuitem_branch_commit.set_sensitive(not self.notbranch)
1089
 
            self.menuitem_branch_tags.set_sensitive(not self.notbranch)
1090
 
            self.menuitem_branch_status.set_sensitive(not self.notbranch)
1091
 
            self.menuitem_branch_missing.set_sensitive(not self.notbranch)
1092
 
            self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
1093
 
            self.menuitem_stats.set_sensitive(not self.notbranch)
1094
 
            self.menuitem_stats_diff.set_sensitive(not self.notbranch)
1095
 
            self.menuitem_add_files.set_sensitive(not self.notbranch)
1096
 
            self.menuitem_remove_files.set_sensitive(not self.notbranch)
1097
 
            self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
1098
 
            self.menuitem_file_rename.set_sensitive(not self.notbranch)
1099
 
            self.menuitem_file_move.set_sensitive(not self.notbranch)
1100
 
            self.menuitem_file_annotate.set_sensitive(not self.notbranch)
1101
 
            #self.menutoolbutton_diff.set_sensitive(True)
1102
 
            self.toolbutton_diff.set_sensitive(not self.notbranch)
1103
 
            self.toolbutton_log.set_sensitive(not self.notbranch)
1104
 
            self.toolbutton_commit.set_sensitive(not self.notbranch)
1105
 
            self.toolbutton_pull.set_sensitive(not self.notbranch)
1106
 
            self.toolbutton_push.set_sensitive(not self.notbranch)
1107
 
            self.toolbutton_update.set_sensitive(not self.notbranch)
1108
 
        else:
1109
 
            # We're remote
1110
 
            self.menuitem_branch_init.set_sensitive(False)
1111
 
            self.menuitem_branch_get.set_sensitive(True)
1112
 
            self.menuitem_branch_checkout.set_sensitive(True)
1113
 
            self.menuitem_branch_pull.set_sensitive(False)
1114
 
            self.menuitem_branch_push.set_sensitive(False)
1115
 
            self.menuitem_branch_update.set_sensitive(False)
1116
 
            self.menuitem_branch_revert.set_sensitive(False)
1117
 
            self.menuitem_branch_merge.set_sensitive(False)
1118
 
            self.menuitem_branch_commit.set_sensitive(False)
1119
 
            self.menuitem_branch_tags.set_sensitive(True)
1120
 
            self.menuitem_branch_status.set_sensitive(False)
1121
 
            self.menuitem_branch_missing.set_sensitive(False)
1122
 
            self.menuitem_branch_conflicts.set_sensitive(False)
1123
 
            self.menuitem_stats.set_sensitive(True)
1124
 
            self.menuitem_stats_diff.set_sensitive(False)
1125
 
            self.menuitem_add_files.set_sensitive(False)
1126
 
            self.menuitem_remove_files.set_sensitive(False)
1127
 
            self.menuitem_file_make_directory.set_sensitive(False)
1128
 
            self.menuitem_file_rename.set_sensitive(False)
1129
 
            self.menuitem_file_move.set_sensitive(False)
1130
 
            self.menuitem_file_annotate.set_sensitive(False)
1131
 
            #self.menutoolbutton_diff.set_sensitive(True)
1132
 
            self.toolbutton_diff.set_sensitive(False)
1133
 
            self.toolbutton_log.set_sensitive(True)
1134
 
            self.toolbutton_commit.set_sensitive(False)
1135
 
            self.toolbutton_pull.set_sensitive(False)
1136
 
            self.toolbutton_push.set_sensitive(False)
1137
 
            self.toolbutton_update.set_sensitive(False)
1138
 
    
1139
 
    def refresh_left(self):
1140
 
        """ Refresh the bookmark list. """
1141
 
        
1142
 
        # Get TreeStore and clear it
1143
 
        treestore = self.treeview_left.get_model()
1144
 
        treestore.clear()
1145
 
 
1146
 
        # Re-read preferences
1147
 
        self.pref.read()
1148
 
        
1149
 
        # Get bookmarks
1150
 
        bookmarks = self.pref.get_bookmarks()
1151
 
 
1152
 
        # Add them to the TreeStore
1153
 
        titer = treestore.append(None, [_('Bookmarks'), None])
1154
 
        for item in bookmarks:
1155
 
            title = self.pref.get_bookmark_title(item)
1156
 
            treestore.append(titer, [title, item])
1157
 
 
1158
 
        # Add the TreeStore to the TreeView
1159
 
        self.treeview_left.set_model(treestore)
1160
 
 
1161
 
        # Expand the tree
1162
 
        self.treeview_left.expand_all()
1163
 
 
1164
 
    def refresh_right(self, path=None):
1165
 
        """ Refresh the file list. """
1166
 
        if not self.remote:
1167
 
            # We're local
1168
 
            from bzrlib.workingtree import WorkingTree
1169
 
    
1170
 
            if path is None:
1171
 
                path = self.get_path()
1172
 
    
1173
 
            # A workaround for double-clicking Bookmarks
1174
 
            if not os.path.exists(path):
1175
 
                return
1176
 
    
1177
 
            # Get ListStore and clear it
1178
 
            liststore = self.treeview_right.get_model()
1179
 
            liststore.clear()
1180
 
            
1181
 
            # Show Status column
1182
 
            self._tvcolumn_status.set_visible(True)
1183
 
    
1184
 
            dirs = []
1185
 
            files = []
1186
 
    
1187
 
            # Fill the appropriate lists
1188
 
            dotted_files = self.pref.get_preference('dotted_files', 'bool')
1189
 
            ignored_files = self.pref.get_preference('ignored_files', 'bool')
1190
 
 
1191
 
            for item in os.listdir(path):
1192
 
                if not dotted_files and item[0] == '.':
1193
 
                    continue
1194
 
                if os.path.isdir(path + os.sep + item):
1195
 
                    dirs.append(item)
1196
 
                else:
1197
 
                    files.append(item)
1198
 
            
1199
 
            # Try to open the working tree
1200
 
            notbranch = False
1201
 
            try:
1202
 
                tree1 = WorkingTree.open_containing(path)[0]
1203
 
            except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
1204
 
                notbranch = True
1205
 
            
1206
 
            if not notbranch:
1207
 
                branch = tree1.branch
1208
 
                tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
1209
 
            
1210
 
                delta = tree1.changes_from(tree2, want_unchanged=True)
1211
 
                
1212
 
            # Add'em to the ListStore
1213
 
            for item in dirs:
1214
 
                statinfo = os.stat(self.path + os.sep + item)
1215
 
                liststore.append([gtk.STOCK_DIRECTORY,
1216
 
                                  True,
1217
 
                                  item,
1218
 
                                  '',
1219
 
                                  '',
1220
 
                                  "<DIR>",
1221
 
                                  "<DIR>",
1222
 
                                  statinfo.st_mtime,
1223
 
                                  self._format_date(statinfo.st_mtime),
1224
 
                                  ''])
1225
 
            for item in files:
1226
 
                status = 'unknown'
1227
 
                fileid = ''
1228
 
                if not notbranch:
1229
 
                    filename = tree1.relpath(path + os.sep + item)
1230
 
                    
1231
 
                    try:
1232
 
                        self.wt.lock_read()
1233
 
                        
1234
 
                        for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1235
 
                            if rpathnew == filename:
1236
 
                                status = 'renamed'
1237
 
                                fileid = id
1238
 
                        for rpath, id, kind in delta.added:
1239
 
                            if rpath == filename:
1240
 
                                status = 'added'
1241
 
                                fileid = id
1242
 
                        for rpath, id, kind in delta.removed:
1243
 
                            if rpath == filename:
1244
 
                                status = 'removed'
1245
 
                                fileid = id
1246
 
                        for rpath, id, kind, text_modified, meta_modified in delta.modified:
1247
 
                            if rpath == filename:
1248
 
                                status = 'modified'
1249
 
                                fileid = id
1250
 
                        for rpath, id, kind in delta.unchanged:
1251
 
                            if rpath == filename:
1252
 
                                status = 'unchanged'
1253
 
                                fileid = id
1254
 
                        for rpath, file_class, kind, id, entry in self.wt.list_files():
1255
 
                            if rpath == filename and file_class == 'I':
1256
 
                                status = 'ignored'
1257
 
                    finally:
1258
 
                        self.wt.unlock()
1259
 
                
1260
 
                if status == 'renamed':
1261
 
                    st = _('renamed')
1262
 
                elif status == 'removed':
1263
 
                    st = _('removed')
1264
 
                elif status == 'added':
1265
 
                    st = _('added')
1266
 
                elif status == 'modified':
1267
 
                    st = _('modified')
1268
 
                elif status == 'unchanged':
1269
 
                    st = _('unchanged')
1270
 
                elif status == 'ignored':
1271
 
                    st = _('ignored')
1272
 
                    if not ignored_files:
1273
 
                        continue
1274
 
                else:
1275
 
                    st = _('unknown')
1276
 
                
1277
 
                statinfo = os.stat(self.path + os.sep + item)
1278
 
                liststore.append([gtk.STOCK_FILE,
1279
 
                                  False,
1280
 
                                  item,
1281
 
                                  st,
1282
 
                                  status,
1283
 
                                  str(statinfo.st_size),
1284
 
                                  self._format_size(statinfo.st_size),
1285
 
                                  statinfo.st_mtime,
1286
 
                                  self._format_date(statinfo.st_mtime),
1287
 
                                  fileid])
1288
 
        else:
1289
 
            # We're remote
1290
 
            
1291
 
            # Get ListStore and clear it
1292
 
            liststore = self.treeview_right.get_model()
1293
 
            liststore.clear()
1294
 
            
1295
 
            # Hide Status column
1296
 
            self._tvcolumn_status.set_visible(False)
1297
 
            
1298
 
            dirs = []
1299
 
            files = []
1300
 
            
1301
 
            self._show_stock_image(gtk.STOCK_REFRESH)
1302
 
            
1303
 
            for (name, type) in self.remote_entries:
1304
 
                if type.kind == 'directory':
1305
 
                    dirs.append(type)
1306
 
                elif type.kind == 'file':
1307
 
                    files.append(type)
1308
 
            
1309
 
            class HistoryCache:
1310
 
                """ Cache based on revision history. """
1311
 
                def __init__(self, history):
1312
 
                    self._history = history
1313
 
                
1314
 
                def _lookup_revision(self, revid):
1315
 
                    for r in self._history:
1316
 
                        if r.revision_id == revid:
1317
 
                            return r
1318
 
                    rev = repo.get_revision(revid)
1319
 
                    self._history.append(rev)
1320
 
                    return rev
1321
 
            
1322
 
            repo = self.remote_branch.repository
1323
 
            
1324
 
            revhistory = self.remote_branch.revision_history()
1325
 
            try:
1326
 
                revs = repo.get_revisions(revhistory)
1327
 
                cache = HistoryCache(revs)
1328
 
            except bzrerrors.InvalidHttpResponse:
1329
 
                # Fallback to dummy algorithm, because of LP: #115209
1330
 
                cache = HistoryCache([])
1331
 
            
1332
 
            for item in dirs:
1333
 
                if item.parent_id == self.remote_parent:
1334
 
                    rev = cache._lookup_revision(item.revision)
1335
 
                    liststore.append([ gtk.STOCK_DIRECTORY,
1336
 
                                       True,
1337
 
                                       item.name,
1338
 
                                       '',
1339
 
                                       '',
1340
 
                                       "<DIR>",
1341
 
                                       "<DIR>",
1342
 
                                       rev.timestamp,
1343
 
                                       self._format_date(rev.timestamp),
1344
 
                                       ''
1345
 
                                   ])
1346
 
                while gtk.events_pending():
1347
 
                    gtk.main_iteration()
1348
 
            
1349
 
            for item in files:
1350
 
                if item.parent_id == self.remote_parent:
1351
 
                    rev = cache._lookup_revision(item.revision)
1352
 
                    liststore.append([ gtk.STOCK_FILE,
1353
 
                                       False,
1354
 
                                       item.name,
1355
 
                                       '',
1356
 
                                       '',
1357
 
                                       str(item.text_size),
1358
 
                                       self._format_size(item.text_size),
1359
 
                                       rev.timestamp,
1360
 
                                       self._format_date(rev.timestamp),
1361
 
                                       item.file_id
1362
 
                                   ])
1363
 
                while gtk.events_pending():
1364
 
                    gtk.main_iteration()
1365
 
            
1366
 
            self.image_location_error.destroy()
1367
 
 
1368
 
        # Columns should auto-size
1369
 
        self.treeview_right.columns_autosize()
1370
 
        
1371
 
        # Set sensitivity
1372
 
        self.set_sensitivity()
1373
 
 
1374
 
    def _harddisks(self):
1375
 
        """ Returns hard drive letters under Win32. """
1376
 
        try:
1377
 
            import win32file
1378
 
            import string
1379
 
        except ImportError:
1380
 
            if sys.platform == 'win32':
1381
 
                print "pyWin32 modules needed to run Olive on Win32."
1382
 
                sys.exit(1)
1383
 
            else:
1384
 
                pass
1385
 
        
1386
 
        driveletters = []
1387
 
        for drive in string.ascii_uppercase:
1388
 
            if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
1389
 
                driveletters.append(drive+':')
1390
 
        return driveletters
1391
 
    
1392
 
    def gen_hard_selector(self):
1393
 
        """ Generate the hard drive selector under Win32. """
1394
 
        drives = self._harddisks()
1395
 
        for drive in drives:
1396
 
            self.combobox_drive.append_text(drive)
1397
 
        self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1398
 
    
1399
 
    def _refresh_drives(self, combobox):
1400
 
        if self._just_started:
1401
 
            return
1402
 
        model = combobox.get_model()
1403
 
        active = combobox.get_active()
1404
 
        if active >= 0:
1405
 
            drive = model[active][0]
1406
 
            self.set_path(drive + '\\')
1407
 
            self.refresh_right(drive + '\\')
1408
 
    
1409
 
    def check_for_changes(self):
1410
 
        """ Check whether there were changes in the current working tree. """
1411
 
        old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
1412
 
        delta = self.wt.changes_from(old_tree)
1413
 
 
1414
 
        changes = False
1415
 
        
1416
 
        if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
1417
 
            changes = True
1418
 
        
1419
 
        return changes
1420
 
    
1421
 
    def _sort_filelist_callback(self, model, iter1, iter2, data):
1422
 
        """ The sort callback for the file list, return values:
1423
 
        -1: iter1 < iter2
1424
 
        0: iter1 = iter2
1425
 
        1: iter1 > iter2
1426
 
        """
1427
 
        name1 = model.get_value(iter1, 2)
1428
 
        name2 = model.get_value(iter2, 2)
1429
 
        
1430
 
        if model.get_value(iter1, 1):
1431
 
            # item1 is a directory
1432
 
            if not model.get_value(iter2, 1):
1433
 
                # item2 isn't
1434
 
                return -1
1435
 
            else:
1436
 
                # both of them are directories, we compare their names
1437
 
                if name1 < name2:
1438
 
                    return -1
1439
 
                elif name1 == name2:
1440
 
                    return 0
1441
 
                else:
1442
 
                    return 1
1443
 
        else:
1444
 
            # item1 is not a directory
1445
 
            if model.get_value(iter2, 1):
1446
 
                # item2 is
1447
 
                return 1
1448
 
            else:
1449
 
                # both of them are files, compare them
1450
 
                if name1 < name2:
1451
 
                    return -1
1452
 
                elif name1 == name2:
1453
 
                    return 0
1454
 
                else:
1455
 
                    return 1
1456
 
    
1457
 
    def _format_size(self, size):
1458
 
        """ Format size to a human readable format. """
1459
 
        if size < 1000:
1460
 
            return "%d[B]" % (size,)
1461
 
        size = size / 1000.0
1462
 
        
1463
 
        for metric in ["kB","MB","GB","TB"]:
1464
 
            if size < 1000:
1465
 
                break
1466
 
            size = size / 1000.0
1467
 
        return "%.1f[%s]" % (size,metric) 
1468
 
    
1469
 
    def _format_date(self, timestamp):
1470
 
        """ Format the time (given in secs) to a human readable format. """
1471
 
        return time.ctime(timestamp)
1472
 
    
1473
 
    def _is_remote_dir(self, location):
1474
 
        """ Determine whether the given location is a directory or not. """
1475
 
        if not self.remote:
1476
 
            # We're in local mode
1477
 
            return False
1478
 
        else:
1479
 
            branch, path = Branch.open_containing(location)
1480
 
            for (name, type) in self.remote_entries:
1481
 
                if name == path and type.kind == 'directory':
1482
 
                    # We got it
1483
 
                    return True
1484
 
            # Either it's not a directory or not in the inventory
1485
 
            return False
1486
 
    
1487
 
    def _show_stock_image(self, stock_id):
1488
 
        """ Show a stock image next to the location entry. """
1489
 
        self.image_location_error.destroy()
1490
 
        self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
1491
 
        self.hbox_location.pack_start(self.image_location_error, False, False, 0)
1492
 
        if sys.platform == 'win32':
1493
 
            self.hbox_location.reorder_child(self.image_location_error, 2)
1494
 
        else:
1495
 
            self.hbox_location.reorder_child(self.image_location_error, 1)
1496
 
        self.image_location_error.show()
1497
 
        while gtk.events_pending():
1498
 
            gtk.main_iteration()
1499
 
 
1500
 
import ConfigParser
1501
 
 
1502
 
class Preferences:
1503
 
    """ A class which handles Olive's preferences. """
1504
 
    def __init__(self, path=None):
1505
 
        """ Initialize the Preferences class. """
1506
 
        # Some default options
1507
 
        self.defaults = { 'strict_commit' : False,
1508
 
                          'dotted_files'  : False,
1509
 
                          'ignored_files' : True,
1510
 
                          'window_width'  : 700,
1511
 
                          'window_height' : 400,
1512
 
                          'window_x'      : 40,
1513
 
                          'window_y'      : 40,
1514
 
                          'paned_position': 200 }
1515
 
 
1516
 
        # Create a config parser object
1517
 
        self.config = ConfigParser.RawConfigParser()
1518
 
 
1519
 
        # Set filename
1520
 
        if path is None:
1521
 
            if sys.platform == 'win32':
1522
 
                # Windows - no dotted files
1523
 
                self._filename = os.path.expanduser('~/olive.conf')
1524
 
            else:
1525
 
                self._filename = os.path.expanduser('~/.olive.conf')
1526
 
        else:
1527
 
            self._filename = path
1528
 
        
1529
 
        # Load the configuration
1530
 
        self.read()
1531
 
        
1532
 
    def _get_default(self, option):
1533
 
        """ Get the default option for a preference. """
1534
 
        try:
1535
 
            ret = self.defaults[option]
1536
 
        except KeyError:
1537
 
            return None
1538
 
        else:
1539
 
            return ret
1540
 
 
1541
 
    def refresh(self):
1542
 
        """ Refresh the configuration. """
1543
 
        # First write out the changes
1544
 
        self.write()
1545
 
        # Then load the configuration again
1546
 
        self.read()
1547
 
 
1548
 
    def read(self):
1549
 
        """ Just read the configuration. """
1550
 
        # Re-initialize the config parser object to avoid some bugs
1551
 
        self.config = ConfigParser.RawConfigParser()
1552
 
        self.config.read([self._filename])
1553
 
    
1554
 
    def write(self):
1555
 
        """ Write the configuration to the appropriate files. """
1556
 
        fp = open(self._filename, 'w')
1557
 
        self.config.write(fp)
1558
 
        fp.close()
1559
 
 
1560
 
    def get_bookmarks(self):
1561
 
        """ Return the list of bookmarks. """
1562
 
        bookmarks = self.config.sections()
1563
 
        if self.config.has_section('preferences'):
1564
 
            bookmarks.remove('preferences')
1565
 
        return bookmarks
1566
 
 
1567
 
    def add_bookmark(self, path):
1568
 
        """ Add bookmark. """
1569
 
        try:
1570
 
            self.config.add_section(path)
1571
 
        except ConfigParser.DuplicateSectionError:
1572
 
            return False
1573
 
        else:
1574
 
            return True
1575
 
 
1576
 
    def get_bookmark_title(self, path):
1577
 
        """ Get bookmark title. """
1578
 
        try:
1579
 
            ret = self.config.get(path, 'title')
1580
 
        except ConfigParser.NoOptionError:
1581
 
            ret = path
1582
 
        
1583
 
        return ret
1584
 
    
1585
 
    def set_bookmark_title(self, path, title):
1586
 
        """ Set bookmark title. """
1587
 
        # FIXME: What if path isn't listed yet?
1588
 
        # FIXME: Canonicalize paths first?
1589
 
        self.config.set(path, 'title', title)
1590
 
    
1591
 
    def remove_bookmark(self, path):
1592
 
        """ Remove bookmark. """
1593
 
        return self.config.remove_section(path)
1594
 
 
1595
 
    def set_preference(self, option, value):
1596
 
        """ Set the value of the given option. """
1597
 
        if value is True:
1598
 
            value = 'yes'
1599
 
        elif value is False:
1600
 
            value = 'no'
1601
 
        
1602
 
        if self.config.has_section('preferences'):
1603
 
            self.config.set('preferences', option, value)
1604
 
        else:
1605
 
            self.config.add_section('preferences')
1606
 
            self.config.set('preferences', option, value)
1607
 
 
1608
 
    def get_preference(self, option, kind='str'):
1609
 
        """ Get the value of the given option.
1610
 
        
1611
 
        :param kind: str/bool/int/float. default: str
1612
 
        """
1613
 
        if self.config.has_option('preferences', option):
1614
 
            if kind == 'bool':
1615
 
                return self.config.getboolean('preferences', option)
1616
 
            elif kind == 'int':
1617
 
                return self.config.getint('preferences', option)
1618
 
            elif kind == 'float':
1619
 
                return self.config.getfloat('preferences', option)
1620
 
            else:
1621
 
                return self.config.get('preferences', option)
1622
 
        else:
1623
 
            try:
1624
 
                return self._get_default(option)
1625
 
            except KeyError:
1626
 
                return None
1627