1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
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.
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.
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
22
gettext.install('olive-gtk')
34
from bzrlib.branch import Branch
35
import bzrlib.errors as bzrerrors
36
from bzrlib.lazy_import import lazy_import
37
from bzrlib.workingtree import WorkingTree
39
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
40
from bzrlib.plugins.gtk.errors import show_bzr_error
41
from guifiles import GLADEFILENAME
43
from bzrlib.plugins.gtk.diff import DiffWindow
44
lazy_import(globals(), """
45
from bzrlib.plugins.gtk.viz import branchwin
47
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
48
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
49
from bzrlib.plugins.gtk.commit import CommitDialog
50
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
51
from bzrlib.plugins.gtk.push import PushDialog
54
""" The main Olive GTK frontend class. This is called when launching the
58
self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
59
self.window = self.toplevel.get_widget('window_main')
60
self.pref = Preferences()
63
# Initialize the statusbar
64
self.statusbar = self.toplevel.get_widget('statusbar')
65
self.context_id = self.statusbar.get_context_id('olive')
68
self.window_main = self.toplevel.get_widget('window_main')
70
self.hpaned_main = self.toplevel.get_widget('hpaned_main')
72
self.treeview_left = self.toplevel.get_widget('treeview_left')
73
self.treeview_right = self.toplevel.get_widget('treeview_right')
74
# Get some important menu items
75
self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
76
self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
77
self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
78
self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
79
self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
80
self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
81
self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
82
self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
83
self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
84
self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
85
self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
86
self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
87
self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
88
self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
89
self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
90
self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
91
self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
92
self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
93
self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
94
self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
95
self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
96
self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
97
# Get some toolbuttons
98
#self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
99
self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
100
self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
101
self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
102
self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
103
self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
104
# Get the drive selector
105
self.combobox_drive = gtk.combo_box_new_text()
106
self.combobox_drive.connect("changed", self._refresh_drives)
108
self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
111
# Dictionary for signal_autoconnect
112
dic = { "on_window_main_destroy": gtk.main_quit,
113
"on_window_main_delete_event": self.on_window_main_delete_event,
114
"on_quit_activate": self.on_window_main_delete_event,
115
"on_about_activate": self.on_about_activate,
116
"on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
117
"on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
118
"on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
119
"on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
120
"on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
121
"on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
122
"on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
123
"on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
124
"on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
125
"on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
126
"on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
127
"on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
128
"on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
129
"on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
130
"on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
131
"on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
132
"on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
133
"on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
134
"on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
135
"on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
136
"on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
137
"on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
138
"on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
139
"on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
140
#"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
141
"on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
142
"on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
143
"on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
144
"on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
145
"on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
146
"on_treeview_right_row_activated": self.on_treeview_right_row_activated,
147
"on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
148
"on_treeview_left_row_activated": self.on_treeview_left_row_activated }
150
# Connect the signals to the handlers
151
self.toplevel.signal_autoconnect(dic)
153
self._just_started = True
155
# Apply window size and position
156
width = self.pref.get_preference('window_width', 'int')
157
height = self.pref.get_preference('window_height', 'int')
158
self.window.resize(width, height)
159
x = self.pref.get_preference('window_x', 'int')
160
y = self.pref.get_preference('window_y', 'int')
161
self.window.move(x, y)
162
# Apply paned position
163
pos = self.pref.get_preference('paned_position', 'int')
164
self.hpaned_main.set_position(pos)
166
# Apply menu to the toolbutton
167
#menubutton = self.toplevel.get_widget('menutoolbutton_diff')
168
#menubutton.set_menu(handler.menu.toolbar_diff)
170
# Now we can show the window
173
# Show drive selector if under Win32
174
if sys.platform == 'win32':
175
self.vbox_main_right.pack_start(self.combobox_drive, False, True, 0)
176
self.vbox_main_right.reorder_child(self.combobox_drive, 0)
177
self.combobox_drive.show()
178
self.gen_hard_selector()
183
self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
185
self.set_path(os.getcwd())
188
self._just_started = False
190
def set_path(self, path):
192
self.notbranch = False
195
self.wt, self.wtpath = WorkingTree.open_containing(self.path)
196
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
197
self.notbranch = True
199
self.statusbar.push(self.context_id, path)
204
def on_about_activate(self, widget):
205
from bzrlib.plugins.gtk.dialog import about
208
def on_menuitem_add_files_activate(self, widget):
209
""" Add file(s)... menu handler. """
210
from add import OliveAdd
211
add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
214
def on_menuitem_branch_get_activate(self, widget):
215
""" Branch/Get... menu handler. """
216
from bzrlib.plugins.gtk.branch import BranchDialog
217
branch = BranchDialog(self.get_path(), self.window)
218
response = branch.run()
219
if response != gtk.RESPONSE_NONE:
222
if response == gtk.RESPONSE_OK:
227
def on_menuitem_branch_checkout_activate(self, widget):
228
""" Branch/Checkout... menu handler. """
229
from bzrlib.plugins.gtk.checkout import CheckoutDialog
230
checkout = CheckoutDialog(self.get_path(), self.window)
231
response = checkout.run()
232
if response != gtk.RESPONSE_NONE:
235
if response == gtk.RESPONSE_OK:
241
def on_menuitem_branch_commit_activate(self, widget):
242
""" Branch/Commit... menu handler. """
243
commit = CommitDialog(self.wt, self.wtpath, self.notbranch, self.get_selected_right(), self.window)
244
response = commit.run()
245
if response != gtk.RESPONSE_NONE:
248
if response == gtk.RESPONSE_OK:
253
def on_menuitem_branch_conflicts_activate(self, widget):
254
""" Branch/Conflicts... menu handler. """
255
conflicts = ConflictsDialog(self.wt, self.window)
256
response = conflicts.run()
257
if response != gtk.RESPONSE_NONE:
260
def on_menuitem_branch_merge_activate(self, widget):
261
""" Branch/Merge... menu handler. """
262
from bzrlib.plugins.gtk.merge import MergeDialog
264
if self.check_for_changes():
265
error_dialog(_('There are local changes in the branch'),
266
_('Please commit or revert the changes before merging.'))
268
merge = MergeDialog(self.wt, self.wtpath)
272
def on_menuitem_branch_missing_revisions_activate(self, widget):
273
""" Branch/Missing revisions menu handler. """
274
local_branch = self.wt.branch
276
other_branch = local_branch.get_parent()
277
if other_branch is None:
278
error_dialog(_('Parent location is unknown'),
279
_('Cannot determine missing revisions if no parent location is known.'))
282
remote_branch = Branch.open(other_branch)
284
if remote_branch.base == local_branch.base:
285
remote_branch = local_branch
287
ret = len(local_branch.missing_revisions(remote_branch))
290
info_dialog(_('There are missing revisions'),
291
_('%d revision(s) missing.') % ret)
293
info_dialog(_('Local branch up to date'),
294
_('There are no missing revisions.'))
297
def on_menuitem_branch_pull_activate(self, widget):
298
""" Branch/Pull menu handler. """
299
branch_to = self.wt.branch
301
location = branch_to.get_parent()
303
error_dialog(_('Parent location is unknown'),
304
_('Pulling is not possible until there is a parent location.'))
307
branch_from = Branch.open(location)
309
if branch_to.get_parent() is None:
310
branch_to.set_parent(branch_from.base)
312
#old_rh = branch_to.revision_history()
313
#if tree_to is not None:
314
# tree_to.pull(branch_from)
316
# branch_to.pull(branch_from)
317
ret = branch_to.pull(branch_from)
319
info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
321
def on_menuitem_branch_push_activate(self, widget):
322
""" Branch/Push... menu handler. """
323
push = PushDialog(self.wt.branch, self.window)
324
response = push.run()
325
if response != gtk.RESPONSE_NONE:
329
def on_menuitem_branch_revert_activate(self, widget):
330
""" Branch/Revert all changes menu handler. """
331
ret = self.wt.revert([])
333
warning_dialog(_('Conflicts detected'),
334
_('Please have a look at the working tree before continuing.'))
336
info_dialog(_('Revert successful'),
337
_('All files reverted to last revision.'))
340
def on_menuitem_branch_status_activate(self, widget):
341
""" Branch/Status... menu handler. """
342
from bzrlib.plugins.gtk.status import StatusDialog
343
status = StatusDialog(self.wt, self.wtpath)
347
def on_menuitem_branch_initialize_activate(self, widget):
348
""" Initialize current directory. """
349
import bzrlib.bzrdir as bzrdir
351
if not os.path.exists(self.path):
355
existing_bzrdir = bzrdir.BzrDir.open(self.path)
356
except bzrerrors.NotBranchError:
357
bzrdir.BzrDir.create_branch_convenience(self.path)
359
if existing_bzrdir.has_branch():
360
if existing_bzrdir.has_workingtree():
361
raise bzrerrors.AlreadyBranchError(self.path)
363
raise bzrerrors.BranchExistsWithoutWorkingTree(self.path)
365
existing_bzrdir.create_branch()
366
existing_bzrdir.create_workingtree()
367
info_dialog(_('Initialize successful'),
368
_('Directory successfully initialized.'))
371
def on_menuitem_file_annotate_activate(self, widget):
372
""" File/Annotate... menu handler. """
373
if self.get_selected_right() is None:
374
error_dialog(_('No file was selected'),
375
_('Please select a file from the list.'))
378
branch = self.wt.branch
379
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
381
window = GAnnotateWindow(all=False, plain=False)
382
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
383
config = GAnnotateConfig(window)
387
window.annotate(self.wt, branch, file_id)
391
def on_menuitem_file_make_directory_activate(self, widget):
392
""" File/Make directory... menu handler. """
393
from mkdir import OliveMkdir
394
mkdir = OliveMkdir(self.wt, self.wtpath)
397
def on_menuitem_file_move_activate(self, widget):
398
""" File/Move... menu handler. """
399
from move import OliveMove
400
move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
403
def on_menuitem_file_rename_activate(self, widget):
404
""" File/Rename... menu handler. """
405
from rename import OliveRename
406
rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
409
def on_menuitem_remove_file_activate(self, widget):
410
""" Remove (unversion) selected file. """
411
from remove import OliveRemoveDialog
412
remove = OliveRemoveDialog(self.wt, self.wtpath,
413
selected=self.get_selected_right(),
415
response = remove.run()
417
if response != gtk.RESPONSE_NONE:
420
if response == gtk.RESPONSE_OK:
421
self.set_path(self.path)
426
def on_menuitem_stats_diff_activate(self, widget):
427
""" Statistics/Differences... menu handler. """
428
window = DiffWindow()
429
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
430
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
433
def on_menuitem_stats_infos_activate(self, widget):
434
""" Statistics/Informations... menu handler. """
435
from info import OliveInfo
436
info = OliveInfo(self.wt)
439
def on_menuitem_stats_log_activate(self, widget):
440
""" Statistics/Log... menu handler. """
441
window = branchwin.BranchWindow()
442
window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
445
def on_menuitem_view_refresh_activate(self, widget):
446
""" View/Refresh menu handler. """
447
# Refresh the left pane
449
# Refresh the right pane
452
def on_menuitem_view_show_hidden_files_activate(self, widget):
453
""" View/Show hidden files menu handler. """
454
self.pref.set_preference('dotted_files', widget.get_active())
455
if self.path is not None:
458
def on_treeview_left_button_press_event(self, widget, event):
459
""" Occurs when somebody right-clicks in the bookmark list. """
460
if event.button == 3:
461
# Don't show context with nothing selected
462
if self.get_selected_left() == None:
466
from menu import OliveMenu
467
menu = OliveMenu(path=self.get_path(),
468
selected=self.get_selected_left(),
471
menu.left_context_menu().popup(None, None, None, 0,
474
def on_treeview_left_row_activated(self, treeview, path, view_column):
475
""" Occurs when somebody double-clicks or enters an item in the
478
newdir = self.get_selected_left()
482
self.set_path(newdir)
485
def on_treeview_right_button_press_event(self, widget, event):
486
""" Occurs when somebody right-clicks in the file list. """
487
if event.button == 3:
489
from menu import OliveMenu
490
menu = OliveMenu(path=self.get_path(),
491
selected=self.get_selected_right(),
494
m_add = menu.ui.get_widget('/context_right/add')
495
m_remove = menu.ui.get_widget('/context_right/remove')
496
m_rename = menu.ui.get_widget('/context_right/rename')
497
m_revert = menu.ui.get_widget('/context_right/revert')
498
m_commit = menu.ui.get_widget('/context_right/commit')
499
m_diff = menu.ui.get_widget('/context_right/diff')
500
# check if we're in a branch
502
from bzrlib.branch import Branch
503
Branch.open_containing(self.get_path())
504
m_add.set_sensitive(True)
505
m_remove.set_sensitive(True)
506
m_rename.set_sensitive(True)
507
m_revert.set_sensitive(True)
508
m_commit.set_sensitive(True)
509
m_diff.set_sensitive(True)
510
except bzrerrors.NotBranchError:
511
m_add.set_sensitive(False)
512
m_remove.set_sensitive(False)
513
m_rename.set_sensitive(False)
514
m_revert.set_sensitive(False)
515
m_commit.set_sensitive(False)
516
m_diff.set_sensitive(False)
518
menu.right_context_menu().popup(None, None, None, 0,
521
def on_treeview_right_row_activated(self, treeview, path, view_column):
522
""" Occurs when somebody double-clicks or enters an item in the
524
from launch import launch
526
newdir = self.get_selected_right()
529
self.set_path(os.path.split(self.get_path())[0])
531
fullpath = os.path.join(self.get_path(), newdir)
532
if os.path.isdir(fullpath):
533
# selected item is an existant directory
534
self.set_path(fullpath)
540
def on_window_main_delete_event(self, widget, event=None):
541
""" Do some stuff before exiting. """
542
width, height = self.window_main.get_size()
543
self.pref.set_preference('window_width', width)
544
self.pref.set_preference('window_height', height)
545
x, y = self.window_main.get_position()
546
self.pref.set_preference('window_x', x)
547
self.pref.set_preference('window_y', y)
548
self.pref.set_preference('paned_position',
549
self.hpaned_main.get_position())
552
self.window_main.destroy()
554
def _load_left(self):
555
""" Load data into the left panel. (Bookmarks) """
557
treestore = gtk.TreeStore(str, str)
560
bookmarks = self.pref.get_bookmarks()
562
# Add them to the TreeStore
563
titer = treestore.append(None, [_('Bookmarks'), None])
564
for item in bookmarks:
565
title = self.pref.get_bookmark_title(item)
566
treestore.append(titer, [title, item])
568
# Create the column and add it to the TreeView
569
self.treeview_left.set_model(treestore)
570
tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
571
self.treeview_left.append_column(tvcolumn_bookmark)
574
cell = gtk.CellRendererText()
575
tvcolumn_bookmark.pack_start(cell, True)
576
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
579
self.treeview_left.expand_all()
581
def _add_updir_to_dirlist(self, dirlist, curdir):
582
"""Add .. to the top of directories list if we not in root directory
584
:param dirlist: list of directories (modified in place)
585
:param curdir: current directory
591
if sys.platform == 'win32':
592
drive, tail = os.path.splitdrive(curdir)
593
if tail in ('', '/', '\\'):
599
# insert always as first element
600
dirlist.insert(0, '..')
602
def _load_right(self):
603
""" Load data into the right panel. (Filelist) """
605
liststore = gtk.ListStore(str, str, str)
610
# Fill the appropriate lists
611
dotted_files = self.pref.get_preference('dotted_files', 'bool')
612
for item in os.listdir(self.path):
613
if not dotted_files and item[0] == '.':
615
if os.path.isdir(self.path + os.sep + item):
624
# add updir link to dirs
625
self._add_updir_to_dirlist(dirs, self.path)
627
if not self.notbranch:
628
branch = self.wt.branch
629
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
631
delta = self.wt.changes_from(tree2, want_unchanged=True)
633
# Add'em to the ListStore
635
liststore.append([gtk.STOCK_DIRECTORY, item, ''])
638
if not self.notbranch:
639
filename = self.wt.relpath(self.path + os.sep + item)
644
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
645
if rpathnew == filename:
647
for rpath, id, kind in delta.added:
648
if rpath == filename:
650
for rpath, id, kind in delta.removed:
651
if rpath == filename:
653
for rpath, id, kind, text_modified, meta_modified in delta.modified:
654
if rpath == filename:
656
for rpath, id, kind in delta.unchanged:
657
if rpath == filename:
659
for rpath, file_class, kind, id, entry in self.wt.list_files():
660
if rpath == filename and file_class == 'I':
666
# status = fileops.status(path + os.sep + item)
667
#except errors.PermissionDenied:
670
if status == 'renamed':
672
elif status == 'removed':
674
elif status == 'added':
676
elif status == 'modified':
678
elif status == 'unchanged':
680
elif status == 'ignored':
684
liststore.append([gtk.STOCK_FILE, item, st])
686
# Create the columns and add them to the TreeView
687
self.treeview_right.set_model(liststore)
688
tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
689
tvcolumn_status = gtk.TreeViewColumn(_('Status'))
690
self.treeview_right.append_column(tvcolumn_filename)
691
self.treeview_right.append_column(tvcolumn_status)
694
cellpb = gtk.CellRendererPixbuf()
695
cell = gtk.CellRendererText()
696
tvcolumn_filename.pack_start(cellpb, False)
697
tvcolumn_filename.pack_start(cell, True)
698
tvcolumn_filename.set_attributes(cellpb, stock_id=0)
699
tvcolumn_filename.add_attribute(cell, 'text', 1)
700
tvcolumn_status.pack_start(cell, True)
701
tvcolumn_status.add_attribute(cell, 'text', 2)
704
self.set_sensitivity()
706
def get_selected_right(self):
707
""" Get the selected filename. """
708
treeselection = self.treeview_right.get_selection()
709
(model, iter) = treeselection.get_selected()
714
return model.get_value(iter, 1)
716
def get_selected_left(self):
717
""" Get the selected bookmark. """
718
treeselection = self.treeview_left.get_selection()
719
(model, iter) = treeselection.get_selected()
724
return model.get_value(iter, 1)
726
def set_statusbar(self, message):
727
""" Set the statusbar message. """
728
self.statusbar.push(self.context_id, message)
730
def clear_statusbar(self):
731
""" Clean the last message from the statusbar. """
732
self.statusbar.pop(self.context_id)
734
def set_sensitivity(self):
735
""" Set menu and toolbar sensitivity. """
736
self.menuitem_branch_init.set_sensitive(self.notbranch)
737
self.menuitem_branch_get.set_sensitive(self.notbranch)
738
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
739
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
740
self.menuitem_branch_push.set_sensitive(not self.notbranch)
741
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
742
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
743
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
744
self.menuitem_branch_status.set_sensitive(not self.notbranch)
745
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
746
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
747
self.menuitem_stats.set_sensitive(not self.notbranch)
748
self.menuitem_add_files.set_sensitive(not self.notbranch)
749
self.menuitem_remove_files.set_sensitive(not self.notbranch)
750
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
751
self.menuitem_file_rename.set_sensitive(not self.notbranch)
752
self.menuitem_file_move.set_sensitive(not self.notbranch)
753
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
754
#self.menutoolbutton_diff.set_sensitive(True)
755
self.toolbutton_diff.set_sensitive(not self.notbranch)
756
self.toolbutton_log.set_sensitive(not self.notbranch)
757
self.toolbutton_commit.set_sensitive(not self.notbranch)
758
self.toolbutton_pull.set_sensitive(not self.notbranch)
759
self.toolbutton_push.set_sensitive(not self.notbranch)
761
def refresh_left(self):
762
""" Refresh the bookmark list. """
764
# Get TreeStore and clear it
765
treestore = self.treeview_left.get_model()
768
# Re-read preferences
772
bookmarks = self.pref.get_bookmarks()
774
# Add them to the TreeStore
775
titer = treestore.append(None, [_('Bookmarks'), None])
776
for item in bookmarks:
777
title = self.pref.get_bookmark_title(item)
778
treestore.append(titer, [title, item])
780
# Add the TreeStore to the TreeView
781
self.treeview_left.set_model(treestore)
784
self.treeview_left.expand_all()
786
def refresh_right(self, path=None):
787
""" Refresh the file list. """
788
from bzrlib.workingtree import WorkingTree
791
path = self.get_path()
793
# A workaround for double-clicking Bookmarks
794
if not os.path.exists(path):
797
# Get ListStore and clear it
798
liststore = self.treeview_right.get_model()
804
# Fill the appropriate lists
805
dotted_files = self.pref.get_preference('dotted_files', 'bool')
806
for item in os.listdir(path):
807
if not dotted_files and item[0] == '.':
809
if os.path.isdir(path + os.sep + item):
818
# add updir link to dirs
819
self._add_updir_to_dirlist(dirs, path)
821
# Try to open the working tree
824
tree1 = WorkingTree.open_containing(path)[0]
825
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
829
branch = tree1.branch
830
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
832
delta = tree1.changes_from(tree2, want_unchanged=True)
834
# Add'em to the ListStore
836
liststore.append([gtk.STOCK_DIRECTORY, item, ''])
840
filename = tree1.relpath(path + os.sep + item)
845
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
846
if rpathnew == filename:
848
for rpath, id, kind in delta.added:
849
if rpath == filename:
851
for rpath, id, kind in delta.removed:
852
if rpath == filename:
854
for rpath, id, kind, text_modified, meta_modified in delta.modified:
855
if rpath == filename:
857
for rpath, id, kind in delta.unchanged:
858
if rpath == filename:
860
for rpath, file_class, kind, id, entry in self.wt.list_files():
861
if rpath == filename and file_class == 'I':
867
# status = fileops.status(path + os.sep + item)
868
#except errors.PermissionDenied:
871
if status == 'renamed':
873
elif status == 'removed':
875
elif status == 'added':
877
elif status == 'modified':
879
elif status == 'unchanged':
881
elif status == 'ignored':
885
liststore.append([gtk.STOCK_FILE, item, st])
887
# Add the ListStore to the TreeView
888
self.treeview_right.set_model(liststore)
891
self.set_sensitivity()
893
def _harddisks(self):
894
""" Returns hard drive letters under Win32. """
899
if sys.platform == 'win32':
900
print "pyWin32 modules needed to run Olive on Win32."
906
for drive in string.ascii_uppercase:
907
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
908
driveletters.append(drive+':')
911
def gen_hard_selector(self):
912
""" Generate the hard drive selector under Win32. """
913
drives = self._harddisks()
915
self.combobox_drive.append_text(drive)
916
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
918
def _refresh_drives(self, combobox):
919
if self._just_started:
921
model = combobox.get_model()
922
active = combobox.get_active()
924
drive = model[active][0]
925
self.set_path(drive + '\\')
926
self.refresh_right(drive + '\\')
928
def check_for_changes(self):
929
""" Check whether there were changes in the current working tree. """
930
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
931
delta = self.wt.changes_from(old_tree)
935
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
943
""" A class which handles Olive's preferences. """
944
def __init__(self, path=None):
945
""" Initialize the Preferences class. """
946
# Some default options
947
self.defaults = { 'strict_commit' : False,
948
'dotted_files' : False,
949
'window_width' : 700,
950
'window_height' : 400,
953
'paned_position': 200 }
955
# Create a config parser object
956
self.config = ConfigParser.RawConfigParser()
960
if sys.platform == 'win32':
961
# Windows - no dotted files
962
self._filename = os.path.expanduser('~/olive.conf')
964
self._filename = os.path.expanduser('~/.olive.conf')
966
self._filename = path
968
# Load the configuration
971
def _get_default(self, option):
972
""" Get the default option for a preference. """
974
ret = self.defaults[option]
981
""" Refresh the configuration. """
982
# First write out the changes
984
# Then load the configuration again
988
""" Just read the configuration. """
989
# Re-initialize the config parser object to avoid some bugs
990
self.config = ConfigParser.RawConfigParser()
991
self.config.read([self._filename])
994
""" Write the configuration to the appropriate files. """
995
fp = open(self._filename, 'w')
996
self.config.write(fp)
999
def get_bookmarks(self):
1000
""" Return the list of bookmarks. """
1001
bookmarks = self.config.sections()
1002
if self.config.has_section('preferences'):
1003
bookmarks.remove('preferences')
1006
def add_bookmark(self, path):
1007
""" Add bookmark. """
1009
self.config.add_section(path)
1010
except ConfigParser.DuplicateSectionError:
1015
def get_bookmark_title(self, path):
1016
""" Get bookmark title. """
1018
ret = self.config.get(path, 'title')
1019
except ConfigParser.NoOptionError:
1024
def set_bookmark_title(self, path, title):
1025
""" Set bookmark title. """
1026
# FIXME: What if path isn't listed yet?
1027
# FIXME: Canonicalize paths first?
1028
self.config.set(path, 'title', title)
1030
def remove_bookmark(self, path):
1031
""" Remove bookmark. """
1032
return self.config.remove_section(path)
1034
def set_preference(self, option, value):
1035
""" Set the value of the given option. """
1038
elif value is False:
1041
if self.config.has_section('preferences'):
1042
self.config.set('preferences', option, value)
1044
self.config.add_section('preferences')
1045
self.config.set('preferences', option, value)
1047
def get_preference(self, option, kind='str'):
1048
""" Get the value of the given option.
1050
:param kind: str/bool/int/float. default: str
1052
if self.config.has_option('preferences', option):
1054
return self.config.getboolean('preferences', option)
1056
return self.config.getint('preferences', option)
1057
elif kind == 'float':
1058
return self.config.getfloat('preferences', option)
1060
return self.config.get('preferences', option)
1063
return self._get_default(option)