/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
0.8.84 by Szilveszter Farkas (Phanatic)
Huge cleanup after removing backend codebase.
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 ConfigParser
18
import os
19
import os.path
20
import sys
21
22
try:
23
    import pygtk
24
    pygtk.require("2.0")
25
except:
26
    pass
27
try:
28
    import gtk
29
    import gtk.gdk
30
    import gtk.glade
31
except:
32
    sys.exit(1)
33
34
from handler import OliveHandler
35
import bzrlib.errors as errors
36
37
# Olive GTK UI version
38
__version__ = '0.11.0'
39
40
class OliveGtk:
41
    """ The main Olive GTK frontend class. This is called when launching the
42
    program. """
43
    
44
    def __init__(self):
45
        # Load the glade file
46
        if sys.platform == 'win32':
47
            self.gladefile = os.path.dirname(sys.executable) + "/share/olive/olive.glade"
48
        else:
49
            self.gladefile = "/usr/share/olive/olive.glade"
50
51
        if not os.path.exists(self.gladefile):
52
            # Load from current directory if not installed
53
            self.gladefile = "olive.glade"
54
            # Check again
55
            if not os.path.exists(self.gladefile):
56
                # Fail
57
                print _('Glade file cannot be found.')
58
                sys.exit(1)
59
60
        self.toplevel = gtk.glade.XML(self.gladefile, 'window_main', 'olive-gtk')
61
        
62
        self.window = self.toplevel.get_widget('window_main')
63
        
64
        self.pref = OlivePreferences()
65
        self.comm = OliveCommunicator(self.toplevel, self.pref)
66
        handler = OliveHandler(self.gladefile, self.comm)
67
        
68
        # Dictionary for signal_autoconnect
69
        dic = { "on_window_main_destroy": gtk.main_quit,
70
                "on_window_main_delete_event": handler.on_window_main_delete_event,
71
                "on_quit_activate": handler.on_window_main_delete_event,
72
                "on_about_activate": handler.on_about_activate,
73
                "on_menuitem_add_files_activate": handler.on_menuitem_add_files_activate,
74
                "on_menuitem_remove_file_activate": handler.on_menuitem_remove_file_activate,
75
                "on_menuitem_file_make_directory_activate": handler.on_menuitem_file_make_directory_activate,
76
                "on_menuitem_file_move_activate": handler.on_menuitem_file_move_activate,
77
                "on_menuitem_file_rename_activate": handler.on_menuitem_file_rename_activate,
78
                "on_menuitem_view_show_hidden_files_activate": handler.on_menuitem_view_show_hidden_files_activate,
79
                "on_menuitem_view_refresh_activate": handler.on_menuitem_view_refresh_activate,
80
                "on_menuitem_branch_initialize_activate": handler.on_menuitem_branch_initialize_activate,
81
                "on_menuitem_branch_get_activate": handler.on_menuitem_branch_get_activate,
82
                "on_menuitem_branch_checkout_activate": handler.on_menuitem_branch_checkout_activate,
83
                "on_menuitem_branch_commit_activate": handler.on_menuitem_branch_commit_activate,
84
                "on_menuitem_branch_push_activate": handler.on_menuitem_branch_push_activate,
85
                "on_menuitem_branch_pull_activate": handler.on_menuitem_branch_pull_activate,
86
                "on_menuitem_branch_status_activate": handler.on_menuitem_branch_status_activate,
87
                "on_menuitem_branch_missing_revisions_activate": handler.on_menuitem_branch_missing_revisions_activate,
88
                "on_menuitem_stats_diff_activate": handler.on_menuitem_stats_diff_activate,
89
                "on_menuitem_stats_log_activate": handler.on_menuitem_stats_log_activate,
90
                "on_menuitem_stats_infos_activate": handler.on_menuitem_stats_infos_activate,
91
                "on_toolbutton_refresh_clicked": handler.on_menuitem_view_refresh_activate,
92
                "on_toolbutton_log_clicked": handler.on_menuitem_stats_log_activate,
93
                #"on_menutoolbutton_diff_clicked": handler.on_menuitem_stats_diff_activate,
94
                "on_toolbutton_diff_clicked": handler.on_menuitem_stats_diff_activate,
95
                "on_toolbutton_commit_clicked": handler.on_menuitem_branch_commit_activate,
96
                "on_toolbutton_pull_clicked": handler.on_menuitem_branch_pull_activate,
97
                "on_toolbutton_push_clicked": handler.on_menuitem_branch_push_activate,
98
                "on_treeview_right_button_press_event": handler.on_treeview_right_button_press_event,
99
                "on_treeview_right_row_activated": handler.on_treeview_right_row_activated,
100
                "on_treeview_left_button_press_event": handler.on_treeview_left_button_press_event,
101
                "on_treeview_left_row_activated": handler.on_treeview_left_row_activated }
102
        
103
        # Connect the signals to the handlers
104
        self.toplevel.signal_autoconnect(dic)
105
        
106
        # Apply window size and position
107
        width = self.pref.get_preference('window_width', 'int')
108
        height = self.pref.get_preference('window_height', 'int')
109
        self.window.resize(width, height)
110
        x = self.pref.get_preference('window_x', 'int')
111
        y = self.pref.get_preference('window_y', 'int')
112
        self.window.move(x, y)
113
        # Apply paned position
114
        pos = self.pref.get_preference('paned_position', 'int')
115
        self.comm.hpaned_main.set_position(pos)
116
        
117
        # Apply menu to the toolbutton
118
        #menubutton = self.toplevel.get_widget('menutoolbutton_diff')
119
        #menubutton.set_menu(handler.menu.toolbar_diff)
120
        
121
        # Now we can show the window
122
        self.window.show()
123
        
124
        # Show drive selector if under Win32
125
        if sys.platform == 'win32':
126
            self.comm.vbox_main_right.pack_start(self.comm.combobox_drive, False, True, 0)
127
            self.comm.vbox_main_right.reorder_child(self.comm.combobox_drive, 0)
128
            self.comm.combobox_drive.show()
129
            self.comm.gen_hard_selector()
130
        
131
        # Load default data into the panels
132
        self.treeview_left = self.toplevel.get_widget('treeview_left')
133
        self.treeview_right = self.toplevel.get_widget('treeview_right')
134
        self._load_left()
135
        self._load_right()
136
137
        # Apply menu state
138
        self.comm.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
139
        
140
    def _load_left(self):
141
        """ Load data into the left panel. (Bookmarks) """
142
        # set cursor to busy
143
        self.comm.set_busy(self.treeview_left)
144
        
145
        # Create TreeStore
146
        treestore = gtk.TreeStore(str, str)
147
        
148
        # Get bookmarks
149
        bookmarks = self.comm.pref.get_bookmarks()
150
        
151
        # Add them to the TreeStore
152
        titer = treestore.append(None, [_('Bookmarks'), None])
153
        for item in bookmarks:
154
            title = self.comm.pref.get_bookmark_title(item)
155
            treestore.append(titer, [title, item])
156
        
157
        # Create the column and add it to the TreeView
158
        self.treeview_left.set_model(treestore)
159
        tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
160
        self.treeview_left.append_column(tvcolumn_bookmark)
161
        
162
        # Set up the cells
163
        cell = gtk.CellRendererText()
164
        tvcolumn_bookmark.pack_start(cell, True)
165
        tvcolumn_bookmark.add_attribute(cell, 'text', 0)
166
        
167
        # Expand the tree
168
        self.treeview_left.expand_all()
169
170
        self.comm.set_busy(self.treeview_left, False)
171
        
172
    def _load_right(self):
173
        """ Load data into the right panel. (Filelist) """
174
        from bzrlib.workingtree import WorkingTree
175
        
176
        # set cursor to busy
177
        self.comm.set_busy(self.treeview_right)
178
        
179
        # Create ListStore
180
        liststore = gtk.ListStore(str, str, str)
181
        
182
        dirs = ['..']
183
        files = []
184
        
185
        # Fill the appropriate lists
186
        path = self.comm.get_path()
187
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
188
        for item in os.listdir(path):
189
            if not dotted_files and item[0] == '.':
190
                continue
191
            if os.path.isdir(path + os.sep + item):
192
                dirs.append(item)
193
            else:
194
                files.append(item)
195
            
196
        # Sort'em
197
        dirs.sort()
198
        files.sort()
199
        
200
        # Try to open the working tree
201
        notbranch = False
202
        try:
203
            tree1 = WorkingTree.open_containing(path)[0]
204
        except errors.NotBranchError:
205
            notbranch = True
206
        except errors.PermissionDenied:
207
            print "DEBUG: permission denied."
208
        
209
        if not notbranch:
210
            branch = tree1.branch
211
            tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
212
        
213
            delta = tree1.changes_from(tree2, want_unchanged=True)
214
        
215
        # Add'em to the ListStore
216
        for item in dirs:    
217
            liststore.append([gtk.STOCK_DIRECTORY, item, ''])
218
        for item in files:
219
            status = 'unknown'
220
            if not notbranch:
221
                filename = tree1.relpath(path + os.sep + item)
222
                
223
                for rpath, id, kind, text_modified, meta_modified in delta.renamed:
224
                    if rpath == filename:
225
                        status = 'renamed'
226
                for rpath, id, kind in delta.added:
227
                    if rpath == filename:
228
                        status = 'added'
229
                for rpath, id, kind, text_modified, meta_modified in delta.removed:
230
                    if rpath == filename:
231
                        status = 'removed'
232
                for rpath, id, kind, text_modified, meta_modified in delta.modified:
233
                    if rpath == filename:
234
                        status = 'modified'
235
                for rpath, id, kind in delta.unchanged:
236
                    if rpath == filename:
237
                        status = 'unchanged'
238
            
239
            #try:
240
            #    status = fileops.status(path + os.sep + item)
241
            #except errors.PermissionDenied:
242
            #    continue
243
            
244
            if status == 'renamed':
245
                st = _('renamed')
246
            elif status == 'removed':
247
                st = _('removed')
248
            elif status == 'added':
249
                st = _('added')
250
            elif status == 'modified':
251
                st = _('modified')
252
            elif status == 'unchanged':
253
                st = _('unchanged')
254
            else:
255
                st = _('unknown')
256
            liststore.append([gtk.STOCK_FILE, item, st])
257
        
258
        # Create the columns and add them to the TreeView
259
        self.treeview_right.set_model(liststore)
260
        tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
261
        tvcolumn_status = gtk.TreeViewColumn(_('Status'))
262
        self.treeview_right.append_column(tvcolumn_filename)
263
        self.treeview_right.append_column(tvcolumn_status)
264
        
265
        # Set up the cells
266
        cellpb = gtk.CellRendererPixbuf()
267
        cell = gtk.CellRendererText()
268
        tvcolumn_filename.pack_start(cellpb, False)
269
        tvcolumn_filename.pack_start(cell, True)
270
        tvcolumn_filename.set_attributes(cellpb, stock_id=0)
271
        tvcolumn_filename.add_attribute(cell, 'text', 1)
272
        tvcolumn_status.pack_start(cell, True)
273
        tvcolumn_status.add_attribute(cell, 'text', 2)
274
        
275
        # Check if current directory is a branch
276
        if not notbranch:
277
            # Activate some items
278
            self.comm.menuitem_branch_init.set_sensitive(False)
279
            self.comm.menuitem_branch_get.set_sensitive(True)
280
            self.comm.menuitem_branch_checkout.set_sensitive(True)
281
            self.comm.menuitem_branch_pull.set_sensitive(True)
282
            self.comm.menuitem_branch_push.set_sensitive(True)
283
            self.comm.menuitem_branch_commit.set_sensitive(True)
284
            self.comm.menuitem_branch_status.set_sensitive(True)
285
            self.comm.menuitem_branch_missing.set_sensitive(True)
286
            self.comm.menuitem_stats.set_sensitive(True)
287
            self.comm.menuitem_add_files.set_sensitive(True)
288
            self.comm.menuitem_remove_files.set_sensitive(True)
289
            self.comm.menuitem_file_make_directory.set_sensitive(True)
290
            self.comm.menuitem_file_rename.set_sensitive(True)
291
            self.comm.menuitem_file_move.set_sensitive(True)
292
            #self.comm.menutoolbutton_diff.set_sensitive(True)
293
            self.comm.toolbutton_diff.set_sensitive(True)
294
            self.comm.toolbutton_log.set_sensitive(True)
295
            self.comm.toolbutton_commit.set_sensitive(True)
296
            self.comm.toolbutton_pull.set_sensitive(True)
297
            self.comm.toolbutton_push.set_sensitive(True)
298
        else:
299
            # Deactivate some items
300
            self.comm.menuitem_branch_init.set_sensitive(True)
301
            self.comm.menuitem_branch_get.set_sensitive(False)
302
            self.comm.menuitem_branch_checkout.set_sensitive(False)
303
            self.comm.menuitem_branch_pull.set_sensitive(False)
304
            self.comm.menuitem_branch_push.set_sensitive(False)
305
            self.comm.menuitem_branch_commit.set_sensitive(False)
306
            self.comm.menuitem_branch_status.set_sensitive(False)
307
            self.comm.menuitem_branch_missing.set_sensitive(False)
308
            self.comm.menuitem_stats.set_sensitive(False)
309
            self.comm.menuitem_add_files.set_sensitive(False)
310
            self.comm.menuitem_remove_files.set_sensitive(False)
311
            self.comm.menuitem_file_make_directory.set_sensitive(False)
312
            self.comm.menuitem_file_rename.set_sensitive(False)
313
            self.comm.menuitem_file_move.set_sensitive(False)
314
            #self.comm.menutoolbutton_diff.set_sensitive(False)
315
            self.comm.toolbutton_diff.set_sensitive(False)
316
            self.comm.toolbutton_log.set_sensitive(False)
317
            self.comm.toolbutton_commit.set_sensitive(False)
318
            self.comm.toolbutton_pull.set_sensitive(False)
319
            self.comm.toolbutton_push.set_sensitive(False)
320
        
321
        # set cursor to default
322
        self.comm.set_busy(self.treeview_right, False)
323
324
class OliveCommunicator:
325
    """ This class is responsible for the communication between the different
326
    modules. """
327
    def __init__(self, toplevel, pref):
328
        # Get glade main component
329
        self.toplevel = toplevel
330
        # Preferences object
331
        self.pref = pref
332
        # Default path
333
        self._path = os.getcwd()
334
335
        # Initialize the statusbar
336
        self.statusbar = self.toplevel.get_widget('statusbar')
337
        self.context_id = self.statusbar.get_context_id('olive')
338
        
339
        # Get the main window
340
        self.window_main = self.toplevel.get_widget('window_main')
341
        # Get the HPaned
342
        self.hpaned_main = self.toplevel.get_widget('hpaned_main')
343
        # Get the TreeViews
344
        self.treeview_left = self.toplevel.get_widget('treeview_left')
345
        self.treeview_right = self.toplevel.get_widget('treeview_right')
346
        # Get some important menu items
347
        self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
348
        self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
349
        self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
350
        self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
351
        self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
352
        self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
353
        self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
354
        self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
355
        self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
356
        self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
357
        self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
358
        self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
359
        self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
360
        self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
361
        self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
362
        self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
363
        self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
364
        self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
365
        # Get some toolbuttons
366
        #self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
367
        self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
368
        self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
369
        self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
370
        self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
371
        self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
372
        # Get the drive selector
373
        self.combobox_drive = gtk.combo_box_new_text()
374
        self.combobox_drive.connect("changed", self._refresh_drives)
375
        
376
        self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
377
    
378
    def set_path(self, path):
379
        """ Set the current path while browsing the directories. """
380
        self._path = path
381
    
382
    def get_path(self):
383
        """ Get the current path. """
384
        return self._path
385
    
386
    def get_selected_right(self):
387
        """ Get the selected filename. """
388
        treeselection = self.treeview_right.get_selection()
389
        (model, iter) = treeselection.get_selected()
390
        
391
        if iter is None:
392
            return None
393
        else:
394
            return model.get_value(iter, 1)
395
    
396
    def get_selected_left(self):
397
        """ Get the selected bookmark. """
398
        treeselection = self.treeview_left.get_selection()
399
        (model, iter) = treeselection.get_selected()
400
        
401
        if iter is None:
402
            return None
403
        else:
404
            return model.get_value(iter, 1)
405
406
    def set_statusbar(self, message):
407
        """ Set the statusbar message. """
408
        self.statusbar.push(self.context_id, message)
409
    
410
    def clear_statusbar(self):
411
        """ Clean the last message from the statusbar. """
412
        self.statusbar.pop(self.context_id)
413
    
414
    def refresh_left(self):
415
        """ Refresh the bookmark list. """
416
        # set cursor to busy
417
        self.set_busy(self.treeview_left)
418
        
419
        # Get TreeStore and clear it
420
        treestore = self.treeview_left.get_model()
421
        treestore.clear()
422
423
        # Get bookmarks
424
        bookmarks = self.pref.get_bookmarks()
425
426
        # Add them to the TreeStore
427
        titer = treestore.append(None, [_('Bookmarks'), None])
428
        for item in bookmarks:
429
            title = self.pref.get_bookmark_title(item)
430
            treestore.append(titer, [title, item])
431
432
        # Add the TreeStore to the TreeView
433
        self.treeview_left.set_model(treestore)
434
435
        # Expand the tree
436
        self.treeview_left.expand_all()
437
438
        self.set_busy(self.treeview_left, False)
439
440
    def refresh_right(self, path=None):
441
        """ Refresh the file list. """
442
        from bzrlib.workingtree import WorkingTree
443
444
        self.set_busy(self.treeview_right)
445
446
        if path is None:
447
            path = self.get_path()
448
449
        # A workaround for double-clicking Bookmarks
450
        if not os.path.exists(path):
451
            self.set_busy(self.treeview_right, False)
452
            return
453
454
        # Get ListStore and clear it
455
        liststore = self.treeview_right.get_model()
456
        liststore.clear()
457
458
        dirs = ['..']
459
        files = []
460
461
        # Fill the appropriate lists
462
        dotted_files = self.pref.get_preference('dotted_files', 'bool')
463
        for item in os.listdir(path):
464
            if not dotted_files and item[0] == '.':
465
                continue
466
            if os.path.isdir(path + os.sep + item):
467
                dirs.append(item)
468
            else:
469
                files.append(item)
470
471
        # Sort'em
472
        dirs.sort()
473
        files.sort()
474
        
475
        # Try to open the working tree
476
        notbranch = False
477
        try:
478
            tree1 = WorkingTree.open_containing(path)[0]
479
        except errors.NotBranchError:
480
            notbranch = True
481
        except errors.PermissionDenied:
482
            print "DEBUG: permission denied."
483
        
484
        if not notbranch:
485
            branch = tree1.branch
486
            tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
487
        
488
            delta = tree1.changes_from(tree2, want_unchanged=True)
489
490
        # Add'em to the ListStore
491
        for item in dirs:
492
            liststore.append([gtk.STOCK_DIRECTORY, item, ''])
493
        for item in files:
494
            status = 'unknown'
495
            if not notbranch:
496
                filename = tree1.relpath(path + os.sep + item)
497
                
498
                for rpath, id, kind, text_modified, meta_modified in delta.renamed:
499
                    if rpath == filename:
500
                        status = 'renamed'
501
                for rpath, id, kind in delta.added:
502
                    if rpath == filename:
503
                        status = 'added'
504
                for rpath, id, kind, text_modified, meta_modified in delta.removed:
505
                    if rpath == filename:
506
                        status = 'removed'
507
                for rpath, id, kind, text_modified, meta_modified in delta.modified:
508
                    if rpath == filename:
509
                        status = 'modified'
510
                for rpath, id, kind in delta.unchanged:
511
                    if rpath == filename:
512
                        status = 'unchanged'
513
            
514
            #try:
515
            #    status = fileops.status(path + os.sep + item)
516
            #except errors.PermissionDenied:
517
            #    continue
518
519
            if status == 'renamed':
520
                st = _('renamed')
521
            elif status == 'removed':
522
                st = _('removed')
523
            elif status == 'added':
524
                st = _('added')
525
            elif status == 'modified':
526
                st = _('modified')
527
            elif status == 'unchanged':
528
                st = _('unchanged')
529
            else:
530
                st = _('unknown')
531
            liststore.append([gtk.STOCK_FILE, item, st])
532
533
        # Add the ListStore to the TreeView
534
        self.treeview_right.set_model(liststore)
535
536
        # Check if current directory is a branch
537
        if not notbranch:
538
            # Activate some items
539
            self.menuitem_branch_init.set_sensitive(False)
540
            self.menuitem_branch_get.set_sensitive(True)
541
            self.menuitem_branch_checkout.set_sensitive(True)
542
            self.menuitem_branch_pull.set_sensitive(True)
543
            self.menuitem_branch_push.set_sensitive(True)
544
            self.menuitem_branch_commit.set_sensitive(True)
545
            self.menuitem_branch_status.set_sensitive(True)
546
            self.menuitem_branch_missing.set_sensitive(True)
547
            self.menuitem_stats.set_sensitive(True)
548
            self.menuitem_add_files.set_sensitive(True)
549
            self.menuitem_remove_files.set_sensitive(True)
550
            self.menuitem_file_make_directory.set_sensitive(True)
551
            self.menuitem_file_rename.set_sensitive(True)
552
            self.menuitem_file_move.set_sensitive(True)
553
            #self.menutoolbutton_diff.set_sensitive(True)
554
            self.toolbutton_diff.set_sensitive(True)
555
            self.toolbutton_log.set_sensitive(True)
556
            self.toolbutton_commit.set_sensitive(True)
557
            self.toolbutton_pull.set_sensitive(True)
558
            self.toolbutton_push.set_sensitive(True)
559
        else:
560
            # Deactivate some items
561
            self.menuitem_branch_init.set_sensitive(True)
562
            self.menuitem_branch_get.set_sensitive(False)
563
            self.menuitem_branch_checkout.set_sensitive(False)
564
            self.menuitem_branch_pull.set_sensitive(False)
565
            self.menuitem_branch_push.set_sensitive(False)
566
            self.menuitem_branch_commit.set_sensitive(False)
567
            self.menuitem_branch_status.set_sensitive(False)
568
            self.menuitem_branch_missing.set_sensitive(False)
569
            self.menuitem_stats.set_sensitive(False)
570
            self.menuitem_add_files.set_sensitive(False)
571
            self.menuitem_remove_files.set_sensitive(False)
572
            self.menuitem_file_make_directory.set_sensitive(False)
573
            self.menuitem_file_rename.set_sensitive(False)
574
            self.menuitem_file_move.set_sensitive(False)
575
            #self.menutoolbutton_diff.set_sensitive(False)
576
            self.toolbutton_diff.set_sensitive(False)
577
            self.toolbutton_log.set_sensitive(False)
578
            self.toolbutton_commit.set_sensitive(False)
579
            self.toolbutton_pull.set_sensitive(False)
580
            self.toolbutton_push.set_sensitive(False)
581
582
        self.set_busy(self.treeview_right, False)
583
584
    def set_busy(self, widget, busy=True):
585
        if busy:
586
            widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
587
        else:
588
            widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
589
590
        gtk.main_iteration(0)
591
592
    def _harddisks(self):
593
        """ Returns hard drive letters under Win32. """
594
        try:
595
            import win32file
596
            import string
597
        except ImportError:
598
            if sys.platform == 'win32':
599
                print "pyWin32 modules needed to run Olive on Win32."
600
                sys.exit(1)
601
            else:
602
                pass
603
        
604
        driveletters = []
605
        for drive in string.ascii_uppercase:
606
            if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
607
                driveletters.append(drive+':')
608
        return driveletters
609
    
610
    def gen_hard_selector(self):
611
        """ Generate the hard drive selector under Win32. """
612
        drives = self._harddisks()
613
        for drive in drives:
614
            self.combobox_drive.append_text(drive)
615
    
616
    def _refresh_drives(self, combobox):
617
        model = combobox.get_model()
618
        active = combobox.get_active()
619
        if active >= 0:
620
            drive = model[active][0]
621
            self.refresh_right(drive + '\\')
622
623
class OlivePreferences:
624
    """ A class which handles Olive's preferences. """
625
    def __init__(self):
626
        """ Initialize the Preferences class. """
627
        # Some default options
628
        self.defaults = { 'strict_commit' : False,
629
                          'dotted_files'  : False,
630
                          'window_width'  : 700,
631
                          'window_height' : 400,
632
                          'window_x'      : 40,
633
                          'window_y'      : 40,
634
                          'paned_position': 200 }
635
636
        # Create a config parser object
637
        self.config = ConfigParser.RawConfigParser()
638
        
639
        # Load the configuration
640
        if sys.platform == 'win32':
641
            # Windows - no dotted files
642
            self.config.read([os.path.expanduser('~/olive.conf')])
643
        else:
644
            self.config.read([os.path.expanduser('~/.olive.conf')])
645
        
646
    def _get_default(self, option):
647
        """ Get the default option for a preference. """
648
        try:
649
            ret = self.defaults[option]
650
        except KeyError:
651
            return None
652
        else:
653
            return ret
654
655
    def refresh(self):
656
        """ Refresh the configuration. """
657
        # First write out the changes
658
        self.write()
659
        # Then load the configuration again
660
        if sys.platform == 'win32':
661
            # Windows - no dotted files
662
            self.config.read([os.path.expanduser('~/olive.conf')])
663
        else:
664
            self.config.read([os.path.expanduser('~/.olive.conf')])
665
666
    def write(self):
667
        """ Write the configuration to the appropriate files. """
668
        if sys.platform == 'win32':
669
            # Windows - no dotted files
670
            fp = open(os.path.expanduser('~/olive.conf'), 'w')
671
            self.config.write(fp)
672
            fp.close()
673
        else:
674
            fp = open(os.path.expanduser('~/.olive.conf'), 'w')
675
            self.config.write(fp)
676
            fp.close()
677
678
    def get_preference(self, option, kind='str'):
679
        """ Get the value of the given option.
680
        
681
        :param kind: str/bool/int/float. default: str
682
        """
683
        if self.config.has_option('preferences', option):
684
            if kind == 'bool':
685
                return self.config.getboolean('preferences', option)
686
            elif kind == 'int':
687
                return self.config.getint('preferences', option)
688
            elif kind == 'float':
689
                return self.config.getfloat('preferences', option)
690
            else:
691
                return self.config.get('preferences', option)
692
        else:
693
            try:
694
                return self._get_default(option)
695
            except KeyError:
696
                return None
697
    
698
    def set_preference(self, option, value):
699
        """ Set the value of the given option. """
700
        if self.config.has_section('preferences'):
701
            self.config.set('preferences', option, value)
702
        else:
703
            self.config.add_section('preferences')
704
            self.config.set('preferences', option, value)
705
    
706
    def get_bookmarks(self):
707
        """ Return the list of bookmarks. """
708
        bookmarks = self.config.sections()
709
        if self.config.has_section('preferences'):
710
            bookmarks.remove('preferences')
711
        return bookmarks
712
713
    def add_bookmark(self, path):
714
        """ Add bookmark. """
715
        try:
716
            self.config.add_section(path)
717
        except ConfigParser.DuplicateSectionError:
718
            return False
719
        else:
720
            return True
721
722
    def get_bookmark_title(self, path):
723
        """ Get bookmark title. """
724
        try:
725
            ret = self.config.get(path, 'title')
726
        except ConfigParser.NoOptionError:
727
            ret = path
728
        
729
        return ret
730
    
731
    def set_bookmark_title(self, path, title):
732
        """ Set bookmark title. """
733
        self.config.set(path, 'title', title)
734
    
735
    def remove_bookmark(self, path):
736
        """ Remove bookmark. """
737
        return self.config.remove_section(path)