/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: Szilveszter Farkas (Phanatic)
  • Date: 2006-09-27 19:10:00 UTC
  • mto: (0.14.1 main)
  • mto: This revision was merged to the branch mainline in revision 83.
  • Revision ID: Szilveszter.Farkas@gmail.com-20060927191000-2b5382eda85d82e4
Fixed a context menu bug; set release date.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 #!/usr/bin/python
2
 
 
3
1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
4
2
#
5
3
# This program is free software; you can redistribute it and/or modify
16
14
# along with this program; if not, write to the Free Software
17
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
16
 
 
17
import ConfigParser
19
18
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')
26
21
 
27
22
try:
28
23
    import pygtk
29
24
    pygtk.require("2.0")
30
25
except:
31
26
    pass
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()
 
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'
84
39
 
85
40
class OliveGtk:
86
41
    """ The main Olive GTK frontend class. This is called when launching the
87
42
    program. """
88
43
    
89
44
    def __init__(self):
90
 
        self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
 
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
        
91
62
        self.window = self.toplevel.get_widget('window_main')
92
 
        self.pref = Preferences()
93
 
        self.path = None
 
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()
94
334
 
95
335
        # Initialize the statusbar
96
336
        self.statusbar = self.toplevel.get_widget('statusbar')
106
346
        # Get some important menu items
107
347
        self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
108
348
        self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
109
 
        self.menuitem_file_bookmark = self.toplevel.get_widget('menuitem_file_bookmark')
110
349
        self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
111
350
        self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
112
351
        self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
113
 
        self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
114
352
        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')
116
353
        self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
117
354
        self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
118
355
        self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
119
356
        self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
120
357
        self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
121
358
        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')
125
359
        self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
126
 
        self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
127
360
        self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
128
361
        self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
129
 
        self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
130
362
        self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
131
363
        self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
132
364
        self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
137
369
        self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
138
370
        self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
139
371
        self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
140
 
        self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
141
372
        # Get the drive selector
142
373
        self.combobox_drive = gtk.combo_box_new_text()
143
374
        self.combobox_drive.connect("changed", self._refresh_drives)
144
375
        
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
 
        
157
376
        self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
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
 
 
 
377
    
 
378
    def set_path(self, path):
 
379
        """ Set the current path while browsing the directories. """
 
380
        self._path = path
 
381
    
383
382
    def get_path(self):
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)
 
383
        """ Get the current path. """
 
384
        return self._path
1135
385
    
1136
386
    def get_selected_right(self):
1137
387
        """ Get the selected filename. """
1141
391
        if iter is None:
1142
392
            return None
1143
393
        else:
1144
 
            return model.get_value(iter, 2)
 
394
            return model.get_value(iter, 1)
1145
395
    
1146
396
    def get_selected_left(self):
1147
397
        """ Get the selected bookmark. """
1161
411
        """ Clean the last message from the statusbar. """
1162
412
        self.statusbar.pop(self.context_id)
1163
413
    
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
 
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
1198
539
            self.menuitem_branch_init.set_sensitive(False)
1199
540
            self.menuitem_branch_get.set_sensitive(True)
1200
541
            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)
1201
564
            self.menuitem_branch_pull.set_sensitive(False)
1202
565
            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)
1206
566
            self.menuitem_branch_commit.set_sensitive(False)
1207
 
            self.menuitem_branch_tags.set_sensitive(True)
1208
567
            self.menuitem_branch_status.set_sensitive(False)
1209
568
            self.menuitem_branch_missing.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)
 
569
            self.menuitem_stats.set_sensitive(False)
1213
570
            self.menuitem_add_files.set_sensitive(False)
1214
571
            self.menuitem_remove_files.set_sensitive(False)
1215
572
            self.menuitem_file_make_directory.set_sensitive(False)
1216
573
            self.menuitem_file_rename.set_sensitive(False)
1217
574
            self.menuitem_file_move.set_sensitive(False)
1218
 
            self.menuitem_file_annotate.set_sensitive(False)
1219
 
            #self.menutoolbutton_diff.set_sensitive(True)
 
575
            #self.menutoolbutton_diff.set_sensitive(False)
1220
576
            self.toolbutton_diff.set_sensitive(False)
1221
 
            self.toolbutton_log.set_sensitive(True)
 
577
            self.toolbutton_log.set_sensitive(False)
1222
578
            self.toolbutton_commit.set_sensitive(False)
1223
579
            self.toolbutton_pull.set_sensitive(False)
1224
580
            self.toolbutton_push.set_sensitive(False)
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])
 
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))
1388
587
        else:
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()
 
588
            widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
1467
589
 
1468
 
        # Columns should auto-size
1469
 
        self.treeview_right.columns_autosize()
1470
 
        
1471
 
        # Set sensitivity
1472
 
        self.set_sensitivity()
 
590
        gtk.main_iteration(0)
1473
591
 
1474
592
    def _harddisks(self):
1475
593
        """ Returns hard drive letters under Win32. """
1494
612
        drives = self._harddisks()
1495
613
        for drive in drives:
1496
614
            self.combobox_drive.append_text(drive)
1497
 
        self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1498
615
    
1499
616
    def _refresh_drives(self, combobox):
1500
 
        if self._just_started:
1501
 
            return
1502
617
        model = combobox.get_model()
1503
618
        active = combobox.get_active()
1504
619
        if active >= 0:
1505
620
            drive = model[active][0]
1506
 
            self.set_path(drive + '\\')
1507
621
            self.refresh_right(drive + '\\')
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:
 
622
 
 
623
class OlivePreferences:
1603
624
    """ A class which handles Olive's preferences. """
1604
 
    def __init__(self, path=None):
 
625
    def __init__(self):
1605
626
        """ Initialize the Preferences class. """
1606
627
        # Some default options
1607
628
        self.defaults = { 'strict_commit' : False,
1608
629
                          'dotted_files'  : False,
1609
 
                          'ignored_files' : True,
1610
630
                          'window_width'  : 700,
1611
631
                          'window_height' : 400,
1612
632
                          'window_x'      : 40,
1615
635
 
1616
636
        # Create a config parser object
1617
637
        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
1628
638
        
1629
639
        # Load the configuration
1630
 
        self.read()
 
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')])
1631
645
        
1632
646
    def _get_default(self, option):
1633
647
        """ Get the default option for a preference. """
1643
657
        # First write out the changes
1644
658
        self.write()
1645
659
        # Then load the configuration again
1646
 
        self.read()
 
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')])
1647
665
 
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
 
    
1654
666
    def write(self):
1655
667
        """ Write the configuration to the appropriate files. """
1656
 
        fp = open(self._filename, 'w')
1657
 
        self.config.write(fp)
1658
 
        fp.close()
 
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()
1659
677
 
 
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
    
1660
706
    def get_bookmarks(self):
1661
707
        """ Return the list of bookmarks. """
1662
708
        bookmarks = self.config.sections()
1684
730
    
1685
731
    def set_bookmark_title(self, path, title):
1686
732
        """ Set bookmark title. """
1687
 
        # FIXME: What if path isn't listed yet?
1688
 
        # FIXME: Canonicalize paths first?
1689
733
        self.config.set(path, 'title', title)
1690
734
    
1691
735
    def remove_bookmark(self, path):
1692
736
        """ Remove bookmark. """
1693
737
        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