/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: Martin Albisetti
  • Date: 2008-03-04 22:13:43 UTC
  • mto: This revision was merged to the branch mainline in revision 439.
  • Revision ID: argentina@gmail.com-20080304221343-ixbdn2uf87z3jnfl
Added custom colored context icons

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