/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: Aaron Bentley
  • Date: 2006-11-21 14:19:41 UTC
  • mfrom: (91.1.12 trunk)
  • mto: (66.6.5 gtk)
  • mto: This revision was merged to the branch mainline in revision 112.
  • Revision ID: abentley@panoramicfeedback.com-20061121141941-m82u7iqexarwirkv
MergeĀ fromĀ upstream

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