3
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
gettext.install('olive-gtk')
39
from bzrlib.branch import Branch
40
import bzrlib.errors as bzrerrors
41
from bzrlib.lazy_import import lazy_import
42
from bzrlib.ui import ui_factory
43
from bzrlib.workingtree import WorkingTree
45
from bzrlib.plugins.gtk import _i18n
46
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
47
from bzrlib.plugins.gtk.errors import show_bzr_error
48
from guifiles import GLADEFILENAME
50
from bzrlib.plugins.gtk.diff import DiffWindow
51
lazy_import(globals(), """
52
from bzrlib.plugins.gtk.viz import branchwin
54
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
55
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
56
from bzrlib.plugins.gtk.commit import CommitDialog
57
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
58
from bzrlib.plugins.gtk.initialize import InitDialog
59
from bzrlib.plugins.gtk.push import PushDialog
60
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
63
""" Display the AboutDialog. """
64
from bzrlib.plugins.gtk import __version__, icon_path
66
iconpath = icon_path() + os.sep
68
dialog = gtk.AboutDialog()
69
dialog.set_name("Olive")
70
dialog.set_version(__version__)
71
dialog.set_copyright("Copyright (C) 2006 Szilveszter Farkas (Phanatic)")
72
dialog.set_website("https://launchpad.net/products/olive")
73
dialog.set_website_label("https://launchpad.net/products/olive")
74
dialog.set_icon_from_file(iconpath+"oliveicon2.png")
75
dialog.set_logo(gtk.gdk.pixbuf_new_from_file(iconpath+"oliveicon2.png"))
76
dialog.set_authors([ _i18n("Lead Developer:"),
77
"Szilveszter Farkas <szilveszter.farkas@gmail.com>",
78
_i18n("Contributors:"),
79
"Jelmer Vernooij <jelmer@samba.org>",
80
"Mateusz Korniak <mateusz.korniak@ant.gliwice.pl>",
81
"Gary van der Merwe <garyvdm@gmail.com>" ])
82
dialog.set_artists([ "Simon Pascal Klein <klepas@klepas.org>",
83
"Jakub Steiner <jimmac@novell.com>" ])
90
""" The main Olive GTK frontend class. This is called when launching the
94
self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
95
self.window = self.toplevel.get_widget('window_main')
96
self.pref = Preferences()
99
# Initialize the statusbar
100
self.statusbar = self.toplevel.get_widget('statusbar')
101
self.context_id = self.statusbar.get_context_id('olive')
103
# Get the main window
104
self.window_main = self.toplevel.get_widget('window_main')
106
self.hpaned_main = self.toplevel.get_widget('hpaned_main')
108
self.treeview_left = self.toplevel.get_widget('treeview_left')
109
self.treeview_right = self.toplevel.get_widget('treeview_right')
110
# Get some important menu items
111
self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
112
self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
113
self.menuitem_file_bookmark = self.toplevel.get_widget('menuitem_file_bookmark')
114
self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
115
self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
116
self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
117
self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
118
self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
119
self.menuitem_view_show_ignored_files = self.toplevel.get_widget('menuitem_view_show_ignored_files')
120
self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
121
self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
122
self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
123
self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
124
self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
125
self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
126
self.menuitem_branch_update = self.toplevel.get_widget('menuitem_branch_update')
127
self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
128
self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
129
self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
130
self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
131
self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
132
self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
133
self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
134
self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
135
self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
136
self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
137
# Get some toolbuttons
138
#self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
139
self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
140
self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
141
self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
142
self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
143
self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
144
self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
145
# Get the drive selector
146
self.combobox_drive = gtk.combo_box_new_text()
147
self.combobox_drive.connect("changed", self._refresh_drives)
149
# Get the navigation widgets
150
self.hbox_location = self.toplevel.get_widget('hbox_location')
151
self.button_location_up = self.toplevel.get_widget('button_location_up')
152
self.button_location_jump = self.toplevel.get_widget('button_location_jump')
153
self.entry_location = self.toplevel.get_widget('entry_location')
154
self.image_location_error = self.toplevel.get_widget('image_location_error')
156
# Get the History widgets
157
self.check_history = self.toplevel.get_widget('checkbutton_history')
158
self.entry_history = self.toplevel.get_widget('entry_history_revno')
159
self.button_history = self.toplevel.get_widget('button_history_browse')
161
self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
163
# Dictionary for signal_autoconnect
164
dic = { "on_window_main_destroy": gtk.main_quit,
165
"on_window_main_delete_event": self.on_window_main_delete_event,
166
"on_quit_activate": self.on_window_main_delete_event,
167
"on_about_activate": self.on_about_activate,
168
"on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
169
"on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
170
"on_menuitem_file_bookmark_activate": self.on_menuitem_file_bookmark_activate,
171
"on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
172
"on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
173
"on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
174
"on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
175
"on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
176
"on_menuitem_view_show_ignored_files_activate": self.on_menuitem_view_show_ignored_files_activate,
177
"on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
178
"on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
179
"on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
180
"on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
181
"on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
182
"on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
183
"on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
184
"on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
185
"on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
186
"on_menuitem_branch_update_activate": self.on_menuitem_branch_update_activate,
187
"on_menuitem_branch_tags_activate": self.on_menuitem_branch_tags_activate,
188
"on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
189
"on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
190
"on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
191
"on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
192
"on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
193
"on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
194
"on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
195
"on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
196
#"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
197
"on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
198
"on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
199
"on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
200
"on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
201
"on_toolbutton_update_clicked": self.on_menuitem_branch_update_activate,
202
"on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
203
"on_treeview_right_row_activated": self.on_treeview_right_row_activated,
204
"on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
205
"on_treeview_left_button_release_event": self.on_treeview_left_button_release_event,
206
"on_treeview_left_row_activated": self.on_treeview_left_row_activated,
207
"on_button_location_up_clicked": self.on_button_location_up_clicked,
208
"on_button_location_jump_clicked": self.on_button_location_jump_clicked,
209
"on_entry_location_key_press_event": self.on_entry_location_key_press_event,
210
"on_checkbutton_history_toggled": self.on_checkbutton_history_toggled,
211
"on_entry_history_revno_key_press_event": self.on_entry_history_revno_key_press_event,
212
"on_button_history_browse_clicked": self.on_button_history_browse_clicked
215
# Connect the signals to the handlers
216
self.toplevel.signal_autoconnect(dic)
218
self._just_started = True
220
# Apply window size and position
221
width = self.pref.get_preference('window_width', 'int')
222
height = self.pref.get_preference('window_height', 'int')
223
self.window.resize(width, height)
224
x = self.pref.get_preference('window_x', 'int')
225
y = self.pref.get_preference('window_y', 'int')
226
self.window.move(x, y)
227
# Apply paned position
228
pos = self.pref.get_preference('paned_position', 'int')
229
self.hpaned_main.set_position(pos)
231
# Apply menu to the toolbutton
232
#menubutton = self.toplevel.get_widget('menutoolbutton_diff')
233
#menubutton.set_menu(handler.menu.toolbar_diff)
235
# Now we can show the window
238
# Show drive selector if under Win32
239
if sys.platform == 'win32':
240
self.hbox_location.pack_start(self.combobox_drive, False, False, 0)
241
self.hbox_location.reorder_child(self.combobox_drive, 1)
242
self.combobox_drive.show()
243
self.gen_hard_selector()
245
# Acceptable errors when loading files/folders in the treeviews
246
self.acceptable_errors = (errno.ENOENT, errno.ELOOP)
251
self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
252
self.menuitem_view_show_ignored_files.set_active(self.pref.get_preference('ignored_files', 'bool'))
254
# We're starting local
256
self.remote_branch = None
257
self.remote_path = None
258
self.remote_revision = None
260
self.set_path(os.getcwd())
263
self._just_started = False
265
def set_path(self, path, force_remote=False):
266
self.notbranch = False
269
# Forcing remote mode (reading data from inventory)
270
self._show_stock_image(gtk.STOCK_DISCONNECT)
272
br = Branch.open_containing(path)[0]
273
except bzrerrors.NotBranchError:
274
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
275
self.check_history.set_active(False)
276
self.check_history.set_sensitive(False)
278
except bzrerrors.UnsupportedProtocol:
279
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
280
self.check_history.set_active(False)
281
self.check_history.set_sensitive(False)
284
self._show_stock_image(gtk.STOCK_CONNECT)
289
self.remote_branch, self.remote_path = Branch.open_containing(path)
291
if self.remote_revision is None:
292
self.remote_revision = self.remote_branch.last_revision()
294
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
296
if len(self.remote_path) == 0:
297
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
299
for (name, type) in self.remote_entries:
300
if name == self.remote_path:
301
self.remote_parent = type.file_id
304
if not path.endswith('/'):
307
if self.remote_branch.base == path:
308
self.button_location_up.set_sensitive(False)
310
self.button_location_up.set_sensitive(True)
312
if os.path.isdir(path):
313
self.image_location_error.destroy()
318
self.wt, self.wtpath = WorkingTree.open_containing(path)
319
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
320
self.notbranch = True
322
# If we're in the root, we cannot go up anymore
323
if sys.platform == 'win32':
324
drive, tail = os.path.splitdrive(path)
325
if tail in ('', '/', '\\'):
326
self.button_location_up.set_sensitive(False)
328
self.button_location_up.set_sensitive(True)
331
self.button_location_up.set_sensitive(False)
333
self.button_location_up.set_sensitive(True)
334
elif not os.path.isfile(path):
335
# Doesn't seem to be a file nor a directory, trying to open a
337
self._show_stock_image(gtk.STOCK_DISCONNECT)
339
br = Branch.open_containing(path)[0]
340
except bzrerrors.NotBranchError:
341
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
342
self.check_history.set_active(False)
343
self.check_history.set_sensitive(False)
345
except bzrerrors.UnsupportedProtocol:
346
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
347
self.check_history.set_active(False)
348
self.check_history.set_sensitive(False)
351
self._show_stock_image(gtk.STOCK_CONNECT)
356
self.remote_branch, self.remote_path = Branch.open_containing(path)
358
if self.remote_revision is None:
359
self.remote_revision = self.remote_branch.last_revision()
361
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
363
if len(self.remote_path) == 0:
364
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
366
for (name, type) in self.remote_entries:
367
if name == self.remote_path:
368
self.remote_parent = type.file_id
371
if not path.endswith('/'):
374
if self.remote_branch.base == path:
375
self.button_location_up.set_sensitive(False)
377
self.button_location_up.set_sensitive(True)
380
self.check_history.set_active(False)
381
self.check_history.set_sensitive(False)
383
self.check_history.set_sensitive(True)
385
self.statusbar.push(self.context_id, path)
386
self.entry_location.set_text(path)
395
if len(self.remote_path) > 0:
396
return self.remote_branch.base + self.remote_path + '/'
398
return self.remote_branch.base
400
def on_about_activate(self, widget):
403
def on_button_history_browse_clicked(self, widget):
404
""" Browse for revision button handler. """
406
br = self.remote_branch
410
revb = RevisionBrowser(br, self.window)
411
response = revb.run()
412
if response != gtk.RESPONSE_NONE:
415
if response == gtk.RESPONSE_OK:
416
if revb.selected_revno is not None:
417
self.entry_history.set_text(revb.selected_revno)
421
def on_button_location_jump_clicked(self, widget):
422
""" Location Jump button handler. """
423
location = self.entry_location.get_text()
425
if self.set_path(location):
428
def on_button_location_up_clicked(self, widget):
429
""" Location Up button handler. """
432
self.set_path(os.path.split(self.get_path())[0])
436
newpath = delim.join(self.get_path().split(delim)[:-2])
438
self.set_path(newpath)
442
def on_checkbutton_history_toggled(self, widget):
443
""" History Mode toggle handler. """
444
if self.check_history.get_active():
445
# History Mode activated
446
self.entry_history.set_sensitive(True)
447
self.button_history.set_sensitive(True)
449
# History Mode deactivated
450
self.entry_history.set_sensitive(False)
451
self.button_history.set_sensitive(False)
453
# Return right window to normal view by acting like we jump to it
454
self.on_button_location_jump_clicked(widget)
457
def on_entry_history_revno_key_press_event(self, widget, event):
458
""" Key pressed handler for the history entry. """
459
if event.keyval == gtk.gdk.keyval_from_name('Return') or event.keyval == gtk.gdk.keyval_from_name('KP_Enter'):
460
# Return was hit, so we have to load that specific revision
461
# Emulate being remote, so inventory should be used
462
path = self.get_path()
465
self.remote_branch = self.wt.branch
467
revno = int(self.entry_history.get_text())
468
self.remote_revision = self.remote_branch.get_rev_id(revno)
469
if self.set_path(path, True):
472
def on_entry_location_key_press_event(self, widget, event):
473
""" Key pressed handler for the location entry. """
474
if event.keyval == gtk.gdk.keyval_from_name('Return') or event.keyval == gtk.gdk.keyval_from_name('KP_Enter'):
475
# Return was hit, so we have to jump
476
self.on_button_location_jump_clicked(widget)
478
def on_menuitem_add_files_activate(self, widget):
479
""" Add file(s)... menu handler. """
480
from bzrlib.plugins.gtk.olive.add import AddDialog
481
add = AddDialog(self.wt, self.wtpath, self.get_selected_right(), self.window)
484
if response == gtk.RESPONSE_OK:
487
def on_menuitem_branch_get_activate(self, widget):
488
""" Branch/Get... menu handler. """
489
from bzrlib.plugins.gtk.branch import BranchDialog
492
branch = BranchDialog(os.getcwd(), self.window, self.remote_branch.base)
494
branch = BranchDialog(self.get_path(), self.window)
495
response = branch.run()
496
if response != gtk.RESPONSE_NONE:
499
if response == gtk.RESPONSE_OK:
504
def on_menuitem_branch_checkout_activate(self, widget):
505
""" Branch/Checkout... menu handler. """
506
from bzrlib.plugins.gtk.checkout import CheckoutDialog
509
checkout = CheckoutDialog(os.getcwd(), self.window, self.remote_branch.base)
511
checkout = CheckoutDialog(self.get_path(), self.window)
512
response = checkout.run()
513
if response != gtk.RESPONSE_NONE:
516
if response == gtk.RESPONSE_OK:
522
def on_menuitem_branch_commit_activate(self, widget):
523
""" Branch/Commit... menu handler. """
524
# def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
525
selected = self.get_selected_right()
527
selected = os.path.join(self.wtpath, selected)
528
commit = CommitDialog(wt=self.wt,
532
response = commit.run()
533
if response != gtk.RESPONSE_NONE:
536
if response == gtk.RESPONSE_OK:
541
def on_menuitem_branch_conflicts_activate(self, widget):
542
""" Branch/Conflicts... menu handler. """
543
conflicts = ConflictsDialog(self.wt, self.window)
544
response = conflicts.run()
545
if response != gtk.RESPONSE_NONE:
548
def on_menuitem_branch_merge_activate(self, widget):
549
""" Branch/Merge... menu handler. """
550
from bzrlib.plugins.gtk.merge import MergeDialog
552
if self.check_for_changes():
553
error_dialog(_i18n('There are local changes in the branch'),
554
_i18n('Please commit or revert the changes before merging.'))
556
parent_branch_path = self.wt.branch.get_parent()
557
merge = MergeDialog(self.wt, self.wtpath, parent_branch_path, self.window)
558
response = merge.run()
560
if response == gtk.RESPONSE_OK:
564
def on_menuitem_branch_missing_revisions_activate(self, widget):
565
""" Branch/Missing revisions menu handler. """
567
from bzrlib.missing import find_unmerged, iter_log_revisions
569
local_branch = self.wt.branch
570
parent_branch_path = local_branch.get_parent()
571
if parent_branch_path is None:
572
error_dialog(_i18n('Parent location is unknown'),
573
_i18n('Cannot determine missing revisions if no parent location is known.'))
576
parent_branch = Branch.open(parent_branch_path)
578
if parent_branch.base == local_branch.base:
579
parent_branch = local_branch
581
local_extra, remote_extra = find_unmerged(local_branch,parent_branch)
583
if local_extra or remote_extra:
585
## def log_revision_one_line_text(log_revision):
586
## """ Generates one line description of log_revison ended with end of line."""
587
## revision = log_revision.rev
588
## txt = "- %s (%s)\n" % (revision.get_summary(), revision.committer, )
589
## txt = txt.replace("<"," ") # Seems < > chars are expected to be xml tags ...
590
## txt = txt.replace(">"," ")
595
dlg_txt += _i18n('%d local extra revision(s). \n') % (len(local_extra),)
596
## NOTE: We do not want such ugly info about missing revisions
597
## Revision Browser should be used there
598
## max_revisions = 10
599
## for log_revision in iter_log_revisions(local_extra, local_branch.repository, verbose=1):
600
## dlg_txt += log_revision_one_line_text(log_revision)
601
## if max_revisions <= 0:
602
## dlg_txt += _i18n("more ... \n")
604
## max_revisions -= 1
607
dlg_txt += _i18n('%d local missing revision(s).\n') % (len(remote_extra),)
608
## max_revisions = 10
609
## for log_revision in iter_log_revisions(remote_extra, parent_branch.repository, verbose=1):
610
## dlg_txt += log_revision_one_line_text(log_revision)
611
## if max_revisions <= 0:
612
## dlg_txt += _i18n("more ... \n")
614
## max_revisions -= 1
616
info_dialog(_i18n('There are missing revisions'),
619
info_dialog(_i18n('Local branch up to date'),
620
_i18n('There are no missing revisions.'))
623
def on_menuitem_branch_pull_activate(self, widget):
624
""" Branch/Pull menu handler. """
625
branch_to = self.wt.branch
627
location = branch_to.get_parent()
629
error_dialog(_i18n('Parent location is unknown'),
630
_i18n('Pulling is not possible until there is a parent location.'))
633
branch_from = Branch.open(location)
635
if branch_to.get_parent() is None:
636
branch_to.set_parent(branch_from.base)
638
ret = branch_to.pull(branch_from)
640
info_dialog(_i18n('Pull successful'), _i18n('%d revision(s) pulled.') % ret)
643
def on_menuitem_branch_update_activate(self, widget):
644
""" Brranch/checkout update menu handler. """
646
ret = self.wt.update()
647
conflicts = self.wt.conflicts()
649
info_dialog(_i18n('Update successful but conflicts generated'), _i18n('Number of conflicts generated: %d.') % (len(conflicts),) )
651
info_dialog(_i18n('Update successful'), _i18n('No conflicts generated.') )
653
def on_menuitem_branch_push_activate(self, widget):
654
""" Branch/Push... menu handler. """
655
push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
656
response = push.run()
657
if response != gtk.RESPONSE_NONE:
661
def on_menuitem_branch_revert_activate(self, widget):
662
""" Branch/Revert all changes menu handler. """
663
ret = self.wt.revert([])
665
warning_dialog(_i18n('Conflicts detected'),
666
_i18n('Please have a look at the working tree before continuing.'))
668
info_dialog(_i18n('Revert successful'),
669
_i18n('All files reverted to last revision.'))
672
def on_menuitem_branch_status_activate(self, widget):
673
""" Branch/Status... menu handler. """
674
from bzrlib.plugins.gtk.status import StatusDialog
675
status = StatusDialog(self.wt, self.wtpath)
676
response = status.run()
677
if response != gtk.RESPONSE_NONE:
680
def on_menuitem_branch_initialize_activate(self, widget):
681
""" Initialize current directory. """
682
init = InitDialog(self.path, self.window)
683
response = init.run()
684
if response != gtk.RESPONSE_NONE:
687
if response == gtk.RESPONSE_OK:
692
def on_menuitem_branch_tags_activate(self, widget):
693
""" Branch/Tags... menu handler. """
694
from bzrlib.plugins.gtk.tags import TagsWindow
696
window = TagsWindow(self.wt.branch, self.window)
698
window = TagsWindow(self.remote_branch, self.window)
701
def on_menuitem_file_annotate_activate(self, widget):
702
""" File/Annotate... menu handler. """
703
if self.get_selected_right() is None:
704
error_dialog(_i18n('No file was selected'),
705
_i18n('Please select a file from the list.'))
708
branch = self.wt.branch
709
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
711
window = GAnnotateWindow(all=False, plain=False, parent=self.window)
712
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
713
config = GAnnotateConfig(window)
717
window.annotate(self.wt, branch, file_id)
721
def on_menuitem_file_bookmark_activate(self, widget):
722
""" File/Bookmark current directory menu handler. """
723
if self.pref.add_bookmark(self.path):
724
info_dialog(_i18n('Bookmark successfully added'),
725
_i18n('The current directory was bookmarked. You can reach\nit by selecting it from the left panel.'))
728
warning_dialog(_i18n('Location already bookmarked'),
729
_i18n('The current directory is already bookmarked.\nSee the left panel for reference.'))
733
def on_menuitem_file_make_directory_activate(self, widget):
734
""" File/Make directory... menu handler. """
735
from bzrlib.plugins.gtk.olive.mkdir import MkdirDialog
736
mkdir = MkdirDialog(self.wt, self.wtpath, self.window)
737
response = mkdir.run()
739
if response == gtk.RESPONSE_OK:
742
def on_menuitem_file_move_activate(self, widget):
743
""" File/Move... menu handler. """
744
from bzrlib.plugins.gtk.olive.move import MoveDialog
745
move = MoveDialog(self.wt, self.wtpath, self.get_selected_right(), self.window)
746
response = move.run()
748
if response == gtk.RESPONSE_OK:
751
def on_menuitem_file_rename_activate(self, widget):
752
""" File/Rename... menu handler. """
753
from bzrlib.plugins.gtk.olive.rename import RenameDialog
754
rename = RenameDialog(self.wt, self.wtpath, self.get_selected_right(), self.window)
755
response = rename.run()
757
if response == gtk.RESPONSE_OK:
760
def on_menuitem_remove_file_activate(self, widget):
761
""" Remove (unversion) selected file. """
762
from bzrlib.plugins.gtk.olive.remove import RemoveDialog
763
remove = RemoveDialog(self.wt, self.wtpath,
764
selected=self.get_selected_right(),
766
response = remove.run()
768
if response != gtk.RESPONSE_NONE:
771
if response == gtk.RESPONSE_OK:
772
self.set_path(self.path)
777
def on_menuitem_stats_diff_activate(self, widget):
778
""" Statistics/Differences... menu handler. """
779
window = DiffWindow(parent=self.window)
780
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
781
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
784
def on_menuitem_stats_infos_activate(self, widget):
785
""" Statistics/Informations... menu handler. """
786
from info import OliveInfo
788
info = OliveInfo(self.remote_branch)
790
info = OliveInfo(self.wt.branch)
793
def on_menuitem_stats_log_activate(self, widget):
794
""" Statistics/Log... menu handler. """
797
branch = self.wt.branch
799
branch = self.remote_branch
801
window = branchwin.BranchWindow(branch, [branch.last_revision()], None,
805
def on_menuitem_view_refresh_activate(self, widget):
806
""" View/Refresh menu handler. """
807
# Refresh the left pane
809
# Refresh the right pane
812
def on_menuitem_view_show_hidden_files_activate(self, widget):
813
""" View/Show hidden files menu handler. """
814
self.pref.set_preference('dotted_files', widget.get_active())
815
if self.path is not None:
818
def on_menuitem_view_show_ignored_files_activate(self, widget):
819
""" Hide/Show ignored files menu handler. """
820
self.pref.set_preference('ignored_files', widget.get_active())
821
if self.path is not None:
824
def on_treeview_left_button_press_event(self, widget, event):
825
""" Occurs when somebody right-clicks in the bookmark list. """
826
if event.button == 3:
827
# Don't show context with nothing selected
828
if self.get_selected_left() == None:
832
from menu import OliveMenu
833
menu = OliveMenu(path=self.get_path(),
834
selected=self.get_selected_left(),
837
menu.left_context_menu().popup(None, None, None, 0,
840
def on_treeview_left_button_release_event(self, widget, event):
841
""" Occurs when somebody just clicks a bookmark. """
842
if event.button != 3:
843
# Allow one-click bookmark opening
844
if self.get_selected_left() == None:
847
newdir = self.get_selected_left()
851
if self.set_path(newdir):
854
def on_treeview_left_row_activated(self, treeview, path, view_column):
855
""" Occurs when somebody double-clicks or enters an item in the
858
newdir = self.get_selected_left()
862
if self.set_path(newdir):
865
def on_treeview_right_button_press_event(self, widget, event):
866
""" Occurs when somebody right-clicks in the file list. """
867
if event.button == 3:
869
from menu import OliveMenu
870
menu = OliveMenu(path=self.get_path(),
871
selected=self.get_selected_right(),
874
m_open = menu.ui.get_widget('/context_right/open')
875
m_add = menu.ui.get_widget('/context_right/add')
876
m_remove = menu.ui.get_widget('/context_right/remove')
877
m_rename = menu.ui.get_widget('/context_right/rename')
878
m_revert = menu.ui.get_widget('/context_right/revert')
879
m_commit = menu.ui.get_widget('/context_right/commit')
880
m_annotate = menu.ui.get_widget('/context_right/annotate')
881
m_diff = menu.ui.get_widget('/context_right/diff')
882
# check if we're in a branch
884
from bzrlib.branch import Branch
885
Branch.open_containing(self.get_path())
887
m_open.set_sensitive(False)
888
m_add.set_sensitive(False)
889
m_remove.set_sensitive(False)
890
m_rename.set_sensitive(False)
891
m_revert.set_sensitive(False)
892
m_commit.set_sensitive(False)
893
m_annotate.set_sensitive(False)
894
m_diff.set_sensitive(False)
896
m_open.set_sensitive(True)
897
m_add.set_sensitive(True)
898
m_remove.set_sensitive(True)
899
m_rename.set_sensitive(True)
900
m_revert.set_sensitive(True)
901
m_commit.set_sensitive(True)
902
m_annotate.set_sensitive(True)
903
m_diff.set_sensitive(True)
904
except bzrerrors.NotBranchError:
905
m_open.set_sensitive(True)
906
m_add.set_sensitive(False)
907
m_remove.set_sensitive(False)
908
m_rename.set_sensitive(False)
909
m_revert.set_sensitive(False)
910
m_commit.set_sensitive(False)
911
m_annotate.set_sensitive(False)
912
m_diff.set_sensitive(False)
915
menu.right_context_menu().popup(None, None, None, 0,
918
menu.remote_context_menu().popup(None, None, None, 0,
921
def on_treeview_right_row_activated(self, treeview, path, view_column):
922
""" Occurs when somebody double-clicks or enters an item in the
924
from launch import launch
926
newdir = self.get_selected_right()
931
self.set_path(os.path.split(self.get_path())[0])
933
fullpath = os.path.join(self.get_path(), newdir)
934
if os.path.isdir(fullpath):
935
# selected item is an existant directory
936
self.set_path(fullpath)
941
if self._is_remote_dir(self.get_path() + newdir):
942
self.set_path(self.get_path() + newdir)
946
def on_window_main_delete_event(self, widget, event=None):
947
""" Do some stuff before exiting. """
948
width, height = self.window_main.get_size()
949
self.pref.set_preference('window_width', width)
950
self.pref.set_preference('window_height', height)
951
x, y = self.window_main.get_position()
952
self.pref.set_preference('window_x', x)
953
self.pref.set_preference('window_y', y)
954
self.pref.set_preference('paned_position',
955
self.hpaned_main.get_position())
958
self.window_main.destroy()
960
def _load_left(self):
961
""" Load data into the left panel. (Bookmarks) """
963
treestore = gtk.TreeStore(str, str)
966
bookmarks = self.pref.get_bookmarks()
968
# Add them to the TreeStore
969
titer = treestore.append(None, [_i18n('Bookmarks'), None])
971
# Get titles and sort by title
972
bookmarks = [[self.pref.get_bookmark_title(item), item] for item in bookmarks]
974
for title_item in bookmarks:
975
treestore.append(titer, title_item)
977
# Create the column and add it to the TreeView
978
self.treeview_left.set_model(treestore)
979
tvcolumn_bookmark = gtk.TreeViewColumn(_i18n('Bookmark'))
980
self.treeview_left.append_column(tvcolumn_bookmark)
983
cell = gtk.CellRendererText()
984
tvcolumn_bookmark.pack_start(cell, True)
985
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
988
self.treeview_left.expand_all()
990
def _load_right(self):
991
""" Load data into the right panel. (Filelist) """
993
# Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
994
liststore = gtk.ListStore(gobject.TYPE_STRING,
995
gobject.TYPE_BOOLEAN,
1000
gobject.TYPE_STRING,
1002
gobject.TYPE_STRING,
1003
gobject.TYPE_STRING)
1008
# Fill the appropriate lists
1009
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1010
for item in os.listdir(self.path):
1011
if not dotted_files and item[0] == '.':
1013
if os.path.isdir(self.path + os.sep + item):
1018
if not self.notbranch:
1019
branch = self.wt.branch
1020
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
1022
delta = self.wt.changes_from(tree2, want_unchanged=True)
1024
# Add'em to the ListStore
1027
statinfo = os.stat(self.path + os.sep + item)
1029
if e.errno in self.acceptable_errors:
1033
liststore.append([ gtk.STOCK_DIRECTORY,
1041
self._format_date(statinfo.st_mtime),
1046
if not self.notbranch:
1047
filename = self.wt.relpath(self.path + os.sep + item)
1052
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1053
if rpathnew == filename:
1056
for rpath, id, kind in delta.added:
1057
if rpath == filename:
1060
for rpath, id, kind in delta.removed:
1061
if rpath == filename:
1064
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1065
if rpath == filename:
1068
for rpath, id, kind in delta.unchanged:
1069
if rpath == filename:
1070
status = 'unchanged'
1072
for rpath, file_class, kind, id, entry in self.wt.list_files():
1073
if rpath == filename and file_class == 'I':
1078
if status == 'renamed':
1079
st = _i18n('renamed')
1080
elif status == 'removed':
1081
st = _i18n('removed')
1082
elif status == 'added':
1084
elif status == 'modified':
1085
st = _i18n('modified')
1086
elif status == 'unchanged':
1087
st = _i18n('unchanged')
1088
elif status == 'ignored':
1089
st = _i18n('ignored')
1091
st = _i18n('unknown')
1094
statinfo = os.stat(self.path + os.sep + item)
1096
if e.errno in self.acceptable_errors:
1100
liststore.append([gtk.STOCK_FILE,
1105
str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
1106
self._format_size(statinfo.st_size),
1108
self._format_date(statinfo.st_mtime),
1111
# Create the columns and add them to the TreeView
1112
self.treeview_right.set_model(liststore)
1113
self._tvcolumn_filename = gtk.TreeViewColumn(_i18n('Filename'))
1114
self._tvcolumn_status = gtk.TreeViewColumn(_i18n('Status'))
1115
self._tvcolumn_size = gtk.TreeViewColumn(_i18n('Size'))
1116
self._tvcolumn_mtime = gtk.TreeViewColumn(_i18n('Last modified'))
1117
self.treeview_right.append_column(self._tvcolumn_filename)
1118
self.treeview_right.append_column(self._tvcolumn_status)
1119
self.treeview_right.append_column(self._tvcolumn_size)
1120
self.treeview_right.append_column(self._tvcolumn_mtime)
1123
cellpb = gtk.CellRendererPixbuf()
1124
cell = gtk.CellRendererText()
1125
self._tvcolumn_filename.pack_start(cellpb, False)
1126
self._tvcolumn_filename.pack_start(cell, True)
1127
self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
1128
self._tvcolumn_filename.add_attribute(cell, 'text', 2)
1129
self._tvcolumn_status.pack_start(cell, True)
1130
self._tvcolumn_status.add_attribute(cell, 'text', 3)
1131
self._tvcolumn_size.pack_start(cell, True)
1132
self._tvcolumn_size.add_attribute(cell, 'text', 6)
1133
self._tvcolumn_mtime.pack_start(cell, True)
1134
self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
1136
# Set up the properties of the TreeView
1137
self.treeview_right.set_headers_visible(True)
1138
self.treeview_right.set_headers_clickable(True)
1139
self.treeview_right.set_search_column(1)
1140
self._tvcolumn_filename.set_resizable(True)
1141
self._tvcolumn_status.set_resizable(True)
1142
self._tvcolumn_size.set_resizable(True)
1143
self._tvcolumn_mtime.set_resizable(True)
1145
liststore.set_sort_func(13, self._sort_filelist_callback, None)
1146
liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
1147
self._tvcolumn_filename.set_sort_column_id(13)
1148
self._tvcolumn_status.set_sort_column_id(3)
1149
self._tvcolumn_size.set_sort_column_id(5)
1150
self._tvcolumn_mtime.set_sort_column_id(7)
1153
self.set_sensitivity()
1155
def get_selected_fileid(self):
1156
""" Get the file_id of the selected file. """
1157
treeselection = self.treeview_right.get_selection()
1158
(model, iter) = treeselection.get_selected()
1163
return model.get_value(iter, 9)
1165
def get_selected_right(self):
1166
""" Get the selected filename. """
1167
treeselection = self.treeview_right.get_selection()
1168
(model, iter) = treeselection.get_selected()
1173
return model.get_value(iter, 2)
1175
def get_selected_left(self):
1176
""" Get the selected bookmark. """
1177
treeselection = self.treeview_left.get_selection()
1178
(model, iter) = treeselection.get_selected()
1183
return model.get_value(iter, 1)
1185
def set_statusbar(self, message):
1186
""" Set the statusbar message. """
1187
self.statusbar.push(self.context_id, message)
1189
def clear_statusbar(self):
1190
""" Clean the last message from the statusbar. """
1191
self.statusbar.pop(self.context_id)
1193
def set_sensitivity(self):
1194
""" Set menu and toolbar sensitivity. """
1197
self.menuitem_branch_init.set_sensitive(self.notbranch)
1198
self.menuitem_branch_get.set_sensitive(self.notbranch)
1199
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
1200
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
1201
self.menuitem_branch_push.set_sensitive(not self.notbranch)
1202
self.menuitem_branch_update.set_sensitive(not self.notbranch)
1203
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
1204
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
1205
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
1206
self.menuitem_branch_tags.set_sensitive(not self.notbranch)
1207
self.menuitem_branch_status.set_sensitive(not self.notbranch)
1208
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
1209
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
1210
self.menuitem_stats.set_sensitive(not self.notbranch)
1211
self.menuitem_stats_diff.set_sensitive(not self.notbranch)
1212
self.menuitem_add_files.set_sensitive(not self.notbranch)
1213
self.menuitem_remove_files.set_sensitive(not self.notbranch)
1214
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
1215
self.menuitem_file_rename.set_sensitive(not self.notbranch)
1216
self.menuitem_file_move.set_sensitive(not self.notbranch)
1217
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
1218
#self.menutoolbutton_diff.set_sensitive(True)
1219
self.toolbutton_diff.set_sensitive(not self.notbranch)
1220
self.toolbutton_log.set_sensitive(not self.notbranch)
1221
self.toolbutton_commit.set_sensitive(not self.notbranch)
1222
self.toolbutton_pull.set_sensitive(not self.notbranch)
1223
self.toolbutton_push.set_sensitive(not self.notbranch)
1224
self.toolbutton_update.set_sensitive(not self.notbranch)
1227
self.menuitem_branch_init.set_sensitive(False)
1228
self.menuitem_branch_get.set_sensitive(True)
1229
self.menuitem_branch_checkout.set_sensitive(True)
1230
self.menuitem_branch_pull.set_sensitive(False)
1231
self.menuitem_branch_push.set_sensitive(False)
1232
self.menuitem_branch_update.set_sensitive(False)
1233
self.menuitem_branch_revert.set_sensitive(False)
1234
self.menuitem_branch_merge.set_sensitive(False)
1235
self.menuitem_branch_commit.set_sensitive(False)
1236
self.menuitem_branch_tags.set_sensitive(True)
1237
self.menuitem_branch_status.set_sensitive(False)
1238
self.menuitem_branch_missing.set_sensitive(False)
1239
self.menuitem_branch_conflicts.set_sensitive(False)
1240
self.menuitem_stats.set_sensitive(True)
1241
self.menuitem_stats_diff.set_sensitive(False)
1242
self.menuitem_add_files.set_sensitive(False)
1243
self.menuitem_remove_files.set_sensitive(False)
1244
self.menuitem_file_make_directory.set_sensitive(False)
1245
self.menuitem_file_rename.set_sensitive(False)
1246
self.menuitem_file_move.set_sensitive(False)
1247
self.menuitem_file_annotate.set_sensitive(False)
1248
#self.menutoolbutton_diff.set_sensitive(True)
1249
self.toolbutton_diff.set_sensitive(False)
1250
self.toolbutton_log.set_sensitive(True)
1251
self.toolbutton_commit.set_sensitive(False)
1252
self.toolbutton_pull.set_sensitive(False)
1253
self.toolbutton_push.set_sensitive(False)
1254
self.toolbutton_update.set_sensitive(False)
1256
def refresh_left(self):
1257
""" Refresh the bookmark list. """
1259
# Get TreeStore and clear it
1260
treestore = self.treeview_left.get_model()
1263
# Re-read preferences
1267
bookmarks = self.pref.get_bookmarks()
1269
# Add them to the TreeStore
1270
titer = treestore.append(None, [_i18n('Bookmarks'), None])
1272
# Get titles and sort by title
1273
bookmarks = [[self.pref.get_bookmark_title(item), item] for item in bookmarks]
1275
for title_item in bookmarks:
1276
treestore.append(titer, title_item)
1278
# Add the TreeStore to the TreeView
1279
self.treeview_left.set_model(treestore)
1282
self.treeview_left.expand_all()
1284
def refresh_right(self, path=None):
1285
""" Refresh the file list. """
1288
from bzrlib.workingtree import WorkingTree
1291
path = self.get_path()
1293
# A workaround for double-clicking Bookmarks
1294
if not os.path.exists(path):
1297
# Get ListStore and clear it
1298
liststore = self.treeview_right.get_model()
1301
# Show Status column
1302
self._tvcolumn_status.set_visible(True)
1307
# Fill the appropriate lists
1308
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1309
ignored_files = self.pref.get_preference('ignored_files', 'bool')
1311
for item in os.listdir(path):
1312
if not dotted_files and item[0] == '.':
1314
if os.path.isdir(path + os.sep + item):
1319
# Try to open the working tree
1322
tree1 = WorkingTree.open_containing(path)[0]
1323
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
1327
branch = tree1.branch
1328
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
1330
delta = tree1.changes_from(tree2, want_unchanged=True)
1332
# Add'em to the ListStore
1335
statinfo = os.stat(self.path + os.sep + item)
1337
if e.errno in self.acceptable_errors:
1341
liststore.append([gtk.STOCK_DIRECTORY,
1349
self._format_date(statinfo.st_mtime),
1355
filename = tree1.relpath(path + os.sep + item)
1360
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1361
if rpathnew == filename:
1364
for rpath, id, kind in delta.added:
1365
if rpath == filename:
1368
for rpath, id, kind in delta.removed:
1369
if rpath == filename:
1372
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1373
if rpath == filename:
1376
for rpath, id, kind in delta.unchanged:
1377
if rpath == filename:
1378
status = 'unchanged'
1380
for rpath, file_class, kind, id, entry in self.wt.list_files():
1381
if rpath == filename and file_class == 'I':
1386
if status == 'renamed':
1387
st = _i18n('renamed')
1388
elif status == 'removed':
1389
st = _i18n('removed')
1390
elif status == 'added':
1392
elif status == 'modified':
1393
st = _i18n('modified')
1394
elif status == 'unchanged':
1395
st = _i18n('unchanged')
1396
elif status == 'ignored':
1397
st = _i18n('ignored')
1398
if not ignored_files:
1401
st = _i18n('unknown')
1404
statinfo = os.stat(self.path + os.sep + item)
1406
if e.errno in self.acceptable_errors:
1410
liststore.append([gtk.STOCK_FILE,
1415
str(statinfo.st_size),
1416
self._format_size(statinfo.st_size),
1418
self._format_date(statinfo.st_mtime),
1423
# Get ListStore and clear it
1424
liststore = self.treeview_right.get_model()
1427
# Hide Status column
1428
self._tvcolumn_status.set_visible(False)
1433
self._show_stock_image(gtk.STOCK_REFRESH)
1435
for (name, type) in self.remote_entries:
1436
if type.kind == 'directory':
1438
elif type.kind == 'file':
1442
""" Cache based on revision history. """
1443
def __init__(self, history):
1444
self._history = history
1446
def _lookup_revision(self, revid):
1447
for r in self._history:
1448
if r.revision_id == revid:
1450
rev = repo.get_revision(revid)
1451
self._history.append(rev)
1454
repo = self.remote_branch.repository
1456
revhistory = self.remote_branch.revision_history()
1458
revs = repo.get_revisions(revhistory)
1459
cache = HistoryCache(revs)
1460
except bzrerrors.InvalidHttpResponse:
1461
# Fallback to dummy algorithm, because of LP: #115209
1462
cache = HistoryCache([])
1465
if item.parent_id == self.remote_parent:
1466
rev = cache._lookup_revision(item.revision)
1467
liststore.append([ gtk.STOCK_DIRECTORY,
1475
self._format_date(rev.timestamp),
1478
while gtk.events_pending():
1479
gtk.main_iteration()
1482
if item.parent_id == self.remote_parent:
1483
rev = cache._lookup_revision(item.revision)
1484
liststore.append([ gtk.STOCK_FILE,
1489
str(item.text_size),
1490
self._format_size(item.text_size),
1492
self._format_date(rev.timestamp),
1495
while gtk.events_pending():
1496
gtk.main_iteration()
1498
self.image_location_error.destroy()
1500
# Columns should auto-size
1501
self.treeview_right.columns_autosize()
1504
self.set_sensitivity()
1506
def _harddisks(self):
1507
""" Returns hard drive letters under Win32. """
1512
if sys.platform == 'win32':
1513
print "pyWin32 modules needed to run Olive on Win32."
1519
for drive in string.ascii_uppercase:
1520
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED or\
1521
win32file.GetDriveType(drive+':') == win32file.DRIVE_REMOTE:
1522
driveletters.append(drive+':')
1525
def gen_hard_selector(self):
1526
""" Generate the hard drive selector under Win32. """
1527
drives = self._harddisks()
1528
for drive in drives:
1529
self.combobox_drive.append_text(drive)
1530
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1532
def _refresh_drives(self, combobox):
1533
if self._just_started:
1535
model = combobox.get_model()
1536
active = combobox.get_active()
1538
drive = model[active][0]
1539
self.set_path(drive + '\\')
1540
self.refresh_right(drive + '\\')
1542
def check_for_changes(self):
1543
""" Check whether there were changes in the current working tree. """
1544
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
1545
delta = self.wt.changes_from(old_tree)
1549
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
1554
def _sort_filelist_callback(self, model, iter1, iter2, data):
1555
""" The sort callback for the file list, return values:
1560
name1 = model.get_value(iter1, 2)
1561
name2 = model.get_value(iter2, 2)
1563
if model.get_value(iter1, 1):
1564
# item1 is a directory
1565
if not model.get_value(iter2, 1):
1569
# both of them are directories, we compare their names
1572
elif name1 == name2:
1577
# item1 is not a directory
1578
if model.get_value(iter2, 1):
1582
# both of them are files, compare them
1585
elif name1 == name2:
1590
def _format_size(self, size):
1591
""" Format size to a human readable format. """
1593
return "%d[B]" % (size,)
1594
size = size / 1000.0
1596
for metric in ["kB","MB","GB","TB"]:
1599
size = size / 1000.0
1600
return "%.1f[%s]" % (size,metric)
1602
def _format_date(self, timestamp):
1603
""" Format the time (given in secs) to a human readable format. """
1604
return time.ctime(timestamp)
1606
def _is_remote_dir(self, location):
1607
""" Determine whether the given location is a directory or not. """
1609
# We're in local mode
1612
branch, path = Branch.open_containing(location)
1613
for (name, type) in self.remote_entries:
1614
if name == path and type.kind == 'directory':
1617
# Either it's not a directory or not in the inventory
1620
def _show_stock_image(self, stock_id):
1621
""" Show a stock image next to the location entry. """
1622
self.image_location_error.destroy()
1623
self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
1624
self.hbox_location.pack_start(self.image_location_error, False, False, 0)
1625
if sys.platform == 'win32':
1626
self.hbox_location.reorder_child(self.image_location_error, 2)
1628
self.hbox_location.reorder_child(self.image_location_error, 1)
1629
self.image_location_error.show()
1630
while gtk.events_pending():
1631
gtk.main_iteration()
1636
""" A class which handles Olive's preferences. """
1637
def __init__(self, path=None):
1638
""" Initialize the Preferences class. """
1639
# Some default options
1640
self.defaults = { 'strict_commit' : False,
1641
'dotted_files' : False,
1642
'ignored_files' : True,
1643
'window_width' : 700,
1644
'window_height' : 400,
1647
'paned_position': 200 }
1649
# Create a config parser object
1650
self.config = ConfigParser.RawConfigParser()
1654
if sys.platform == 'win32':
1655
# Windows - no dotted files
1656
self._filename = os.path.expanduser('~/olive.conf')
1658
self._filename = os.path.expanduser('~/.olive.conf')
1660
self._filename = path
1662
# Load the configuration
1665
def _get_default(self, option):
1666
""" Get the default option for a preference. """
1668
ret = self.defaults[option]
1675
""" Refresh the configuration. """
1676
# First write out the changes
1678
# Then load the configuration again
1682
""" Just read the configuration. """
1683
# Re-initialize the config parser object to avoid some bugs
1684
self.config = ConfigParser.RawConfigParser()
1685
self.config.read([self._filename])
1688
""" Write the configuration to the appropriate files. """
1689
fp = open(self._filename, 'w')
1690
self.config.write(fp)
1693
def get_bookmarks(self):
1694
""" Return the list of bookmarks. """
1695
bookmarks = self.config.sections()
1696
if self.config.has_section('preferences'):
1697
bookmarks.remove('preferences')
1700
def add_bookmark(self, path):
1701
""" Add bookmark. """
1703
self.config.add_section(path)
1704
except ConfigParser.DuplicateSectionError:
1709
def get_bookmark_title(self, path):
1710
""" Get bookmark title. """
1712
ret = self.config.get(path, 'title')
1713
except ConfigParser.NoOptionError:
1718
def set_bookmark_title(self, path, title):
1719
""" Set bookmark title. """
1720
# FIXME: What if path isn't listed yet?
1721
# FIXME: Canonicalize paths first?
1722
self.config.set(path, 'title', title)
1724
def remove_bookmark(self, path):
1725
""" Remove bookmark. """
1726
return self.config.remove_section(path)
1728
def set_preference(self, option, value):
1729
""" Set the value of the given option. """
1732
elif value is False:
1735
if self.config.has_section('preferences'):
1736
self.config.set('preferences', option, value)
1738
self.config.add_section('preferences')
1739
self.config.set('preferences', option, value)
1741
def get_preference(self, option, kind='str'):
1742
""" Get the value of the given option.
1744
:param kind: str/bool/int/float. default: str
1746
if self.config.has_option('preferences', option):
1748
return self.config.getboolean('preferences', option)
1750
return self.config.getint('preferences', option)
1751
elif kind == 'float':
1752
return self.config.getfloat('preferences', option)
1754
return self.config.get('preferences', option)
1757
return self._get_default(option)