/b-gtk/fix-viz

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

« back to all changes in this revision

Viewing changes to olive/__init__.py

  • Committer: Jelmer Vernooij
  • Date: 2012-07-09 15:23:26 UTC
  • mto: This revision was merged to the branch mainline in revision 794.
  • Revision ID: jelmer@samba.org-20120709152326-dzxb8zoz0btull7n
Remove bzr-notify.

Show diffs side-by-side

added added

removed removed

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