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
25
gettext.install('olive-gtk')
38
from bzrlib.branch import Branch
39
import bzrlib.errors as bzrerrors
40
from bzrlib.lazy_import import lazy_import
41
from bzrlib.ui import ui_factory
42
from bzrlib.workingtree import WorkingTree
44
from bzrlib.plugins.gtk import _i18n
45
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
46
from bzrlib.plugins.gtk.errors import show_bzr_error
47
from guifiles import GLADEFILENAME
49
from bzrlib.plugins.gtk.diff import DiffWindow
50
lazy_import(globals(), """
51
from bzrlib.plugins.gtk.viz import branchwin
53
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
54
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
55
from bzrlib.plugins.gtk.commit import CommitDialog
56
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
57
from bzrlib.plugins.gtk.initialize import InitDialog
58
from bzrlib.plugins.gtk.push import PushDialog
59
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
62
""" Display the AboutDialog. """
63
from bzrlib.plugins.gtk import __version__
64
from bzrlib.plugins.gtk.olive.guifiles import GLADEFILENAME
66
# Load AboutDialog description
67
dglade = gtk.glade.XML(GLADEFILENAME, 'aboutdialog')
68
dialog = dglade.get_widget('aboutdialog')
71
dialog.set_version(__version__)
72
dialog.set_authors([ _i18n("Lead Developer:"),
73
"Szilveszter Farkas <szilveszter.farkas@gmail.com>",
74
_i18n("Contributors:"),
75
"Jelmer Vernooij <jelmer@samba.org>",
76
"Mateusz Korniak <mateusz.korniak@ant.gliwice.pl>",
77
"Gary van der Merwe <garyvdm@gmail.com>" ])
78
dialog.set_artists([ "Simon Pascal Klein <klepas@klepas.org>",
79
"Jakub Steiner <jimmac@novell.com>" ])
86
""" The main Olive GTK frontend class. This is called when launching the
90
self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
91
self.window = self.toplevel.get_widget('window_main')
92
self.pref = Preferences()
95
# Initialize the statusbar
96
self.statusbar = self.toplevel.get_widget('statusbar')
97
self.context_id = self.statusbar.get_context_id('olive')
100
self.window_main = self.toplevel.get_widget('window_main')
102
self.hpaned_main = self.toplevel.get_widget('hpaned_main')
104
self.treeview_left = self.toplevel.get_widget('treeview_left')
105
self.treeview_right = self.toplevel.get_widget('treeview_right')
106
# Get some important menu items
107
self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
108
self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
109
self.menuitem_file_bookmark = self.toplevel.get_widget('menuitem_file_bookmark')
110
self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
111
self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
112
self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
113
self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
114
self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
115
self.menuitem_view_show_ignored_files = self.toplevel.get_widget('menuitem_view_show_ignored_files')
116
self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
117
self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
118
self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
119
self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
120
self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
121
self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
122
self.menuitem_branch_update = self.toplevel.get_widget('menuitem_branch_update')
123
self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
124
self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
125
self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
126
self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
127
self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
128
self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
129
self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
130
self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
131
self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
132
self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
133
# Get some toolbuttons
134
#self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
135
self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
136
self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
137
self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
138
self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
139
self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
140
self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
141
# Get the drive selector
142
self.combobox_drive = gtk.combo_box_new_text()
143
self.combobox_drive.connect("changed", self._refresh_drives)
145
# Get the navigation widgets
146
self.hbox_location = self.toplevel.get_widget('hbox_location')
147
self.button_location_up = self.toplevel.get_widget('button_location_up')
148
self.button_location_jump = self.toplevel.get_widget('button_location_jump')
149
self.entry_location = self.toplevel.get_widget('entry_location')
150
self.image_location_error = self.toplevel.get_widget('image_location_error')
152
# Get the History widgets
153
self.check_history = self.toplevel.get_widget('checkbutton_history')
154
self.entry_history = self.toplevel.get_widget('entry_history_revno')
155
self.button_history = self.toplevel.get_widget('button_history_browse')
157
self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
159
# Dictionary for signal_autoconnect
160
dic = { "on_window_main_destroy": gtk.main_quit,
161
"on_window_main_delete_event": self.on_window_main_delete_event,
162
"on_quit_activate": self.on_window_main_delete_event,
163
"on_about_activate": self.on_about_activate,
164
"on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
165
"on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
166
"on_menuitem_file_bookmark_activate": self.on_menuitem_file_bookmark_activate,
167
"on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
168
"on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
169
"on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
170
"on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
171
"on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
172
"on_menuitem_view_show_ignored_files_activate": self.on_menuitem_view_show_ignored_files_activate,
173
"on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
174
"on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
175
"on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
176
"on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
177
"on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
178
"on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
179
"on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
180
"on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
181
"on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
182
"on_menuitem_branch_update_activate": self.on_menuitem_branch_update_activate,
183
"on_menuitem_branch_tags_activate": self.on_menuitem_branch_tags_activate,
184
"on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
185
"on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
186
"on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
187
"on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
188
"on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
189
"on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
190
"on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
191
"on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
192
#"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
193
"on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
194
"on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
195
"on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
196
"on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
197
"on_toolbutton_update_clicked": self.on_menuitem_branch_update_activate,
198
"on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
199
"on_treeview_right_row_activated": self.on_treeview_right_row_activated,
200
"on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
201
"on_treeview_left_button_release_event": self.on_treeview_left_button_release_event,
202
"on_treeview_left_row_activated": self.on_treeview_left_row_activated,
203
"on_button_location_up_clicked": self.on_button_location_up_clicked,
204
"on_button_location_jump_clicked": self.on_button_location_jump_clicked,
205
"on_entry_location_key_press_event": self.on_entry_location_key_press_event,
206
"on_checkbutton_history_toggled": self.on_checkbutton_history_toggled,
207
"on_entry_history_revno_key_press_event": self.on_entry_history_revno_key_press_event,
208
"on_button_history_browse_clicked": self.on_button_history_browse_clicked
211
# Connect the signals to the handlers
212
self.toplevel.signal_autoconnect(dic)
214
self._just_started = True
216
# Apply window size and position
217
width = self.pref.get_preference('window_width', 'int')
218
height = self.pref.get_preference('window_height', 'int')
219
self.window.resize(width, height)
220
x = self.pref.get_preference('window_x', 'int')
221
y = self.pref.get_preference('window_y', 'int')
222
self.window.move(x, y)
223
# Apply paned position
224
pos = self.pref.get_preference('paned_position', 'int')
225
self.hpaned_main.set_position(pos)
227
# Apply menu to the toolbutton
228
#menubutton = self.toplevel.get_widget('menutoolbutton_diff')
229
#menubutton.set_menu(handler.menu.toolbar_diff)
231
# Now we can show the window
234
# Show drive selector if under Win32
235
if sys.platform == 'win32':
236
self.hbox_location.pack_start(self.combobox_drive, False, False, 0)
237
self.hbox_location.reorder_child(self.combobox_drive, 1)
238
self.combobox_drive.show()
239
self.gen_hard_selector()
244
self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
245
self.menuitem_view_show_ignored_files.set_active(self.pref.get_preference('ignored_files', 'bool'))
247
# We're starting local
249
self.remote_branch = None
250
self.remote_path = None
251
self.remote_revision = None
253
self.set_path(os.getcwd())
256
self._just_started = False
258
def set_path(self, path, force_remote=False):
259
self.notbranch = False
262
# Forcing remote mode (reading data from inventory)
263
self._show_stock_image(gtk.STOCK_DISCONNECT)
265
br = Branch.open_containing(path)[0]
266
except bzrerrors.NotBranchError:
267
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
268
self.check_history.set_active(False)
269
self.check_history.set_sensitive(False)
271
except bzrerrors.UnsupportedProtocol:
272
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
273
self.check_history.set_active(False)
274
self.check_history.set_sensitive(False)
277
self._show_stock_image(gtk.STOCK_CONNECT)
282
self.remote_branch, self.remote_path = Branch.open_containing(path)
284
if self.remote_revision is None:
285
self.remote_revision = self.remote_branch.last_revision()
287
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
289
if len(self.remote_path) == 0:
290
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
292
for (name, type) in self.remote_entries:
293
if name == self.remote_path:
294
self.remote_parent = type.file_id
297
if not path.endswith('/'):
300
if self.remote_branch.base == path:
301
self.button_location_up.set_sensitive(False)
303
self.button_location_up.set_sensitive(True)
305
if os.path.isdir(path):
306
self.image_location_error.destroy()
311
self.wt, self.wtpath = WorkingTree.open_containing(path)
312
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
313
self.notbranch = True
315
# If we're in the root, we cannot go up anymore
316
if sys.platform == 'win32':
317
drive, tail = os.path.splitdrive(path)
318
if tail in ('', '/', '\\'):
319
self.button_location_up.set_sensitive(False)
321
self.button_location_up.set_sensitive(True)
324
self.button_location_up.set_sensitive(False)
326
self.button_location_up.set_sensitive(True)
327
elif not os.path.isfile(path):
328
# Doesn't seem to be a file nor a directory, trying to open a
330
self._show_stock_image(gtk.STOCK_DISCONNECT)
332
br = Branch.open_containing(path)[0]
333
except bzrerrors.NotBranchError:
334
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
335
self.check_history.set_active(False)
336
self.check_history.set_sensitive(False)
338
except bzrerrors.UnsupportedProtocol:
339
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
340
self.check_history.set_active(False)
341
self.check_history.set_sensitive(False)
344
self._show_stock_image(gtk.STOCK_CONNECT)
349
self.remote_branch, self.remote_path = Branch.open_containing(path)
351
if self.remote_revision is None:
352
self.remote_revision = self.remote_branch.last_revision()
354
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
356
if len(self.remote_path) == 0:
357
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
359
for (name, type) in self.remote_entries:
360
if name == self.remote_path:
361
self.remote_parent = type.file_id
364
if not path.endswith('/'):
367
if self.remote_branch.base == path:
368
self.button_location_up.set_sensitive(False)
370
self.button_location_up.set_sensitive(True)
373
self.check_history.set_active(False)
374
self.check_history.set_sensitive(False)
376
self.check_history.set_sensitive(True)
378
self.statusbar.push(self.context_id, path)
379
self.entry_location.set_text(path)
388
if len(self.remote_path) > 0:
389
return self.remote_branch.base + self.remote_path + '/'
391
return self.remote_branch.base
393
def on_about_activate(self, widget):
396
def on_button_history_browse_clicked(self, widget):
397
""" Browse for revision button handler. """
399
br = self.remote_branch
403
revb = RevisionBrowser(br, self.window)
404
response = revb.run()
405
if response != gtk.RESPONSE_NONE:
408
if response == gtk.RESPONSE_OK:
409
if revb.selected_revno is not None:
410
self.entry_history.set_text(revb.selected_revno)
414
def on_button_location_jump_clicked(self, widget):
415
""" Location Jump button handler. """
416
location = self.entry_location.get_text()
418
if self.set_path(location):
421
def on_button_location_up_clicked(self, widget):
422
""" Location Up button handler. """
425
self.set_path(os.path.split(self.get_path())[0])
429
newpath = delim.join(self.get_path().split(delim)[:-2])
431
self.set_path(newpath)
435
def on_checkbutton_history_toggled(self, widget):
436
""" History Mode toggle handler. """
437
if self.check_history.get_active():
438
# History Mode activated
439
self.entry_history.set_sensitive(True)
440
self.button_history.set_sensitive(True)
442
# History Mode deactivated
443
self.entry_history.set_sensitive(False)
444
self.button_history.set_sensitive(False)
446
# Return right window to normal view by acting like we jump to it
447
self.on_button_location_jump_clicked(widget)
450
def on_entry_history_revno_key_press_event(self, widget, event):
451
""" Key pressed handler for the history entry. """
452
if event.keyval == gtk.gdk.keyval_from_name('Return') or event.keyval == gtk.gdk.keyval_from_name('KP_Enter'):
453
# Return was hit, so we have to load that specific revision
454
# Emulate being remote, so inventory should be used
455
path = self.get_path()
458
self.remote_branch = self.wt.branch
460
revno = int(self.entry_history.get_text())
461
self.remote_revision = self.remote_branch.get_rev_id(revno)
462
if self.set_path(path, True):
465
def on_entry_location_key_press_event(self, widget, event):
466
""" Key pressed handler for the location entry. """
467
if event.keyval == gtk.gdk.keyval_from_name('Return') or event.keyval == gtk.gdk.keyval_from_name('KP_Enter'):
468
# Return was hit, so we have to jump
469
self.on_button_location_jump_clicked(widget)
471
def on_menuitem_add_files_activate(self, widget):
472
""" Add file(s)... menu handler. """
473
from add import OliveAdd
474
add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
477
def on_menuitem_branch_get_activate(self, widget):
478
""" Branch/Get... menu handler. """
479
from bzrlib.plugins.gtk.branch import BranchDialog
482
branch = BranchDialog(os.getcwd(), self.window, self.remote_branch.base)
484
branch = BranchDialog(self.get_path(), self.window)
485
response = branch.run()
486
if response != gtk.RESPONSE_NONE:
489
if response == gtk.RESPONSE_OK:
494
def on_menuitem_branch_checkout_activate(self, widget):
495
""" Branch/Checkout... menu handler. """
496
from bzrlib.plugins.gtk.checkout import CheckoutDialog
499
checkout = CheckoutDialog(os.getcwd(), self.window, self.remote_branch.base)
501
checkout = CheckoutDialog(self.get_path(), self.window)
502
response = checkout.run()
503
if response != gtk.RESPONSE_NONE:
506
if response == gtk.RESPONSE_OK:
512
def on_menuitem_branch_commit_activate(self, widget):
513
""" Branch/Commit... menu handler. """
514
# def __init__(self, wt, wtpath, notbranch, selected=None, parent=None):
515
selected = self.get_selected_right()
517
selected = os.path.join(self.wtpath, selected)
518
commit = CommitDialog(wt=self.wt,
522
response = commit.run()
523
if response != gtk.RESPONSE_NONE:
526
if response == gtk.RESPONSE_OK:
531
def on_menuitem_branch_conflicts_activate(self, widget):
532
""" Branch/Conflicts... menu handler. """
533
conflicts = ConflictsDialog(self.wt, self.window)
534
response = conflicts.run()
535
if response != gtk.RESPONSE_NONE:
538
def on_menuitem_branch_merge_activate(self, widget):
539
""" Branch/Merge... menu handler. """
540
from bzrlib.plugins.gtk.merge import MergeDialog
542
if self.check_for_changes():
543
error_dialog(_i18n('There are local changes in the branch'),
544
_i18n('Please commit or revert the changes before merging.'))
546
parent_branch_path = self.wt.branch.get_parent()
547
merge = MergeDialog(self.wt, self.wtpath,default_branch_path=parent_branch_path )
551
def on_menuitem_branch_missing_revisions_activate(self, widget):
552
""" Branch/Missing revisions menu handler. """
554
from bzrlib.missing import find_unmerged, iter_log_revisions
556
local_branch = self.wt.branch
557
parent_branch_path = local_branch.get_parent()
558
if parent_branch_path is None:
559
error_dialog(_i18n('Parent location is unknown'),
560
_i18n('Cannot determine missing revisions if no parent location is known.'))
563
parent_branch = Branch.open(parent_branch_path)
565
if parent_branch.base == local_branch.base:
566
parent_branch = local_branch
568
local_extra, remote_extra = find_unmerged(local_branch,parent_branch)
570
if local_extra or remote_extra:
572
## def log_revision_one_line_text(log_revision):
573
## """ Generates one line description of log_revison ended with end of line."""
574
## revision = log_revision.rev
575
## txt = "- %s (%s)\n" % (revision.get_summary(), revision.committer, )
576
## txt = txt.replace("<"," ") # Seems < > chars are expected to be xml tags ...
577
## txt = txt.replace(">"," ")
582
dlg_txt += _i18n('%d local extra revision(s). \n') % (len(local_extra),)
583
## NOTE: We do not want such ugly info about missing revisions
584
## Revision Browser should be used there
585
## max_revisions = 10
586
## for log_revision in iter_log_revisions(local_extra, local_branch.repository, verbose=1):
587
## dlg_txt += log_revision_one_line_text(log_revision)
588
## if max_revisions <= 0:
589
## dlg_txt += _i18n("more ... \n")
591
## max_revisions -= 1
594
dlg_txt += _i18n('%d local missing revision(s).\n') % (len(remote_extra),)
595
## max_revisions = 10
596
## for log_revision in iter_log_revisions(remote_extra, parent_branch.repository, verbose=1):
597
## dlg_txt += log_revision_one_line_text(log_revision)
598
## if max_revisions <= 0:
599
## dlg_txt += _i18n("more ... \n")
601
## max_revisions -= 1
603
info_dialog(_i18n('There are missing revisions'),
606
info_dialog(_i18n('Local branch up to date'),
607
_i18n('There are no missing revisions.'))
610
def on_menuitem_branch_pull_activate(self, widget):
611
""" Branch/Pull menu handler. """
612
branch_to = self.wt.branch
614
location = branch_to.get_parent()
616
error_dialog(_i18n('Parent location is unknown'),
617
_i18n('Pulling is not possible until there is a parent location.'))
620
branch_from = Branch.open(location)
622
if branch_to.get_parent() is None:
623
branch_to.set_parent(branch_from.base)
625
ret = branch_to.pull(branch_from)
627
info_dialog(_i18n('Pull successful'), _i18n('%d revision(s) pulled.') % ret)
630
def on_menuitem_branch_update_activate(self, widget):
631
""" Brranch/checkout update menu handler. """
633
ret = self.wt.update()
634
conflicts = self.wt.conflicts()
636
info_dialog(_i18n('Update successful but conflicts generated'), _i18n('Number of conflicts generated: %d.') % (len(conflicts),) )
638
info_dialog(_i18n('Update successful'), _i18n('No conflicts generated.') )
640
def on_menuitem_branch_push_activate(self, widget):
641
""" Branch/Push... menu handler. """
642
push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
643
response = push.run()
644
if response != gtk.RESPONSE_NONE:
648
def on_menuitem_branch_revert_activate(self, widget):
649
""" Branch/Revert all changes menu handler. """
650
ret = self.wt.revert([])
652
warning_dialog(_i18n('Conflicts detected'),
653
_i18n('Please have a look at the working tree before continuing.'))
655
info_dialog(_i18n('Revert successful'),
656
_i18n('All files reverted to last revision.'))
659
def on_menuitem_branch_status_activate(self, widget):
660
""" Branch/Status... menu handler. """
661
from bzrlib.plugins.gtk.status import StatusDialog
662
status = StatusDialog(self.wt, self.wtpath)
663
response = status.run()
664
if response != gtk.RESPONSE_NONE:
667
def on_menuitem_branch_initialize_activate(self, widget):
668
""" Initialize current directory. """
669
init = InitDialog(self.path, self.window)
670
response = init.run()
671
if response != gtk.RESPONSE_NONE:
674
if response == gtk.RESPONSE_OK:
679
def on_menuitem_branch_tags_activate(self, widget):
680
""" Branch/Tags... menu handler. """
681
from bzrlib.plugins.gtk.tags import TagsWindow
683
window = TagsWindow(self.wt.branch, self.window)
685
window = TagsWindow(self.remote_branch, self.window)
688
def on_menuitem_file_annotate_activate(self, widget):
689
""" File/Annotate... menu handler. """
690
if self.get_selected_right() is None:
691
error_dialog(_i18n('No file was selected'),
692
_i18n('Please select a file from the list.'))
695
branch = self.wt.branch
696
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
698
window = GAnnotateWindow(all=False, plain=False, parent=self.window)
699
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
700
config = GAnnotateConfig(window)
704
window.annotate(self.wt, branch, file_id)
708
def on_menuitem_file_bookmark_activate(self, widget):
709
""" File/Bookmark current directory menu handler. """
710
if self.pref.add_bookmark(self.path):
711
info_dialog(_i18n('Bookmark successfully added'),
712
_i18n('The current directory was bookmarked. You can reach\nit by selecting it from the left panel.'))
715
warning_dialog(_i18n('Location already bookmarked'),
716
_i18n('The current directory is already bookmarked.\nSee the left panel for reference.'))
720
def on_menuitem_file_make_directory_activate(self, widget):
721
""" File/Make directory... menu handler. """
722
from mkdir import OliveMkdir
723
mkdir = OliveMkdir(self.wt, self.wtpath)
726
def on_menuitem_file_move_activate(self, widget):
727
""" File/Move... menu handler. """
728
from move import OliveMove
729
move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
732
def on_menuitem_file_rename_activate(self, widget):
733
""" File/Rename... menu handler. """
734
from rename import OliveRename
735
rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
738
def on_menuitem_remove_file_activate(self, widget):
739
""" Remove (unversion) selected file. """
740
from remove import OliveRemoveDialog
741
remove = OliveRemoveDialog(self.wt, self.wtpath,
742
selected=self.get_selected_right(),
744
response = remove.run()
746
if response != gtk.RESPONSE_NONE:
749
if response == gtk.RESPONSE_OK:
750
self.set_path(self.path)
755
def on_menuitem_stats_diff_activate(self, widget):
756
""" Statistics/Differences... menu handler. """
757
window = DiffWindow(parent=self.window)
758
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
759
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
762
def on_menuitem_stats_infos_activate(self, widget):
763
""" Statistics/Informations... menu handler. """
764
from info import OliveInfo
766
info = OliveInfo(self.remote_branch)
768
info = OliveInfo(self.wt.branch)
771
def on_menuitem_stats_log_activate(self, widget):
772
""" Statistics/Log... menu handler. """
775
branch = self.wt.branch
777
branch = self.remote_branch
779
window = branchwin.BranchWindow(branch, [branch.last_revision()], None,
783
def on_menuitem_view_refresh_activate(self, widget):
784
""" View/Refresh menu handler. """
785
# Refresh the left pane
787
# Refresh the right pane
790
def on_menuitem_view_show_hidden_files_activate(self, widget):
791
""" View/Show hidden files menu handler. """
792
self.pref.set_preference('dotted_files', widget.get_active())
793
if self.path is not None:
796
def on_menuitem_view_show_ignored_files_activate(self, widget):
797
""" Hide/Show ignored files menu handler. """
798
self.pref.set_preference('ignored_files', widget.get_active())
799
if self.path is not None:
802
def on_treeview_left_button_press_event(self, widget, event):
803
""" Occurs when somebody right-clicks in the bookmark list. """
804
if event.button == 3:
805
# Don't show context with nothing selected
806
if self.get_selected_left() == None:
810
from menu import OliveMenu
811
menu = OliveMenu(path=self.get_path(),
812
selected=self.get_selected_left(),
815
menu.left_context_menu().popup(None, None, None, 0,
818
def on_treeview_left_button_release_event(self, widget, event):
819
""" Occurs when somebody just clicks a bookmark. """
820
if event.button != 3:
821
# Allow one-click bookmark opening
822
if self.get_selected_left() == None:
825
newdir = self.get_selected_left()
829
if self.set_path(newdir):
832
def on_treeview_left_row_activated(self, treeview, path, view_column):
833
""" Occurs when somebody double-clicks or enters an item in the
836
newdir = self.get_selected_left()
840
if self.set_path(newdir):
843
def on_treeview_right_button_press_event(self, widget, event):
844
""" Occurs when somebody right-clicks in the file list. """
845
if event.button == 3:
847
from menu import OliveMenu
848
menu = OliveMenu(path=self.get_path(),
849
selected=self.get_selected_right(),
852
m_open = menu.ui.get_widget('/context_right/open')
853
m_add = menu.ui.get_widget('/context_right/add')
854
m_remove = menu.ui.get_widget('/context_right/remove')
855
m_rename = menu.ui.get_widget('/context_right/rename')
856
m_revert = menu.ui.get_widget('/context_right/revert')
857
m_commit = menu.ui.get_widget('/context_right/commit')
858
m_annotate = menu.ui.get_widget('/context_right/annotate')
859
m_diff = menu.ui.get_widget('/context_right/diff')
860
# check if we're in a branch
862
from bzrlib.branch import Branch
863
Branch.open_containing(self.get_path())
865
m_open.set_sensitive(False)
866
m_add.set_sensitive(False)
867
m_remove.set_sensitive(False)
868
m_rename.set_sensitive(False)
869
m_revert.set_sensitive(False)
870
m_commit.set_sensitive(False)
871
m_annotate.set_sensitive(False)
872
m_diff.set_sensitive(False)
874
m_open.set_sensitive(True)
875
m_add.set_sensitive(True)
876
m_remove.set_sensitive(True)
877
m_rename.set_sensitive(True)
878
m_revert.set_sensitive(True)
879
m_commit.set_sensitive(True)
880
m_annotate.set_sensitive(True)
881
m_diff.set_sensitive(True)
882
except bzrerrors.NotBranchError:
883
m_open.set_sensitive(True)
884
m_add.set_sensitive(False)
885
m_remove.set_sensitive(False)
886
m_rename.set_sensitive(False)
887
m_revert.set_sensitive(False)
888
m_commit.set_sensitive(False)
889
m_annotate.set_sensitive(False)
890
m_diff.set_sensitive(False)
893
menu.right_context_menu().popup(None, None, None, 0,
896
menu.remote_context_menu().popup(None, None, None, 0,
899
def on_treeview_right_row_activated(self, treeview, path, view_column):
900
""" Occurs when somebody double-clicks or enters an item in the
902
from launch import launch
904
newdir = self.get_selected_right()
909
self.set_path(os.path.split(self.get_path())[0])
911
fullpath = os.path.join(self.get_path(), newdir)
912
if os.path.isdir(fullpath):
913
# selected item is an existant directory
914
self.set_path(fullpath)
919
if self._is_remote_dir(self.get_path() + newdir):
920
self.set_path(self.get_path() + newdir)
924
def on_window_main_delete_event(self, widget, event=None):
925
""" Do some stuff before exiting. """
926
width, height = self.window_main.get_size()
927
self.pref.set_preference('window_width', width)
928
self.pref.set_preference('window_height', height)
929
x, y = self.window_main.get_position()
930
self.pref.set_preference('window_x', x)
931
self.pref.set_preference('window_y', y)
932
self.pref.set_preference('paned_position',
933
self.hpaned_main.get_position())
936
self.window_main.destroy()
938
def _load_left(self):
939
""" Load data into the left panel. (Bookmarks) """
941
treestore = gtk.TreeStore(str, str)
944
bookmarks = self.pref.get_bookmarks()
946
# Add them to the TreeStore
947
titer = treestore.append(None, [_i18n('Bookmarks'), None])
948
for item in bookmarks:
949
title = self.pref.get_bookmark_title(item)
950
treestore.append(titer, [title, item])
952
# Create the column and add it to the TreeView
953
self.treeview_left.set_model(treestore)
954
tvcolumn_bookmark = gtk.TreeViewColumn(_i18n('Bookmark'))
955
self.treeview_left.append_column(tvcolumn_bookmark)
958
cell = gtk.CellRendererText()
959
tvcolumn_bookmark.pack_start(cell, True)
960
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
963
self.treeview_left.expand_all()
965
def _load_right(self):
966
""" Load data into the right panel. (Filelist) """
968
# Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
969
liststore = gtk.ListStore(gobject.TYPE_STRING,
970
gobject.TYPE_BOOLEAN,
983
# Fill the appropriate lists
984
dotted_files = self.pref.get_preference('dotted_files', 'bool')
985
for item in os.listdir(self.path):
986
if not dotted_files and item[0] == '.':
988
if os.path.isdir(self.path + os.sep + item):
993
if not self.notbranch:
994
branch = self.wt.branch
995
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
997
delta = self.wt.changes_from(tree2, want_unchanged=True)
999
# Add'em to the ListStore
1002
statinfo = os.stat(self.path + os.sep + item)
1008
liststore.append([ gtk.STOCK_DIRECTORY,
1016
self._format_date(statinfo.st_mtime),
1021
if not self.notbranch:
1022
filename = self.wt.relpath(self.path + os.sep + item)
1027
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1028
if rpathnew == filename:
1031
for rpath, id, kind in delta.added:
1032
if rpath == filename:
1035
for rpath, id, kind in delta.removed:
1036
if rpath == filename:
1039
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1040
if rpath == filename:
1043
for rpath, id, kind in delta.unchanged:
1044
if rpath == filename:
1045
status = 'unchanged'
1047
for rpath, file_class, kind, id, entry in self.wt.list_files():
1048
if rpath == filename and file_class == 'I':
1053
if status == 'renamed':
1054
st = _i18n('renamed')
1055
elif status == 'removed':
1056
st = _i18n('removed')
1057
elif status == 'added':
1059
elif status == 'modified':
1060
st = _i18n('modified')
1061
elif status == 'unchanged':
1062
st = _i18n('unchanged')
1063
elif status == 'ignored':
1064
st = _i18n('ignored')
1066
st = _i18n('unknown')
1069
statinfo = os.stat(self.path + os.sep + item)
1075
liststore.append([gtk.STOCK_FILE,
1080
str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
1081
self._format_size(statinfo.st_size),
1083
self._format_date(statinfo.st_mtime),
1086
# Create the columns and add them to the TreeView
1087
self.treeview_right.set_model(liststore)
1088
self._tvcolumn_filename = gtk.TreeViewColumn(_i18n('Filename'))
1089
self._tvcolumn_status = gtk.TreeViewColumn(_i18n('Status'))
1090
self._tvcolumn_size = gtk.TreeViewColumn(_i18n('Size'))
1091
self._tvcolumn_mtime = gtk.TreeViewColumn(_i18n('Last modified'))
1092
self.treeview_right.append_column(self._tvcolumn_filename)
1093
self.treeview_right.append_column(self._tvcolumn_status)
1094
self.treeview_right.append_column(self._tvcolumn_size)
1095
self.treeview_right.append_column(self._tvcolumn_mtime)
1098
cellpb = gtk.CellRendererPixbuf()
1099
cell = gtk.CellRendererText()
1100
self._tvcolumn_filename.pack_start(cellpb, False)
1101
self._tvcolumn_filename.pack_start(cell, True)
1102
self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
1103
self._tvcolumn_filename.add_attribute(cell, 'text', 2)
1104
self._tvcolumn_status.pack_start(cell, True)
1105
self._tvcolumn_status.add_attribute(cell, 'text', 3)
1106
self._tvcolumn_size.pack_start(cell, True)
1107
self._tvcolumn_size.add_attribute(cell, 'text', 6)
1108
self._tvcolumn_mtime.pack_start(cell, True)
1109
self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
1111
# Set up the properties of the TreeView
1112
self.treeview_right.set_headers_visible(True)
1113
self.treeview_right.set_headers_clickable(True)
1114
self.treeview_right.set_search_column(1)
1115
self._tvcolumn_filename.set_resizable(True)
1116
self._tvcolumn_status.set_resizable(True)
1117
self._tvcolumn_size.set_resizable(True)
1118
self._tvcolumn_mtime.set_resizable(True)
1120
liststore.set_sort_func(13, self._sort_filelist_callback, None)
1121
liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
1122
self._tvcolumn_filename.set_sort_column_id(13)
1123
self._tvcolumn_status.set_sort_column_id(3)
1124
self._tvcolumn_size.set_sort_column_id(5)
1125
self._tvcolumn_mtime.set_sort_column_id(7)
1128
self.set_sensitivity()
1130
def get_selected_fileid(self):
1131
""" Get the file_id of the selected file. """
1132
treeselection = self.treeview_right.get_selection()
1133
(model, iter) = treeselection.get_selected()
1138
return model.get_value(iter, 9)
1140
def get_selected_right(self):
1141
""" Get the selected filename. """
1142
treeselection = self.treeview_right.get_selection()
1143
(model, iter) = treeselection.get_selected()
1148
return model.get_value(iter, 2)
1150
def get_selected_left(self):
1151
""" Get the selected bookmark. """
1152
treeselection = self.treeview_left.get_selection()
1153
(model, iter) = treeselection.get_selected()
1158
return model.get_value(iter, 1)
1160
def set_statusbar(self, message):
1161
""" Set the statusbar message. """
1162
self.statusbar.push(self.context_id, message)
1164
def clear_statusbar(self):
1165
""" Clean the last message from the statusbar. """
1166
self.statusbar.pop(self.context_id)
1168
def set_sensitivity(self):
1169
""" Set menu and toolbar sensitivity. """
1172
self.menuitem_branch_init.set_sensitive(self.notbranch)
1173
self.menuitem_branch_get.set_sensitive(self.notbranch)
1174
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
1175
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
1176
self.menuitem_branch_push.set_sensitive(not self.notbranch)
1177
self.menuitem_branch_update.set_sensitive(not self.notbranch)
1178
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
1179
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
1180
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
1181
self.menuitem_branch_tags.set_sensitive(not self.notbranch)
1182
self.menuitem_branch_status.set_sensitive(not self.notbranch)
1183
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
1184
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
1185
self.menuitem_stats.set_sensitive(not self.notbranch)
1186
self.menuitem_stats_diff.set_sensitive(not self.notbranch)
1187
self.menuitem_add_files.set_sensitive(not self.notbranch)
1188
self.menuitem_remove_files.set_sensitive(not self.notbranch)
1189
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
1190
self.menuitem_file_rename.set_sensitive(not self.notbranch)
1191
self.menuitem_file_move.set_sensitive(not self.notbranch)
1192
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
1193
#self.menutoolbutton_diff.set_sensitive(True)
1194
self.toolbutton_diff.set_sensitive(not self.notbranch)
1195
self.toolbutton_log.set_sensitive(not self.notbranch)
1196
self.toolbutton_commit.set_sensitive(not self.notbranch)
1197
self.toolbutton_pull.set_sensitive(not self.notbranch)
1198
self.toolbutton_push.set_sensitive(not self.notbranch)
1199
self.toolbutton_update.set_sensitive(not self.notbranch)
1202
self.menuitem_branch_init.set_sensitive(False)
1203
self.menuitem_branch_get.set_sensitive(True)
1204
self.menuitem_branch_checkout.set_sensitive(True)
1205
self.menuitem_branch_pull.set_sensitive(False)
1206
self.menuitem_branch_push.set_sensitive(False)
1207
self.menuitem_branch_update.set_sensitive(False)
1208
self.menuitem_branch_revert.set_sensitive(False)
1209
self.menuitem_branch_merge.set_sensitive(False)
1210
self.menuitem_branch_commit.set_sensitive(False)
1211
self.menuitem_branch_tags.set_sensitive(True)
1212
self.menuitem_branch_status.set_sensitive(False)
1213
self.menuitem_branch_missing.set_sensitive(False)
1214
self.menuitem_branch_conflicts.set_sensitive(False)
1215
self.menuitem_stats.set_sensitive(True)
1216
self.menuitem_stats_diff.set_sensitive(False)
1217
self.menuitem_add_files.set_sensitive(False)
1218
self.menuitem_remove_files.set_sensitive(False)
1219
self.menuitem_file_make_directory.set_sensitive(False)
1220
self.menuitem_file_rename.set_sensitive(False)
1221
self.menuitem_file_move.set_sensitive(False)
1222
self.menuitem_file_annotate.set_sensitive(False)
1223
#self.menutoolbutton_diff.set_sensitive(True)
1224
self.toolbutton_diff.set_sensitive(False)
1225
self.toolbutton_log.set_sensitive(True)
1226
self.toolbutton_commit.set_sensitive(False)
1227
self.toolbutton_pull.set_sensitive(False)
1228
self.toolbutton_push.set_sensitive(False)
1229
self.toolbutton_update.set_sensitive(False)
1231
def refresh_left(self):
1232
""" Refresh the bookmark list. """
1234
# Get TreeStore and clear it
1235
treestore = self.treeview_left.get_model()
1238
# Re-read preferences
1242
bookmarks = self.pref.get_bookmarks()
1244
# Add them to the TreeStore
1245
titer = treestore.append(None, [_i18n('Bookmarks'), None])
1246
for item in bookmarks:
1247
title = self.pref.get_bookmark_title(item)
1248
treestore.append(titer, [title, item])
1250
# Add the TreeStore to the TreeView
1251
self.treeview_left.set_model(treestore)
1254
self.treeview_left.expand_all()
1256
def refresh_right(self, path=None):
1257
""" Refresh the file list. """
1260
from bzrlib.workingtree import WorkingTree
1263
path = self.get_path()
1265
# A workaround for double-clicking Bookmarks
1266
if not os.path.exists(path):
1269
# Get ListStore and clear it
1270
liststore = self.treeview_right.get_model()
1273
# Show Status column
1274
self._tvcolumn_status.set_visible(True)
1279
# Fill the appropriate lists
1280
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1281
ignored_files = self.pref.get_preference('ignored_files', 'bool')
1283
for item in os.listdir(path):
1284
if not dotted_files and item[0] == '.':
1286
if os.path.isdir(path + os.sep + item):
1291
# Try to open the working tree
1294
tree1 = WorkingTree.open_containing(path)[0]
1295
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
1299
branch = tree1.branch
1300
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
1302
delta = tree1.changes_from(tree2, want_unchanged=True)
1304
# Add'em to the ListStore
1307
statinfo = os.stat(self.path + os.sep + item)
1313
liststore.append([gtk.STOCK_DIRECTORY,
1321
self._format_date(statinfo.st_mtime),
1327
filename = tree1.relpath(path + os.sep + item)
1332
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1333
if rpathnew == filename:
1336
for rpath, id, kind in delta.added:
1337
if rpath == filename:
1340
for rpath, id, kind in delta.removed:
1341
if rpath == filename:
1344
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1345
if rpath == filename:
1348
for rpath, id, kind in delta.unchanged:
1349
if rpath == filename:
1350
status = 'unchanged'
1352
for rpath, file_class, kind, id, entry in self.wt.list_files():
1353
if rpath == filename and file_class == 'I':
1358
if status == 'renamed':
1359
st = _i18n('renamed')
1360
elif status == 'removed':
1361
st = _i18n('removed')
1362
elif status == 'added':
1364
elif status == 'modified':
1365
st = _i18n('modified')
1366
elif status == 'unchanged':
1367
st = _i18n('unchanged')
1368
elif status == 'ignored':
1369
st = _i18n('ignored')
1370
if not ignored_files:
1373
st = _i18n('unknown')
1376
statinfo = os.stat(self.path + os.sep + item)
1382
liststore.append([gtk.STOCK_FILE,
1387
str(statinfo.st_size),
1388
self._format_size(statinfo.st_size),
1390
self._format_date(statinfo.st_mtime),
1395
# Get ListStore and clear it
1396
liststore = self.treeview_right.get_model()
1399
# Hide Status column
1400
self._tvcolumn_status.set_visible(False)
1405
self._show_stock_image(gtk.STOCK_REFRESH)
1407
for (name, type) in self.remote_entries:
1408
if type.kind == 'directory':
1410
elif type.kind == 'file':
1414
""" Cache based on revision history. """
1415
def __init__(self, history):
1416
self._history = history
1418
def _lookup_revision(self, revid):
1419
for r in self._history:
1420
if r.revision_id == revid:
1422
rev = repo.get_revision(revid)
1423
self._history.append(rev)
1426
repo = self.remote_branch.repository
1428
revhistory = self.remote_branch.revision_history()
1430
revs = repo.get_revisions(revhistory)
1431
cache = HistoryCache(revs)
1432
except bzrerrors.InvalidHttpResponse:
1433
# Fallback to dummy algorithm, because of LP: #115209
1434
cache = HistoryCache([])
1437
if item.parent_id == self.remote_parent:
1438
rev = cache._lookup_revision(item.revision)
1439
liststore.append([ gtk.STOCK_DIRECTORY,
1447
self._format_date(rev.timestamp),
1450
while gtk.events_pending():
1451
gtk.main_iteration()
1454
if item.parent_id == self.remote_parent:
1455
rev = cache._lookup_revision(item.revision)
1456
liststore.append([ gtk.STOCK_FILE,
1461
str(item.text_size),
1462
self._format_size(item.text_size),
1464
self._format_date(rev.timestamp),
1467
while gtk.events_pending():
1468
gtk.main_iteration()
1470
self.image_location_error.destroy()
1472
# Columns should auto-size
1473
self.treeview_right.columns_autosize()
1476
self.set_sensitivity()
1478
def _harddisks(self):
1479
""" Returns hard drive letters under Win32. """
1484
if sys.platform == 'win32':
1485
print "pyWin32 modules needed to run Olive on Win32."
1491
for drive in string.ascii_uppercase:
1492
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED or\
1493
win32file.GetDriveType(drive+':') == win32file.DRIVE_REMOTE:
1494
driveletters.append(drive+':')
1497
def gen_hard_selector(self):
1498
""" Generate the hard drive selector under Win32. """
1499
drives = self._harddisks()
1500
for drive in drives:
1501
self.combobox_drive.append_text(drive)
1502
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1504
def _refresh_drives(self, combobox):
1505
if self._just_started:
1507
model = combobox.get_model()
1508
active = combobox.get_active()
1510
drive = model[active][0]
1511
self.set_path(drive + '\\')
1512
self.refresh_right(drive + '\\')
1514
def check_for_changes(self):
1515
""" Check whether there were changes in the current working tree. """
1516
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
1517
delta = self.wt.changes_from(old_tree)
1521
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
1526
def _sort_filelist_callback(self, model, iter1, iter2, data):
1527
""" The sort callback for the file list, return values:
1532
name1 = model.get_value(iter1, 2)
1533
name2 = model.get_value(iter2, 2)
1535
if model.get_value(iter1, 1):
1536
# item1 is a directory
1537
if not model.get_value(iter2, 1):
1541
# both of them are directories, we compare their names
1544
elif name1 == name2:
1549
# item1 is not a directory
1550
if model.get_value(iter2, 1):
1554
# both of them are files, compare them
1557
elif name1 == name2:
1562
def _format_size(self, size):
1563
""" Format size to a human readable format. """
1565
return "%d[B]" % (size,)
1566
size = size / 1000.0
1568
for metric in ["kB","MB","GB","TB"]:
1571
size = size / 1000.0
1572
return "%.1f[%s]" % (size,metric)
1574
def _format_date(self, timestamp):
1575
""" Format the time (given in secs) to a human readable format. """
1576
return time.ctime(timestamp)
1578
def _is_remote_dir(self, location):
1579
""" Determine whether the given location is a directory or not. """
1581
# We're in local mode
1584
branch, path = Branch.open_containing(location)
1585
for (name, type) in self.remote_entries:
1586
if name == path and type.kind == 'directory':
1589
# Either it's not a directory or not in the inventory
1592
def _show_stock_image(self, stock_id):
1593
""" Show a stock image next to the location entry. """
1594
self.image_location_error.destroy()
1595
self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
1596
self.hbox_location.pack_start(self.image_location_error, False, False, 0)
1597
if sys.platform == 'win32':
1598
self.hbox_location.reorder_child(self.image_location_error, 2)
1600
self.hbox_location.reorder_child(self.image_location_error, 1)
1601
self.image_location_error.show()
1602
while gtk.events_pending():
1603
gtk.main_iteration()
1608
""" A class which handles Olive's preferences. """
1609
def __init__(self, path=None):
1610
""" Initialize the Preferences class. """
1611
# Some default options
1612
self.defaults = { 'strict_commit' : False,
1613
'dotted_files' : False,
1614
'ignored_files' : True,
1615
'window_width' : 700,
1616
'window_height' : 400,
1619
'paned_position': 200 }
1621
# Create a config parser object
1622
self.config = ConfigParser.RawConfigParser()
1626
if sys.platform == 'win32':
1627
# Windows - no dotted files
1628
self._filename = os.path.expanduser('~/olive.conf')
1630
self._filename = os.path.expanduser('~/.olive.conf')
1632
self._filename = path
1634
# Load the configuration
1637
def _get_default(self, option):
1638
""" Get the default option for a preference. """
1640
ret = self.defaults[option]
1647
""" Refresh the configuration. """
1648
# First write out the changes
1650
# Then load the configuration again
1654
""" Just read the configuration. """
1655
# Re-initialize the config parser object to avoid some bugs
1656
self.config = ConfigParser.RawConfigParser()
1657
self.config.read([self._filename])
1660
""" Write the configuration to the appropriate files. """
1661
fp = open(self._filename, 'w')
1662
self.config.write(fp)
1665
def get_bookmarks(self):
1666
""" Return the list of bookmarks. """
1667
bookmarks = self.config.sections()
1668
if self.config.has_section('preferences'):
1669
bookmarks.remove('preferences')
1672
def add_bookmark(self, path):
1673
""" Add bookmark. """
1675
self.config.add_section(path)
1676
except ConfigParser.DuplicateSectionError:
1681
def get_bookmark_title(self, path):
1682
""" Get bookmark title. """
1684
ret = self.config.get(path, 'title')
1685
except ConfigParser.NoOptionError:
1690
def set_bookmark_title(self, path, title):
1691
""" Set bookmark title. """
1692
# FIXME: What if path isn't listed yet?
1693
# FIXME: Canonicalize paths first?
1694
self.config.set(path, 'title', title)
1696
def remove_bookmark(self, path):
1697
""" Remove bookmark. """
1698
return self.config.remove_section(path)
1700
def set_preference(self, option, value):
1701
""" Set the value of the given option. """
1704
elif value is False:
1707
if self.config.has_section('preferences'):
1708
self.config.set('preferences', option, value)
1710
self.config.add_section('preferences')
1711
self.config.set('preferences', option, value)
1713
def get_preference(self, option, kind='str'):
1714
""" Get the value of the given option.
1716
:param kind: str/bool/int/float. default: str
1718
if self.config.has_option('preferences', option):
1720
return self.config.getboolean('preferences', option)
1722
return self.config.getint('preferences', option)
1723
elif kind == 'float':
1724
return self.config.getfloat('preferences', option)
1726
return self.config.get('preferences', option)
1729
return self._get_default(option)