/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: 2008-06-29 19:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 515.
  • Revision ID: jelmer@samba.org-20080629191834-ha2ecpv5szt96nge
Make sure signed testament matches repository data.

Show diffs side-by-side

added added

removed removed

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