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