1
# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
gettext.install('olive-gtk')
36
from bzrlib.branch import Branch
37
import bzrlib.errors as bzrerrors
38
from bzrlib.lazy_import import lazy_import
39
from bzrlib.ui import ui_factory
40
from bzrlib.workingtree import WorkingTree
42
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
43
from bzrlib.plugins.gtk.errors import show_bzr_error
44
from guifiles import GLADEFILENAME
46
from bzrlib.plugins.gtk.diff import DiffWindow
47
lazy_import(globals(), """
48
from bzrlib.plugins.gtk.viz import branchwin
50
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
51
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
52
from bzrlib.plugins.gtk.commit import CommitDialog
53
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
54
from bzrlib.plugins.gtk.initialize import InitDialog
55
from bzrlib.plugins.gtk.push import PushDialog
56
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
59
""" Display the AboutDialog. """
60
from bzrlib.plugins.gtk import __version__
61
from bzrlib.plugins.gtk.olive.guifiles import GLADEFILENAME
63
# Load AboutDialog description
64
dglade = gtk.glade.XML(GLADEFILENAME, 'aboutdialog')
65
dialog = dglade.get_widget('aboutdialog')
68
dialog.set_version(__version__)
69
dialog.set_authors([ _("Lead Developer:"),
70
"Szilveszter Farkas <szilveszter.farkas@gmail.com>",
72
"Jelmer Vernooij <jelmer@samba.org>",
73
"Mateusz Korniak <mateusz.korniak@ant.gliwice.pl>",
74
"Gary van der Merwe <garyvdm@gmail.com>" ])
75
dialog.set_artists([ "Simon Pascal Klein <klepas@klepas.org>",
76
"Jakub Steiner <jimmac@novell.com>" ])
83
""" The main Olive GTK frontend class. This is called when launching the
87
self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
88
self.window = self.toplevel.get_widget('window_main')
89
self.pref = Preferences()
92
# Initialize the statusbar
93
self.statusbar = self.toplevel.get_widget('statusbar')
94
self.context_id = self.statusbar.get_context_id('olive')
97
self.window_main = self.toplevel.get_widget('window_main')
99
self.hpaned_main = self.toplevel.get_widget('hpaned_main')
101
self.treeview_left = self.toplevel.get_widget('treeview_left')
102
self.treeview_right = self.toplevel.get_widget('treeview_right')
103
# Get some important menu items
104
self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
105
self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
106
self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
107
self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
108
self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
109
self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
110
self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
111
self.menuitem_view_show_ignored_files = self.toplevel.get_widget('menuitem_view_show_ignored_files')
112
self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
113
self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
114
self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
115
self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
116
self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
117
self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
118
self.menuitem_branch_update = self.toplevel.get_widget('menuitem_branch_update')
119
self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
120
self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
121
self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
122
self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
123
self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
124
self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
125
self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
126
self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
127
self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
128
self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
129
# Get some toolbuttons
130
#self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
131
self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
132
self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
133
self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
134
self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
135
self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
136
self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
137
# Get the drive selector
138
self.combobox_drive = gtk.combo_box_new_text()
139
self.combobox_drive.connect("changed", self._refresh_drives)
141
# Get the navigation widgets
142
self.hbox_location = self.toplevel.get_widget('hbox_location')
143
self.button_location_up = self.toplevel.get_widget('button_location_up')
144
self.button_location_jump = self.toplevel.get_widget('button_location_jump')
145
self.entry_location = self.toplevel.get_widget('entry_location')
146
self.image_location_error = self.toplevel.get_widget('image_location_error')
148
# Get the History widgets
149
self.check_history = self.toplevel.get_widget('checkbutton_history')
150
self.entry_history = self.toplevel.get_widget('entry_history_revno')
151
self.button_history = self.toplevel.get_widget('button_history_browse')
153
self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
155
# Dictionary for signal_autoconnect
156
dic = { "on_window_main_destroy": gtk.main_quit,
157
"on_window_main_delete_event": self.on_window_main_delete_event,
158
"on_quit_activate": self.on_window_main_delete_event,
159
"on_about_activate": self.on_about_activate,
160
"on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
161
"on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
162
"on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
163
"on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
164
"on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
165
"on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
166
"on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
167
"on_menuitem_view_show_ignored_files_activate": self.on_menuitem_view_show_ignored_files_activate,
168
"on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
169
"on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
170
"on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
171
"on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
172
"on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
173
"on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
174
"on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
175
"on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
176
"on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
177
"on_menuitem_branch_update_activate": self.on_menuitem_branch_update_activate,
178
"on_menuitem_branch_tags_activate": self.on_menuitem_branch_tags_activate,
179
"on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
180
"on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
181
"on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
182
"on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
183
"on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
184
"on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
185
"on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
186
"on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
187
#"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
188
"on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
189
"on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
190
"on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
191
"on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
192
"on_toolbutton_update_clicked": self.on_menuitem_branch_update_activate,
193
"on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
194
"on_treeview_right_row_activated": self.on_treeview_right_row_activated,
195
"on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
196
"on_treeview_left_row_activated": self.on_treeview_left_row_activated,
197
"on_button_location_up_clicked": self.on_button_location_up_clicked,
198
"on_button_location_jump_clicked": self.on_button_location_jump_clicked,
199
"on_entry_location_key_press_event": self.on_entry_location_key_press_event,
200
"on_checkbutton_history_toggled": self.on_checkbutton_history_toggled,
201
"on_entry_history_revno_key_press_event": self.on_entry_history_revno_key_press_event,
202
"on_button_history_browse_clicked": self.on_button_history_browse_clicked
205
# Connect the signals to the handlers
206
self.toplevel.signal_autoconnect(dic)
208
self._just_started = True
210
# Apply window size and position
211
width = self.pref.get_preference('window_width', 'int')
212
height = self.pref.get_preference('window_height', 'int')
213
self.window.resize(width, height)
214
x = self.pref.get_preference('window_x', 'int')
215
y = self.pref.get_preference('window_y', 'int')
216
self.window.move(x, y)
217
# Apply paned position
218
pos = self.pref.get_preference('paned_position', 'int')
219
self.hpaned_main.set_position(pos)
221
# Apply menu to the toolbutton
222
#menubutton = self.toplevel.get_widget('menutoolbutton_diff')
223
#menubutton.set_menu(handler.menu.toolbar_diff)
225
# Now we can show the window
228
# Show drive selector if under Win32
229
if sys.platform == 'win32':
230
self.hbox_location.pack_start(self.combobox_drive, False, False, 0)
231
self.hbox_location.reorder_child(self.combobox_drive, 1)
232
self.combobox_drive.show()
233
self.gen_hard_selector()
238
self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
239
self.menuitem_view_show_ignored_files.set_active(self.pref.get_preference('ignored_files', 'bool'))
241
# We're starting local
243
self.remote_branch = None
244
self.remote_path = None
245
self.remote_revision = None
247
self.set_path(os.getcwd())
250
self._just_started = False
252
def set_path(self, path, force_remote=False):
253
self.notbranch = False
256
# Forcing remote mode (reading data from inventory)
257
self._show_stock_image(gtk.STOCK_DISCONNECT)
259
br = Branch.open_containing(path)[0]
260
except bzrerrors.NotBranchError:
261
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
262
self.check_history.set_active(False)
263
self.check_history.set_sensitive(False)
265
except bzrerrors.UnsupportedProtocol:
266
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
267
self.check_history.set_active(False)
268
self.check_history.set_sensitive(False)
271
self._show_stock_image(gtk.STOCK_CONNECT)
276
self.remote_branch, self.remote_path = Branch.open_containing(path)
278
if self.remote_revision is None:
279
self.remote_revision = self.remote_branch.last_revision()
281
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
283
if len(self.remote_path) == 0:
284
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
286
for (name, type) in self.remote_entries:
287
if name == self.remote_path:
288
self.remote_parent = type.file_id
291
if not path.endswith('/'):
294
if self.remote_branch.base == path:
295
self.button_location_up.set_sensitive(False)
297
self.button_location_up.set_sensitive(True)
299
if os.path.isdir(path):
300
self.image_location_error.destroy()
305
self.wt, self.wtpath = WorkingTree.open_containing(path)
306
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
307
self.notbranch = True
309
# If we're in the root, we cannot go up anymore
310
if sys.platform == 'win32':
311
drive, tail = os.path.splitdrive(path)
312
if tail in ('', '/', '\\'):
313
self.button_location_up.set_sensitive(False)
315
self.button_location_up.set_sensitive(True)
318
self.button_location_up.set_sensitive(False)
320
self.button_location_up.set_sensitive(True)
321
elif not os.path.isfile(path):
322
# Doesn't seem to be a file nor a directory, trying to open a
324
self._show_stock_image(gtk.STOCK_DISCONNECT)
326
br = Branch.open_containing(path)[0]
327
except bzrerrors.NotBranchError:
328
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
329
self.check_history.set_active(False)
330
self.check_history.set_sensitive(False)
332
except bzrerrors.UnsupportedProtocol:
333
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
334
self.check_history.set_active(False)
335
self.check_history.set_sensitive(False)
338
self._show_stock_image(gtk.STOCK_CONNECT)
343
self.remote_branch, self.remote_path = Branch.open_containing(path)
345
if self.remote_revision is None:
346
self.remote_revision = self.remote_branch.last_revision()
348
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
350
if len(self.remote_path) == 0:
351
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
353
for (name, type) in self.remote_entries:
354
if name == self.remote_path:
355
self.remote_parent = type.file_id
358
if not path.endswith('/'):
361
if self.remote_branch.base == path:
362
self.button_location_up.set_sensitive(False)
364
self.button_location_up.set_sensitive(True)
367
self.check_history.set_active(False)
368
self.check_history.set_sensitive(False)
370
self.check_history.set_sensitive(True)
372
self.statusbar.push(self.context_id, path)
373
self.entry_location.set_text(path)
382
if len(self.remote_path) > 0:
383
return self.remote_branch.base + self.remote_path + '/'
385
return self.remote_branch.base
387
def on_about_activate(self, widget):
390
def on_button_history_browse_clicked(self, widget):
391
""" Browse for revision button handler. """
393
br = self.remote_branch
397
revb = RevisionBrowser(br, self.window)
398
response = revb.run()
399
if response != gtk.RESPONSE_NONE:
402
if response == gtk.RESPONSE_OK:
403
if revb.selected_revno is not None:
404
self.entry_history.set_text(revb.selected_revno)
408
def on_button_location_jump_clicked(self, widget):
409
""" Location Jump button handler. """
410
location = self.entry_location.get_text()
412
if self.set_path(location):
415
def on_button_location_up_clicked(self, widget):
416
""" Location Up button handler. """
419
self.set_path(os.path.split(self.get_path())[0])
423
newpath = delim.join(self.get_path().split(delim)[:-2])
425
self.set_path(newpath)
429
def on_checkbutton_history_toggled(self, widget):
430
""" History Mode toggle handler. """
431
if self.check_history.get_active():
432
# History Mode activated
433
self.entry_history.set_sensitive(True)
434
self.button_history.set_sensitive(True)
436
# History Mode deactivated
437
self.entry_history.set_sensitive(False)
438
self.button_history.set_sensitive(False)
441
def on_entry_history_revno_key_press_event(self, widget, event):
442
""" Key pressed handler for the history entry. """
443
if event.keyval == 65293:
444
# Return was hit, so we have to load that specific revision
445
# Emulate being remote, so inventory should be used
446
path = self.get_path()
449
self.remote_branch = self.wt.branch
451
revno = int(self.entry_history.get_text())
452
self.remote_revision = self.remote_branch.get_rev_id(revno)
453
if self.set_path(path, True):
456
def on_entry_location_key_press_event(self, widget, event):
457
""" Key pressed handler for the location entry. """
458
if event.keyval == 65293:
459
# Return was hit, so we have to jump
460
self.on_button_location_jump_clicked(widget)
462
def on_menuitem_add_files_activate(self, widget):
463
""" Add file(s)... menu handler. """
464
from add import OliveAdd
465
add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
468
def on_menuitem_branch_get_activate(self, widget):
469
""" Branch/Get... menu handler. """
470
from bzrlib.plugins.gtk.branch import BranchDialog
473
branch = BranchDialog(os.getcwd(), self.window, self.remote_branch.base)
475
branch = BranchDialog(self.get_path(), self.window)
476
response = branch.run()
477
if response != gtk.RESPONSE_NONE:
480
if response == gtk.RESPONSE_OK:
485
def on_menuitem_branch_checkout_activate(self, widget):
486
""" Branch/Checkout... menu handler. """
487
from bzrlib.plugins.gtk.checkout import CheckoutDialog
490
checkout = CheckoutDialog(os.getcwd(), self.window, self.remote_branch.base)
492
checkout = CheckoutDialog(self.get_path(), self.window)
493
response = checkout.run()
494
if response != gtk.RESPONSE_NONE:
497
if response == gtk.RESPONSE_OK:
503
def on_menuitem_branch_commit_activate(self, widget):
504
""" Branch/Commit... menu handler. """
505
commit = CommitDialog(self.wt, self.wtpath, self.notbranch, self.get_selected_right(), self.window)
506
response = commit.run()
507
if response != gtk.RESPONSE_NONE:
510
if response == gtk.RESPONSE_OK:
515
def on_menuitem_branch_conflicts_activate(self, widget):
516
""" Branch/Conflicts... menu handler. """
517
conflicts = ConflictsDialog(self.wt, self.window)
518
response = conflicts.run()
519
if response != gtk.RESPONSE_NONE:
522
def on_menuitem_branch_merge_activate(self, widget):
523
""" Branch/Merge... menu handler. """
524
from bzrlib.plugins.gtk.merge import MergeDialog
526
if self.check_for_changes():
527
error_dialog(_('There are local changes in the branch'),
528
_('Please commit or revert the changes before merging.'))
530
parent_branch_path = self.wt.branch.get_parent()
531
merge = MergeDialog(self.wt, self.wtpath,default_branch_path=parent_branch_path )
535
def on_menuitem_branch_missing_revisions_activate(self, widget):
536
""" Branch/Missing revisions menu handler. """
538
from bzrlib.missing import find_unmerged, iter_log_revisions
540
local_branch = self.wt.branch
541
parent_branch_path = local_branch.get_parent()
542
if parent_branch_path is None:
543
error_dialog(_('Parent location is unknown'),
544
_('Cannot determine missing revisions if no parent location is known.'))
547
parent_branch = Branch.open(parent_branch_path)
549
if parent_branch.base == local_branch.base:
550
parent_branch = local_branch
552
local_extra, remote_extra = find_unmerged(local_branch,parent_branch)
554
if local_extra or remote_extra:
556
## def log_revision_one_line_text(log_revision):
557
## """ Generates one line description of log_revison ended with end of line."""
558
## revision = log_revision.rev
559
## txt = "- %s (%s)\n" % (revision.get_summary(), revision.committer, )
560
## txt = txt.replace("<"," ") # Seems < > chars are expected to be xml tags ...
561
## txt = txt.replace(">"," ")
566
dlg_txt += _('%d local extra revision(s). \n') % (len(local_extra),)
567
## NOTE: We do not want such ugly info about missing revisions
568
## Revision Browser should be used there
569
## max_revisions = 10
570
## for log_revision in iter_log_revisions(local_extra, local_branch.repository, verbose=1):
571
## dlg_txt += log_revision_one_line_text(log_revision)
572
## if max_revisions <= 0:
573
## dlg_txt += _("more ... \n")
575
## max_revisions -= 1
578
dlg_txt += _('%d local missing revision(s).\n') % (len(remote_extra),)
579
## max_revisions = 10
580
## for log_revision in iter_log_revisions(remote_extra, parent_branch.repository, verbose=1):
581
## dlg_txt += log_revision_one_line_text(log_revision)
582
## if max_revisions <= 0:
583
## dlg_txt += _("more ... \n")
585
## max_revisions -= 1
587
info_dialog(_('There are missing revisions'),
590
info_dialog(_('Local branch up to date'),
591
_('There are no missing revisions.'))
594
def on_menuitem_branch_pull_activate(self, widget):
595
""" Branch/Pull menu handler. """
596
branch_to = self.wt.branch
598
location = branch_to.get_parent()
600
error_dialog(_('Parent location is unknown'),
601
_('Pulling is not possible until there is a parent location.'))
604
branch_from = Branch.open(location)
606
if branch_to.get_parent() is None:
607
branch_to.set_parent(branch_from.base)
609
ret = branch_to.pull(branch_from)
611
info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
614
def on_menuitem_branch_update_activate(self, widget):
615
""" Brranch/checkout update menu handler. """
617
ret = self.wt.update()
618
conflicts = self.wt.conflicts()
620
info_dialog(_('Update successful but conflicts generated'), _('Number of conflicts generated: %d.') % (len(conflicts),) )
622
info_dialog(_('Update successful'), _('No conflicts generated.') )
624
def on_menuitem_branch_push_activate(self, widget):
625
""" Branch/Push... menu handler. """
626
push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
627
response = push.run()
628
if response != gtk.RESPONSE_NONE:
632
def on_menuitem_branch_revert_activate(self, widget):
633
""" Branch/Revert all changes menu handler. """
634
ret = self.wt.revert([])
636
warning_dialog(_('Conflicts detected'),
637
_('Please have a look at the working tree before continuing.'))
639
info_dialog(_('Revert successful'),
640
_('All files reverted to last revision.'))
643
def on_menuitem_branch_status_activate(self, widget):
644
""" Branch/Status... menu handler. """
645
from bzrlib.plugins.gtk.status import StatusDialog
646
status = StatusDialog(self.wt, self.wtpath)
647
response = status.run()
648
if response != gtk.RESPONSE_NONE:
651
def on_menuitem_branch_initialize_activate(self, widget):
652
""" Initialize current directory. """
653
init = InitDialog(self.path, self.window)
654
response = init.run()
655
if response != gtk.RESPONSE_NONE:
658
if response == gtk.RESPONSE_OK:
663
def on_menuitem_branch_tags_activate(self, widget):
664
""" Branch/Tags... menu handler. """
665
from bzrlib.plugins.gtk.tags import TagsWindow
667
window = TagsWindow(self.wt.branch, self.window)
669
window = TagsWindow(self.remote_branch, self.window)
672
def on_menuitem_file_annotate_activate(self, widget):
673
""" File/Annotate... menu handler. """
674
if self.get_selected_right() is None:
675
error_dialog(_('No file was selected'),
676
_('Please select a file from the list.'))
679
branch = self.wt.branch
680
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
682
window = GAnnotateWindow(all=False, plain=False, parent=self.window)
683
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
684
config = GAnnotateConfig(window)
688
window.annotate(self.wt, branch, file_id)
692
def on_menuitem_file_make_directory_activate(self, widget):
693
""" File/Make directory... menu handler. """
694
from mkdir import OliveMkdir
695
mkdir = OliveMkdir(self.wt, self.wtpath)
698
def on_menuitem_file_move_activate(self, widget):
699
""" File/Move... menu handler. """
700
from move import OliveMove
701
move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
704
def on_menuitem_file_rename_activate(self, widget):
705
""" File/Rename... menu handler. """
706
from rename import OliveRename
707
rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
710
def on_menuitem_remove_file_activate(self, widget):
711
""" Remove (unversion) selected file. """
712
from remove import OliveRemoveDialog
713
remove = OliveRemoveDialog(self.wt, self.wtpath,
714
selected=self.get_selected_right(),
716
response = remove.run()
718
if response != gtk.RESPONSE_NONE:
721
if response == gtk.RESPONSE_OK:
722
self.set_path(self.path)
727
def on_menuitem_stats_diff_activate(self, widget):
728
""" Statistics/Differences... menu handler. """
729
window = DiffWindow()
730
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
731
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
734
def on_menuitem_stats_infos_activate(self, widget):
735
""" Statistics/Informations... menu handler. """
736
from info import OliveInfo
738
info = OliveInfo(self.remote_branch)
740
info = OliveInfo(self.wt.branch)
743
def on_menuitem_stats_log_activate(self, widget):
744
""" Statistics/Log... menu handler. """
745
window = branchwin.BranchWindow(parent=self.window)
747
window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
749
window.set_branch(self.remote_branch, self.remote_branch.last_revision(), None)
752
def on_menuitem_view_refresh_activate(self, widget):
753
""" View/Refresh menu handler. """
754
# Refresh the left pane
756
# Refresh the right pane
759
def on_menuitem_view_show_hidden_files_activate(self, widget):
760
""" View/Show hidden files menu handler. """
761
self.pref.set_preference('dotted_files', widget.get_active())
762
if self.path is not None:
765
def on_menuitem_view_show_ignored_files_activate(self, widget):
766
""" Hide/Show ignored files menu handler. """
767
self.pref.set_preference('ignored_files', widget.get_active())
768
if self.path is not None:
771
def on_treeview_left_button_press_event(self, widget, event):
772
""" Occurs when somebody right-clicks in the bookmark list. """
773
if event.button == 3:
774
# Don't show context with nothing selected
775
if self.get_selected_left() == None:
779
from menu import OliveMenu
780
menu = OliveMenu(path=self.get_path(),
781
selected=self.get_selected_left(),
784
menu.left_context_menu().popup(None, None, None, 0,
787
def on_treeview_left_row_activated(self, treeview, path, view_column):
788
""" Occurs when somebody double-clicks or enters an item in the
791
newdir = self.get_selected_left()
795
if self.set_path(newdir):
798
def on_treeview_right_button_press_event(self, widget, event):
799
""" Occurs when somebody right-clicks in the file list. """
800
if event.button == 3:
802
from menu import OliveMenu
803
menu = OliveMenu(path=self.get_path(),
804
selected=self.get_selected_right(),
807
m_open = menu.ui.get_widget('/context_right/open')
808
m_add = menu.ui.get_widget('/context_right/add')
809
m_remove = menu.ui.get_widget('/context_right/remove')
810
m_rename = menu.ui.get_widget('/context_right/rename')
811
m_revert = menu.ui.get_widget('/context_right/revert')
812
m_commit = menu.ui.get_widget('/context_right/commit')
813
m_annotate = menu.ui.get_widget('/context_right/annotate')
814
m_diff = menu.ui.get_widget('/context_right/diff')
815
# check if we're in a branch
817
from bzrlib.branch import Branch
818
Branch.open_containing(self.get_path())
820
m_open.set_sensitive(False)
821
m_add.set_sensitive(False)
822
m_remove.set_sensitive(False)
823
m_rename.set_sensitive(False)
824
m_revert.set_sensitive(False)
825
m_commit.set_sensitive(False)
826
m_annotate.set_sensitive(False)
827
m_diff.set_sensitive(False)
829
m_open.set_sensitive(True)
830
m_add.set_sensitive(True)
831
m_remove.set_sensitive(True)
832
m_rename.set_sensitive(True)
833
m_revert.set_sensitive(True)
834
m_commit.set_sensitive(True)
835
m_annotate.set_sensitive(True)
836
m_diff.set_sensitive(True)
837
except bzrerrors.NotBranchError:
838
m_open.set_sensitive(True)
839
m_add.set_sensitive(False)
840
m_remove.set_sensitive(False)
841
m_rename.set_sensitive(False)
842
m_revert.set_sensitive(False)
843
m_commit.set_sensitive(False)
844
m_annotate.set_sensitive(False)
845
m_diff.set_sensitive(False)
848
menu.right_context_menu().popup(None, None, None, 0,
851
menu.remote_context_menu().popup(None, None, None, 0,
854
def on_treeview_right_row_activated(self, treeview, path, view_column):
855
""" Occurs when somebody double-clicks or enters an item in the
857
from launch import launch
859
newdir = self.get_selected_right()
864
self.set_path(os.path.split(self.get_path())[0])
866
fullpath = os.path.join(self.get_path(), newdir)
867
if os.path.isdir(fullpath):
868
# selected item is an existant directory
869
self.set_path(fullpath)
874
if self._is_remote_dir(self.get_path() + newdir):
875
self.set_path(self.get_path() + newdir)
879
def on_window_main_delete_event(self, widget, event=None):
880
""" Do some stuff before exiting. """
881
width, height = self.window_main.get_size()
882
self.pref.set_preference('window_width', width)
883
self.pref.set_preference('window_height', height)
884
x, y = self.window_main.get_position()
885
self.pref.set_preference('window_x', x)
886
self.pref.set_preference('window_y', y)
887
self.pref.set_preference('paned_position',
888
self.hpaned_main.get_position())
891
self.window_main.destroy()
893
def _load_left(self):
894
""" Load data into the left panel. (Bookmarks) """
896
treestore = gtk.TreeStore(str, str)
899
bookmarks = self.pref.get_bookmarks()
901
# Add them to the TreeStore
902
titer = treestore.append(None, [_('Bookmarks'), None])
903
for item in bookmarks:
904
title = self.pref.get_bookmark_title(item)
905
treestore.append(titer, [title, item])
907
# Create the column and add it to the TreeView
908
self.treeview_left.set_model(treestore)
909
tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
910
self.treeview_left.append_column(tvcolumn_bookmark)
913
cell = gtk.CellRendererText()
914
tvcolumn_bookmark.pack_start(cell, True)
915
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
918
self.treeview_left.expand_all()
920
def _load_right(self):
921
""" Load data into the right panel. (Filelist) """
923
# Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
924
liststore = gtk.ListStore(gobject.TYPE_STRING,
925
gobject.TYPE_BOOLEAN,
938
# Fill the appropriate lists
939
dotted_files = self.pref.get_preference('dotted_files', 'bool')
940
for item in os.listdir(self.path):
941
if not dotted_files and item[0] == '.':
943
if os.path.isdir(self.path + os.sep + item):
948
if not self.notbranch:
949
branch = self.wt.branch
950
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
952
delta = self.wt.changes_from(tree2, want_unchanged=True)
954
# Add'em to the ListStore
956
statinfo = os.stat(self.path + os.sep + item)
957
liststore.append([ gtk.STOCK_DIRECTORY,
965
self._format_date(statinfo.st_mtime),
970
if not self.notbranch:
971
filename = self.wt.relpath(self.path + os.sep + item)
976
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
977
if rpathnew == filename:
980
for rpath, id, kind in delta.added:
981
if rpath == filename:
984
for rpath, id, kind in delta.removed:
985
if rpath == filename:
988
for rpath, id, kind, text_modified, meta_modified in delta.modified:
989
if rpath == filename:
992
for rpath, id, kind in delta.unchanged:
993
if rpath == filename:
996
for rpath, file_class, kind, id, entry in self.wt.list_files():
997
if rpath == filename and file_class == 'I':
1002
if status == 'renamed':
1004
elif status == 'removed':
1006
elif status == 'added':
1008
elif status == 'modified':
1010
elif status == 'unchanged':
1012
elif status == 'ignored':
1017
statinfo = os.stat(self.path + os.sep + item)
1018
liststore.append([gtk.STOCK_FILE,
1023
str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
1024
self._format_size(statinfo.st_size),
1026
self._format_date(statinfo.st_mtime),
1029
# Create the columns and add them to the TreeView
1030
self.treeview_right.set_model(liststore)
1031
self._tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
1032
self._tvcolumn_status = gtk.TreeViewColumn(_('Status'))
1033
self._tvcolumn_size = gtk.TreeViewColumn(_('Size'))
1034
self._tvcolumn_mtime = gtk.TreeViewColumn(_('Last modified'))
1035
self.treeview_right.append_column(self._tvcolumn_filename)
1036
self.treeview_right.append_column(self._tvcolumn_status)
1037
self.treeview_right.append_column(self._tvcolumn_size)
1038
self.treeview_right.append_column(self._tvcolumn_mtime)
1041
cellpb = gtk.CellRendererPixbuf()
1042
cell = gtk.CellRendererText()
1043
self._tvcolumn_filename.pack_start(cellpb, False)
1044
self._tvcolumn_filename.pack_start(cell, True)
1045
self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
1046
self._tvcolumn_filename.add_attribute(cell, 'text', 2)
1047
self._tvcolumn_status.pack_start(cell, True)
1048
self._tvcolumn_status.add_attribute(cell, 'text', 3)
1049
self._tvcolumn_size.pack_start(cell, True)
1050
self._tvcolumn_size.add_attribute(cell, 'text', 6)
1051
self._tvcolumn_mtime.pack_start(cell, True)
1052
self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
1054
# Set up the properties of the TreeView
1055
self.treeview_right.set_headers_visible(True)
1056
self.treeview_right.set_headers_clickable(True)
1057
self.treeview_right.set_search_column(1)
1058
self._tvcolumn_filename.set_resizable(True)
1059
self._tvcolumn_status.set_resizable(True)
1060
self._tvcolumn_size.set_resizable(True)
1061
self._tvcolumn_mtime.set_resizable(True)
1063
liststore.set_sort_func(13, self._sort_filelist_callback, None)
1064
liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
1065
self._tvcolumn_filename.set_sort_column_id(13)
1066
self._tvcolumn_status.set_sort_column_id(3)
1067
self._tvcolumn_size.set_sort_column_id(5)
1068
self._tvcolumn_mtime.set_sort_column_id(7)
1071
self.set_sensitivity()
1073
def get_selected_fileid(self):
1074
""" Get the file_id of the selected file. """
1075
treeselection = self.treeview_right.get_selection()
1076
(model, iter) = treeselection.get_selected()
1081
return model.get_value(iter, 9)
1083
def get_selected_right(self):
1084
""" Get the selected filename. """
1085
treeselection = self.treeview_right.get_selection()
1086
(model, iter) = treeselection.get_selected()
1091
return model.get_value(iter, 2)
1093
def get_selected_left(self):
1094
""" Get the selected bookmark. """
1095
treeselection = self.treeview_left.get_selection()
1096
(model, iter) = treeselection.get_selected()
1101
return model.get_value(iter, 1)
1103
def set_statusbar(self, message):
1104
""" Set the statusbar message. """
1105
self.statusbar.push(self.context_id, message)
1107
def clear_statusbar(self):
1108
""" Clean the last message from the statusbar. """
1109
self.statusbar.pop(self.context_id)
1111
def set_sensitivity(self):
1112
""" Set menu and toolbar sensitivity. """
1115
self.menuitem_branch_init.set_sensitive(self.notbranch)
1116
self.menuitem_branch_get.set_sensitive(self.notbranch)
1117
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
1118
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
1119
self.menuitem_branch_push.set_sensitive(not self.notbranch)
1120
self.menuitem_branch_update.set_sensitive(not self.notbranch)
1121
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
1122
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
1123
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
1124
self.menuitem_branch_tags.set_sensitive(not self.notbranch)
1125
self.menuitem_branch_status.set_sensitive(not self.notbranch)
1126
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
1127
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
1128
self.menuitem_stats.set_sensitive(not self.notbranch)
1129
self.menuitem_stats_diff.set_sensitive(not self.notbranch)
1130
self.menuitem_add_files.set_sensitive(not self.notbranch)
1131
self.menuitem_remove_files.set_sensitive(not self.notbranch)
1132
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
1133
self.menuitem_file_rename.set_sensitive(not self.notbranch)
1134
self.menuitem_file_move.set_sensitive(not self.notbranch)
1135
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
1136
#self.menutoolbutton_diff.set_sensitive(True)
1137
self.toolbutton_diff.set_sensitive(not self.notbranch)
1138
self.toolbutton_log.set_sensitive(not self.notbranch)
1139
self.toolbutton_commit.set_sensitive(not self.notbranch)
1140
self.toolbutton_pull.set_sensitive(not self.notbranch)
1141
self.toolbutton_push.set_sensitive(not self.notbranch)
1142
self.toolbutton_update.set_sensitive(not self.notbranch)
1145
self.menuitem_branch_init.set_sensitive(False)
1146
self.menuitem_branch_get.set_sensitive(True)
1147
self.menuitem_branch_checkout.set_sensitive(True)
1148
self.menuitem_branch_pull.set_sensitive(False)
1149
self.menuitem_branch_push.set_sensitive(False)
1150
self.menuitem_branch_update.set_sensitive(False)
1151
self.menuitem_branch_revert.set_sensitive(False)
1152
self.menuitem_branch_merge.set_sensitive(False)
1153
self.menuitem_branch_commit.set_sensitive(False)
1154
self.menuitem_branch_tags.set_sensitive(True)
1155
self.menuitem_branch_status.set_sensitive(False)
1156
self.menuitem_branch_missing.set_sensitive(False)
1157
self.menuitem_branch_conflicts.set_sensitive(False)
1158
self.menuitem_stats.set_sensitive(True)
1159
self.menuitem_stats_diff.set_sensitive(False)
1160
self.menuitem_add_files.set_sensitive(False)
1161
self.menuitem_remove_files.set_sensitive(False)
1162
self.menuitem_file_make_directory.set_sensitive(False)
1163
self.menuitem_file_rename.set_sensitive(False)
1164
self.menuitem_file_move.set_sensitive(False)
1165
self.menuitem_file_annotate.set_sensitive(False)
1166
#self.menutoolbutton_diff.set_sensitive(True)
1167
self.toolbutton_diff.set_sensitive(False)
1168
self.toolbutton_log.set_sensitive(True)
1169
self.toolbutton_commit.set_sensitive(False)
1170
self.toolbutton_pull.set_sensitive(False)
1171
self.toolbutton_push.set_sensitive(False)
1172
self.toolbutton_update.set_sensitive(False)
1174
def refresh_left(self):
1175
""" Refresh the bookmark list. """
1177
# Get TreeStore and clear it
1178
treestore = self.treeview_left.get_model()
1181
# Re-read preferences
1185
bookmarks = self.pref.get_bookmarks()
1187
# Add them to the TreeStore
1188
titer = treestore.append(None, [_('Bookmarks'), None])
1189
for item in bookmarks:
1190
title = self.pref.get_bookmark_title(item)
1191
treestore.append(titer, [title, item])
1193
# Add the TreeStore to the TreeView
1194
self.treeview_left.set_model(treestore)
1197
self.treeview_left.expand_all()
1199
def refresh_right(self, path=None):
1200
""" Refresh the file list. """
1203
from bzrlib.workingtree import WorkingTree
1206
path = self.get_path()
1208
# A workaround for double-clicking Bookmarks
1209
if not os.path.exists(path):
1212
# Get ListStore and clear it
1213
liststore = self.treeview_right.get_model()
1216
# Show Status column
1217
self._tvcolumn_status.set_visible(True)
1222
# Fill the appropriate lists
1223
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1224
ignored_files = self.pref.get_preference('ignored_files', 'bool')
1226
for item in os.listdir(path):
1227
if not dotted_files and item[0] == '.':
1229
if os.path.isdir(path + os.sep + item):
1234
# Try to open the working tree
1237
tree1 = WorkingTree.open_containing(path)[0]
1238
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
1242
branch = tree1.branch
1243
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
1245
delta = tree1.changes_from(tree2, want_unchanged=True)
1247
# Add'em to the ListStore
1249
statinfo = os.stat(self.path + os.sep + item)
1250
liststore.append([gtk.STOCK_DIRECTORY,
1258
self._format_date(statinfo.st_mtime),
1264
filename = tree1.relpath(path + os.sep + item)
1269
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1270
if rpathnew == filename:
1273
for rpath, id, kind in delta.added:
1274
if rpath == filename:
1277
for rpath, id, kind in delta.removed:
1278
if rpath == filename:
1281
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1282
if rpath == filename:
1285
for rpath, id, kind in delta.unchanged:
1286
if rpath == filename:
1287
status = 'unchanged'
1289
for rpath, file_class, kind, id, entry in self.wt.list_files():
1290
if rpath == filename and file_class == 'I':
1295
if status == 'renamed':
1297
elif status == 'removed':
1299
elif status == 'added':
1301
elif status == 'modified':
1303
elif status == 'unchanged':
1305
elif status == 'ignored':
1307
if not ignored_files:
1312
statinfo = os.stat(self.path + os.sep + item)
1313
liststore.append([gtk.STOCK_FILE,
1318
str(statinfo.st_size),
1319
self._format_size(statinfo.st_size),
1321
self._format_date(statinfo.st_mtime),
1326
# Get ListStore and clear it
1327
liststore = self.treeview_right.get_model()
1330
# Hide Status column
1331
self._tvcolumn_status.set_visible(False)
1336
self._show_stock_image(gtk.STOCK_REFRESH)
1338
for (name, type) in self.remote_entries:
1339
if type.kind == 'directory':
1341
elif type.kind == 'file':
1345
""" Cache based on revision history. """
1346
def __init__(self, history):
1347
self._history = history
1349
def _lookup_revision(self, revid):
1350
for r in self._history:
1351
if r.revision_id == revid:
1353
rev = repo.get_revision(revid)
1354
self._history.append(rev)
1357
repo = self.remote_branch.repository
1359
revhistory = self.remote_branch.revision_history()
1361
revs = repo.get_revisions(revhistory)
1362
cache = HistoryCache(revs)
1363
except bzrerrors.InvalidHttpResponse:
1364
# Fallback to dummy algorithm, because of LP: #115209
1365
cache = HistoryCache([])
1368
if item.parent_id == self.remote_parent:
1369
rev = cache._lookup_revision(item.revision)
1370
liststore.append([ gtk.STOCK_DIRECTORY,
1378
self._format_date(rev.timestamp),
1381
while gtk.events_pending():
1382
gtk.main_iteration()
1385
if item.parent_id == self.remote_parent:
1386
rev = cache._lookup_revision(item.revision)
1387
liststore.append([ gtk.STOCK_FILE,
1392
str(item.text_size),
1393
self._format_size(item.text_size),
1395
self._format_date(rev.timestamp),
1398
while gtk.events_pending():
1399
gtk.main_iteration()
1401
self.image_location_error.destroy()
1403
# Columns should auto-size
1404
self.treeview_right.columns_autosize()
1407
self.set_sensitivity()
1409
def _harddisks(self):
1410
""" Returns hard drive letters under Win32. """
1415
if sys.platform == 'win32':
1416
print "pyWin32 modules needed to run Olive on Win32."
1422
for drive in string.ascii_uppercase:
1423
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
1424
driveletters.append(drive+':')
1427
def gen_hard_selector(self):
1428
""" Generate the hard drive selector under Win32. """
1429
drives = self._harddisks()
1430
for drive in drives:
1431
self.combobox_drive.append_text(drive)
1432
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1434
def _refresh_drives(self, combobox):
1435
if self._just_started:
1437
model = combobox.get_model()
1438
active = combobox.get_active()
1440
drive = model[active][0]
1441
self.set_path(drive + '\\')
1442
self.refresh_right(drive + '\\')
1444
def check_for_changes(self):
1445
""" Check whether there were changes in the current working tree. """
1446
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
1447
delta = self.wt.changes_from(old_tree)
1451
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
1456
def _sort_filelist_callback(self, model, iter1, iter2, data):
1457
""" The sort callback for the file list, return values:
1462
name1 = model.get_value(iter1, 2)
1463
name2 = model.get_value(iter2, 2)
1465
if model.get_value(iter1, 1):
1466
# item1 is a directory
1467
if not model.get_value(iter2, 1):
1471
# both of them are directories, we compare their names
1474
elif name1 == name2:
1479
# item1 is not a directory
1480
if model.get_value(iter2, 1):
1484
# both of them are files, compare them
1487
elif name1 == name2:
1492
def _format_size(self, size):
1493
""" Format size to a human readable format. """
1495
return "%d[B]" % (size,)
1496
size = size / 1000.0
1498
for metric in ["kB","MB","GB","TB"]:
1501
size = size / 1000.0
1502
return "%.1f[%s]" % (size,metric)
1504
def _format_date(self, timestamp):
1505
""" Format the time (given in secs) to a human readable format. """
1506
return time.ctime(timestamp)
1508
def _is_remote_dir(self, location):
1509
""" Determine whether the given location is a directory or not. """
1511
# We're in local mode
1514
branch, path = Branch.open_containing(location)
1515
for (name, type) in self.remote_entries:
1516
if name == path and type.kind == 'directory':
1519
# Either it's not a directory or not in the inventory
1522
def _show_stock_image(self, stock_id):
1523
""" Show a stock image next to the location entry. """
1524
self.image_location_error.destroy()
1525
self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
1526
self.hbox_location.pack_start(self.image_location_error, False, False, 0)
1527
if sys.platform == 'win32':
1528
self.hbox_location.reorder_child(self.image_location_error, 2)
1530
self.hbox_location.reorder_child(self.image_location_error, 1)
1531
self.image_location_error.show()
1532
while gtk.events_pending():
1533
gtk.main_iteration()
1538
""" A class which handles Olive's preferences. """
1539
def __init__(self, path=None):
1540
""" Initialize the Preferences class. """
1541
# Some default options
1542
self.defaults = { 'strict_commit' : False,
1543
'dotted_files' : False,
1544
'ignored_files' : True,
1545
'window_width' : 700,
1546
'window_height' : 400,
1549
'paned_position': 200 }
1551
# Create a config parser object
1552
self.config = ConfigParser.RawConfigParser()
1556
if sys.platform == 'win32':
1557
# Windows - no dotted files
1558
self._filename = os.path.expanduser('~/olive.conf')
1560
self._filename = os.path.expanduser('~/.olive.conf')
1562
self._filename = path
1564
# Load the configuration
1567
def _get_default(self, option):
1568
""" Get the default option for a preference. """
1570
ret = self.defaults[option]
1577
""" Refresh the configuration. """
1578
# First write out the changes
1580
# Then load the configuration again
1584
""" Just read the configuration. """
1585
# Re-initialize the config parser object to avoid some bugs
1586
self.config = ConfigParser.RawConfigParser()
1587
self.config.read([self._filename])
1590
""" Write the configuration to the appropriate files. """
1591
fp = open(self._filename, 'w')
1592
self.config.write(fp)
1595
def get_bookmarks(self):
1596
""" Return the list of bookmarks. """
1597
bookmarks = self.config.sections()
1598
if self.config.has_section('preferences'):
1599
bookmarks.remove('preferences')
1602
def add_bookmark(self, path):
1603
""" Add bookmark. """
1605
self.config.add_section(path)
1606
except ConfigParser.DuplicateSectionError:
1611
def get_bookmark_title(self, path):
1612
""" Get bookmark title. """
1614
ret = self.config.get(path, 'title')
1615
except ConfigParser.NoOptionError:
1620
def set_bookmark_title(self, path, title):
1621
""" Set bookmark title. """
1622
# FIXME: What if path isn't listed yet?
1623
# FIXME: Canonicalize paths first?
1624
self.config.set(path, 'title', title)
1626
def remove_bookmark(self, path):
1627
""" Remove bookmark. """
1628
return self.config.remove_section(path)
1630
def set_preference(self, option, value):
1631
""" Set the value of the given option. """
1634
elif value is False:
1637
if self.config.has_section('preferences'):
1638
self.config.set('preferences', option, value)
1640
self.config.add_section('preferences')
1641
self.config.set('preferences', option, value)
1643
def get_preference(self, option, kind='str'):
1644
""" Get the value of the given option.
1646
:param kind: str/bool/int/float. default: str
1648
if self.config.has_option('preferences', option):
1650
return self.config.getboolean('preferences', option)
1652
return self.config.getint('preferences', option)
1653
elif kind == 'float':
1654
return self.config.getfloat('preferences', option)
1656
return self.config.get('preferences', option)
1659
return self._get_default(option)