/b-gtk/fix-viz

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

« back to all changes in this revision

Viewing changes to olive/__init__.py

  • Committer: Jelmer Vernooij
  • Date: 2007-02-01 15:50:40 UTC
  • Revision ID: jelmer@samba.org-20070201155040-3hq4mfbxs99kzazy
add framework for tests.

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