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_artists(["Simon Pascal Klein"])
76
""" The main Olive GTK frontend class. This is called when launching the
80
self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
81
self.window = self.toplevel.get_widget('window_main')
82
self.pref = Preferences()
85
# Initialize the statusbar
86
self.statusbar = self.toplevel.get_widget('statusbar')
87
self.context_id = self.statusbar.get_context_id('olive')
90
self.window_main = self.toplevel.get_widget('window_main')
92
self.hpaned_main = self.toplevel.get_widget('hpaned_main')
94
self.treeview_left = self.toplevel.get_widget('treeview_left')
95
self.treeview_right = self.toplevel.get_widget('treeview_right')
96
# Get some important menu items
97
self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
98
self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
99
self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
100
self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
101
self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
102
self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
103
self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
104
self.menuitem_view_show_ignored_files = self.toplevel.get_widget('menuitem_view_show_ignored_files')
105
self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
106
self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
107
self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
108
self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
109
self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
110
self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
111
self.menuitem_branch_update = self.toplevel.get_widget('menuitem_branch_update')
112
self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
113
self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
114
self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
115
self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
116
self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
117
self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
118
self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
119
self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
120
self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
121
self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
122
# Get some toolbuttons
123
#self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
124
self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
125
self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
126
self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
127
self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
128
self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
129
self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
130
# Get the drive selector
131
self.combobox_drive = gtk.combo_box_new_text()
132
self.combobox_drive.connect("changed", self._refresh_drives)
134
# Get the navigation widgets
135
self.hbox_location = self.toplevel.get_widget('hbox_location')
136
self.button_location_up = self.toplevel.get_widget('button_location_up')
137
self.button_location_jump = self.toplevel.get_widget('button_location_jump')
138
self.entry_location = self.toplevel.get_widget('entry_location')
139
self.image_location_error = self.toplevel.get_widget('image_location_error')
141
# Get the History widgets
142
self.check_history = self.toplevel.get_widget('checkbutton_history')
143
self.entry_history = self.toplevel.get_widget('entry_history_revno')
144
self.button_history = self.toplevel.get_widget('button_history_browse')
146
self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
148
# Dictionary for signal_autoconnect
149
dic = { "on_window_main_destroy": gtk.main_quit,
150
"on_window_main_delete_event": self.on_window_main_delete_event,
151
"on_quit_activate": self.on_window_main_delete_event,
152
"on_about_activate": self.on_about_activate,
153
"on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
154
"on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
155
"on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
156
"on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
157
"on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
158
"on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
159
"on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
160
"on_menuitem_view_show_ignored_files_activate": self.on_menuitem_view_show_ignored_files_activate,
161
"on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
162
"on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
163
"on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
164
"on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
165
"on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
166
"on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
167
"on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
168
"on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
169
"on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
170
"on_menuitem_branch_update_activate": self.on_menuitem_branch_update_activate,
171
"on_menuitem_branch_tags_activate": self.on_menuitem_branch_tags_activate,
172
"on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
173
"on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
174
"on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
175
"on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
176
"on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
177
"on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
178
"on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
179
"on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
180
#"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
181
"on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
182
"on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
183
"on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
184
"on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
185
"on_toolbutton_update_clicked": self.on_menuitem_branch_update_activate,
186
"on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
187
"on_treeview_right_row_activated": self.on_treeview_right_row_activated,
188
"on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
189
"on_treeview_left_row_activated": self.on_treeview_left_row_activated,
190
"on_button_location_up_clicked": self.on_button_location_up_clicked,
191
"on_button_location_jump_clicked": self.on_button_location_jump_clicked,
192
"on_entry_location_key_press_event": self.on_entry_location_key_press_event,
193
"on_checkbutton_history_toggled": self.on_checkbutton_history_toggled,
194
"on_entry_history_revno_key_press_event": self.on_entry_history_revno_key_press_event,
195
"on_button_history_browse_clicked": self.on_button_history_browse_clicked
198
# Connect the signals to the handlers
199
self.toplevel.signal_autoconnect(dic)
201
self._just_started = True
203
# Apply window size and position
204
width = self.pref.get_preference('window_width', 'int')
205
height = self.pref.get_preference('window_height', 'int')
206
self.window.resize(width, height)
207
x = self.pref.get_preference('window_x', 'int')
208
y = self.pref.get_preference('window_y', 'int')
209
self.window.move(x, y)
210
# Apply paned position
211
pos = self.pref.get_preference('paned_position', 'int')
212
self.hpaned_main.set_position(pos)
214
# Apply menu to the toolbutton
215
#menubutton = self.toplevel.get_widget('menutoolbutton_diff')
216
#menubutton.set_menu(handler.menu.toolbar_diff)
218
# Now we can show the window
221
# Show drive selector if under Win32
222
if sys.platform == 'win32':
223
self.hbox_location.pack_start(self.combobox_drive, False, False, 0)
224
self.hbox_location.reorder_child(self.combobox_drive, 1)
225
self.combobox_drive.show()
226
self.gen_hard_selector()
231
self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
232
self.menuitem_view_show_ignored_files.set_active(self.pref.get_preference('ignored_files', 'bool'))
234
# We're starting local
236
self.remote_branch = None
237
self.remote_path = None
238
self.remote_revision = None
240
self.set_path(os.getcwd())
243
self._just_started = False
245
def set_path(self, path, force_remote=False):
246
self.notbranch = False
249
# Forcing remote mode (reading data from inventory)
250
self._show_stock_image(gtk.STOCK_DISCONNECT)
252
br = Branch.open_containing(path)[0]
253
except bzrerrors.NotBranchError:
254
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
255
self.check_history.set_active(False)
256
self.check_history.set_sensitive(False)
258
except bzrerrors.UnsupportedProtocol:
259
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
260
self.check_history.set_active(False)
261
self.check_history.set_sensitive(False)
264
self._show_stock_image(gtk.STOCK_CONNECT)
269
self.remote_branch, self.remote_path = Branch.open_containing(path)
271
if self.remote_revision is None:
272
self.remote_revision = self.remote_branch.last_revision()
274
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
276
if len(self.remote_path) == 0:
277
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
279
for (name, type) in self.remote_entries:
280
if name == self.remote_path:
281
self.remote_parent = type.file_id
284
if not path.endswith('/'):
287
if self.remote_branch.base == path:
288
self.button_location_up.set_sensitive(False)
290
self.button_location_up.set_sensitive(True)
292
if os.path.isdir(path):
293
self.image_location_error.destroy()
298
self.wt, self.wtpath = WorkingTree.open_containing(path)
299
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
300
self.notbranch = True
302
# If we're in the root, we cannot go up anymore
303
if sys.platform == 'win32':
304
drive, tail = os.path.splitdrive(path)
305
if tail in ('', '/', '\\'):
306
self.button_location_up.set_sensitive(False)
308
self.button_location_up.set_sensitive(True)
311
self.button_location_up.set_sensitive(False)
313
self.button_location_up.set_sensitive(True)
314
elif not os.path.isfile(path):
315
# Doesn't seem to be a file nor a directory, trying to open a
317
self._show_stock_image(gtk.STOCK_DISCONNECT)
319
br = Branch.open_containing(path)[0]
320
except bzrerrors.NotBranchError:
321
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
322
self.check_history.set_active(False)
323
self.check_history.set_sensitive(False)
325
except bzrerrors.UnsupportedProtocol:
326
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
327
self.check_history.set_active(False)
328
self.check_history.set_sensitive(False)
331
self._show_stock_image(gtk.STOCK_CONNECT)
336
self.remote_branch, self.remote_path = Branch.open_containing(path)
338
if self.remote_revision is None:
339
self.remote_revision = self.remote_branch.last_revision()
341
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
343
if len(self.remote_path) == 0:
344
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
346
for (name, type) in self.remote_entries:
347
if name == self.remote_path:
348
self.remote_parent = type.file_id
351
if not path.endswith('/'):
354
if self.remote_branch.base == path:
355
self.button_location_up.set_sensitive(False)
357
self.button_location_up.set_sensitive(True)
360
self.check_history.set_active(False)
361
self.check_history.set_sensitive(False)
363
self.check_history.set_sensitive(True)
365
self.statusbar.push(self.context_id, path)
366
self.entry_location.set_text(path)
375
if len(self.remote_path) > 0:
376
return self.remote_branch.base + self.remote_path + '/'
378
return self.remote_branch.base
380
def on_about_activate(self, widget):
383
def on_button_history_browse_clicked(self, widget):
384
""" Browse for revision button handler. """
386
br = self.remote_branch
390
revb = RevisionBrowser(br, self.window)
391
response = revb.run()
392
if response != gtk.RESPONSE_NONE:
395
if response == gtk.RESPONSE_OK:
396
if revb.selected_revno is not None:
397
self.entry_history.set_text(revb.selected_revno)
401
def on_button_location_jump_clicked(self, widget):
402
""" Location Jump button handler. """
403
location = self.entry_location.get_text()
405
if self.set_path(location):
408
def on_button_location_up_clicked(self, widget):
409
""" Location Up button handler. """
412
self.set_path(os.path.split(self.get_path())[0])
416
newpath = delim.join(self.get_path().split(delim)[:-2])
418
self.set_path(newpath)
422
def on_checkbutton_history_toggled(self, widget):
423
""" History Mode toggle handler. """
424
if self.check_history.get_active():
425
# History Mode activated
426
self.entry_history.set_sensitive(True)
427
self.button_history.set_sensitive(True)
429
# History Mode deactivated
430
self.entry_history.set_sensitive(False)
431
self.button_history.set_sensitive(False)
434
def on_entry_history_revno_key_press_event(self, widget, event):
435
""" Key pressed handler for the history entry. """
436
if event.keyval == 65293:
437
# Return was hit, so we have to load that specific revision
438
# Emulate being remote, so inventory should be used
439
path = self.get_path()
442
self.remote_branch = self.wt.branch
444
revno = int(self.entry_history.get_text())
445
self.remote_revision = self.remote_branch.get_rev_id(revno)
446
if self.set_path(path, True):
449
def on_entry_location_key_press_event(self, widget, event):
450
""" Key pressed handler for the location entry. """
451
if event.keyval == 65293:
452
# Return was hit, so we have to jump
453
self.on_button_location_jump_clicked(widget)
455
def on_menuitem_add_files_activate(self, widget):
456
""" Add file(s)... menu handler. """
457
from add import OliveAdd
458
add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
461
def on_menuitem_branch_get_activate(self, widget):
462
""" Branch/Get... menu handler. """
463
from bzrlib.plugins.gtk.branch import BranchDialog
466
branch = BranchDialog(os.getcwd(), self.window, self.remote_branch.base)
468
branch = BranchDialog(self.get_path(), self.window)
469
response = branch.run()
470
if response != gtk.RESPONSE_NONE:
473
if response == gtk.RESPONSE_OK:
478
def on_menuitem_branch_checkout_activate(self, widget):
479
""" Branch/Checkout... menu handler. """
480
from bzrlib.plugins.gtk.checkout import CheckoutDialog
483
checkout = CheckoutDialog(os.getcwd(), self.window, self.remote_branch.base)
485
checkout = CheckoutDialog(self.get_path(), self.window)
486
response = checkout.run()
487
if response != gtk.RESPONSE_NONE:
490
if response == gtk.RESPONSE_OK:
496
def on_menuitem_branch_commit_activate(self, widget):
497
""" Branch/Commit... menu handler. """
498
commit = CommitDialog(self.wt, self.wtpath, self.notbranch, self.get_selected_right(), self.window)
499
response = commit.run()
500
if response != gtk.RESPONSE_NONE:
503
if response == gtk.RESPONSE_OK:
508
def on_menuitem_branch_conflicts_activate(self, widget):
509
""" Branch/Conflicts... menu handler. """
510
conflicts = ConflictsDialog(self.wt, self.window)
511
response = conflicts.run()
512
if response != gtk.RESPONSE_NONE:
515
def on_menuitem_branch_merge_activate(self, widget):
516
""" Branch/Merge... menu handler. """
517
from bzrlib.plugins.gtk.merge import MergeDialog
519
if self.check_for_changes():
520
error_dialog(_('There are local changes in the branch'),
521
_('Please commit or revert the changes before merging.'))
523
merge = MergeDialog(self.wt, self.wtpath)
527
def on_menuitem_branch_missing_revisions_activate(self, widget):
528
""" Branch/Missing revisions menu handler. """
529
local_branch = self.wt.branch
531
other_branch = local_branch.get_parent()
532
if other_branch is None:
533
error_dialog(_('Parent location is unknown'),
534
_('Cannot determine missing revisions if no parent location is known.'))
537
remote_branch = Branch.open(other_branch)
539
if remote_branch.base == local_branch.base:
540
remote_branch = local_branch
542
ret = len(local_branch.missing_revisions(remote_branch))
545
info_dialog(_('There are missing revisions'),
546
_('%d revision(s) missing.') % ret)
548
info_dialog(_('Local branch up to date'),
549
_('There are no missing revisions.'))
552
def on_menuitem_branch_pull_activate(self, widget):
553
""" Branch/Pull menu handler. """
554
branch_to = self.wt.branch
556
location = branch_to.get_parent()
558
error_dialog(_('Parent location is unknown'),
559
_('Pulling is not possible until there is a parent location.'))
562
branch_from = Branch.open(location)
564
if branch_to.get_parent() is None:
565
branch_to.set_parent(branch_from.base)
567
ret = branch_to.pull(branch_from)
569
info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
572
def on_menuitem_branch_update_activate(self, widget):
573
""" Brranch/checkout update menu handler. """
575
ret = self.wt.update()
576
conflicts = self.wt.conflicts()
578
info_dialog(_('Update successful but conflicts generated'), _('Number of conflicts generated: %d.') % (len(conflicts),) )
580
info_dialog(_('Update successful'), _('No conflicts generated.') )
582
def on_menuitem_branch_push_activate(self, widget):
583
""" Branch/Push... menu handler. """
584
push = PushDialog(self.wt.branch, self.window)
585
response = push.run()
586
if response != gtk.RESPONSE_NONE:
590
def on_menuitem_branch_revert_activate(self, widget):
591
""" Branch/Revert all changes menu handler. """
592
ret = self.wt.revert([])
594
warning_dialog(_('Conflicts detected'),
595
_('Please have a look at the working tree before continuing.'))
597
info_dialog(_('Revert successful'),
598
_('All files reverted to last revision.'))
601
def on_menuitem_branch_status_activate(self, widget):
602
""" Branch/Status... menu handler. """
603
from bzrlib.plugins.gtk.status import StatusDialog
604
status = StatusDialog(self.wt, self.wtpath)
605
response = status.run()
606
if response != gtk.RESPONSE_NONE:
609
def on_menuitem_branch_initialize_activate(self, widget):
610
""" Initialize current directory. """
611
init = InitDialog(self.path, self.window)
612
response = init.run()
613
if response != gtk.RESPONSE_NONE:
616
if response == gtk.RESPONSE_OK:
621
def on_menuitem_branch_tags_activate(self, widget):
622
""" Branch/Tags... menu handler. """
623
from bzrlib.plugins.gtk.tags import TagsWindow
625
window = TagsWindow(self.wt.branch, self.window)
627
window = TagsWindow(self.remote_branch, self.window)
630
def on_menuitem_file_annotate_activate(self, widget):
631
""" File/Annotate... menu handler. """
632
if self.get_selected_right() is None:
633
error_dialog(_('No file was selected'),
634
_('Please select a file from the list.'))
637
branch = self.wt.branch
638
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
640
window = GAnnotateWindow(all=False, plain=False)
641
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
642
config = GAnnotateConfig(window)
646
window.annotate(self.wt, branch, file_id)
650
def on_menuitem_file_make_directory_activate(self, widget):
651
""" File/Make directory... menu handler. """
652
from mkdir import OliveMkdir
653
mkdir = OliveMkdir(self.wt, self.wtpath)
656
def on_menuitem_file_move_activate(self, widget):
657
""" File/Move... menu handler. """
658
from move import OliveMove
659
move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
662
def on_menuitem_file_rename_activate(self, widget):
663
""" File/Rename... menu handler. """
664
from rename import OliveRename
665
rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
668
def on_menuitem_remove_file_activate(self, widget):
669
""" Remove (unversion) selected file. """
670
from remove import OliveRemoveDialog
671
remove = OliveRemoveDialog(self.wt, self.wtpath,
672
selected=self.get_selected_right(),
674
response = remove.run()
676
if response != gtk.RESPONSE_NONE:
679
if response == gtk.RESPONSE_OK:
680
self.set_path(self.path)
685
def on_menuitem_stats_diff_activate(self, widget):
686
""" Statistics/Differences... menu handler. """
687
window = DiffWindow()
688
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
689
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
692
def on_menuitem_stats_infos_activate(self, widget):
693
""" Statistics/Informations... menu handler. """
694
from info import OliveInfo
696
info = OliveInfo(self.remote_branch)
698
info = OliveInfo(self.wt.branch)
701
def on_menuitem_stats_log_activate(self, widget):
702
""" Statistics/Log... menu handler. """
703
window = branchwin.BranchWindow()
705
window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
707
window.set_branch(self.remote_branch, self.remote_branch.last_revision(), None)
710
def on_menuitem_view_refresh_activate(self, widget):
711
""" View/Refresh menu handler. """
712
# Refresh the left pane
714
# Refresh the right pane
717
def on_menuitem_view_show_hidden_files_activate(self, widget):
718
""" View/Show hidden files menu handler. """
719
self.pref.set_preference('dotted_files', widget.get_active())
720
if self.path is not None:
723
def on_menuitem_view_show_ignored_files_activate(self, widget):
724
""" Hide/Show ignored files menu handler. """
725
self.pref.set_preference('ignored_files', widget.get_active())
726
if self.path is not None:
729
def on_treeview_left_button_press_event(self, widget, event):
730
""" Occurs when somebody right-clicks in the bookmark list. """
731
if event.button == 3:
732
# Don't show context with nothing selected
733
if self.get_selected_left() == None:
737
from menu import OliveMenu
738
menu = OliveMenu(path=self.get_path(),
739
selected=self.get_selected_left(),
742
menu.left_context_menu().popup(None, None, None, 0,
745
def on_treeview_left_row_activated(self, treeview, path, view_column):
746
""" Occurs when somebody double-clicks or enters an item in the
749
newdir = self.get_selected_left()
753
if self.set_path(newdir):
756
def on_treeview_right_button_press_event(self, widget, event):
757
""" Occurs when somebody right-clicks in the file list. """
758
if event.button == 3:
760
from menu import OliveMenu
761
menu = OliveMenu(path=self.get_path(),
762
selected=self.get_selected_right(),
765
m_open = menu.ui.get_widget('/context_right/open')
766
m_add = menu.ui.get_widget('/context_right/add')
767
m_remove = menu.ui.get_widget('/context_right/remove')
768
m_rename = menu.ui.get_widget('/context_right/rename')
769
m_revert = menu.ui.get_widget('/context_right/revert')
770
m_commit = menu.ui.get_widget('/context_right/commit')
771
m_annotate = menu.ui.get_widget('/context_right/annotate')
772
m_diff = menu.ui.get_widget('/context_right/diff')
773
# check if we're in a branch
775
from bzrlib.branch import Branch
776
Branch.open_containing(self.get_path())
778
m_open.set_sensitive(False)
779
m_add.set_sensitive(False)
780
m_remove.set_sensitive(False)
781
m_rename.set_sensitive(False)
782
m_revert.set_sensitive(False)
783
m_commit.set_sensitive(False)
784
m_annotate.set_sensitive(False)
785
m_diff.set_sensitive(False)
787
m_open.set_sensitive(True)
788
m_add.set_sensitive(True)
789
m_remove.set_sensitive(True)
790
m_rename.set_sensitive(True)
791
m_revert.set_sensitive(True)
792
m_commit.set_sensitive(True)
793
m_annotate.set_sensitive(True)
794
m_diff.set_sensitive(True)
795
except bzrerrors.NotBranchError:
796
m_open.set_sensitive(True)
797
m_add.set_sensitive(False)
798
m_remove.set_sensitive(False)
799
m_rename.set_sensitive(False)
800
m_revert.set_sensitive(False)
801
m_commit.set_sensitive(False)
802
m_annotate.set_sensitive(False)
803
m_diff.set_sensitive(False)
806
menu.right_context_menu().popup(None, None, None, 0,
809
menu.remote_context_menu().popup(None, None, None, 0,
812
def on_treeview_right_row_activated(self, treeview, path, view_column):
813
""" Occurs when somebody double-clicks or enters an item in the
815
from launch import launch
817
newdir = self.get_selected_right()
822
self.set_path(os.path.split(self.get_path())[0])
824
fullpath = os.path.join(self.get_path(), newdir)
825
if os.path.isdir(fullpath):
826
# selected item is an existant directory
827
self.set_path(fullpath)
832
if self._is_remote_dir(self.get_path() + newdir):
833
self.set_path(self.get_path() + newdir)
837
def on_window_main_delete_event(self, widget, event=None):
838
""" Do some stuff before exiting. """
839
width, height = self.window_main.get_size()
840
self.pref.set_preference('window_width', width)
841
self.pref.set_preference('window_height', height)
842
x, y = self.window_main.get_position()
843
self.pref.set_preference('window_x', x)
844
self.pref.set_preference('window_y', y)
845
self.pref.set_preference('paned_position',
846
self.hpaned_main.get_position())
849
self.window_main.destroy()
851
def _load_left(self):
852
""" Load data into the left panel. (Bookmarks) """
854
treestore = gtk.TreeStore(str, str)
857
bookmarks = self.pref.get_bookmarks()
859
# Add them to the TreeStore
860
titer = treestore.append(None, [_('Bookmarks'), None])
861
for item in bookmarks:
862
title = self.pref.get_bookmark_title(item)
863
treestore.append(titer, [title, item])
865
# Create the column and add it to the TreeView
866
self.treeview_left.set_model(treestore)
867
tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
868
self.treeview_left.append_column(tvcolumn_bookmark)
871
cell = gtk.CellRendererText()
872
tvcolumn_bookmark.pack_start(cell, True)
873
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
876
self.treeview_left.expand_all()
878
def _load_right(self):
879
""" Load data into the right panel. (Filelist) """
881
# Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
882
liststore = gtk.ListStore(gobject.TYPE_STRING,
883
gobject.TYPE_BOOLEAN,
896
# Fill the appropriate lists
897
dotted_files = self.pref.get_preference('dotted_files', 'bool')
898
for item in os.listdir(self.path):
899
if not dotted_files and item[0] == '.':
901
if os.path.isdir(self.path + os.sep + item):
906
if not self.notbranch:
907
branch = self.wt.branch
908
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
910
delta = self.wt.changes_from(tree2, want_unchanged=True)
912
# Add'em to the ListStore
914
statinfo = os.stat(self.path + os.sep + item)
915
liststore.append([ gtk.STOCK_DIRECTORY,
923
self._format_date(statinfo.st_mtime),
928
if not self.notbranch:
929
filename = self.wt.relpath(self.path + os.sep + item)
934
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
935
if rpathnew == filename:
938
for rpath, id, kind in delta.added:
939
if rpath == filename:
942
for rpath, id, kind in delta.removed:
943
if rpath == filename:
946
for rpath, id, kind, text_modified, meta_modified in delta.modified:
947
if rpath == filename:
950
for rpath, id, kind in delta.unchanged:
951
if rpath == filename:
954
for rpath, file_class, kind, id, entry in self.wt.list_files():
955
if rpath == filename and file_class == 'I':
960
if status == 'renamed':
962
elif status == 'removed':
964
elif status == 'added':
966
elif status == 'modified':
968
elif status == 'unchanged':
970
elif status == 'ignored':
975
statinfo = os.stat(self.path + os.sep + item)
976
liststore.append([gtk.STOCK_FILE,
981
str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
982
self._format_size(statinfo.st_size),
984
self._format_date(statinfo.st_mtime),
987
# Create the columns and add them to the TreeView
988
self.treeview_right.set_model(liststore)
989
self._tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
990
self._tvcolumn_status = gtk.TreeViewColumn(_('Status'))
991
self._tvcolumn_size = gtk.TreeViewColumn(_('Size'))
992
self._tvcolumn_mtime = gtk.TreeViewColumn(_('Last modified'))
993
self.treeview_right.append_column(self._tvcolumn_filename)
994
self.treeview_right.append_column(self._tvcolumn_status)
995
self.treeview_right.append_column(self._tvcolumn_size)
996
self.treeview_right.append_column(self._tvcolumn_mtime)
999
cellpb = gtk.CellRendererPixbuf()
1000
cell = gtk.CellRendererText()
1001
self._tvcolumn_filename.pack_start(cellpb, False)
1002
self._tvcolumn_filename.pack_start(cell, True)
1003
self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
1004
self._tvcolumn_filename.add_attribute(cell, 'text', 2)
1005
self._tvcolumn_status.pack_start(cell, True)
1006
self._tvcolumn_status.add_attribute(cell, 'text', 3)
1007
self._tvcolumn_size.pack_start(cell, True)
1008
self._tvcolumn_size.add_attribute(cell, 'text', 6)
1009
self._tvcolumn_mtime.pack_start(cell, True)
1010
self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
1012
# Set up the properties of the TreeView
1013
self.treeview_right.set_headers_visible(True)
1014
self.treeview_right.set_headers_clickable(True)
1015
self.treeview_right.set_search_column(1)
1016
self._tvcolumn_filename.set_resizable(True)
1017
self._tvcolumn_status.set_resizable(True)
1018
self._tvcolumn_size.set_resizable(True)
1019
self._tvcolumn_mtime.set_resizable(True)
1021
liststore.set_sort_func(13, self._sort_filelist_callback, None)
1022
liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
1023
self._tvcolumn_filename.set_sort_column_id(13)
1024
self._tvcolumn_status.set_sort_column_id(3)
1025
self._tvcolumn_size.set_sort_column_id(5)
1026
self._tvcolumn_mtime.set_sort_column_id(7)
1029
self.set_sensitivity()
1031
def get_selected_fileid(self):
1032
""" Get the file_id of the selected file. """
1033
treeselection = self.treeview_right.get_selection()
1034
(model, iter) = treeselection.get_selected()
1039
return model.get_value(iter, 9)
1041
def get_selected_right(self):
1042
""" Get the selected filename. """
1043
treeselection = self.treeview_right.get_selection()
1044
(model, iter) = treeselection.get_selected()
1049
return model.get_value(iter, 2)
1051
def get_selected_left(self):
1052
""" Get the selected bookmark. """
1053
treeselection = self.treeview_left.get_selection()
1054
(model, iter) = treeselection.get_selected()
1059
return model.get_value(iter, 1)
1061
def set_statusbar(self, message):
1062
""" Set the statusbar message. """
1063
self.statusbar.push(self.context_id, message)
1065
def clear_statusbar(self):
1066
""" Clean the last message from the statusbar. """
1067
self.statusbar.pop(self.context_id)
1069
def set_sensitivity(self):
1070
""" Set menu and toolbar sensitivity. """
1073
self.menuitem_branch_init.set_sensitive(self.notbranch)
1074
self.menuitem_branch_get.set_sensitive(self.notbranch)
1075
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
1076
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
1077
self.menuitem_branch_push.set_sensitive(not self.notbranch)
1078
self.menuitem_branch_update.set_sensitive(not self.notbranch)
1079
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
1080
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
1081
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
1082
self.menuitem_branch_tags.set_sensitive(not self.notbranch)
1083
self.menuitem_branch_status.set_sensitive(not self.notbranch)
1084
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
1085
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
1086
self.menuitem_stats.set_sensitive(not self.notbranch)
1087
self.menuitem_stats_diff.set_sensitive(not self.notbranch)
1088
self.menuitem_add_files.set_sensitive(not self.notbranch)
1089
self.menuitem_remove_files.set_sensitive(not self.notbranch)
1090
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
1091
self.menuitem_file_rename.set_sensitive(not self.notbranch)
1092
self.menuitem_file_move.set_sensitive(not self.notbranch)
1093
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
1094
#self.menutoolbutton_diff.set_sensitive(True)
1095
self.toolbutton_diff.set_sensitive(not self.notbranch)
1096
self.toolbutton_log.set_sensitive(not self.notbranch)
1097
self.toolbutton_commit.set_sensitive(not self.notbranch)
1098
self.toolbutton_pull.set_sensitive(not self.notbranch)
1099
self.toolbutton_push.set_sensitive(not self.notbranch)
1100
self.toolbutton_update.set_sensitive(not self.notbranch)
1103
self.menuitem_branch_init.set_sensitive(False)
1104
self.menuitem_branch_get.set_sensitive(True)
1105
self.menuitem_branch_checkout.set_sensitive(True)
1106
self.menuitem_branch_pull.set_sensitive(False)
1107
self.menuitem_branch_push.set_sensitive(False)
1108
self.menuitem_branch_update.set_sensitive(False)
1109
self.menuitem_branch_revert.set_sensitive(False)
1110
self.menuitem_branch_merge.set_sensitive(False)
1111
self.menuitem_branch_commit.set_sensitive(False)
1112
self.menuitem_branch_tags.set_sensitive(True)
1113
self.menuitem_branch_status.set_sensitive(False)
1114
self.menuitem_branch_missing.set_sensitive(False)
1115
self.menuitem_branch_conflicts.set_sensitive(False)
1116
self.menuitem_stats.set_sensitive(True)
1117
self.menuitem_stats_diff.set_sensitive(False)
1118
self.menuitem_add_files.set_sensitive(False)
1119
self.menuitem_remove_files.set_sensitive(False)
1120
self.menuitem_file_make_directory.set_sensitive(False)
1121
self.menuitem_file_rename.set_sensitive(False)
1122
self.menuitem_file_move.set_sensitive(False)
1123
self.menuitem_file_annotate.set_sensitive(False)
1124
#self.menutoolbutton_diff.set_sensitive(True)
1125
self.toolbutton_diff.set_sensitive(False)
1126
self.toolbutton_log.set_sensitive(True)
1127
self.toolbutton_commit.set_sensitive(False)
1128
self.toolbutton_pull.set_sensitive(False)
1129
self.toolbutton_push.set_sensitive(False)
1130
self.toolbutton_update.set_sensitive(False)
1132
def refresh_left(self):
1133
""" Refresh the bookmark list. """
1135
# Get TreeStore and clear it
1136
treestore = self.treeview_left.get_model()
1139
# Re-read preferences
1143
bookmarks = self.pref.get_bookmarks()
1145
# Add them to the TreeStore
1146
titer = treestore.append(None, [_('Bookmarks'), None])
1147
for item in bookmarks:
1148
title = self.pref.get_bookmark_title(item)
1149
treestore.append(titer, [title, item])
1151
# Add the TreeStore to the TreeView
1152
self.treeview_left.set_model(treestore)
1155
self.treeview_left.expand_all()
1157
def refresh_right(self, path=None):
1158
""" Refresh the file list. """
1161
from bzrlib.workingtree import WorkingTree
1164
path = self.get_path()
1166
# A workaround for double-clicking Bookmarks
1167
if not os.path.exists(path):
1170
# Get ListStore and clear it
1171
liststore = self.treeview_right.get_model()
1174
# Show Status column
1175
self._tvcolumn_status.set_visible(True)
1180
# Fill the appropriate lists
1181
dotted_files = self.pref.get_preference('dotted_files', 'bool')
1182
ignored_files = self.pref.get_preference('ignored_files', 'bool')
1184
for item in os.listdir(path):
1185
if not dotted_files and item[0] == '.':
1187
if os.path.isdir(path + os.sep + item):
1192
# Try to open the working tree
1195
tree1 = WorkingTree.open_containing(path)[0]
1196
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
1200
branch = tree1.branch
1201
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
1203
delta = tree1.changes_from(tree2, want_unchanged=True)
1205
# Add'em to the ListStore
1207
statinfo = os.stat(self.path + os.sep + item)
1208
liststore.append([gtk.STOCK_DIRECTORY,
1216
self._format_date(statinfo.st_mtime),
1222
filename = tree1.relpath(path + os.sep + item)
1227
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
1228
if rpathnew == filename:
1231
for rpath, id, kind in delta.added:
1232
if rpath == filename:
1235
for rpath, id, kind in delta.removed:
1236
if rpath == filename:
1239
for rpath, id, kind, text_modified, meta_modified in delta.modified:
1240
if rpath == filename:
1243
for rpath, id, kind in delta.unchanged:
1244
if rpath == filename:
1245
status = 'unchanged'
1247
for rpath, file_class, kind, id, entry in self.wt.list_files():
1248
if rpath == filename and file_class == 'I':
1253
if status == 'renamed':
1255
elif status == 'removed':
1257
elif status == 'added':
1259
elif status == 'modified':
1261
elif status == 'unchanged':
1263
elif status == 'ignored':
1265
if not ignored_files:
1270
statinfo = os.stat(self.path + os.sep + item)
1271
liststore.append([gtk.STOCK_FILE,
1276
str(statinfo.st_size),
1277
self._format_size(statinfo.st_size),
1279
self._format_date(statinfo.st_mtime),
1284
# Get ListStore and clear it
1285
liststore = self.treeview_right.get_model()
1288
# Hide Status column
1289
self._tvcolumn_status.set_visible(False)
1294
self._show_stock_image(gtk.STOCK_REFRESH)
1296
for (name, type) in self.remote_entries:
1297
if type.kind == 'directory':
1299
elif type.kind == 'file':
1303
""" Cache based on revision history. """
1304
def __init__(self, history):
1305
self._history = history
1307
def _lookup_revision(self, revid):
1308
for r in self._history:
1309
if r.revision_id == revid:
1311
rev = repo.get_revision(revid)
1312
self._history.append(rev)
1315
repo = self.remote_branch.repository
1317
revhistory = self.remote_branch.revision_history()
1319
revs = repo.get_revisions(revhistory)
1320
cache = HistoryCache(revs)
1321
except bzrerrors.InvalidHttpResponse:
1322
# Fallback to dummy algorithm, because of LP: #115209
1323
cache = HistoryCache([])
1326
if item.parent_id == self.remote_parent:
1327
rev = cache._lookup_revision(item.revision)
1328
liststore.append([ gtk.STOCK_DIRECTORY,
1336
self._format_date(rev.timestamp),
1339
while gtk.events_pending():
1340
gtk.main_iteration()
1343
if item.parent_id == self.remote_parent:
1344
rev = cache._lookup_revision(item.revision)
1345
liststore.append([ gtk.STOCK_FILE,
1350
str(item.text_size),
1351
self._format_size(item.text_size),
1353
self._format_date(rev.timestamp),
1356
while gtk.events_pending():
1357
gtk.main_iteration()
1359
self.image_location_error.destroy()
1361
# Columns should auto-size
1362
self.treeview_right.columns_autosize()
1365
self.set_sensitivity()
1367
def _harddisks(self):
1368
""" Returns hard drive letters under Win32. """
1373
if sys.platform == 'win32':
1374
print "pyWin32 modules needed to run Olive on Win32."
1380
for drive in string.ascii_uppercase:
1381
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
1382
driveletters.append(drive+':')
1385
def gen_hard_selector(self):
1386
""" Generate the hard drive selector under Win32. """
1387
drives = self._harddisks()
1388
for drive in drives:
1389
self.combobox_drive.append_text(drive)
1390
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
1392
def _refresh_drives(self, combobox):
1393
if self._just_started:
1395
model = combobox.get_model()
1396
active = combobox.get_active()
1398
drive = model[active][0]
1399
self.set_path(drive + '\\')
1400
self.refresh_right(drive + '\\')
1402
def check_for_changes(self):
1403
""" Check whether there were changes in the current working tree. """
1404
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
1405
delta = self.wt.changes_from(old_tree)
1409
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
1414
def _sort_filelist_callback(self, model, iter1, iter2, data):
1415
""" The sort callback for the file list, return values:
1420
name1 = model.get_value(iter1, 2)
1421
name2 = model.get_value(iter2, 2)
1423
if model.get_value(iter1, 1):
1424
# item1 is a directory
1425
if not model.get_value(iter2, 1):
1429
# both of them are directories, we compare their names
1432
elif name1 == name2:
1437
# item1 is not a directory
1438
if model.get_value(iter2, 1):
1442
# both of them are files, compare them
1445
elif name1 == name2:
1450
def _format_size(self, size):
1451
""" Format size to a human readable format. """
1453
return "%d[B]" % (size,)
1454
size = size / 1000.0
1456
for metric in ["kB","MB","GB","TB"]:
1459
size = size / 1000.0
1460
return "%.1f[%s]" % (size,metric)
1462
def _format_date(self, timestamp):
1463
""" Format the time (given in secs) to a human readable format. """
1464
return time.ctime(timestamp)
1466
def _is_remote_dir(self, location):
1467
""" Determine whether the given location is a directory or not. """
1469
# We're in local mode
1472
branch, path = Branch.open_containing(location)
1473
for (name, type) in self.remote_entries:
1474
if name == path and type.kind == 'directory':
1477
# Either it's not a directory or not in the inventory
1480
def _show_stock_image(self, stock_id):
1481
""" Show a stock image next to the location entry. """
1482
self.image_location_error.destroy()
1483
self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
1484
self.hbox_location.pack_start(self.image_location_error, False, False, 0)
1485
if sys.platform == 'win32':
1486
self.hbox_location.reorder_child(self.image_location_error, 2)
1488
self.hbox_location.reorder_child(self.image_location_error, 1)
1489
self.image_location_error.show()
1490
while gtk.events_pending():
1491
gtk.main_iteration()
1496
""" A class which handles Olive's preferences. """
1497
def __init__(self, path=None):
1498
""" Initialize the Preferences class. """
1499
# Some default options
1500
self.defaults = { 'strict_commit' : False,
1501
'dotted_files' : False,
1502
'ignored_files' : True,
1503
'window_width' : 700,
1504
'window_height' : 400,
1507
'paned_position': 200 }
1509
# Create a config parser object
1510
self.config = ConfigParser.RawConfigParser()
1514
if sys.platform == 'win32':
1515
# Windows - no dotted files
1516
self._filename = os.path.expanduser('~/olive.conf')
1518
self._filename = os.path.expanduser('~/.olive.conf')
1520
self._filename = path
1522
# Load the configuration
1525
def _get_default(self, option):
1526
""" Get the default option for a preference. """
1528
ret = self.defaults[option]
1535
""" Refresh the configuration. """
1536
# First write out the changes
1538
# Then load the configuration again
1542
""" Just read the configuration. """
1543
# Re-initialize the config parser object to avoid some bugs
1544
self.config = ConfigParser.RawConfigParser()
1545
self.config.read([self._filename])
1548
""" Write the configuration to the appropriate files. """
1549
fp = open(self._filename, 'w')
1550
self.config.write(fp)
1553
def get_bookmarks(self):
1554
""" Return the list of bookmarks. """
1555
bookmarks = self.config.sections()
1556
if self.config.has_section('preferences'):
1557
bookmarks.remove('preferences')
1560
def add_bookmark(self, path):
1561
""" Add bookmark. """
1563
self.config.add_section(path)
1564
except ConfigParser.DuplicateSectionError:
1569
def get_bookmark_title(self, path):
1570
""" Get bookmark title. """
1572
ret = self.config.get(path, 'title')
1573
except ConfigParser.NoOptionError:
1578
def set_bookmark_title(self, path, title):
1579
""" Set bookmark title. """
1580
# FIXME: What if path isn't listed yet?
1581
# FIXME: Canonicalize paths first?
1582
self.config.set(path, 'title', title)
1584
def remove_bookmark(self, path):
1585
""" Remove bookmark. """
1586
return self.config.remove_section(path)
1588
def set_preference(self, option, value):
1589
""" Set the value of the given option. """
1592
elif value is False:
1595
if self.config.has_section('preferences'):
1596
self.config.set('preferences', option, value)
1598
self.config.add_section('preferences')
1599
self.config.set('preferences', option, value)
1601
def get_preference(self, option, kind='str'):
1602
""" Get the value of the given option.
1604
:param kind: str/bool/int/float. default: str
1606
if self.config.has_option('preferences', option):
1608
return self.config.getboolean('preferences', option)
1610
return self.config.getint('preferences', option)
1611
elif kind == 'float':
1612
return self.config.getfloat('preferences', option)
1614
return self.config.get('preferences', option)
1617
return self._get_default(option)