/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to olive/__init__.py

  • Committer: Jelmer Vernooij
  • Date: 2008-07-17 11:51:03 UTC
  • Revision ID: jelmer@samba.org-20080717115103-djh5sb0pvpse2zkb
Add note about glade.

Show diffs side-by-side

added added

removed removed

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