/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: Adeodato Simó
  • Date: 2007-07-03 22:19:26 UTC
  • Revision ID: dato@net.com.org.es-20070703221926-agtx1dc3h1bkg19o
Mention the package the gtksourceview python bindings live in.

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