/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
  • Author(s): Kiddo
  • Date: 2007-11-28 18:00:03 UTC
  • mto: This revision was merged to the branch mainline in revision 410.
  • Revision ID: jelmer@samba.org-20071128180003-oq0m5cfuab52ekk1
Make toolbars follow GNOME standards.

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
#     def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
 
506
        selected = self.get_selected_right()
 
507
        if selected:
 
508
            selected = os.path.join(self.wtpath, selected)
 
509
        commit = CommitDialog(wt=self.wt,
 
510
                              parent=self.window,
 
511
                              selected=selected,
 
512
                             )
 
513
        response = commit.run()
 
514
        if response != gtk.RESPONSE_NONE:
 
515
            commit.hide()
 
516
        
 
517
            if response == gtk.RESPONSE_OK:
 
518
                self.refresh_right()
 
519
            
 
520
            commit.destroy()
 
521
    
 
522
    def on_menuitem_branch_conflicts_activate(self, widget):
 
523
        """ Branch/Conflicts... menu handler. """
 
524
        conflicts = ConflictsDialog(self.wt, self.window)
 
525
        response = conflicts.run()
 
526
        if response != gtk.RESPONSE_NONE:
 
527
            conflicts.destroy()
 
528
    
 
529
    def on_menuitem_branch_merge_activate(self, widget):
 
530
        """ Branch/Merge... menu handler. """
 
531
        from bzrlib.plugins.gtk.merge import MergeDialog
 
532
        
 
533
        if self.check_for_changes():
 
534
            error_dialog(_('There are local changes in the branch'),
 
535
                         _('Please commit or revert the changes before merging.'))
 
536
        else:
 
537
            parent_branch_path = self.wt.branch.get_parent()
 
538
            merge = MergeDialog(self.wt, self.wtpath,default_branch_path=parent_branch_path )
 
539
            merge.display()
 
540
 
 
541
    @show_bzr_error
 
542
    def on_menuitem_branch_missing_revisions_activate(self, widget):
 
543
        """ Branch/Missing revisions menu handler. """
 
544
        
 
545
        from bzrlib.missing import find_unmerged, iter_log_revisions
 
546
        
 
547
        local_branch = self.wt.branch
 
548
        parent_branch_path = local_branch.get_parent()
 
549
        if parent_branch_path is None:
 
550
            error_dialog(_('Parent location is unknown'),
 
551
                         _('Cannot determine missing revisions if no parent location is known.'))
 
552
            return
 
553
        
 
554
        parent_branch = Branch.open(parent_branch_path)
 
555
        
 
556
        if parent_branch.base == local_branch.base:
 
557
            parent_branch = local_branch
 
558
        
 
559
        local_extra, remote_extra = find_unmerged(local_branch,parent_branch)
 
560
 
 
561
        if local_extra or remote_extra:
 
562
            
 
563
            ## def log_revision_one_line_text(log_revision):
 
564
            ##    """ Generates one line description of log_revison ended with end of line."""
 
565
            ##    revision = log_revision.rev
 
566
            ##    txt =  "- %s (%s)\n" % (revision.get_summary(), revision.committer, )
 
567
            ##    txt = txt.replace("<"," ") # Seems < > chars are expected to be xml tags ...
 
568
            ##    txt = txt.replace(">"," ")
 
569
            ##    return txt
 
570
            
 
571
            dlg_txt = ""
 
572
            if local_extra:
 
573
                dlg_txt += _('%d local extra revision(s). \n') % (len(local_extra),) 
 
574
                ## NOTE: We do not want such ugly info about missing revisions
 
575
                ##       Revision Browser should be used there
 
576
                ## max_revisions = 10
 
577
                ## for log_revision in iter_log_revisions(local_extra, local_branch.repository, verbose=1):
 
578
                ##    dlg_txt += log_revision_one_line_text(log_revision)
 
579
                ##    if max_revisions <= 0:
 
580
                ##        dlg_txt += _("more ... \n")
 
581
                ##        break
 
582
                ## max_revisions -= 1
 
583
            ## dlg_txt += "\n"
 
584
            if remote_extra:
 
585
                dlg_txt += _('%d local missing revision(s).\n') % (len(remote_extra),) 
 
586
                ## max_revisions = 10
 
587
                ## for log_revision in iter_log_revisions(remote_extra, parent_branch.repository, verbose=1):
 
588
                ##    dlg_txt += log_revision_one_line_text(log_revision)
 
589
                ##    if max_revisions <= 0:
 
590
                ##        dlg_txt += _("more ... \n")
 
591
                ##        break
 
592
                ##    max_revisions -= 1
 
593
                
 
594
            info_dialog(_('There are missing revisions'),
 
595
                        dlg_txt)
 
596
        else:
 
597
            info_dialog(_('Local branch up to date'),
 
598
                        _('There are no missing revisions.'))
 
599
 
 
600
    @show_bzr_error
 
601
    def on_menuitem_branch_pull_activate(self, widget):
 
602
        """ Branch/Pull menu handler. """
 
603
        branch_to = self.wt.branch
 
604
 
 
605
        location = branch_to.get_parent()
 
606
        if location is None:
 
607
            error_dialog(_('Parent location is unknown'),
 
608
                                     _('Pulling is not possible until there is a parent location.'))
 
609
            return
 
610
 
 
611
        branch_from = Branch.open(location)
 
612
 
 
613
        if branch_to.get_parent() is None:
 
614
            branch_to.set_parent(branch_from.base)
 
615
 
 
616
        ret = branch_to.pull(branch_from)
 
617
        
 
618
        info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
 
619
        
 
620
    @show_bzr_error
 
621
    def on_menuitem_branch_update_activate(self, widget):
 
622
        """ Brranch/checkout update menu handler. """
 
623
        
 
624
        ret = self.wt.update()
 
625
        conflicts = self.wt.conflicts()
 
626
        if conflicts:
 
627
            info_dialog(_('Update successful but conflicts generated'), _('Number of conflicts generated: %d.') % (len(conflicts),) )
 
628
        else:
 
629
            info_dialog(_('Update successful'), _('No conflicts generated.') )
 
630
    
 
631
    def on_menuitem_branch_push_activate(self, widget):
 
632
        """ Branch/Push... menu handler. """
 
633
        push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
 
634
        response = push.run()
 
635
        if response != gtk.RESPONSE_NONE:
 
636
            push.destroy()
 
637
    
 
638
    @show_bzr_error
 
639
    def on_menuitem_branch_revert_activate(self, widget):
 
640
        """ Branch/Revert all changes menu handler. """
 
641
        ret = self.wt.revert([])
 
642
        if ret:
 
643
            warning_dialog(_('Conflicts detected'),
 
644
                           _('Please have a look at the working tree before continuing.'))
 
645
        else:
 
646
            info_dialog(_('Revert successful'),
 
647
                        _('All files reverted to last revision.'))
 
648
        self.refresh_right()
 
649
    
 
650
    def on_menuitem_branch_status_activate(self, widget):
 
651
        """ Branch/Status... menu handler. """
 
652
        from bzrlib.plugins.gtk.status import StatusDialog
 
653
        status = StatusDialog(self.wt, self.wtpath)
 
654
        response = status.run()
 
655
        if response != gtk.RESPONSE_NONE:
 
656
            status.destroy()
 
657
    
 
658
    def on_menuitem_branch_initialize_activate(self, widget):
 
659
        """ Initialize current directory. """
 
660
        init = InitDialog(self.path, self.window)
 
661
        response = init.run()
 
662
        if response != gtk.RESPONSE_NONE:
 
663
            init.hide()
 
664
        
 
665
            if response == gtk.RESPONSE_OK:
 
666
                self.refresh_right()
 
667
            
 
668
            init.destroy()
 
669
        
 
670
    def on_menuitem_branch_tags_activate(self, widget):
 
671
        """ Branch/Tags... menu handler. """
 
672
        from bzrlib.plugins.gtk.tags import TagsWindow
 
673
        if not self.remote:
 
674
            window = TagsWindow(self.wt.branch, self.window)
 
675
        else:
 
676
            window = TagsWindow(self.remote_branch, self.window)
 
677
        window.show()
 
678
    
 
679
    def on_menuitem_file_annotate_activate(self, widget):
 
680
        """ File/Annotate... menu handler. """
 
681
        if self.get_selected_right() is None:
 
682
            error_dialog(_('No file was selected'),
 
683
                         _('Please select a file from the list.'))
 
684
            return
 
685
        
 
686
        branch = self.wt.branch
 
687
        file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
 
688
        
 
689
        window = GAnnotateWindow(all=False, plain=False, parent=self.window)
 
690
        window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
 
691
        config = GAnnotateConfig(window)
 
692
        window.show()
 
693
        branch.lock_read()
 
694
        try:
 
695
            window.annotate(self.wt, branch, file_id)
 
696
        finally:
 
697
            branch.unlock()
 
698
    
 
699
    def on_menuitem_file_make_directory_activate(self, widget):
 
700
        """ File/Make directory... menu handler. """
 
701
        from mkdir import OliveMkdir
 
702
        mkdir = OliveMkdir(self.wt, self.wtpath)
 
703
        mkdir.display()
 
704
    
 
705
    def on_menuitem_file_move_activate(self, widget):
 
706
        """ File/Move... menu handler. """
 
707
        from move import OliveMove
 
708
        move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
 
709
        move.display()
 
710
    
 
711
    def on_menuitem_file_rename_activate(self, widget):
 
712
        """ File/Rename... menu handler. """
 
713
        from rename import OliveRename
 
714
        rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
 
715
        rename.display()
 
716
 
 
717
    def on_menuitem_remove_file_activate(self, widget):
 
718
        """ Remove (unversion) selected file. """
 
719
        from remove import OliveRemoveDialog
 
720
        remove = OliveRemoveDialog(self.wt, self.wtpath,
 
721
                                   selected=self.get_selected_right(),
 
722
                                   parent=self.window)
 
723
        response = remove.run()
 
724
        
 
725
        if response != gtk.RESPONSE_NONE:
 
726
            remove.hide()
 
727
        
 
728
            if response == gtk.RESPONSE_OK:
 
729
                self.set_path(self.path)
 
730
                self.refresh_right()
 
731
            
 
732
            remove.destroy()
 
733
    
 
734
    def on_menuitem_stats_diff_activate(self, widget):
 
735
        """ Statistics/Differences... menu handler. """
 
736
        window = DiffWindow(parent=self.window)
 
737
        parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
738
        window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
 
739
        window.show()
 
740
    
 
741
    def on_menuitem_stats_infos_activate(self, widget):
 
742
        """ Statistics/Informations... menu handler. """
 
743
        from info import OliveInfo
 
744
        if self.remote:
 
745
            info = OliveInfo(self.remote_branch)
 
746
        else:
 
747
            info = OliveInfo(self.wt.branch)
 
748
        info.display()
 
749
    
 
750
    def on_menuitem_stats_log_activate(self, widget):
 
751
        """ Statistics/Log... menu handler. """
 
752
 
 
753
        if not self.remote:
 
754
            branch = self.wt.branch
 
755
        else:
 
756
            branch = self.remote_branch
 
757
 
 
758
        window = branchwin.BranchWindow(branch, branch.last_revision(), None, parent=self.window)
 
759
        window.show()
 
760
    
 
761
    def on_menuitem_view_refresh_activate(self, widget):
 
762
        """ View/Refresh menu handler. """
 
763
        # Refresh the left pane
 
764
        self.refresh_left()
 
765
        # Refresh the right pane
 
766
        self.refresh_right()
 
767
   
 
768
    def on_menuitem_view_show_hidden_files_activate(self, widget):
 
769
        """ View/Show hidden files menu handler. """
 
770
        self.pref.set_preference('dotted_files', widget.get_active())
 
771
        if self.path is not None:
 
772
            self.refresh_right()
 
773
 
 
774
    def on_menuitem_view_show_ignored_files_activate(self, widget):
 
775
        """ Hide/Show ignored files menu handler. """
 
776
        self.pref.set_preference('ignored_files', widget.get_active())
 
777
        if self.path is not None:
 
778
            self.refresh_right()
 
779
            
 
780
    def on_treeview_left_button_press_event(self, widget, event):
 
781
        """ Occurs when somebody right-clicks in the bookmark list. """
 
782
        if event.button == 3:
 
783
            # Don't show context with nothing selected
 
784
            if self.get_selected_left() == None:
 
785
                return
 
786
 
 
787
            # Create a menu
 
788
            from menu import OliveMenu
 
789
            menu = OliveMenu(path=self.get_path(),
 
790
                             selected=self.get_selected_left(),
 
791
                             app=self)
 
792
            
 
793
            menu.left_context_menu().popup(None, None, None, 0,
 
794
                                           event.time)
 
795
 
 
796
    def on_treeview_left_row_activated(self, treeview, path, view_column):
 
797
        """ Occurs when somebody double-clicks or enters an item in the
 
798
        bookmark list. """
 
799
 
 
800
        newdir = self.get_selected_left()
 
801
        if newdir == None:
 
802
            return
 
803
 
 
804
        if self.set_path(newdir):
 
805
            self.refresh_right()
 
806
 
 
807
    def on_treeview_right_button_press_event(self, widget, event):
 
808
        """ Occurs when somebody right-clicks in the file list. """
 
809
        if event.button == 3:
 
810
            # Create a menu
 
811
            from menu import OliveMenu
 
812
            menu = OliveMenu(path=self.get_path(),
 
813
                             selected=self.get_selected_right(),
 
814
                             app=self)
 
815
            # get the menu items
 
816
            m_open = menu.ui.get_widget('/context_right/open')
 
817
            m_add = menu.ui.get_widget('/context_right/add')
 
818
            m_remove = menu.ui.get_widget('/context_right/remove')
 
819
            m_rename = menu.ui.get_widget('/context_right/rename')
 
820
            m_revert = menu.ui.get_widget('/context_right/revert')
 
821
            m_commit = menu.ui.get_widget('/context_right/commit')
 
822
            m_annotate = menu.ui.get_widget('/context_right/annotate')
 
823
            m_diff = menu.ui.get_widget('/context_right/diff')
 
824
            # check if we're in a branch
 
825
            try:
 
826
                from bzrlib.branch import Branch
 
827
                Branch.open_containing(self.get_path())
 
828
                if self.remote:
 
829
                    m_open.set_sensitive(False)
 
830
                    m_add.set_sensitive(False)
 
831
                    m_remove.set_sensitive(False)
 
832
                    m_rename.set_sensitive(False)
 
833
                    m_revert.set_sensitive(False)
 
834
                    m_commit.set_sensitive(False)
 
835
                    m_annotate.set_sensitive(False)
 
836
                    m_diff.set_sensitive(False)
 
837
                else:
 
838
                    m_open.set_sensitive(True)
 
839
                    m_add.set_sensitive(True)
 
840
                    m_remove.set_sensitive(True)
 
841
                    m_rename.set_sensitive(True)
 
842
                    m_revert.set_sensitive(True)
 
843
                    m_commit.set_sensitive(True)
 
844
                    m_annotate.set_sensitive(True)
 
845
                    m_diff.set_sensitive(True)
 
846
            except bzrerrors.NotBranchError:
 
847
                m_open.set_sensitive(True)
 
848
                m_add.set_sensitive(False)
 
849
                m_remove.set_sensitive(False)
 
850
                m_rename.set_sensitive(False)
 
851
                m_revert.set_sensitive(False)
 
852
                m_commit.set_sensitive(False)
 
853
                m_annotate.set_sensitive(False)
 
854
                m_diff.set_sensitive(False)
 
855
 
 
856
            if not self.remote:
 
857
                menu.right_context_menu().popup(None, None, None, 0,
 
858
                                                event.time)
 
859
            else:
 
860
                menu.remote_context_menu().popup(None, None, None, 0,
 
861
                                                 event.time)
 
862
        
 
863
    def on_treeview_right_row_activated(self, treeview, path, view_column):
 
864
        """ Occurs when somebody double-clicks or enters an item in the
 
865
        file list. """
 
866
        from launch import launch
 
867
        
 
868
        newdir = self.get_selected_right()
 
869
        
 
870
        if not self.remote:
 
871
            # We're local
 
872
            if newdir == '..':
 
873
                self.set_path(os.path.split(self.get_path())[0])
 
874
            else:
 
875
                fullpath = os.path.join(self.get_path(), newdir)
 
876
                if os.path.isdir(fullpath):
 
877
                    # selected item is an existant directory
 
878
                    self.set_path(fullpath)
 
879
                else:
 
880
                    launch(fullpath)
 
881
        else:
 
882
            # We're remote
 
883
            if self._is_remote_dir(self.get_path() + newdir):
 
884
                self.set_path(self.get_path() + newdir)
 
885
        
 
886
        self.refresh_right()
 
887
    
 
888
    def on_window_main_delete_event(self, widget, event=None):
 
889
        """ Do some stuff before exiting. """
 
890
        width, height = self.window_main.get_size()
 
891
        self.pref.set_preference('window_width', width)
 
892
        self.pref.set_preference('window_height', height)
 
893
        x, y = self.window_main.get_position()
 
894
        self.pref.set_preference('window_x', x)
 
895
        self.pref.set_preference('window_y', y)
 
896
        self.pref.set_preference('paned_position',
 
897
                                 self.hpaned_main.get_position())
 
898
        
 
899
        self.pref.write()
 
900
        self.window_main.destroy()
 
901
        
 
902
    def _load_left(self):
 
903
        """ Load data into the left panel. (Bookmarks) """
 
904
        # Create TreeStore
 
905
        treestore = gtk.TreeStore(str, str)
 
906
        
 
907
        # Get bookmarks
 
908
        bookmarks = self.pref.get_bookmarks()
 
909
        
 
910
        # Add them to the TreeStore
 
911
        titer = treestore.append(None, [_('Bookmarks'), None])
 
912
        for item in bookmarks:
 
913
            title = self.pref.get_bookmark_title(item)
 
914
            treestore.append(titer, [title, item])
 
915
        
 
916
        # Create the column and add it to the TreeView
 
917
        self.treeview_left.set_model(treestore)
 
918
        tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
 
919
        self.treeview_left.append_column(tvcolumn_bookmark)
 
920
        
 
921
        # Set up the cells
 
922
        cell = gtk.CellRendererText()
 
923
        tvcolumn_bookmark.pack_start(cell, True)
 
924
        tvcolumn_bookmark.add_attribute(cell, 'text', 0)
 
925
        
 
926
        # Expand the tree
 
927
        self.treeview_left.expand_all()
 
928
 
 
929
    def _load_right(self):
 
930
        """ Load data into the right panel. (Filelist) """
 
931
        # Create ListStore
 
932
        # Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
 
933
        liststore = gtk.ListStore(gobject.TYPE_STRING,
 
934
                                  gobject.TYPE_BOOLEAN,
 
935
                                  gobject.TYPE_STRING,
 
936
                                  gobject.TYPE_STRING,
 
937
                                  gobject.TYPE_STRING,
 
938
                                  gobject.TYPE_STRING,
 
939
                                  gobject.TYPE_STRING,
 
940
                                  gobject.TYPE_INT,
 
941
                                  gobject.TYPE_STRING,
 
942
                                  gobject.TYPE_STRING)
 
943
        
 
944
        dirs = []
 
945
        files = []
 
946
        
 
947
        # Fill the appropriate lists
 
948
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
 
949
        for item in os.listdir(self.path):
 
950
            if not dotted_files and item[0] == '.':
 
951
                continue
 
952
            if os.path.isdir(self.path + os.sep + item):
 
953
                dirs.append(item)
 
954
            else:
 
955
                files.append(item)
 
956
        
 
957
        if not self.notbranch:
 
958
            branch = self.wt.branch
 
959
            tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
 
960
        
 
961
            delta = self.wt.changes_from(tree2, want_unchanged=True)
 
962
        
 
963
        # Add'em to the ListStore
 
964
        for item in dirs:
 
965
            statinfo = os.stat(self.path + os.sep + item)
 
966
            liststore.append([ gtk.STOCK_DIRECTORY,
 
967
                               True,
 
968
                               item,
 
969
                               '',
 
970
                               '',
 
971
                               "<DIR>",
 
972
                               "<DIR>",
 
973
                               statinfo.st_mtime,
 
974
                               self._format_date(statinfo.st_mtime),
 
975
                               ''])
 
976
        for item in files:
 
977
            status = 'unknown'
 
978
            fileid = ''
 
979
            if not self.notbranch:
 
980
                filename = self.wt.relpath(self.path + os.sep + item)
 
981
                
 
982
                try:
 
983
                    self.wt.lock_read()
 
984
                    
 
985
                    for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
 
986
                        if rpathnew == filename:
 
987
                            status = 'renamed'
 
988
                            fileid = id
 
989
                    for rpath, id, kind in delta.added:
 
990
                        if rpath == filename:
 
991
                            status = 'added'
 
992
                            fileid = id
 
993
                    for rpath, id, kind in delta.removed:
 
994
                        if rpath == filename:
 
995
                            status = 'removed'
 
996
                            fileid = id
 
997
                    for rpath, id, kind, text_modified, meta_modified in delta.modified:
 
998
                        if rpath == filename:
 
999
                            status = 'modified'
 
1000
                            fileid = id
 
1001
                    for rpath, id, kind in delta.unchanged:
 
1002
                        if rpath == filename:
 
1003
                            status = 'unchanged'
 
1004
                            fileid = id
 
1005
                    for rpath, file_class, kind, id, entry in self.wt.list_files():
 
1006
                        if rpath == filename and file_class == 'I':
 
1007
                            status = 'ignored'
 
1008
                finally:
 
1009
                    self.wt.unlock()
 
1010
            
 
1011
            if status == 'renamed':
 
1012
                st = _('renamed')
 
1013
            elif status == 'removed':
 
1014
                st = _('removed')
 
1015
            elif status == 'added':
 
1016
                st = _('added')
 
1017
            elif status == 'modified':
 
1018
                st = _('modified')
 
1019
            elif status == 'unchanged':
 
1020
                st = _('unchanged')
 
1021
            elif status == 'ignored':
 
1022
                st = _('ignored')
 
1023
            else:
 
1024
                st = _('unknown')
 
1025
            
 
1026
            statinfo = os.stat(self.path + os.sep + item)
 
1027
            liststore.append([gtk.STOCK_FILE,
 
1028
                              False,
 
1029
                              item,
 
1030
                              st,
 
1031
                              status,
 
1032
                              str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
 
1033
                              self._format_size(statinfo.st_size),
 
1034
                              statinfo.st_mtime,
 
1035
                              self._format_date(statinfo.st_mtime),
 
1036
                              fileid])
 
1037
        
 
1038
        # Create the columns and add them to the TreeView
 
1039
        self.treeview_right.set_model(liststore)
 
1040
        self._tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
 
1041
        self._tvcolumn_status = gtk.TreeViewColumn(_('Status'))
 
1042
        self._tvcolumn_size = gtk.TreeViewColumn(_('Size'))
 
1043
        self._tvcolumn_mtime = gtk.TreeViewColumn(_('Last modified'))
 
1044
        self.treeview_right.append_column(self._tvcolumn_filename)
 
1045
        self.treeview_right.append_column(self._tvcolumn_status)
 
1046
        self.treeview_right.append_column(self._tvcolumn_size)
 
1047
        self.treeview_right.append_column(self._tvcolumn_mtime)
 
1048
        
 
1049
        # Set up the cells
 
1050
        cellpb = gtk.CellRendererPixbuf()
 
1051
        cell = gtk.CellRendererText()
 
1052
        self._tvcolumn_filename.pack_start(cellpb, False)
 
1053
        self._tvcolumn_filename.pack_start(cell, True)
 
1054
        self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
 
1055
        self._tvcolumn_filename.add_attribute(cell, 'text', 2)
 
1056
        self._tvcolumn_status.pack_start(cell, True)
 
1057
        self._tvcolumn_status.add_attribute(cell, 'text', 3)
 
1058
        self._tvcolumn_size.pack_start(cell, True)
 
1059
        self._tvcolumn_size.add_attribute(cell, 'text', 6)
 
1060
        self._tvcolumn_mtime.pack_start(cell, True)
 
1061
        self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
 
1062
        
 
1063
        # Set up the properties of the TreeView
 
1064
        self.treeview_right.set_headers_visible(True)
 
1065
        self.treeview_right.set_headers_clickable(True)
 
1066
        self.treeview_right.set_search_column(1)
 
1067
        self._tvcolumn_filename.set_resizable(True)
 
1068
        self._tvcolumn_status.set_resizable(True)
 
1069
        self._tvcolumn_size.set_resizable(True)
 
1070
        self._tvcolumn_mtime.set_resizable(True)
 
1071
        # Set up sorting
 
1072
        liststore.set_sort_func(13, self._sort_filelist_callback, None)
 
1073
        liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
 
1074
        self._tvcolumn_filename.set_sort_column_id(13)
 
1075
        self._tvcolumn_status.set_sort_column_id(3)
 
1076
        self._tvcolumn_size.set_sort_column_id(5)
 
1077
        self._tvcolumn_mtime.set_sort_column_id(7)
 
1078
        
 
1079
        # Set sensitivity
 
1080
        self.set_sensitivity()
 
1081
        
 
1082
    def get_selected_fileid(self):
 
1083
        """ Get the file_id of the selected file. """
 
1084
        treeselection = self.treeview_right.get_selection()
 
1085
        (model, iter) = treeselection.get_selected()
 
1086
        
 
1087
        if iter is None:
 
1088
            return None
 
1089
        else:
 
1090
            return model.get_value(iter, 9)
 
1091
    
 
1092
    def get_selected_right(self):
 
1093
        """ Get the selected filename. """
 
1094
        treeselection = self.treeview_right.get_selection()
 
1095
        (model, iter) = treeselection.get_selected()
 
1096
        
 
1097
        if iter is None:
 
1098
            return None
 
1099
        else:
 
1100
            return model.get_value(iter, 2)
 
1101
    
 
1102
    def get_selected_left(self):
 
1103
        """ Get the selected bookmark. """
 
1104
        treeselection = self.treeview_left.get_selection()
 
1105
        (model, iter) = treeselection.get_selected()
 
1106
        
 
1107
        if iter is None:
 
1108
            return None
 
1109
        else:
 
1110
            return model.get_value(iter, 1)
 
1111
 
 
1112
    def set_statusbar(self, message):
 
1113
        """ Set the statusbar message. """
 
1114
        self.statusbar.push(self.context_id, message)
 
1115
    
 
1116
    def clear_statusbar(self):
 
1117
        """ Clean the last message from the statusbar. """
 
1118
        self.statusbar.pop(self.context_id)
 
1119
    
 
1120
    def set_sensitivity(self):
 
1121
        """ Set menu and toolbar sensitivity. """
 
1122
        if not self.remote:
 
1123
            # We're local
 
1124
            self.menuitem_branch_init.set_sensitive(self.notbranch)
 
1125
            self.menuitem_branch_get.set_sensitive(self.notbranch)
 
1126
            self.menuitem_branch_checkout.set_sensitive(self.notbranch)
 
1127
            self.menuitem_branch_pull.set_sensitive(not self.notbranch)
 
1128
            self.menuitem_branch_push.set_sensitive(not self.notbranch)
 
1129
            self.menuitem_branch_update.set_sensitive(not self.notbranch)
 
1130
            self.menuitem_branch_revert.set_sensitive(not self.notbranch)
 
1131
            self.menuitem_branch_merge.set_sensitive(not self.notbranch)
 
1132
            self.menuitem_branch_commit.set_sensitive(not self.notbranch)
 
1133
            self.menuitem_branch_tags.set_sensitive(not self.notbranch)
 
1134
            self.menuitem_branch_status.set_sensitive(not self.notbranch)
 
1135
            self.menuitem_branch_missing.set_sensitive(not self.notbranch)
 
1136
            self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
 
1137
            self.menuitem_stats.set_sensitive(not self.notbranch)
 
1138
            self.menuitem_stats_diff.set_sensitive(not self.notbranch)
 
1139
            self.menuitem_add_files.set_sensitive(not self.notbranch)
 
1140
            self.menuitem_remove_files.set_sensitive(not self.notbranch)
 
1141
            self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
 
1142
            self.menuitem_file_rename.set_sensitive(not self.notbranch)
 
1143
            self.menuitem_file_move.set_sensitive(not self.notbranch)
 
1144
            self.menuitem_file_annotate.set_sensitive(not self.notbranch)
 
1145
            #self.menutoolbutton_diff.set_sensitive(True)
 
1146
            self.toolbutton_diff.set_sensitive(not self.notbranch)
 
1147
            self.toolbutton_log.set_sensitive(not self.notbranch)
 
1148
            self.toolbutton_commit.set_sensitive(not self.notbranch)
 
1149
            self.toolbutton_pull.set_sensitive(not self.notbranch)
 
1150
            self.toolbutton_push.set_sensitive(not self.notbranch)
 
1151
            self.toolbutton_update.set_sensitive(not self.notbranch)
 
1152
        else:
 
1153
            # We're remote
 
1154
            self.menuitem_branch_init.set_sensitive(False)
 
1155
            self.menuitem_branch_get.set_sensitive(True)
 
1156
            self.menuitem_branch_checkout.set_sensitive(True)
 
1157
            self.menuitem_branch_pull.set_sensitive(False)
 
1158
            self.menuitem_branch_push.set_sensitive(False)
 
1159
            self.menuitem_branch_update.set_sensitive(False)
 
1160
            self.menuitem_branch_revert.set_sensitive(False)
 
1161
            self.menuitem_branch_merge.set_sensitive(False)
 
1162
            self.menuitem_branch_commit.set_sensitive(False)
 
1163
            self.menuitem_branch_tags.set_sensitive(True)
 
1164
            self.menuitem_branch_status.set_sensitive(False)
 
1165
            self.menuitem_branch_missing.set_sensitive(False)
 
1166
            self.menuitem_branch_conflicts.set_sensitive(False)
 
1167
            self.menuitem_stats.set_sensitive(True)
 
1168
            self.menuitem_stats_diff.set_sensitive(False)
 
1169
            self.menuitem_add_files.set_sensitive(False)
 
1170
            self.menuitem_remove_files.set_sensitive(False)
 
1171
            self.menuitem_file_make_directory.set_sensitive(False)
 
1172
            self.menuitem_file_rename.set_sensitive(False)
 
1173
            self.menuitem_file_move.set_sensitive(False)
 
1174
            self.menuitem_file_annotate.set_sensitive(False)
 
1175
            #self.menutoolbutton_diff.set_sensitive(True)
 
1176
            self.toolbutton_diff.set_sensitive(False)
 
1177
            self.toolbutton_log.set_sensitive(True)
 
1178
            self.toolbutton_commit.set_sensitive(False)
 
1179
            self.toolbutton_pull.set_sensitive(False)
 
1180
            self.toolbutton_push.set_sensitive(False)
 
1181
            self.toolbutton_update.set_sensitive(False)
 
1182
    
 
1183
    def refresh_left(self):
 
1184
        """ Refresh the bookmark list. """
 
1185
        
 
1186
        # Get TreeStore and clear it
 
1187
        treestore = self.treeview_left.get_model()
 
1188
        treestore.clear()
 
1189
 
 
1190
        # Re-read preferences
 
1191
        self.pref.read()
 
1192
        
 
1193
        # Get bookmarks
 
1194
        bookmarks = self.pref.get_bookmarks()
 
1195
 
 
1196
        # Add them to the TreeStore
 
1197
        titer = treestore.append(None, [_('Bookmarks'), None])
 
1198
        for item in bookmarks:
 
1199
            title = self.pref.get_bookmark_title(item)
 
1200
            treestore.append(titer, [title, item])
 
1201
 
 
1202
        # Add the TreeStore to the TreeView
 
1203
        self.treeview_left.set_model(treestore)
 
1204
 
 
1205
        # Expand the tree
 
1206
        self.treeview_left.expand_all()
 
1207
 
 
1208
    def refresh_right(self, path=None):
 
1209
        """ Refresh the file list. """
 
1210
        if not self.remote:
 
1211
            # We're local
 
1212
            from bzrlib.workingtree import WorkingTree
 
1213
    
 
1214
            if path is None:
 
1215
                path = self.get_path()
 
1216
    
 
1217
            # A workaround for double-clicking Bookmarks
 
1218
            if not os.path.exists(path):
 
1219
                return
 
1220
    
 
1221
            # Get ListStore and clear it
 
1222
            liststore = self.treeview_right.get_model()
 
1223
            liststore.clear()
 
1224
            
 
1225
            # Show Status column
 
1226
            self._tvcolumn_status.set_visible(True)
 
1227
    
 
1228
            dirs = []
 
1229
            files = []
 
1230
    
 
1231
            # Fill the appropriate lists
 
1232
            dotted_files = self.pref.get_preference('dotted_files', 'bool')
 
1233
            ignored_files = self.pref.get_preference('ignored_files', 'bool')
 
1234
 
 
1235
            for item in os.listdir(path):
 
1236
                if not dotted_files and item[0] == '.':
 
1237
                    continue
 
1238
                if os.path.isdir(path + os.sep + item):
 
1239
                    dirs.append(item)
 
1240
                else:
 
1241
                    files.append(item)
 
1242
            
 
1243
            # Try to open the working tree
 
1244
            notbranch = False
 
1245
            try:
 
1246
                tree1 = WorkingTree.open_containing(path)[0]
 
1247
            except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
 
1248
                notbranch = True
 
1249
            
 
1250
            if not notbranch:
 
1251
                branch = tree1.branch
 
1252
                tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
 
1253
            
 
1254
                delta = tree1.changes_from(tree2, want_unchanged=True)
 
1255
                
 
1256
            # Add'em to the ListStore
 
1257
            for item in dirs:
 
1258
                statinfo = os.stat(self.path + os.sep + item)
 
1259
                liststore.append([gtk.STOCK_DIRECTORY,
 
1260
                                  True,
 
1261
                                  item,
 
1262
                                  '',
 
1263
                                  '',
 
1264
                                  "<DIR>",
 
1265
                                  "<DIR>",
 
1266
                                  statinfo.st_mtime,
 
1267
                                  self._format_date(statinfo.st_mtime),
 
1268
                                  ''])
 
1269
            for item in files:
 
1270
                status = 'unknown'
 
1271
                fileid = ''
 
1272
                if not notbranch:
 
1273
                    filename = tree1.relpath(path + os.sep + item)
 
1274
                    
 
1275
                    try:
 
1276
                        self.wt.lock_read()
 
1277
                        
 
1278
                        for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
 
1279
                            if rpathnew == filename:
 
1280
                                status = 'renamed'
 
1281
                                fileid = id
 
1282
                        for rpath, id, kind in delta.added:
 
1283
                            if rpath == filename:
 
1284
                                status = 'added'
 
1285
                                fileid = id
 
1286
                        for rpath, id, kind in delta.removed:
 
1287
                            if rpath == filename:
 
1288
                                status = 'removed'
 
1289
                                fileid = id
 
1290
                        for rpath, id, kind, text_modified, meta_modified in delta.modified:
 
1291
                            if rpath == filename:
 
1292
                                status = 'modified'
 
1293
                                fileid = id
 
1294
                        for rpath, id, kind in delta.unchanged:
 
1295
                            if rpath == filename:
 
1296
                                status = 'unchanged'
 
1297
                                fileid = id
 
1298
                        for rpath, file_class, kind, id, entry in self.wt.list_files():
 
1299
                            if rpath == filename and file_class == 'I':
 
1300
                                status = 'ignored'
 
1301
                    finally:
 
1302
                        self.wt.unlock()
 
1303
                
 
1304
                if status == 'renamed':
 
1305
                    st = _('renamed')
 
1306
                elif status == 'removed':
 
1307
                    st = _('removed')
 
1308
                elif status == 'added':
 
1309
                    st = _('added')
 
1310
                elif status == 'modified':
 
1311
                    st = _('modified')
 
1312
                elif status == 'unchanged':
 
1313
                    st = _('unchanged')
 
1314
                elif status == 'ignored':
 
1315
                    st = _('ignored')
 
1316
                    if not ignored_files:
 
1317
                        continue
 
1318
                else:
 
1319
                    st = _('unknown')
 
1320
                
 
1321
                statinfo = os.stat(self.path + os.sep + item)
 
1322
                liststore.append([gtk.STOCK_FILE,
 
1323
                                  False,
 
1324
                                  item,
 
1325
                                  st,
 
1326
                                  status,
 
1327
                                  str(statinfo.st_size),
 
1328
                                  self._format_size(statinfo.st_size),
 
1329
                                  statinfo.st_mtime,
 
1330
                                  self._format_date(statinfo.st_mtime),
 
1331
                                  fileid])
 
1332
        else:
 
1333
            # We're remote
 
1334
            
 
1335
            # Get ListStore and clear it
 
1336
            liststore = self.treeview_right.get_model()
 
1337
            liststore.clear()
 
1338
            
 
1339
            # Hide Status column
 
1340
            self._tvcolumn_status.set_visible(False)
 
1341
            
 
1342
            dirs = []
 
1343
            files = []
 
1344
            
 
1345
            self._show_stock_image(gtk.STOCK_REFRESH)
 
1346
            
 
1347
            for (name, type) in self.remote_entries:
 
1348
                if type.kind == 'directory':
 
1349
                    dirs.append(type)
 
1350
                elif type.kind == 'file':
 
1351
                    files.append(type)
 
1352
            
 
1353
            class HistoryCache:
 
1354
                """ Cache based on revision history. """
 
1355
                def __init__(self, history):
 
1356
                    self._history = history
 
1357
                
 
1358
                def _lookup_revision(self, revid):
 
1359
                    for r in self._history:
 
1360
                        if r.revision_id == revid:
 
1361
                            return r
 
1362
                    rev = repo.get_revision(revid)
 
1363
                    self._history.append(rev)
 
1364
                    return rev
 
1365
            
 
1366
            repo = self.remote_branch.repository
 
1367
            
 
1368
            revhistory = self.remote_branch.revision_history()
 
1369
            try:
 
1370
                revs = repo.get_revisions(revhistory)
 
1371
                cache = HistoryCache(revs)
 
1372
            except bzrerrors.InvalidHttpResponse:
 
1373
                # Fallback to dummy algorithm, because of LP: #115209
 
1374
                cache = HistoryCache([])
 
1375
            
 
1376
            for item in dirs:
 
1377
                if item.parent_id == self.remote_parent:
 
1378
                    rev = cache._lookup_revision(item.revision)
 
1379
                    liststore.append([ gtk.STOCK_DIRECTORY,
 
1380
                                       True,
 
1381
                                       item.name,
 
1382
                                       '',
 
1383
                                       '',
 
1384
                                       "<DIR>",
 
1385
                                       "<DIR>",
 
1386
                                       rev.timestamp,
 
1387
                                       self._format_date(rev.timestamp),
 
1388
                                       ''
 
1389
                                   ])
 
1390
                while gtk.events_pending():
 
1391
                    gtk.main_iteration()
 
1392
            
 
1393
            for item in files:
 
1394
                if item.parent_id == self.remote_parent:
 
1395
                    rev = cache._lookup_revision(item.revision)
 
1396
                    liststore.append([ gtk.STOCK_FILE,
 
1397
                                       False,
 
1398
                                       item.name,
 
1399
                                       '',
 
1400
                                       '',
 
1401
                                       str(item.text_size),
 
1402
                                       self._format_size(item.text_size),
 
1403
                                       rev.timestamp,
 
1404
                                       self._format_date(rev.timestamp),
 
1405
                                       item.file_id
 
1406
                                   ])
 
1407
                while gtk.events_pending():
 
1408
                    gtk.main_iteration()
 
1409
            
 
1410
            self.image_location_error.destroy()
 
1411
 
 
1412
        # Columns should auto-size
 
1413
        self.treeview_right.columns_autosize()
 
1414
        
 
1415
        # Set sensitivity
 
1416
        self.set_sensitivity()
 
1417
 
 
1418
    def _harddisks(self):
 
1419
        """ Returns hard drive letters under Win32. """
 
1420
        try:
 
1421
            import win32file
 
1422
            import string
 
1423
        except ImportError:
 
1424
            if sys.platform == 'win32':
 
1425
                print "pyWin32 modules needed to run Olive on Win32."
 
1426
                sys.exit(1)
 
1427
            else:
 
1428
                pass
 
1429
        
 
1430
        driveletters = []
 
1431
        for drive in string.ascii_uppercase:
 
1432
            if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
 
1433
                driveletters.append(drive+':')
 
1434
        return driveletters
 
1435
    
 
1436
    def gen_hard_selector(self):
 
1437
        """ Generate the hard drive selector under Win32. """
 
1438
        drives = self._harddisks()
 
1439
        for drive in drives:
 
1440
            self.combobox_drive.append_text(drive)
 
1441
        self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
 
1442
    
 
1443
    def _refresh_drives(self, combobox):
 
1444
        if self._just_started:
 
1445
            return
 
1446
        model = combobox.get_model()
 
1447
        active = combobox.get_active()
 
1448
        if active >= 0:
 
1449
            drive = model[active][0]
 
1450
            self.set_path(drive + '\\')
 
1451
            self.refresh_right(drive + '\\')
 
1452
    
 
1453
    def check_for_changes(self):
 
1454
        """ Check whether there were changes in the current working tree. """
 
1455
        old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
1456
        delta = self.wt.changes_from(old_tree)
 
1457
 
 
1458
        changes = False
 
1459
        
 
1460
        if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
 
1461
            changes = True
 
1462
        
 
1463
        return changes
 
1464
    
 
1465
    def _sort_filelist_callback(self, model, iter1, iter2, data):
 
1466
        """ The sort callback for the file list, return values:
 
1467
        -1: iter1 < iter2
 
1468
        0: iter1 = iter2
 
1469
        1: iter1 > iter2
 
1470
        """
 
1471
        name1 = model.get_value(iter1, 2)
 
1472
        name2 = model.get_value(iter2, 2)
 
1473
        
 
1474
        if model.get_value(iter1, 1):
 
1475
            # item1 is a directory
 
1476
            if not model.get_value(iter2, 1):
 
1477
                # item2 isn't
 
1478
                return -1
 
1479
            else:
 
1480
                # both of them are directories, we compare their names
 
1481
                if name1 < name2:
 
1482
                    return -1
 
1483
                elif name1 == name2:
 
1484
                    return 0
 
1485
                else:
 
1486
                    return 1
 
1487
        else:
 
1488
            # item1 is not a directory
 
1489
            if model.get_value(iter2, 1):
 
1490
                # item2 is
 
1491
                return 1
 
1492
            else:
 
1493
                # both of them are files, compare them
 
1494
                if name1 < name2:
 
1495
                    return -1
 
1496
                elif name1 == name2:
 
1497
                    return 0
 
1498
                else:
 
1499
                    return 1
 
1500
    
 
1501
    def _format_size(self, size):
 
1502
        """ Format size to a human readable format. """
 
1503
        if size < 1000:
 
1504
            return "%d[B]" % (size,)
 
1505
        size = size / 1000.0
 
1506
        
 
1507
        for metric in ["kB","MB","GB","TB"]:
 
1508
            if size < 1000:
 
1509
                break
 
1510
            size = size / 1000.0
 
1511
        return "%.1f[%s]" % (size,metric) 
 
1512
    
 
1513
    def _format_date(self, timestamp):
 
1514
        """ Format the time (given in secs) to a human readable format. """
 
1515
        return time.ctime(timestamp)
 
1516
    
 
1517
    def _is_remote_dir(self, location):
 
1518
        """ Determine whether the given location is a directory or not. """
 
1519
        if not self.remote:
 
1520
            # We're in local mode
 
1521
            return False
 
1522
        else:
 
1523
            branch, path = Branch.open_containing(location)
 
1524
            for (name, type) in self.remote_entries:
 
1525
                if name == path and type.kind == 'directory':
 
1526
                    # We got it
 
1527
                    return True
 
1528
            # Either it's not a directory or not in the inventory
 
1529
            return False
 
1530
    
 
1531
    def _show_stock_image(self, stock_id):
 
1532
        """ Show a stock image next to the location entry. """
 
1533
        self.image_location_error.destroy()
 
1534
        self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
 
1535
        self.hbox_location.pack_start(self.image_location_error, False, False, 0)
 
1536
        if sys.platform == 'win32':
 
1537
            self.hbox_location.reorder_child(self.image_location_error, 2)
 
1538
        else:
 
1539
            self.hbox_location.reorder_child(self.image_location_error, 1)
 
1540
        self.image_location_error.show()
 
1541
        while gtk.events_pending():
 
1542
            gtk.main_iteration()
 
1543
 
 
1544
import ConfigParser
 
1545
 
 
1546
class Preferences:
 
1547
    """ A class which handles Olive's preferences. """
 
1548
    def __init__(self, path=None):
 
1549
        """ Initialize the Preferences class. """
 
1550
        # Some default options
 
1551
        self.defaults = { 'strict_commit' : False,
 
1552
                          'dotted_files'  : False,
 
1553
                          'ignored_files' : True,
 
1554
                          'window_width'  : 700,
 
1555
                          'window_height' : 400,
 
1556
                          'window_x'      : 40,
 
1557
                          'window_y'      : 40,
 
1558
                          'paned_position': 200 }
 
1559
 
 
1560
        # Create a config parser object
 
1561
        self.config = ConfigParser.RawConfigParser()
 
1562
 
 
1563
        # Set filename
 
1564
        if path is None:
 
1565
            if sys.platform == 'win32':
 
1566
                # Windows - no dotted files
 
1567
                self._filename = os.path.expanduser('~/olive.conf')
 
1568
            else:
 
1569
                self._filename = os.path.expanduser('~/.olive.conf')
 
1570
        else:
 
1571
            self._filename = path
 
1572
        
 
1573
        # Load the configuration
 
1574
        self.read()
 
1575
        
 
1576
    def _get_default(self, option):
 
1577
        """ Get the default option for a preference. """
 
1578
        try:
 
1579
            ret = self.defaults[option]
 
1580
        except KeyError:
 
1581
            return None
 
1582
        else:
 
1583
            return ret
 
1584
 
 
1585
    def refresh(self):
 
1586
        """ Refresh the configuration. """
 
1587
        # First write out the changes
 
1588
        self.write()
 
1589
        # Then load the configuration again
 
1590
        self.read()
 
1591
 
 
1592
    def read(self):
 
1593
        """ Just read the configuration. """
 
1594
        # Re-initialize the config parser object to avoid some bugs
 
1595
        self.config = ConfigParser.RawConfigParser()
 
1596
        self.config.read([self._filename])
 
1597
    
 
1598
    def write(self):
 
1599
        """ Write the configuration to the appropriate files. """
 
1600
        fp = open(self._filename, 'w')
 
1601
        self.config.write(fp)
 
1602
        fp.close()
 
1603
 
 
1604
    def get_bookmarks(self):
 
1605
        """ Return the list of bookmarks. """
 
1606
        bookmarks = self.config.sections()
 
1607
        if self.config.has_section('preferences'):
 
1608
            bookmarks.remove('preferences')
 
1609
        return bookmarks
 
1610
 
 
1611
    def add_bookmark(self, path):
 
1612
        """ Add bookmark. """
 
1613
        try:
 
1614
            self.config.add_section(path)
 
1615
        except ConfigParser.DuplicateSectionError:
 
1616
            return False
 
1617
        else:
 
1618
            return True
 
1619
 
 
1620
    def get_bookmark_title(self, path):
 
1621
        """ Get bookmark title. """
 
1622
        try:
 
1623
            ret = self.config.get(path, 'title')
 
1624
        except ConfigParser.NoOptionError:
 
1625
            ret = path
 
1626
        
 
1627
        return ret
 
1628
    
 
1629
    def set_bookmark_title(self, path, title):
 
1630
        """ Set bookmark title. """
 
1631
        # FIXME: What if path isn't listed yet?
 
1632
        # FIXME: Canonicalize paths first?
 
1633
        self.config.set(path, 'title', title)
 
1634
    
 
1635
    def remove_bookmark(self, path):
 
1636
        """ Remove bookmark. """
 
1637
        return self.config.remove_section(path)
 
1638
 
 
1639
    def set_preference(self, option, value):
 
1640
        """ Set the value of the given option. """
 
1641
        if value is True:
 
1642
            value = 'yes'
 
1643
        elif value is False:
 
1644
            value = 'no'
 
1645
        
 
1646
        if self.config.has_section('preferences'):
 
1647
            self.config.set('preferences', option, value)
 
1648
        else:
 
1649
            self.config.add_section('preferences')
 
1650
            self.config.set('preferences', option, value)
 
1651
 
 
1652
    def get_preference(self, option, kind='str'):
 
1653
        """ Get the value of the given option.
 
1654
        
 
1655
        :param kind: str/bool/int/float. default: str
 
1656
        """
 
1657
        if self.config.has_option('preferences', option):
 
1658
            if kind == 'bool':
 
1659
                return self.config.getboolean('preferences', option)
 
1660
            elif kind == 'int':
 
1661
                return self.config.getint('preferences', option)
 
1662
            elif kind == 'float':
 
1663
                return self.config.getfloat('preferences', option)
 
1664
            else:
 
1665
                return self.config.get('preferences', option)
 
1666
        else:
 
1667
            try:
 
1668
                return self._get_default(option)
 
1669
            except KeyError:
 
1670
                return None
 
1671