/b-gtk/fix-viz

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

« back to all changes in this revision

Viewing changes to olive/__init__.py

  • Committer: Szilveszter Farkas (Phanatic)
  • Date: 2007-04-06 17:48:23 UTC
  • mto: (188.2.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 190.
  • Revision ID: szilveszter.farkas@gmail.com-20070406174823-bclt7vsgn2nkcas2
Implemented init functionality.

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
 
 
20
# gettext support
 
21
import gettext
 
22
gettext.install('olive-gtk')
 
23
 
 
24
try:
 
25
    import pygtk
 
26
    pygtk.require("2.0")
 
27
except:
 
28
    pass
 
29
 
 
30
import gtk
 
31
import gtk.gdk
 
32
import gtk.glade
 
33
 
 
34
from bzrlib.branch import Branch
 
35
import bzrlib.errors as bzrerrors
 
36
from bzrlib.lazy_import import lazy_import
 
37
from bzrlib.workingtree import WorkingTree
 
38
 
 
39
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
 
40
from bzrlib.plugins.gtk.errors import show_bzr_error
 
41
from guifiles import GLADEFILENAME
 
42
 
 
43
from bzrlib.plugins.gtk.diff import DiffWindow
 
44
lazy_import(globals(), """
 
45
from bzrlib.plugins.gtk.viz import branchwin
 
46
""")
 
47
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
 
48
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
 
49
from bzrlib.plugins.gtk.commit import CommitDialog
 
50
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
 
51
from bzrlib.plugins.gtk.push import PushDialog
 
52
 
 
53
class OliveGtk:
 
54
    """ The main Olive GTK frontend class. This is called when launching the
 
55
    program. """
 
56
    
 
57
    def __init__(self):
 
58
        self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
 
59
        self.window = self.toplevel.get_widget('window_main')
 
60
        self.pref = Preferences()
 
61
        self.path = None
 
62
 
 
63
        # Initialize the statusbar
 
64
        self.statusbar = self.toplevel.get_widget('statusbar')
 
65
        self.context_id = self.statusbar.get_context_id('olive')
 
66
        
 
67
        # Get the main window
 
68
        self.window_main = self.toplevel.get_widget('window_main')
 
69
        # Get the HPaned
 
70
        self.hpaned_main = self.toplevel.get_widget('hpaned_main')
 
71
        # Get the TreeViews
 
72
        self.treeview_left = self.toplevel.get_widget('treeview_left')
 
73
        self.treeview_right = self.toplevel.get_widget('treeview_right')
 
74
        # Get some important menu items
 
75
        self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
 
76
        self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
 
77
        self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
 
78
        self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
 
79
        self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
 
80
        self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
 
81
        self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
 
82
        self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
 
83
        self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
 
84
        self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
 
85
        self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
 
86
        self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
 
87
        self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
 
88
        self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
 
89
        self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
 
90
        self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
 
91
        self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
 
92
        self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
 
93
        self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
 
94
        self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
 
95
        self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
 
96
        self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
 
97
        # Get some toolbuttons
 
98
        #self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
 
99
        self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
 
100
        self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
 
101
        self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
 
102
        self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
 
103
        self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
 
104
        # Get the drive selector
 
105
        self.combobox_drive = gtk.combo_box_new_text()
 
106
        self.combobox_drive.connect("changed", self._refresh_drives)
 
107
        
 
108
        self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
 
109
 
 
110
        
 
111
        # Dictionary for signal_autoconnect
 
112
        dic = { "on_window_main_destroy": gtk.main_quit,
 
113
                "on_window_main_delete_event": self.on_window_main_delete_event,
 
114
                "on_quit_activate": self.on_window_main_delete_event,
 
115
                "on_about_activate": self.on_about_activate,
 
116
                "on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
 
117
                "on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
 
118
                "on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
 
119
                "on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
 
120
                "on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
 
121
                "on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
 
122
                "on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
 
123
                "on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
 
124
                "on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
 
125
                "on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
 
126
                "on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
 
127
                "on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
 
128
                "on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
 
129
                "on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
 
130
                "on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
 
131
                "on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
 
132
                "on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
 
133
                "on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
 
134
                "on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
 
135
                "on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
 
136
                "on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
 
137
                "on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
 
138
                "on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
 
139
                "on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
 
140
                #"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
 
141
                "on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
 
142
                "on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
 
143
                "on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
 
144
                "on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
 
145
                "on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
 
146
                "on_treeview_right_row_activated": self.on_treeview_right_row_activated,
 
147
                "on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
 
148
                "on_treeview_left_row_activated": self.on_treeview_left_row_activated }
 
149
        
 
150
        # Connect the signals to the handlers
 
151
        self.toplevel.signal_autoconnect(dic)
 
152
        
 
153
        self._just_started = True
 
154
        
 
155
        # Apply window size and position
 
156
        width = self.pref.get_preference('window_width', 'int')
 
157
        height = self.pref.get_preference('window_height', 'int')
 
158
        self.window.resize(width, height)
 
159
        x = self.pref.get_preference('window_x', 'int')
 
160
        y = self.pref.get_preference('window_y', 'int')
 
161
        self.window.move(x, y)
 
162
        # Apply paned position
 
163
        pos = self.pref.get_preference('paned_position', 'int')
 
164
        self.hpaned_main.set_position(pos)
 
165
        
 
166
        # Apply menu to the toolbutton
 
167
        #menubutton = self.toplevel.get_widget('menutoolbutton_diff')
 
168
        #menubutton.set_menu(handler.menu.toolbar_diff)
 
169
        
 
170
        # Now we can show the window
 
171
        self.window.show()
 
172
        
 
173
        # Show drive selector if under Win32
 
174
        if sys.platform == 'win32':
 
175
            self.vbox_main_right.pack_start(self.combobox_drive, False, True, 0)
 
176
            self.vbox_main_right.reorder_child(self.combobox_drive, 0)
 
177
            self.combobox_drive.show()
 
178
            self.gen_hard_selector()
 
179
        
 
180
        self._load_left()
 
181
 
 
182
        # Apply menu state
 
183
        self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
 
184
 
 
185
        self.set_path(os.getcwd())
 
186
        self._load_right()
 
187
        
 
188
        self._just_started = False
 
189
 
 
190
    def set_path(self, path):
 
191
        self.path = path
 
192
        self.notbranch = False
 
193
        
 
194
        try:
 
195
            self.wt, self.wtpath = WorkingTree.open_containing(self.path)
 
196
        except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
 
197
            self.notbranch = True
 
198
        
 
199
        self.statusbar.push(self.context_id, path)
 
200
 
 
201
    def get_path(self):
 
202
        return self.path
 
203
   
 
204
    def on_about_activate(self, widget):
 
205
        from bzrlib.plugins.gtk.dialog import about
 
206
        about()
 
207
        
 
208
    def on_menuitem_add_files_activate(self, widget):
 
209
        """ Add file(s)... menu handler. """
 
210
        from add import OliveAdd
 
211
        add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
 
212
        add.display()
 
213
    
 
214
    def on_menuitem_branch_get_activate(self, widget):
 
215
        """ Branch/Get... menu handler. """
 
216
        from bzrlib.plugins.gtk.branch import BranchDialog
 
217
        branch = BranchDialog(self.get_path(), self.window)
 
218
        response = branch.run()
 
219
        if response != gtk.RESPONSE_NONE:
 
220
            branch.hide()
 
221
        
 
222
            if response == gtk.RESPONSE_OK:
 
223
                self.refresh_right()
 
224
            
 
225
            branch.destroy()
 
226
    
 
227
    def on_menuitem_branch_checkout_activate(self, widget):
 
228
        """ Branch/Checkout... menu handler. """
 
229
        from bzrlib.plugins.gtk.checkout import CheckoutDialog
 
230
        checkout = CheckoutDialog(self.get_path(), self.window)
 
231
        response = checkout.run()
 
232
        if response != gtk.RESPONSE_NONE:
 
233
            checkout.hide()
 
234
        
 
235
            if response == gtk.RESPONSE_OK:
 
236
                self.refresh_right()
 
237
            
 
238
            checkout.destroy()
 
239
    
 
240
    @show_bzr_error
 
241
    def on_menuitem_branch_commit_activate(self, widget):
 
242
        """ Branch/Commit... menu handler. """
 
243
        commit = CommitDialog(self.wt, self.wtpath, self.notbranch, self.get_selected_right(), self.window)
 
244
        response = commit.run()
 
245
        if response != gtk.RESPONSE_NONE:
 
246
            commit.hide()
 
247
        
 
248
            if response == gtk.RESPONSE_OK:
 
249
                self.refresh_right()
 
250
            
 
251
            commit.destroy()
 
252
    
 
253
    def on_menuitem_branch_conflicts_activate(self, widget):
 
254
        """ Branch/Conflicts... menu handler. """
 
255
        conflicts = ConflictsDialog(self.wt, self.window)
 
256
        response = conflicts.run()
 
257
        if response != gtk.RESPONSE_NONE:
 
258
            conflicts.destroy()
 
259
    
 
260
    def on_menuitem_branch_merge_activate(self, widget):
 
261
        """ Branch/Merge... menu handler. """
 
262
        from bzrlib.plugins.gtk.merge import MergeDialog
 
263
        
 
264
        if self.check_for_changes():
 
265
            error_dialog(_('There are local changes in the branch'),
 
266
                         _('Please commit or revert the changes before merging.'))
 
267
        else:
 
268
            merge = MergeDialog(self.wt, self.wtpath)
 
269
            merge.display()
 
270
 
 
271
    @show_bzr_error
 
272
    def on_menuitem_branch_missing_revisions_activate(self, widget):
 
273
        """ Branch/Missing revisions menu handler. """
 
274
        local_branch = self.wt.branch
 
275
        
 
276
        other_branch = local_branch.get_parent()
 
277
        if other_branch is None:
 
278
            error_dialog(_('Parent location is unknown'),
 
279
                         _('Cannot determine missing revisions if no parent location is known.'))
 
280
            return
 
281
        
 
282
        remote_branch = Branch.open(other_branch)
 
283
        
 
284
        if remote_branch.base == local_branch.base:
 
285
            remote_branch = local_branch
 
286
 
 
287
        ret = len(local_branch.missing_revisions(remote_branch))
 
288
 
 
289
        if ret > 0:
 
290
            info_dialog(_('There are missing revisions'),
 
291
                        _('%d revision(s) missing.') % ret)
 
292
        else:
 
293
            info_dialog(_('Local branch up to date'),
 
294
                        _('There are no missing revisions.'))
 
295
 
 
296
    @show_bzr_error
 
297
    def on_menuitem_branch_pull_activate(self, widget):
 
298
        """ Branch/Pull menu handler. """
 
299
        branch_to = self.wt.branch
 
300
 
 
301
        location = branch_to.get_parent()
 
302
        if location is None:
 
303
            error_dialog(_('Parent location is unknown'),
 
304
                                     _('Pulling is not possible until there is a parent location.'))
 
305
            return
 
306
 
 
307
        branch_from = Branch.open(location)
 
308
 
 
309
        if branch_to.get_parent() is None:
 
310
            branch_to.set_parent(branch_from.base)
 
311
 
 
312
        #old_rh = branch_to.revision_history()
 
313
        #if tree_to is not None:
 
314
        #    tree_to.pull(branch_from)
 
315
        #else:
 
316
        #    branch_to.pull(branch_from)
 
317
        ret = branch_to.pull(branch_from)
 
318
        
 
319
        info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
 
320
    
 
321
    def on_menuitem_branch_push_activate(self, widget):
 
322
        """ Branch/Push... menu handler. """
 
323
        push = PushDialog(self.wt.branch, self.window)
 
324
        response = push.run()
 
325
        if response != gtk.RESPONSE_NONE:
 
326
            push.destroy()
 
327
    
 
328
    @show_bzr_error
 
329
    def on_menuitem_branch_revert_activate(self, widget):
 
330
        """ Branch/Revert all changes menu handler. """
 
331
        ret = self.wt.revert([])
 
332
        if ret:
 
333
            warning_dialog(_('Conflicts detected'),
 
334
                           _('Please have a look at the working tree before continuing.'))
 
335
        else:
 
336
            info_dialog(_('Revert successful'),
 
337
                        _('All files reverted to last revision.'))
 
338
        self.refresh_right()
 
339
    
 
340
    def on_menuitem_branch_status_activate(self, widget):
 
341
        """ Branch/Status... menu handler. """
 
342
        from bzrlib.plugins.gtk.status import StatusDialog
 
343
        status = StatusDialog(self.wt, self.wtpath)
 
344
        status.display()
 
345
    
 
346
    @show_bzr_error
 
347
    def on_menuitem_branch_initialize_activate(self, widget):
 
348
        """ Initialize current directory. """
 
349
        import bzrlib.bzrdir as bzrdir
 
350
        
 
351
        if not os.path.exists(self.path):
 
352
            os.mkdir(self.path)
 
353
 
 
354
        try:
 
355
            existing_bzrdir = bzrdir.BzrDir.open(self.path)
 
356
        except bzrerrors.NotBranchError:
 
357
            bzrdir.BzrDir.create_branch_convenience(self.path)
 
358
        else:
 
359
            if existing_bzrdir.has_branch():
 
360
                if existing_bzrdir.has_workingtree():
 
361
                    raise bzrerrors.AlreadyBranchError(self.path)
 
362
                else:
 
363
                    raise bzrerrors.BranchExistsWithoutWorkingTree(self.path)
 
364
            else:
 
365
                existing_bzrdir.create_branch()
 
366
                existing_bzrdir.create_workingtree()
 
367
        info_dialog(_('Initialize successful'),
 
368
                    _('Directory successfully initialized.'))
 
369
        self.refresh_right()
 
370
        
 
371
    def on_menuitem_file_annotate_activate(self, widget):
 
372
        """ File/Annotate... menu handler. """
 
373
        if self.get_selected_right() is None:
 
374
            error_dialog(_('No file was selected'),
 
375
                         _('Please select a file from the list.'))
 
376
            return
 
377
        
 
378
        branch = self.wt.branch
 
379
        file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
 
380
        
 
381
        window = GAnnotateWindow(all=False, plain=False)
 
382
        window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
 
383
        config = GAnnotateConfig(window)
 
384
        window.show()
 
385
        branch.lock_read()
 
386
        try:
 
387
            window.annotate(self.wt, branch, file_id)
 
388
        finally:
 
389
            branch.unlock()
 
390
    
 
391
    def on_menuitem_file_make_directory_activate(self, widget):
 
392
        """ File/Make directory... menu handler. """
 
393
        from mkdir import OliveMkdir
 
394
        mkdir = OliveMkdir(self.wt, self.wtpath)
 
395
        mkdir.display()
 
396
    
 
397
    def on_menuitem_file_move_activate(self, widget):
 
398
        """ File/Move... menu handler. """
 
399
        from move import OliveMove
 
400
        move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
 
401
        move.display()
 
402
    
 
403
    def on_menuitem_file_rename_activate(self, widget):
 
404
        """ File/Rename... menu handler. """
 
405
        from rename import OliveRename
 
406
        rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
 
407
        rename.display()
 
408
 
 
409
    def on_menuitem_remove_file_activate(self, widget):
 
410
        """ Remove (unversion) selected file. """
 
411
        from remove import OliveRemoveDialog
 
412
        remove = OliveRemoveDialog(self.wt, self.wtpath,
 
413
                                   selected=self.get_selected_right(),
 
414
                                   parent=self.window)
 
415
        response = remove.run()
 
416
        
 
417
        if response != gtk.RESPONSE_NONE:
 
418
            remove.hide()
 
419
        
 
420
            if response == gtk.RESPONSE_OK:
 
421
                self.set_path(self.path)
 
422
                self.refresh_right()
 
423
            
 
424
            remove.destroy()
 
425
    
 
426
    def on_menuitem_stats_diff_activate(self, widget):
 
427
        """ Statistics/Differences... menu handler. """
 
428
        window = DiffWindow()
 
429
        parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
430
        window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
 
431
        window.show()
 
432
    
 
433
    def on_menuitem_stats_infos_activate(self, widget):
 
434
        """ Statistics/Informations... menu handler. """
 
435
        from info import OliveInfo
 
436
        info = OliveInfo(self.wt)
 
437
        info.display()
 
438
    
 
439
    def on_menuitem_stats_log_activate(self, widget):
 
440
        """ Statistics/Log... menu handler. """
 
441
        window = branchwin.BranchWindow()
 
442
        window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
 
443
        window.show()
 
444
    
 
445
    def on_menuitem_view_refresh_activate(self, widget):
 
446
        """ View/Refresh menu handler. """
 
447
        # Refresh the left pane
 
448
        self.refresh_left()
 
449
        # Refresh the right pane
 
450
        self.refresh_right()
 
451
   
 
452
    def on_menuitem_view_show_hidden_files_activate(self, widget):
 
453
        """ View/Show hidden files menu handler. """
 
454
        self.pref.set_preference('dotted_files', widget.get_active())
 
455
        if self.path is not None:
 
456
            self.refresh_right()
 
457
 
 
458
    def on_treeview_left_button_press_event(self, widget, event):
 
459
        """ Occurs when somebody right-clicks in the bookmark list. """
 
460
        if event.button == 3:
 
461
            # Don't show context with nothing selected
 
462
            if self.get_selected_left() == None:
 
463
                return
 
464
 
 
465
            # Create a menu
 
466
            from menu import OliveMenu
 
467
            menu = OliveMenu(path=self.get_path(),
 
468
                             selected=self.get_selected_left(),
 
469
                             app=self)
 
470
            
 
471
            menu.left_context_menu().popup(None, None, None, 0,
 
472
                                           event.time)
 
473
 
 
474
    def on_treeview_left_row_activated(self, treeview, path, view_column):
 
475
        """ Occurs when somebody double-clicks or enters an item in the
 
476
        bookmark list. """
 
477
 
 
478
        newdir = self.get_selected_left()
 
479
        if newdir == None:
 
480
            return
 
481
 
 
482
        self.set_path(newdir)
 
483
        self.refresh_right()
 
484
 
 
485
    def on_treeview_right_button_press_event(self, widget, event):
 
486
        """ Occurs when somebody right-clicks in the file list. """
 
487
        if event.button == 3:
 
488
            # Create a menu
 
489
            from menu import OliveMenu
 
490
            menu = OliveMenu(path=self.get_path(),
 
491
                             selected=self.get_selected_right(),
 
492
                             app=self)
 
493
            # get the menu items
 
494
            m_add = menu.ui.get_widget('/context_right/add')
 
495
            m_remove = menu.ui.get_widget('/context_right/remove')
 
496
            m_rename = menu.ui.get_widget('/context_right/rename')
 
497
            m_revert = menu.ui.get_widget('/context_right/revert')
 
498
            m_commit = menu.ui.get_widget('/context_right/commit')
 
499
            m_diff = menu.ui.get_widget('/context_right/diff')
 
500
            # check if we're in a branch
 
501
            try:
 
502
                from bzrlib.branch import Branch
 
503
                Branch.open_containing(self.get_path())
 
504
                m_add.set_sensitive(True)
 
505
                m_remove.set_sensitive(True)
 
506
                m_rename.set_sensitive(True)
 
507
                m_revert.set_sensitive(True)
 
508
                m_commit.set_sensitive(True)
 
509
                m_diff.set_sensitive(True)
 
510
            except bzrerrors.NotBranchError:
 
511
                m_add.set_sensitive(False)
 
512
                m_remove.set_sensitive(False)
 
513
                m_rename.set_sensitive(False)
 
514
                m_revert.set_sensitive(False)
 
515
                m_commit.set_sensitive(False)
 
516
                m_diff.set_sensitive(False)
 
517
 
 
518
            menu.right_context_menu().popup(None, None, None, 0,
 
519
                                            event.time)
 
520
        
 
521
    def on_treeview_right_row_activated(self, treeview, path, view_column):
 
522
        """ Occurs when somebody double-clicks or enters an item in the
 
523
        file list. """
 
524
        from launch import launch
 
525
        
 
526
        newdir = self.get_selected_right()
 
527
        
 
528
        if newdir == '..':
 
529
            self.set_path(os.path.split(self.get_path())[0])
 
530
        else:
 
531
            fullpath = os.path.join(self.get_path(), newdir)
 
532
            if os.path.isdir(fullpath):
 
533
                # selected item is an existant directory
 
534
                self.set_path(fullpath)
 
535
            else:
 
536
                launch(fullpath) 
 
537
        
 
538
        self.refresh_right()
 
539
    
 
540
    def on_window_main_delete_event(self, widget, event=None):
 
541
        """ Do some stuff before exiting. """
 
542
        width, height = self.window_main.get_size()
 
543
        self.pref.set_preference('window_width', width)
 
544
        self.pref.set_preference('window_height', height)
 
545
        x, y = self.window_main.get_position()
 
546
        self.pref.set_preference('window_x', x)
 
547
        self.pref.set_preference('window_y', y)
 
548
        self.pref.set_preference('paned_position',
 
549
                                 self.hpaned_main.get_position())
 
550
        
 
551
        self.pref.write()
 
552
        self.window_main.destroy()
 
553
        
 
554
    def _load_left(self):
 
555
        """ Load data into the left panel. (Bookmarks) """
 
556
        # Create TreeStore
 
557
        treestore = gtk.TreeStore(str, str)
 
558
        
 
559
        # Get bookmarks
 
560
        bookmarks = self.pref.get_bookmarks()
 
561
        
 
562
        # Add them to the TreeStore
 
563
        titer = treestore.append(None, [_('Bookmarks'), None])
 
564
        for item in bookmarks:
 
565
            title = self.pref.get_bookmark_title(item)
 
566
            treestore.append(titer, [title, item])
 
567
        
 
568
        # Create the column and add it to the TreeView
 
569
        self.treeview_left.set_model(treestore)
 
570
        tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
 
571
        self.treeview_left.append_column(tvcolumn_bookmark)
 
572
        
 
573
        # Set up the cells
 
574
        cell = gtk.CellRendererText()
 
575
        tvcolumn_bookmark.pack_start(cell, True)
 
576
        tvcolumn_bookmark.add_attribute(cell, 'text', 0)
 
577
        
 
578
        # Expand the tree
 
579
        self.treeview_left.expand_all()
 
580
 
 
581
    def _add_updir_to_dirlist(self, dirlist, curdir):
 
582
        """Add .. to the top of directories list if we not in root directory
 
583
 
 
584
        :param dirlist:     list of directories (modified in place)
 
585
        :param curdir:      current directory
 
586
        :return:            nothing
 
587
        """
 
588
        if curdir is None:
 
589
            curdir = self.path
 
590
 
 
591
        if sys.platform == 'win32':
 
592
            drive, tail = os.path.splitdrive(curdir)
 
593
            if tail in ('', '/', '\\'):
 
594
                return
 
595
        else:
 
596
            if curdir == '/':
 
597
                return
 
598
 
 
599
        # insert always as first element
 
600
        dirlist.insert(0, '..')
 
601
 
 
602
    def _load_right(self):
 
603
        """ Load data into the right panel. (Filelist) """
 
604
        # Create ListStore
 
605
        liststore = gtk.ListStore(str, str, str)
 
606
        
 
607
        dirs = []
 
608
        files = []
 
609
        
 
610
        # Fill the appropriate lists
 
611
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
 
612
        for item in os.listdir(self.path):
 
613
            if not dotted_files and item[0] == '.':
 
614
                continue
 
615
            if os.path.isdir(self.path + os.sep + item):
 
616
                dirs.append(item)
 
617
            else:
 
618
                files.append(item)
 
619
            
 
620
        # Sort'em
 
621
        dirs.sort()
 
622
        files.sort()
 
623
 
 
624
        # add updir link to dirs
 
625
        self._add_updir_to_dirlist(dirs, self.path)
 
626
        
 
627
        if not self.notbranch:
 
628
            branch = self.wt.branch
 
629
            tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
 
630
        
 
631
            delta = self.wt.changes_from(tree2, want_unchanged=True)
 
632
        
 
633
        # Add'em to the ListStore
 
634
        for item in dirs:    
 
635
            liststore.append([gtk.STOCK_DIRECTORY, item, ''])
 
636
        for item in files:
 
637
            status = 'unknown'
 
638
            if not self.notbranch:
 
639
                filename = self.wt.relpath(self.path + os.sep + item)
 
640
                
 
641
                try:
 
642
                    self.wt.lock_read()
 
643
                    
 
644
                    for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
 
645
                        if rpathnew == filename:
 
646
                            status = 'renamed'
 
647
                    for rpath, id, kind in delta.added:
 
648
                        if rpath == filename:
 
649
                            status = 'added'
 
650
                    for rpath, id, kind in delta.removed:
 
651
                        if rpath == filename:
 
652
                            status = 'removed'
 
653
                    for rpath, id, kind, text_modified, meta_modified in delta.modified:
 
654
                        if rpath == filename:
 
655
                            status = 'modified'
 
656
                    for rpath, id, kind in delta.unchanged:
 
657
                        if rpath == filename:
 
658
                            status = 'unchanged'
 
659
                    for rpath, file_class, kind, id, entry in self.wt.list_files():
 
660
                        if rpath == filename and file_class == 'I':
 
661
                            status = 'ignored'
 
662
                finally:
 
663
                    self.wt.unlock()
 
664
            
 
665
            #try:
 
666
            #    status = fileops.status(path + os.sep + item)
 
667
            #except errors.PermissionDenied:
 
668
            #    continue
 
669
            
 
670
            if status == 'renamed':
 
671
                st = _('renamed')
 
672
            elif status == 'removed':
 
673
                st = _('removed')
 
674
            elif status == 'added':
 
675
                st = _('added')
 
676
            elif status == 'modified':
 
677
                st = _('modified')
 
678
            elif status == 'unchanged':
 
679
                st = _('unchanged')
 
680
            elif status == 'ignored':
 
681
                st = _('ignored')
 
682
            else:
 
683
                st = _('unknown')
 
684
            liststore.append([gtk.STOCK_FILE, item, st])
 
685
        
 
686
        # Create the columns and add them to the TreeView
 
687
        self.treeview_right.set_model(liststore)
 
688
        tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
 
689
        tvcolumn_status = gtk.TreeViewColumn(_('Status'))
 
690
        self.treeview_right.append_column(tvcolumn_filename)
 
691
        self.treeview_right.append_column(tvcolumn_status)
 
692
        
 
693
        # Set up the cells
 
694
        cellpb = gtk.CellRendererPixbuf()
 
695
        cell = gtk.CellRendererText()
 
696
        tvcolumn_filename.pack_start(cellpb, False)
 
697
        tvcolumn_filename.pack_start(cell, True)
 
698
        tvcolumn_filename.set_attributes(cellpb, stock_id=0)
 
699
        tvcolumn_filename.add_attribute(cell, 'text', 1)
 
700
        tvcolumn_status.pack_start(cell, True)
 
701
        tvcolumn_status.add_attribute(cell, 'text', 2)
 
702
        
 
703
        # Set sensitivity
 
704
        self.set_sensitivity()
 
705
        
 
706
    def get_selected_right(self):
 
707
        """ Get the selected filename. """
 
708
        treeselection = self.treeview_right.get_selection()
 
709
        (model, iter) = treeselection.get_selected()
 
710
        
 
711
        if iter is None:
 
712
            return None
 
713
        else:
 
714
            return model.get_value(iter, 1)
 
715
    
 
716
    def get_selected_left(self):
 
717
        """ Get the selected bookmark. """
 
718
        treeselection = self.treeview_left.get_selection()
 
719
        (model, iter) = treeselection.get_selected()
 
720
        
 
721
        if iter is None:
 
722
            return None
 
723
        else:
 
724
            return model.get_value(iter, 1)
 
725
 
 
726
    def set_statusbar(self, message):
 
727
        """ Set the statusbar message. """
 
728
        self.statusbar.push(self.context_id, message)
 
729
    
 
730
    def clear_statusbar(self):
 
731
        """ Clean the last message from the statusbar. """
 
732
        self.statusbar.pop(self.context_id)
 
733
    
 
734
    def set_sensitivity(self):
 
735
        """ Set menu and toolbar sensitivity. """
 
736
        self.menuitem_branch_init.set_sensitive(self.notbranch)
 
737
        self.menuitem_branch_get.set_sensitive(self.notbranch)
 
738
        self.menuitem_branch_checkout.set_sensitive(self.notbranch)
 
739
        self.menuitem_branch_pull.set_sensitive(not self.notbranch)
 
740
        self.menuitem_branch_push.set_sensitive(not self.notbranch)
 
741
        self.menuitem_branch_revert.set_sensitive(not self.notbranch)
 
742
        self.menuitem_branch_merge.set_sensitive(not self.notbranch)
 
743
        self.menuitem_branch_commit.set_sensitive(not self.notbranch)
 
744
        self.menuitem_branch_status.set_sensitive(not self.notbranch)
 
745
        self.menuitem_branch_missing.set_sensitive(not self.notbranch)
 
746
        self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
 
747
        self.menuitem_stats.set_sensitive(not self.notbranch)
 
748
        self.menuitem_add_files.set_sensitive(not self.notbranch)
 
749
        self.menuitem_remove_files.set_sensitive(not self.notbranch)
 
750
        self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
 
751
        self.menuitem_file_rename.set_sensitive(not self.notbranch)
 
752
        self.menuitem_file_move.set_sensitive(not self.notbranch)
 
753
        self.menuitem_file_annotate.set_sensitive(not self.notbranch)
 
754
        #self.menutoolbutton_diff.set_sensitive(True)
 
755
        self.toolbutton_diff.set_sensitive(not self.notbranch)
 
756
        self.toolbutton_log.set_sensitive(not self.notbranch)
 
757
        self.toolbutton_commit.set_sensitive(not self.notbranch)
 
758
        self.toolbutton_pull.set_sensitive(not self.notbranch)
 
759
        self.toolbutton_push.set_sensitive(not self.notbranch)
 
760
    
 
761
    def refresh_left(self):
 
762
        """ Refresh the bookmark list. """
 
763
        
 
764
        # Get TreeStore and clear it
 
765
        treestore = self.treeview_left.get_model()
 
766
        treestore.clear()
 
767
 
 
768
        # Re-read preferences
 
769
        self.pref.read()
 
770
        
 
771
        # Get bookmarks
 
772
        bookmarks = self.pref.get_bookmarks()
 
773
 
 
774
        # Add them to the TreeStore
 
775
        titer = treestore.append(None, [_('Bookmarks'), None])
 
776
        for item in bookmarks:
 
777
            title = self.pref.get_bookmark_title(item)
 
778
            treestore.append(titer, [title, item])
 
779
 
 
780
        # Add the TreeStore to the TreeView
 
781
        self.treeview_left.set_model(treestore)
 
782
 
 
783
        # Expand the tree
 
784
        self.treeview_left.expand_all()
 
785
 
 
786
    def refresh_right(self, path=None):
 
787
        """ Refresh the file list. """
 
788
        from bzrlib.workingtree import WorkingTree
 
789
 
 
790
        if path is None:
 
791
            path = self.get_path()
 
792
 
 
793
        # A workaround for double-clicking Bookmarks
 
794
        if not os.path.exists(path):
 
795
            return
 
796
 
 
797
        # Get ListStore and clear it
 
798
        liststore = self.treeview_right.get_model()
 
799
        liststore.clear()
 
800
 
 
801
        dirs = []
 
802
        files = []
 
803
 
 
804
        # Fill the appropriate lists
 
805
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
 
806
        for item in os.listdir(path):
 
807
            if not dotted_files and item[0] == '.':
 
808
                continue
 
809
            if os.path.isdir(path + os.sep + item):
 
810
                dirs.append(item)
 
811
            else:
 
812
                files.append(item)
 
813
 
 
814
        # Sort'em
 
815
        dirs.sort()
 
816
        files.sort()
 
817
 
 
818
        # add updir link to dirs
 
819
        self._add_updir_to_dirlist(dirs, path)
 
820
 
 
821
        # Try to open the working tree
 
822
        notbranch = False
 
823
        try:
 
824
            tree1 = WorkingTree.open_containing(path)[0]
 
825
        except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
 
826
            notbranch = True
 
827
        
 
828
        if not notbranch:
 
829
            branch = tree1.branch
 
830
            tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
 
831
        
 
832
            delta = tree1.changes_from(tree2, want_unchanged=True)
 
833
            
 
834
        # Add'em to the ListStore
 
835
        for item in dirs:
 
836
            liststore.append([gtk.STOCK_DIRECTORY, item, ''])
 
837
        for item in files:
 
838
            status = 'unknown'
 
839
            if not notbranch:
 
840
                filename = tree1.relpath(path + os.sep + item)
 
841
                
 
842
                try:
 
843
                    self.wt.lock_read()
 
844
                    
 
845
                    for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
 
846
                        if rpathnew == filename:
 
847
                            status = 'renamed'
 
848
                    for rpath, id, kind in delta.added:
 
849
                        if rpath == filename:
 
850
                            status = 'added'                
 
851
                    for rpath, id, kind in delta.removed:
 
852
                        if rpath == filename:
 
853
                            status = 'removed'
 
854
                    for rpath, id, kind, text_modified, meta_modified in delta.modified:
 
855
                        if rpath == filename:
 
856
                            status = 'modified'
 
857
                    for rpath, id, kind in delta.unchanged:
 
858
                        if rpath == filename:
 
859
                            status = 'unchanged'
 
860
                    for rpath, file_class, kind, id, entry in self.wt.list_files():
 
861
                        if rpath == filename and file_class == 'I':
 
862
                            status = 'ignored'
 
863
                finally:
 
864
                    self.wt.unlock()
 
865
            
 
866
            #try:
 
867
            #    status = fileops.status(path + os.sep + item)
 
868
            #except errors.PermissionDenied:
 
869
            #    continue
 
870
 
 
871
            if status == 'renamed':
 
872
                st = _('renamed')
 
873
            elif status == 'removed':
 
874
                st = _('removed')
 
875
            elif status == 'added':
 
876
                st = _('added')
 
877
            elif status == 'modified':
 
878
                st = _('modified')
 
879
            elif status == 'unchanged':
 
880
                st = _('unchanged')
 
881
            elif status == 'ignored':
 
882
                st = _('ignored')
 
883
            else:
 
884
                st = _('unknown')
 
885
            liststore.append([gtk.STOCK_FILE, item, st])
 
886
 
 
887
        # Add the ListStore to the TreeView
 
888
        self.treeview_right.set_model(liststore)
 
889
        
 
890
        # Set sensitivity
 
891
        self.set_sensitivity()
 
892
 
 
893
    def _harddisks(self):
 
894
        """ Returns hard drive letters under Win32. """
 
895
        try:
 
896
            import win32file
 
897
            import string
 
898
        except ImportError:
 
899
            if sys.platform == 'win32':
 
900
                print "pyWin32 modules needed to run Olive on Win32."
 
901
                sys.exit(1)
 
902
            else:
 
903
                pass
 
904
        
 
905
        driveletters = []
 
906
        for drive in string.ascii_uppercase:
 
907
            if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
 
908
                driveletters.append(drive+':')
 
909
        return driveletters
 
910
    
 
911
    def gen_hard_selector(self):
 
912
        """ Generate the hard drive selector under Win32. """
 
913
        drives = self._harddisks()
 
914
        for drive in drives:
 
915
            self.combobox_drive.append_text(drive)
 
916
        self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
 
917
    
 
918
    def _refresh_drives(self, combobox):
 
919
        if self._just_started:
 
920
            return
 
921
        model = combobox.get_model()
 
922
        active = combobox.get_active()
 
923
        if active >= 0:
 
924
            drive = model[active][0]
 
925
            self.set_path(drive + '\\')
 
926
            self.refresh_right(drive + '\\')
 
927
    
 
928
    def check_for_changes(self):
 
929
        """ Check whether there were changes in the current working tree. """
 
930
        old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
 
931
        delta = self.wt.changes_from(old_tree)
 
932
 
 
933
        changes = False
 
934
        
 
935
        if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
 
936
            changes = True
 
937
        
 
938
        return changes
 
939
 
 
940
import ConfigParser
 
941
 
 
942
class Preferences:
 
943
    """ A class which handles Olive's preferences. """
 
944
    def __init__(self, path=None):
 
945
        """ Initialize the Preferences class. """
 
946
        # Some default options
 
947
        self.defaults = { 'strict_commit' : False,
 
948
                          'dotted_files'  : False,
 
949
                          'window_width'  : 700,
 
950
                          'window_height' : 400,
 
951
                          'window_x'      : 40,
 
952
                          'window_y'      : 40,
 
953
                          'paned_position': 200 }
 
954
 
 
955
        # Create a config parser object
 
956
        self.config = ConfigParser.RawConfigParser()
 
957
 
 
958
        # Set filename
 
959
        if path is None:
 
960
            if sys.platform == 'win32':
 
961
                # Windows - no dotted files
 
962
                self._filename = os.path.expanduser('~/olive.conf')
 
963
            else:
 
964
                self._filename = os.path.expanduser('~/.olive.conf')
 
965
        else:
 
966
            self._filename = path
 
967
        
 
968
        # Load the configuration
 
969
        self.read()
 
970
        
 
971
    def _get_default(self, option):
 
972
        """ Get the default option for a preference. """
 
973
        try:
 
974
            ret = self.defaults[option]
 
975
        except KeyError:
 
976
            return None
 
977
        else:
 
978
            return ret
 
979
 
 
980
    def refresh(self):
 
981
        """ Refresh the configuration. """
 
982
        # First write out the changes
 
983
        self.write()
 
984
        # Then load the configuration again
 
985
        self.read()
 
986
 
 
987
    def read(self):
 
988
        """ Just read the configuration. """
 
989
        # Re-initialize the config parser object to avoid some bugs
 
990
        self.config = ConfigParser.RawConfigParser()
 
991
        self.config.read([self._filename])
 
992
    
 
993
    def write(self):
 
994
        """ Write the configuration to the appropriate files. """
 
995
        fp = open(self._filename, 'w')
 
996
        self.config.write(fp)
 
997
        fp.close()
 
998
 
 
999
    def get_bookmarks(self):
 
1000
        """ Return the list of bookmarks. """
 
1001
        bookmarks = self.config.sections()
 
1002
        if self.config.has_section('preferences'):
 
1003
            bookmarks.remove('preferences')
 
1004
        return bookmarks
 
1005
 
 
1006
    def add_bookmark(self, path):
 
1007
        """ Add bookmark. """
 
1008
        try:
 
1009
            self.config.add_section(path)
 
1010
        except ConfigParser.DuplicateSectionError:
 
1011
            return False
 
1012
        else:
 
1013
            return True
 
1014
 
 
1015
    def get_bookmark_title(self, path):
 
1016
        """ Get bookmark title. """
 
1017
        try:
 
1018
            ret = self.config.get(path, 'title')
 
1019
        except ConfigParser.NoOptionError:
 
1020
            ret = path
 
1021
        
 
1022
        return ret
 
1023
    
 
1024
    def set_bookmark_title(self, path, title):
 
1025
        """ Set bookmark title. """
 
1026
        # FIXME: What if path isn't listed yet?
 
1027
        # FIXME: Canonicalize paths first?
 
1028
        self.config.set(path, 'title', title)
 
1029
    
 
1030
    def remove_bookmark(self, path):
 
1031
        """ Remove bookmark. """
 
1032
        return self.config.remove_section(path)
 
1033
 
 
1034
    def set_preference(self, option, value):
 
1035
        """ Set the value of the given option. """
 
1036
        if value is True:
 
1037
            value = 'yes'
 
1038
        elif value is False:
 
1039
            value = 'no'
 
1040
        
 
1041
        if self.config.has_section('preferences'):
 
1042
            self.config.set('preferences', option, value)
 
1043
        else:
 
1044
            self.config.add_section('preferences')
 
1045
            self.config.set('preferences', option, value)
 
1046
 
 
1047
    def get_preference(self, option, kind='str'):
 
1048
        """ Get the value of the given option.
 
1049
        
 
1050
        :param kind: str/bool/int/float. default: str
 
1051
        """
 
1052
        if self.config.has_option('preferences', option):
 
1053
            if kind == 'bool':
 
1054
                return self.config.getboolean('preferences', option)
 
1055
            elif kind == 'int':
 
1056
                return self.config.getint('preferences', option)
 
1057
            elif kind == 'float':
 
1058
                return self.config.getfloat('preferences', option)
 
1059
            else:
 
1060
                return self.config.get('preferences', option)
 
1061
        else:
 
1062
            try:
 
1063
                return self._get_default(option)
 
1064
            except KeyError:
 
1065
                return None
 
1066