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
merge = MergeDialog(self.wt, self.wtpath, parent_branch_path, self.window)
557
response = merge.run()
559
if response == gtk.RESPONSE_OK:
563
def on_menuitem_branch_missing_revisions_activate(self, widget):
564
""" Branch/Missing revisions menu handler. """
566
from bzrlib.missing import find_unmerged, iter_log_revisions
568
local_branch = self.wt.branch
569
parent_branch_path = local_branch.get_parent()
570
if parent_branch_path is None:
571
error_dialog(_i18n('Parent location is unknown'),
572
_i18n('Cannot determine missing revisions if no parent location is known.'))
575
parent_branch = Branch.open(parent_branch_path)
577
if parent_branch.base == local_branch.base:
578
parent_branch = local_branch
580
local_extra, remote_extra = find_unmerged(local_branch,parent_branch)
582
if local_extra or remote_extra:
584
## def log_revision_one_line_text(log_revision):
585
## """ Generates one line description of log_revison ended with end of line."""
586
## revision = log_revision.rev
587
## txt = "- %s (%s)\n" % (revision.get_summary(), revision.committer, )
588
## txt = txt.replace("<"," ") # Seems < > chars are expected to be xml tags ...
589
## txt = txt.replace(">"," ")
594
dlg_txt += _i18n('%d local extra revision(s). \n') % (len(local_extra),)
595
## NOTE: We do not want such ugly info about missing revisions
596
## Revision Browser should be used there
597
## max_revisions = 10
598
## for log_revision in iter_log_revisions(local_extra, local_branch.repository, verbose=1):
599
## dlg_txt += log_revision_one_line_text(log_revision)
600
## if max_revisions <= 0:
601
## dlg_txt += _i18n("more ... \n")
603
## max_revisions -= 1
606
dlg_txt += _i18n('%d local missing revision(s).\n') % (len(remote_extra),)
607
## max_revisions = 10
608
## for log_revision in iter_log_revisions(remote_extra, parent_branch.repository, verbose=1):
609
## dlg_txt += log_revision_one_line_text(log_revision)
610
## if max_revisions <= 0:
611
## dlg_txt += _i18n("more ... \n")
613
## max_revisions -= 1
615
info_dialog(_i18n('There are missing revisions'),
618
info_dialog(_i18n('Local branch up to date'),
619
_i18n('There are no missing revisions.'))
622
def on_menuitem_branch_pull_activate(self, widget):
623
""" Branch/Pull menu handler. """
624
branch_to = self.wt.branch
626
location = branch_to.get_parent()
628
error_dialog(_i18n('Parent location is unknown'),
629
_i18n('Pulling is not possible until there is a parent location.'))
632
branch_from = Branch.open(location)
634
if branch_to.get_parent() is None:
635
branch_to.set_parent(branch_from.base)
637
ret = branch_to.pull(branch_from)
639
info_dialog(_i18n('Pull successful'), _i18n('%d revision(s) pulled.') % ret)
642
def on_menuitem_branch_update_activate(self, widget):
643
""" Brranch/checkout update menu handler. """
645
ret = self.wt.update()
646
conflicts = self.wt.conflicts()
648
info_dialog(_i18n('Update successful but conflicts generated'), _i18n('Number of conflicts generated: %d.') % (len(conflicts),) )
650
info_dialog(_i18n('Update successful'), _i18n('No conflicts generated.') )
652
def on_menuitem_branch_push_activate(self, widget):
653
""" Branch/Push... menu handler. """
654
push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
655
response = push.run()
656
if response != gtk.RESPONSE_NONE:
660
def on_menuitem_branch_revert_activate(self, widget):
661
""" Branch/Revert all changes menu handler. """
662
ret = self.wt.revert([])
664
warning_dialog(_i18n('Conflicts detected'),
665
_i18n('Please have a look at the working tree before continuing.'))
667
info_dialog(_i18n('Revert successful'),
668
_i18n('All files reverted to last revision.'))
671
def on_menuitem_branch_status_activate(self, widget):
672
""" Branch/Status... menu handler. """
673
from bzrlib.plugins.gtk.status import StatusDialog
674
status = StatusDialog(self.wt, self.wtpath)
675
response = status.run()
676
if response != gtk.RESPONSE_NONE:
679
def on_menuitem_branch_initialize_activate(self, widget):
680
""" Initialize current directory. """
681
init = InitDialog(self.path, self.window)
682
response = init.run()
683
if response != gtk.RESPONSE_NONE:
686
if response == gtk.RESPONSE_OK:
691
def on_menuitem_branch_tags_activate(self, widget):
692
""" Branch/Tags... menu handler. """
693
from bzrlib.plugins.gtk.tags import TagsWindow
695
window = TagsWindow(self.wt.branch, self.window)
697
window = TagsWindow(self.remote_branch, self.window)
700
def on_menuitem_file_annotate_activate(self, widget):
701
""" File/Annotate... menu handler. """
702
if self.get_selected_right() is None:
703
error_dialog(_i18n('No file was selected'),
704
_i18n('Please select a file from the list.'))
707
branch = self.wt.branch
708
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
710
window = GAnnotateWindow(all=False, plain=False, parent=self.window)
711
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
712
config = GAnnotateConfig(window)
716
window.annotate(self.wt, branch, file_id)
720
def on_menuitem_file_bookmark_activate(self, widget):
721
""" File/Bookmark current directory menu handler. """
722
if self.pref.add_bookmark(self.path):
723
info_dialog(_i18n('Bookmark successfully added'),
724
_i18n('The current directory was bookmarked. You can reach\nit by selecting it from the left panel.'))
727
warning_dialog(_i18n('Location already bookmarked'),
728
_i18n('The current directory is already bookmarked.\nSee the left panel for reference.'))
732
def on_menuitem_file_make_directory_activate(self, widget):
733
""" File/Make directory... menu handler. """
734
from bzrlib.plugins.gtk.olive.mkdir import MkdirDialog
735
mkdir = MkdirDialog(self.wt, self.wtpath, self.window)
736
response = mkdir.run()
738
if response == gtk.RESPONSE_OK:
741
def on_menuitem_file_move_activate(self, widget):
742
""" File/Move... menu handler. """
743
from bzrlib.plugins.gtk.olive.move import MoveDialog
744
move = MoveDialog(self.wt, self.wtpath, self.get_selected_right(), self.window)
745
response = move.run()
747
if response == gtk.RESPONSE_OK:
750
def on_menuitem_file_rename_activate(self, widget):
751
""" File/Rename... menu handler. """
752
from bzrlib.plugins.gtk.olive.rename import RenameDialog
753
rename = RenameDialog(self.wt, self.wtpath, self.get_selected_right(), self.window)
754
response = rename.run()
756
if response == gtk.RESPONSE_OK:
759
def on_menuitem_remove_file_activate(self, widget):
760
""" Remove (unversion) selected file. """
761
from bzrlib.plugins.gtk.olive.remove import RemoveDialog
762
remove = RemoveDialog(self.wt, self.wtpath,
763
selected=self.get_selected_right(),
765
response = remove.run()
767
if response != gtk.RESPONSE_NONE:
770
if response == gtk.RESPONSE_OK:
771
self.set_path(self.path)
776
def on_menuitem_stats_diff_activate(self, widget):
777
""" Statistics/Differences... menu handler. """
778
window = DiffWindow(parent=self.window)
779
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
780
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
783
def on_menuitem_stats_infos_activate(self, widget):
784
""" Statistics/Informations... menu handler. """
785
from info import OliveInfo
787
info = OliveInfo(self.remote_branch)
789
info = OliveInfo(self.wt.branch)
792
def on_menuitem_stats_log_activate(self, widget):
793
""" Statistics/Log... menu handler. """
796
branch = self.wt.branch
798
branch = self.remote_branch
800
window = branchwin.BranchWindow(branch, [branch.last_revision()], None,
804
def on_menuitem_view_refresh_activate(self, widget):
805
""" View/Refresh menu handler. """
806
# Refresh the left pane
808
# Refresh the right pane
811
def on_menuitem_view_show_hidden_files_activate(self, widget):
812
""" View/Show hidden files menu handler. """
813
self.pref.set_preference('dotted_files', widget.get_active())
814
if self.path is not None:
817
def on_menuitem_view_show_ignored_files_activate(self, widget):
818
""" Hide/Show ignored files menu handler. """
819
self.pref.set_preference('ignored_files', widget.get_active())
820
if self.path is not None:
823
def on_treeview_left_button_press_event(self, widget, event):
824
""" Occurs when somebody right-clicks in the bookmark list. """
825
if event.button == 3:
826
# Don't show context with nothing selected
827
if self.get_selected_left() == None:
831
from menu import OliveMenu
832
menu = OliveMenu(path=self.get_path(),
833
selected=self.get_selected_left(),
836
menu.left_context_menu().popup(None, None, None, 0,
839
def on_treeview_left_button_release_event(self, widget, event):
840
""" Occurs when somebody just clicks a bookmark. """
841
if event.button != 3:
842
# Allow one-click bookmark opening
843
if self.get_selected_left() == None:
846
newdir = self.get_selected_left()
850
if self.set_path(newdir):
853
def on_treeview_left_row_activated(self, treeview, path, view_column):
854
""" Occurs when somebody double-clicks or enters an item in the
857
newdir = self.get_selected_left()
861
if self.set_path(newdir):
864
def on_treeview_right_button_press_event(self, widget, event):
865
""" Occurs when somebody right-clicks in the file list. """
866
if event.button == 3:
868
from menu import OliveMenu
869
menu = OliveMenu(path=self.get_path(),
870
selected=self.get_selected_right(),
873
m_open = menu.ui.get_widget('/context_right/open')
874
m_add = menu.ui.get_widget('/context_right/add')
875
m_remove = menu.ui.get_widget('/context_right/remove')
876
m_rename = menu.ui.get_widget('/context_right/rename')
877
m_revert = menu.ui.get_widget('/context_right/revert')
878
m_commit = menu.ui.get_widget('/context_right/commit')
879
m_annotate = menu.ui.get_widget('/context_right/annotate')
880
m_diff = menu.ui.get_widget('/context_right/diff')
881
# check if we're in a branch
883
from bzrlib.branch import Branch
884
Branch.open_containing(self.get_path())
886
m_open.set_sensitive(False)
887
m_add.set_sensitive(False)
888
m_remove.set_sensitive(False)
889
m_rename.set_sensitive(False)
890
m_revert.set_sensitive(False)
891
m_commit.set_sensitive(False)
892
m_annotate.set_sensitive(False)
893
m_diff.set_sensitive(False)
895
m_open.set_sensitive(True)
896
m_add.set_sensitive(True)
897
m_remove.set_sensitive(True)
898
m_rename.set_sensitive(True)
899
m_revert.set_sensitive(True)
900
m_commit.set_sensitive(True)
901
m_annotate.set_sensitive(True)
902
m_diff.set_sensitive(True)
903
except bzrerrors.NotBranchError:
904
m_open.set_sensitive(True)
905
m_add.set_sensitive(False)
906
m_remove.set_sensitive(False)
907
m_rename.set_sensitive(False)
908
m_revert.set_sensitive(False)
909
m_commit.set_sensitive(False)
910
m_annotate.set_sensitive(False)
911
m_diff.set_sensitive(False)
914
menu.right_context_menu().popup(None, None, None, 0,
917
menu.remote_context_menu().popup(None, None, None, 0,
920
def on_treeview_right_row_activated(self, treeview, path, view_column):
921
""" Occurs when somebody double-clicks or enters an item in the
923
from launch import launch
925
newdir = self.get_selected_right()
930
self.set_path(os.path.split(self.get_path())[0])
932
fullpath = os.path.join(self.get_path(), newdir)
933
if os.path.isdir(fullpath):
934
# selected item is an existant directory
935
self.set_path(fullpath)
940
if self._is_remote_dir(self.get_path() + newdir):
941
self.set_path(self.get_path() + newdir)
945
def on_window_main_delete_event(self, widget, event=None):
946
""" Do some stuff before exiting. """
947
width, height = self.window_main.get_size()
948
self.pref.set_preference('window_width', width)
949
self.pref.set_preference('window_height', height)
950
x, y = self.window_main.get_position()
951
self.pref.set_preference('window_x', x)
952
self.pref.set_preference('window_y', y)
953
self.pref.set_preference('paned_position',
954
self.hpaned_main.get_position())
957
self.window_main.destroy()
959
def _load_left(self):
960
""" Load data into the left panel. (Bookmarks) """
962
treestore = gtk.TreeStore(str, str)
965
bookmarks = self.pref.get_bookmarks()
967
# Add them to the TreeStore
968
titer = treestore.append(None, [_i18n('Bookmarks'), None])
970
# Get titles and sort by title
971
bookmarks = [[self.pref.get_bookmark_title(item), item] for item in bookmarks]
973
for title_item in bookmarks:
974
treestore.append(titer, title_item)
976
# Create the column and add it to the TreeView
977
self.treeview_left.set_model(treestore)
978
tvcolumn_bookmark = gtk.TreeViewColumn(_i18n('Bookmark'))
979
self.treeview_left.append_column(tvcolumn_bookmark)
982
cell = gtk.CellRendererText()
983
tvcolumn_bookmark.pack_start(cell, True)
984
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
987
self.treeview_left.expand_all()
989
def _load_right(self):
990
""" Load data into the right panel. (Filelist) """
992
# Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
993
liststore = gtk.ListStore(gobject.TYPE_STRING,
994
gobject.TYPE_BOOLEAN,
1001
gobject.TYPE_STRING,
1002
gobject.TYPE_STRING)
1007
# Fill the appropriate lists
1008
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1009
for item in os.listdir(self.path):
1010
if not dotted_files and item[0] == '.':
1012
if os.path.isdir(self.path + os.sep + item):
1017
if not self.notbranch:
1018
branch = self.wt.branch
1019
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
1021
delta = self.wt.changes_from(tree2, want_unchanged=True)
1023
# Add'em to the ListStore
1026
statinfo = os.stat(self.path + os.sep + item)
1028
if e.errno in self.acceptable_errors:
1032
liststore.append([ gtk.STOCK_DIRECTORY,
1040
self._format_date(statinfo.st_mtime),
1045
if not self.notbranch:
1046
filename = self.wt.relpath(self.path + os.sep + item)
1051
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1052
if rpathnew == filename:
1055
for rpath, id, kind in delta.added:
1056
if rpath == filename:
1059
for rpath, id, kind in delta.removed:
1060
if rpath == filename:
1063
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1064
if rpath == filename:
1067
for rpath, id, kind in delta.unchanged:
1068
if rpath == filename:
1069
status = 'unchanged'
1071
for rpath, file_class, kind, id, entry in self.wt.list_files():
1072
if rpath == filename and file_class == 'I':
1077
if status == 'renamed':
1078
st = _i18n('renamed')
1079
elif status == 'removed':
1080
st = _i18n('removed')
1081
elif status == 'added':
1083
elif status == 'modified':
1084
st = _i18n('modified')
1085
elif status == 'unchanged':
1086
st = _i18n('unchanged')
1087
elif status == 'ignored':
1088
st = _i18n('ignored')
1090
st = _i18n('unknown')
1093
statinfo = os.stat(self.path + os.sep + item)
1095
if e.errno in self.acceptable_errors:
1099
liststore.append([gtk.STOCK_FILE,
1104
str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
1105
self._format_size(statinfo.st_size),
1107
self._format_date(statinfo.st_mtime),
1110
# Create the columns and add them to the TreeView
1111
self.treeview_right.set_model(liststore)
1112
self._tvcolumn_filename = gtk.TreeViewColumn(_i18n('Filename'))
1113
self._tvcolumn_status = gtk.TreeViewColumn(_i18n('Status'))
1114
self._tvcolumn_size = gtk.TreeViewColumn(_i18n('Size'))
1115
self._tvcolumn_mtime = gtk.TreeViewColumn(_i18n('Last modified'))
1116
self.treeview_right.append_column(self._tvcolumn_filename)
1117
self.treeview_right.append_column(self._tvcolumn_status)
1118
self.treeview_right.append_column(self._tvcolumn_size)
1119
self.treeview_right.append_column(self._tvcolumn_mtime)
1122
cellpb = gtk.CellRendererPixbuf()
1123
cell = gtk.CellRendererText()
1124
self._tvcolumn_filename.pack_start(cellpb, False)
1125
self._tvcolumn_filename.pack_start(cell, True)
1126
self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
1127
self._tvcolumn_filename.add_attribute(cell, 'text', 2)
1128
self._tvcolumn_status.pack_start(cell, True)
1129
self._tvcolumn_status.add_attribute(cell, 'text', 3)
1130
self._tvcolumn_size.pack_start(cell, True)
1131
self._tvcolumn_size.add_attribute(cell, 'text', 6)
1132
self._tvcolumn_mtime.pack_start(cell, True)
1133
self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
1135
# Set up the properties of the TreeView
1136
self.treeview_right.set_headers_visible(True)
1137
self.treeview_right.set_headers_clickable(True)
1138
self.treeview_right.set_search_column(1)
1139
self._tvcolumn_filename.set_resizable(True)
1140
self._tvcolumn_status.set_resizable(True)
1141
self._tvcolumn_size.set_resizable(True)
1142
self._tvcolumn_mtime.set_resizable(True)
1144
liststore.set_sort_func(13, self._sort_filelist_callback, None)
1145
liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
1146
self._tvcolumn_filename.set_sort_column_id(13)
1147
self._tvcolumn_status.set_sort_column_id(3)
1148
self._tvcolumn_size.set_sort_column_id(5)
1149
self._tvcolumn_mtime.set_sort_column_id(7)
1152
self.set_sensitivity()
1154
def get_selected_fileid(self):
1155
""" Get the file_id of the selected file. """
1156
treeselection = self.treeview_right.get_selection()
1157
(model, iter) = treeselection.get_selected()
1162
return model.get_value(iter, 9)
1164
def get_selected_right(self):
1165
""" Get the selected filename. """
1166
treeselection = self.treeview_right.get_selection()
1167
(model, iter) = treeselection.get_selected()
1172
return model.get_value(iter, 2)
1174
def get_selected_left(self):
1175
""" Get the selected bookmark. """
1176
treeselection = self.treeview_left.get_selection()
1177
(model, iter) = treeselection.get_selected()
1182
return model.get_value(iter, 1)
1184
def set_statusbar(self, message):
1185
""" Set the statusbar message. """
1186
self.statusbar.push(self.context_id, message)
1188
def clear_statusbar(self):
1189
""" Clean the last message from the statusbar. """
1190
self.statusbar.pop(self.context_id)
1192
def set_sensitivity(self):
1193
""" Set menu and toolbar sensitivity. """
1196
self.menuitem_branch_init.set_sensitive(self.notbranch)
1197
self.menuitem_branch_get.set_sensitive(self.notbranch)
1198
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
1199
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
1200
self.menuitem_branch_push.set_sensitive(not self.notbranch)
1201
self.menuitem_branch_update.set_sensitive(not self.notbranch)
1202
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
1203
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
1204
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
1205
self.menuitem_branch_tags.set_sensitive(not self.notbranch)
1206
self.menuitem_branch_status.set_sensitive(not self.notbranch)
1207
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
1208
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
1209
self.menuitem_stats.set_sensitive(not self.notbranch)
1210
self.menuitem_stats_diff.set_sensitive(not self.notbranch)
1211
self.menuitem_add_files.set_sensitive(not self.notbranch)
1212
self.menuitem_remove_files.set_sensitive(not self.notbranch)
1213
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
1214
self.menuitem_file_rename.set_sensitive(not self.notbranch)
1215
self.menuitem_file_move.set_sensitive(not self.notbranch)
1216
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
1217
#self.menutoolbutton_diff.set_sensitive(True)
1218
self.toolbutton_diff.set_sensitive(not self.notbranch)
1219
self.toolbutton_log.set_sensitive(not self.notbranch)
1220
self.toolbutton_commit.set_sensitive(not self.notbranch)
1221
self.toolbutton_pull.set_sensitive(not self.notbranch)
1222
self.toolbutton_push.set_sensitive(not self.notbranch)
1223
self.toolbutton_update.set_sensitive(not self.notbranch)
1226
self.menuitem_branch_init.set_sensitive(False)
1227
self.menuitem_branch_get.set_sensitive(True)
1228
self.menuitem_branch_checkout.set_sensitive(True)
1229
self.menuitem_branch_pull.set_sensitive(False)
1230
self.menuitem_branch_push.set_sensitive(False)
1231
self.menuitem_branch_update.set_sensitive(False)
1232
self.menuitem_branch_revert.set_sensitive(False)
1233
self.menuitem_branch_merge.set_sensitive(False)
1234
self.menuitem_branch_commit.set_sensitive(False)
1235
self.menuitem_branch_tags.set_sensitive(True)
1236
self.menuitem_branch_status.set_sensitive(False)
1237
self.menuitem_branch_missing.set_sensitive(False)
1238
self.menuitem_branch_conflicts.set_sensitive(False)
1239
self.menuitem_stats.set_sensitive(True)
1240
self.menuitem_stats_diff.set_sensitive(False)
1241
self.menuitem_add_files.set_sensitive(False)
1242
self.menuitem_remove_files.set_sensitive(False)
1243
self.menuitem_file_make_directory.set_sensitive(False)
1244
self.menuitem_file_rename.set_sensitive(False)
1245
self.menuitem_file_move.set_sensitive(False)
1246
self.menuitem_file_annotate.set_sensitive(False)
1247
#self.menutoolbutton_diff.set_sensitive(True)
1248
self.toolbutton_diff.set_sensitive(False)
1249
self.toolbutton_log.set_sensitive(True)
1250
self.toolbutton_commit.set_sensitive(False)
1251
self.toolbutton_pull.set_sensitive(False)
1252
self.toolbutton_push.set_sensitive(False)
1253
self.toolbutton_update.set_sensitive(False)
1255
def refresh_left(self):
1256
""" Refresh the bookmark list. """
1258
# Get TreeStore and clear it
1259
treestore = self.treeview_left.get_model()
1262
# Re-read preferences
1266
bookmarks = self.pref.get_bookmarks()
1268
# Add them to the TreeStore
1269
titer = treestore.append(None, [_i18n('Bookmarks'), None])
1271
# Get titles and sort by title
1272
bookmarks = [[self.pref.get_bookmark_title(item), item] for item in bookmarks]
1274
for title_item in bookmarks:
1275
treestore.append(titer, title_item)
1277
# Add the TreeStore to the TreeView
1278
self.treeview_left.set_model(treestore)
1281
self.treeview_left.expand_all()
1283
def refresh_right(self, path=None):
1284
""" Refresh the file list. """
1287
from bzrlib.workingtree import WorkingTree
1290
path = self.get_path()
1292
# A workaround for double-clicking Bookmarks
1293
if not os.path.exists(path):
1296
# Get ListStore and clear it
1297
liststore = self.treeview_right.get_model()
1300
# Show Status column
1301
self._tvcolumn_status.set_visible(True)
1306
# Fill the appropriate lists
1307
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1308
ignored_files = self.pref.get_preference('ignored_files', 'bool')
1310
for item in os.listdir(path):
1311
if not dotted_files and item[0] == '.':
1313
if os.path.isdir(path + os.sep + item):
1318
# Try to open the working tree
1321
tree1 = WorkingTree.open_containing(path)[0]
1322
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
1326
branch = tree1.branch
1327
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
1329
delta = tree1.changes_from(tree2, want_unchanged=True)
1331
# Add'em to the ListStore
1334
statinfo = os.stat(self.path + os.sep + item)
1336
if e.errno in self.acceptable_errors:
1340
liststore.append([gtk.STOCK_DIRECTORY,
1348
self._format_date(statinfo.st_mtime),
1354
filename = tree1.relpath(path + os.sep + item)
1359
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1360
if rpathnew == filename:
1363
for rpath, id, kind in delta.added:
1364
if rpath == filename:
1367
for rpath, id, kind in delta.removed:
1368
if rpath == filename:
1371
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1372
if rpath == filename:
1375
for rpath, id, kind in delta.unchanged:
1376
if rpath == filename:
1377
status = 'unchanged'
1379
for rpath, file_class, kind, id, entry in self.wt.list_files():
1380
if rpath == filename and file_class == 'I':
1385
if status == 'renamed':
1386
st = _i18n('renamed')
1387
elif status == 'removed':
1388
st = _i18n('removed')
1389
elif status == 'added':
1391
elif status == 'modified':
1392
st = _i18n('modified')
1393
elif status == 'unchanged':
1394
st = _i18n('unchanged')
1395
elif status == 'ignored':
1396
st = _i18n('ignored')
1397
if not ignored_files:
1400
st = _i18n('unknown')
1403
statinfo = os.stat(self.path + os.sep + item)
1405
if e.errno in self.acceptable_errors:
1409
liststore.append([gtk.STOCK_FILE,
1414
str(statinfo.st_size),
1415
self._format_size(statinfo.st_size),
1417
self._format_date(statinfo.st_mtime),
1422
# Get ListStore and clear it
1423
liststore = self.treeview_right.get_model()
1426
# Hide Status column
1427
self._tvcolumn_status.set_visible(False)
1432
self._show_stock_image(gtk.STOCK_REFRESH)
1434
for (name, type) in self.remote_entries:
1435
if type.kind == 'directory':
1437
elif type.kind == 'file':
1441
""" Cache based on revision history. """
1442
def __init__(self, history):
1443
self._history = history
1445
def _lookup_revision(self, revid):
1446
for r in self._history:
1447
if r.revision_id == revid:
1449
rev = repo.get_revision(revid)
1450
self._history.append(rev)
1453
repo = self.remote_branch.repository
1455
revhistory = self.remote_branch.revision_history()
1457
revs = repo.get_revisions(revhistory)
1458
cache = HistoryCache(revs)
1459
except bzrerrors.InvalidHttpResponse:
1460
# Fallback to dummy algorithm, because of LP: #115209
1461
cache = HistoryCache([])
1464
if item.parent_id == self.remote_parent:
1465
rev = cache._lookup_revision(item.revision)
1466
liststore.append([ gtk.STOCK_DIRECTORY,
1474
self._format_date(rev.timestamp),
1477
while gtk.events_pending():
1478
gtk.main_iteration()
1481
if item.parent_id == self.remote_parent:
1482
rev = cache._lookup_revision(item.revision)
1483
liststore.append([ gtk.STOCK_FILE,
1488
str(item.text_size),
1489
self._format_size(item.text_size),
1491
self._format_date(rev.timestamp),
1494
while gtk.events_pending():
1495
gtk.main_iteration()
1497
self.image_location_error.destroy()
1499
# Columns should auto-size
1500
self.treeview_right.columns_autosize()
1503
self.set_sensitivity()
1505
def _harddisks(self):
1506
""" Returns hard drive letters under Win32. """
1511
if sys.platform == 'win32':
1512
print "pyWin32 modules needed to run Olive on Win32."
1518
for drive in string.ascii_uppercase:
1519
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED or\
1520
win32file.GetDriveType(drive+':') == win32file.DRIVE_REMOTE:
1521
driveletters.append(drive+':')
1524
def gen_hard_selector(self):
1525
""" Generate the hard drive selector under Win32. """
1526
drives = self._harddisks()
1527
for drive in drives:
1528
self.combobox_drive.append_text(drive)
1529
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1531
def _refresh_drives(self, combobox):
1532
if self._just_started:
1534
model = combobox.get_model()
1535
active = combobox.get_active()
1537
drive = model[active][0]
1538
self.set_path(drive + '\\')
1539
self.refresh_right(drive + '\\')
1541
def check_for_changes(self):
1542
""" Check whether there were changes in the current working tree. """
1543
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
1544
delta = self.wt.changes_from(old_tree)
1548
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
1553
def _sort_filelist_callback(self, model, iter1, iter2, data):
1554
""" The sort callback for the file list, return values:
1559
name1 = model.get_value(iter1, 2)
1560
name2 = model.get_value(iter2, 2)
1562
if model.get_value(iter1, 1):
1563
# item1 is a directory
1564
if not model.get_value(iter2, 1):
1568
# both of them are directories, we compare their names
1571
elif name1 == name2:
1576
# item1 is not a directory
1577
if model.get_value(iter2, 1):
1581
# both of them are files, compare them
1584
elif name1 == name2:
1589
def _format_size(self, size):
1590
""" Format size to a human readable format. """
1592
return "%d[B]" % (size,)
1593
size = size / 1000.0
1595
for metric in ["kB","MB","GB","TB"]:
1598
size = size / 1000.0
1599
return "%.1f[%s]" % (size,metric)
1601
def _format_date(self, timestamp):
1602
""" Format the time (given in secs) to a human readable format. """
1603
return time.ctime(timestamp)
1605
def _is_remote_dir(self, location):
1606
""" Determine whether the given location is a directory or not. """
1608
# We're in local mode
1611
branch, path = Branch.open_containing(location)
1612
for (name, type) in self.remote_entries:
1613
if name == path and type.kind == 'directory':
1616
# Either it's not a directory or not in the inventory
1619
def _show_stock_image(self, stock_id):
1620
""" Show a stock image next to the location entry. """
1621
self.image_location_error.destroy()
1622
self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
1623
self.hbox_location.pack_start(self.image_location_error, False, False, 0)
1624
if sys.platform == 'win32':
1625
self.hbox_location.reorder_child(self.image_location_error, 2)
1627
self.hbox_location.reorder_child(self.image_location_error, 1)
1628
self.image_location_error.show()
1629
while gtk.events_pending():
1630
gtk.main_iteration()
1635
""" A class which handles Olive's preferences. """
1636
def __init__(self, path=None):
1637
""" Initialize the Preferences class. """
1638
# Some default options
1639
self.defaults = { 'strict_commit' : False,
1640
'dotted_files' : False,
1641
'ignored_files' : True,
1642
'window_width' : 700,
1643
'window_height' : 400,
1646
'paned_position': 200 }
1648
# Create a config parser object
1649
self.config = ConfigParser.RawConfigParser()
1653
if sys.platform == 'win32':
1654
# Windows - no dotted files
1655
self._filename = os.path.expanduser('~/olive.conf')
1657
self._filename = os.path.expanduser('~/.olive.conf')
1659
self._filename = path
1661
# Load the configuration
1664
def _get_default(self, option):
1665
""" Get the default option for a preference. """
1667
ret = self.defaults[option]
1674
""" Refresh the configuration. """
1675
# First write out the changes
1677
# Then load the configuration again
1681
""" Just read the configuration. """
1682
# Re-initialize the config parser object to avoid some bugs
1683
self.config = ConfigParser.RawConfigParser()
1684
self.config.read([self._filename])
1687
""" Write the configuration to the appropriate files. """
1688
fp = open(self._filename, 'w')
1689
self.config.write(fp)
1692
def get_bookmarks(self):
1693
""" Return the list of bookmarks. """
1694
bookmarks = self.config.sections()
1695
if self.config.has_section('preferences'):
1696
bookmarks.remove('preferences')
1699
def add_bookmark(self, path):
1700
""" Add bookmark. """
1702
self.config.add_section(path)
1703
except ConfigParser.DuplicateSectionError:
1708
def get_bookmark_title(self, path):
1709
""" Get bookmark title. """
1711
ret = self.config.get(path, 'title')
1712
except ConfigParser.NoOptionError:
1717
def set_bookmark_title(self, path, title):
1718
""" Set bookmark title. """
1719
# FIXME: What if path isn't listed yet?
1720
# FIXME: Canonicalize paths first?
1721
self.config.set(path, 'title', title)
1723
def remove_bookmark(self, path):
1724
""" Remove bookmark. """
1725
return self.config.remove_section(path)
1727
def set_preference(self, option, value):
1728
""" Set the value of the given option. """
1731
elif value is False:
1734
if self.config.has_section('preferences'):
1735
self.config.set('preferences', option, value)
1737
self.config.add_section('preferences')
1738
self.config.set('preferences', option, value)
1740
def get_preference(self, option, kind='str'):
1741
""" Get the value of the given option.
1743
:param kind: str/bool/int/float. default: str
1745
if self.config.has_option('preferences', option):
1747
return self.config.getboolean('preferences', option)
1749
return self.config.getint('preferences', option)
1750
elif kind == 'float':
1751
return self.config.getfloat('preferences', option)
1753
return self.config.get('preferences', option)
1756
return self._get_default(option)