# Copyright (C) 2006 by Szilveszter Farkas (Phanatic) <szilveszter.farkas@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import sys
import time
# gettext support
import gettext
gettext.install('olive-gtk')
try:
import pygtk
pygtk.require("2.0")
except:
pass
import gobject
import gtk
import gtk.gdk
import gtk.glade
from bzrlib.branch import Branch
import bzrlib.errors as bzrerrors
from bzrlib.lazy_import import lazy_import
from bzrlib.ui import ui_factory
from bzrlib.workingtree import WorkingTree
from bzrlib.plugins.gtk.dialog import error_dialog, info_dialog, warning_dialog
from bzrlib.plugins.gtk.errors import show_bzr_error
from guifiles import GLADEFILENAME
from bzrlib.plugins.gtk.diff import DiffWindow
lazy_import(globals(), """
from bzrlib.plugins.gtk.viz import branchwin
""")
from bzrlib.plugins.gtk.annotate.gannotate import GAnnotateWindow
from bzrlib.plugins.gtk.annotate.config import GAnnotateConfig
from bzrlib.plugins.gtk.commit import CommitDialog
from bzrlib.plugins.gtk.conflicts import ConflictsDialog
from bzrlib.plugins.gtk.initialize import InitDialog
from bzrlib.plugins.gtk.push import PushDialog
from bzrlib.plugins.gtk.revbrowser import RevisionBrowser
def about():
""" Display the AboutDialog. """
from bzrlib.plugins.gtk import __version__
from bzrlib.plugins.gtk.olive.guifiles import GLADEFILENAME
# Load AboutDialog description
dglade = gtk.glade.XML(GLADEFILENAME, 'aboutdialog')
dialog = dglade.get_widget('aboutdialog')
# Set version
dialog.set_version(__version__)
dialog.set_authors([ _("Lead Developer:"),
"Szilveszter Farkas <szilveszter.farkas@gmail.com>",
_("Contributors:"),
"Jelmer Vernooij <jelmer@samba.org>",
"Mateusz Korniak <mateusz.korniak@ant.gliwice.pl>",
"Gary van der Merwe <garyvdm@gmail.com>" ])
dialog.set_artists([ "Simon Pascal Klein <klepas@klepas.org>",
"Jakub Steiner <jimmac@novell.com>" ])
dialog.run()
# Destroy the dialog
dialog.destroy()
class OliveGtk:
""" The main Olive GTK frontend class. This is called when launching the
program. """
def __init__(self):
self.toplevel = gtk.glade.XML(GLADEFILENAME, 'window_main', 'olive-gtk')
self.window = self.toplevel.get_widget('window_main')
self.pref = Preferences()
self.path = None
# Initialize the statusbar
self.statusbar = self.toplevel.get_widget('statusbar')
self.context_id = self.statusbar.get_context_id('olive')
# Get the main window
self.window_main = self.toplevel.get_widget('window_main')
# Get the HPaned
self.hpaned_main = self.toplevel.get_widget('hpaned_main')
# Get the TreeViews
self.treeview_left = self.toplevel.get_widget('treeview_left')
self.treeview_right = self.toplevel.get_widget('treeview_right')
# Get some important menu items
self.menuitem_add_files = self.toplevel.get_widget('menuitem_add_files')
self.menuitem_remove_files = self.toplevel.get_widget('menuitem_remove_file')
self.menuitem_file_make_directory = self.toplevel.get_widget('menuitem_file_make_directory')
self.menuitem_file_rename = self.toplevel.get_widget('menuitem_file_rename')
self.menuitem_file_move = self.toplevel.get_widget('menuitem_file_move')
self.menuitem_file_annotate = self.toplevel.get_widget('menuitem_file_annotate')
self.menuitem_view_show_hidden_files = self.toplevel.get_widget('menuitem_view_show_hidden_files')
self.menuitem_view_show_ignored_files = self.toplevel.get_widget('menuitem_view_show_ignored_files')
self.menuitem_branch = self.toplevel.get_widget('menuitem_branch')
self.menuitem_branch_init = self.toplevel.get_widget('menuitem_branch_initialize')
self.menuitem_branch_get = self.toplevel.get_widget('menuitem_branch_get')
self.menuitem_branch_checkout = self.toplevel.get_widget('menuitem_branch_checkout')
self.menuitem_branch_pull = self.toplevel.get_widget('menuitem_branch_pull')
self.menuitem_branch_push = self.toplevel.get_widget('menuitem_branch_push')
self.menuitem_branch_update = self.toplevel.get_widget('menuitem_branch_update')
self.menuitem_branch_revert = self.toplevel.get_widget('menuitem_branch_revert')
self.menuitem_branch_merge = self.toplevel.get_widget('menuitem_branch_merge')
self.menuitem_branch_commit = self.toplevel.get_widget('menuitem_branch_commit')
self.menuitem_branch_tags = self.toplevel.get_widget('menuitem_branch_tags')
self.menuitem_branch_status = self.toplevel.get_widget('menuitem_branch_status')
self.menuitem_branch_missing = self.toplevel.get_widget('menuitem_branch_missing_revisions')
self.menuitem_branch_conflicts = self.toplevel.get_widget('menuitem_branch_conflicts')
self.menuitem_stats = self.toplevel.get_widget('menuitem_stats')
self.menuitem_stats_diff = self.toplevel.get_widget('menuitem_stats_diff')
self.menuitem_stats_log = self.toplevel.get_widget('menuitem_stats_log')
# Get some toolbuttons
#self.menutoolbutton_diff = self.toplevel.get_widget('menutoolbutton_diff')
self.toolbutton_diff = self.toplevel.get_widget('toolbutton_diff')
self.toolbutton_log = self.toplevel.get_widget('toolbutton_log')
self.toolbutton_commit = self.toplevel.get_widget('toolbutton_commit')
self.toolbutton_pull = self.toplevel.get_widget('toolbutton_pull')
self.toolbutton_push = self.toplevel.get_widget('toolbutton_push')
self.toolbutton_update = self.toplevel.get_widget('toolbutton_update')
# Get the drive selector
self.combobox_drive = gtk.combo_box_new_text()
self.combobox_drive.connect("changed", self._refresh_drives)
# Get the navigation widgets
self.hbox_location = self.toplevel.get_widget('hbox_location')
self.button_location_up = self.toplevel.get_widget('button_location_up')
self.button_location_jump = self.toplevel.get_widget('button_location_jump')
self.entry_location = self.toplevel.get_widget('entry_location')
self.image_location_error = self.toplevel.get_widget('image_location_error')
# Get the History widgets
self.check_history = self.toplevel.get_widget('checkbutton_history')
self.entry_history = self.toplevel.get_widget('entry_history_revno')
self.button_history = self.toplevel.get_widget('button_history_browse')
self.vbox_main_right = self.toplevel.get_widget('vbox_main_right')
# Dictionary for signal_autoconnect
dic = { "on_window_main_destroy": gtk.main_quit,
"on_window_main_delete_event": self.on_window_main_delete_event,
"on_quit_activate": self.on_window_main_delete_event,
"on_about_activate": self.on_about_activate,
"on_menuitem_add_files_activate": self.on_menuitem_add_files_activate,
"on_menuitem_remove_file_activate": self.on_menuitem_remove_file_activate,
"on_menuitem_file_make_directory_activate": self.on_menuitem_file_make_directory_activate,
"on_menuitem_file_move_activate": self.on_menuitem_file_move_activate,
"on_menuitem_file_rename_activate": self.on_menuitem_file_rename_activate,
"on_menuitem_file_annotate_activate": self.on_menuitem_file_annotate_activate,
"on_menuitem_view_show_hidden_files_activate": self.on_menuitem_view_show_hidden_files_activate,
"on_menuitem_view_show_ignored_files_activate": self.on_menuitem_view_show_ignored_files_activate,
"on_menuitem_view_refresh_activate": self.on_menuitem_view_refresh_activate,
"on_menuitem_branch_initialize_activate": self.on_menuitem_branch_initialize_activate,
"on_menuitem_branch_get_activate": self.on_menuitem_branch_get_activate,
"on_menuitem_branch_checkout_activate": self.on_menuitem_branch_checkout_activate,
"on_menuitem_branch_revert_activate": self.on_menuitem_branch_revert_activate,
"on_menuitem_branch_merge_activate": self.on_menuitem_branch_merge_activate,
"on_menuitem_branch_commit_activate": self.on_menuitem_branch_commit_activate,
"on_menuitem_branch_push_activate": self.on_menuitem_branch_push_activate,
"on_menuitem_branch_pull_activate": self.on_menuitem_branch_pull_activate,
"on_menuitem_branch_update_activate": self.on_menuitem_branch_update_activate,
"on_menuitem_branch_tags_activate": self.on_menuitem_branch_tags_activate,
"on_menuitem_branch_status_activate": self.on_menuitem_branch_status_activate,
"on_menuitem_branch_missing_revisions_activate": self.on_menuitem_branch_missing_revisions_activate,
"on_menuitem_branch_conflicts_activate": self.on_menuitem_branch_conflicts_activate,
"on_menuitem_stats_diff_activate": self.on_menuitem_stats_diff_activate,
"on_menuitem_stats_log_activate": self.on_menuitem_stats_log_activate,
"on_menuitem_stats_infos_activate": self.on_menuitem_stats_infos_activate,
"on_toolbutton_refresh_clicked": self.on_menuitem_view_refresh_activate,
"on_toolbutton_log_clicked": self.on_menuitem_stats_log_activate,
#"on_menutoolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
"on_toolbutton_diff_clicked": self.on_menuitem_stats_diff_activate,
"on_toolbutton_commit_clicked": self.on_menuitem_branch_commit_activate,
"on_toolbutton_pull_clicked": self.on_menuitem_branch_pull_activate,
"on_toolbutton_push_clicked": self.on_menuitem_branch_push_activate,
"on_toolbutton_update_clicked": self.on_menuitem_branch_update_activate,
"on_treeview_right_button_press_event": self.on_treeview_right_button_press_event,
"on_treeview_right_row_activated": self.on_treeview_right_row_activated,
"on_treeview_left_button_press_event": self.on_treeview_left_button_press_event,
"on_treeview_left_row_activated": self.on_treeview_left_row_activated,
"on_button_location_up_clicked": self.on_button_location_up_clicked,
"on_button_location_jump_clicked": self.on_button_location_jump_clicked,
"on_entry_location_key_press_event": self.on_entry_location_key_press_event,
"on_checkbutton_history_toggled": self.on_checkbutton_history_toggled,
"on_entry_history_revno_key_press_event": self.on_entry_history_revno_key_press_event,
"on_button_history_browse_clicked": self.on_button_history_browse_clicked
}
# Connect the signals to the handlers
self.toplevel.signal_autoconnect(dic)
self._just_started = True
# Apply window size and position
width = self.pref.get_preference('window_width', 'int')
height = self.pref.get_preference('window_height', 'int')
self.window.resize(width, height)
x = self.pref.get_preference('window_x', 'int')
y = self.pref.get_preference('window_y', 'int')
self.window.move(x, y)
# Apply paned position
pos = self.pref.get_preference('paned_position', 'int')
self.hpaned_main.set_position(pos)
# Apply menu to the toolbutton
#menubutton = self.toplevel.get_widget('menutoolbutton_diff')
#menubutton.set_menu(handler.menu.toolbar_diff)
# Now we can show the window
self.window.show()
# Show drive selector if under Win32
if sys.platform == 'win32':
self.hbox_location.pack_start(self.combobox_drive, False, False, 0)
self.hbox_location.reorder_child(self.combobox_drive, 1)
self.combobox_drive.show()
self.gen_hard_selector()
self._load_left()
# Apply menu state
self.menuitem_view_show_hidden_files.set_active(self.pref.get_preference('dotted_files', 'bool'))
self.menuitem_view_show_ignored_files.set_active(self.pref.get_preference('ignored_files', 'bool'))
# We're starting local
self.remote = False
self.remote_branch = None
self.remote_path = None
self.remote_revision = None
self.set_path(os.getcwd())
self._load_right()
self._just_started = False
def set_path(self, path, force_remote=False):
self.notbranch = False
if force_remote:
# Forcing remote mode (reading data from inventory)
self._show_stock_image(gtk.STOCK_DISCONNECT)
try:
br = Branch.open_containing(path)[0]
except bzrerrors.NotBranchError:
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
self.check_history.set_active(False)
self.check_history.set_sensitive(False)
return False
except bzrerrors.UnsupportedProtocol:
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
self.check_history.set_active(False)
self.check_history.set_sensitive(False)
return False
self._show_stock_image(gtk.STOCK_CONNECT)
self.remote = True
# We're remote
self.remote_branch, self.remote_path = Branch.open_containing(path)
if self.remote_revision is None:
self.remote_revision = self.remote_branch.last_revision()
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
if len(self.remote_path) == 0:
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
else:
for (name, type) in self.remote_entries:
if name == self.remote_path:
self.remote_parent = type.file_id
break
if not path.endswith('/'):
path += '/'
if self.remote_branch.base == path:
self.button_location_up.set_sensitive(False)
else:
self.button_location_up.set_sensitive(True)
else:
if os.path.isdir(path):
self.image_location_error.destroy()
self.remote = False
# We're local
try:
self.wt, self.wtpath = WorkingTree.open_containing(path)
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
self.notbranch = True
# If we're in the root, we cannot go up anymore
if sys.platform == 'win32':
drive, tail = os.path.splitdrive(path)
if tail in ('', '/', '\\'):
self.button_location_up.set_sensitive(False)
else:
self.button_location_up.set_sensitive(True)
else:
if self.path == '/':
self.button_location_up.set_sensitive(False)
else:
self.button_location_up.set_sensitive(True)
elif not os.path.isfile(path):
# Doesn't seem to be a file nor a directory, trying to open a
# remote location
self._show_stock_image(gtk.STOCK_DISCONNECT)
try:
br = Branch.open_containing(path)[0]
except bzrerrors.NotBranchError:
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
self.check_history.set_active(False)
self.check_history.set_sensitive(False)
return False
except bzrerrors.UnsupportedProtocol:
self._show_stock_image(gtk.STOCK_DIALOG_ERROR)
self.check_history.set_active(False)
self.check_history.set_sensitive(False)
return False
self._show_stock_image(gtk.STOCK_CONNECT)
self.remote = True
# We're remote
self.remote_branch, self.remote_path = Branch.open_containing(path)
if self.remote_revision is None:
self.remote_revision = self.remote_branch.last_revision()
self.remote_entries = self.remote_branch.repository.get_inventory(self.remote_revision).entries()
if len(self.remote_path) == 0:
self.remote_parent = self.remote_branch.repository.get_inventory(self.remote_branch.last_revision()).iter_entries_by_dir().next()[1].file_id
else:
for (name, type) in self.remote_entries:
if name == self.remote_path:
self.remote_parent = type.file_id
break
if not path.endswith('/'):
path += '/'
if self.remote_branch.base == path:
self.button_location_up.set_sensitive(False)
else:
self.button_location_up.set_sensitive(True)
if self.notbranch:
self.check_history.set_active(False)
self.check_history.set_sensitive(False)
else:
self.check_history.set_sensitive(True)
self.statusbar.push(self.context_id, path)
self.entry_location.set_text(path)
self.path = path
return True
def get_path(self):
if not self.remote:
return self.path
else:
# Remote mode
if len(self.remote_path) > 0:
return self.remote_branch.base + self.remote_path + '/'
else:
return self.remote_branch.base
def on_about_activate(self, widget):
about()
def on_button_history_browse_clicked(self, widget):
""" Browse for revision button handler. """
if self.remote:
br = self.remote_branch
else:
br = self.wt.branch
revb = RevisionBrowser(br, self.window)
response = revb.run()
if response != gtk.RESPONSE_NONE:
revb.hide()
if response == gtk.RESPONSE_OK:
if revb.selected_revno is not None:
self.entry_history.set_text(revb.selected_revno)
revb.destroy()
def on_button_location_jump_clicked(self, widget):
""" Location Jump button handler. """
location = self.entry_location.get_text()
if self.set_path(location):
self.refresh_right()
def on_button_location_up_clicked(self, widget):
""" Location Up button handler. """
if not self.remote:
# Local mode
self.set_path(os.path.split(self.get_path())[0])
else:
# Remote mode
delim = '/'
newpath = delim.join(self.get_path().split(delim)[:-2])
newpath += '/'
self.set_path(newpath)
self.refresh_right()
def on_checkbutton_history_toggled(self, widget):
""" History Mode toggle handler. """
if self.check_history.get_active():
# History Mode activated
self.entry_history.set_sensitive(True)
self.button_history.set_sensitive(True)
else:
# History Mode deactivated
self.entry_history.set_sensitive(False)
self.button_history.set_sensitive(False)
@show_bzr_error
def on_entry_history_revno_key_press_event(self, widget, event):
""" Key pressed handler for the history entry. """
if event.keyval == 65293:
# Return was hit, so we have to load that specific revision
# Emulate being remote, so inventory should be used
path = self.get_path()
if not self.remote:
self.remote = True
self.remote_branch = self.wt.branch
revno = int(self.entry_history.get_text())
self.remote_revision = self.remote_branch.get_rev_id(revno)
if self.set_path(path, True):
self.refresh_right()
def on_entry_location_key_press_event(self, widget, event):
""" Key pressed handler for the location entry. """
if event.keyval == 65293:
# Return was hit, so we have to jump
self.on_button_location_jump_clicked(widget)
def on_menuitem_add_files_activate(self, widget):
""" Add file(s)... menu handler. """
from add import OliveAdd
add = OliveAdd(self.wt, self.wtpath, self.get_selected_right())
add.display()
def on_menuitem_branch_get_activate(self, widget):
""" Branch/Get... menu handler. """
from bzrlib.plugins.gtk.branch import BranchDialog
if self.remote:
branch = BranchDialog(os.getcwd(), self.window, self.remote_branch.base)
else:
branch = BranchDialog(self.get_path(), self.window)
response = branch.run()
if response != gtk.RESPONSE_NONE:
branch.hide()
if response == gtk.RESPONSE_OK:
self.refresh_right()
branch.destroy()
def on_menuitem_branch_checkout_activate(self, widget):
""" Branch/Checkout... menu handler. """
from bzrlib.plugins.gtk.checkout import CheckoutDialog
if self.remote:
checkout = CheckoutDialog(os.getcwd(), self.window, self.remote_branch.base)
else:
checkout = CheckoutDialog(self.get_path(), self.window)
response = checkout.run()
if response != gtk.RESPONSE_NONE:
checkout.hide()
if response == gtk.RESPONSE_OK:
self.refresh_right()
checkout.destroy()
@show_bzr_error
def on_menuitem_branch_commit_activate(self, widget):
""" Branch/Commit... menu handler. """
commit = CommitDialog(self.wt, self.wtpath, self.notbranch, self.get_selected_right(), self.window)
response = commit.run()
if response != gtk.RESPONSE_NONE:
commit.hide()
if response == gtk.RESPONSE_OK:
self.refresh_right()
commit.destroy()
def on_menuitem_branch_conflicts_activate(self, widget):
""" Branch/Conflicts... menu handler. """
conflicts = ConflictsDialog(self.wt, self.window)
response = conflicts.run()
if response != gtk.RESPONSE_NONE:
conflicts.destroy()
def on_menuitem_branch_merge_activate(self, widget):
""" Branch/Merge... menu handler. """
from bzrlib.plugins.gtk.merge import MergeDialog
if self.check_for_changes():
error_dialog(_('There are local changes in the branch'),
_('Please commit or revert the changes before merging.'))
else:
parent_branch_path = self.wt.branch.get_parent()
merge = MergeDialog(self.wt, self.wtpath,default_branch_path=parent_branch_path )
merge.display()
@show_bzr_error
def on_menuitem_branch_missing_revisions_activate(self, widget):
""" Branch/Missing revisions menu handler. """
from bzrlib.missing import find_unmerged, iter_log_revisions
local_branch = self.wt.branch
parent_branch_path = local_branch.get_parent()
if parent_branch_path is None:
error_dialog(_('Parent location is unknown'),
_('Cannot determine missing revisions if no parent location is known.'))
return
parent_branch = Branch.open(parent_branch_path)
if parent_branch.base == local_branch.base:
parent_branch = local_branch
local_extra, remote_extra = find_unmerged(local_branch,parent_branch)
if local_extra or remote_extra:
## def log_revision_one_line_text(log_revision):
## """ Generates one line description of log_revison ended with end of line."""
## revision = log_revision.rev
## txt = "- %s (%s)\n" % (revision.get_summary(), revision.committer, )
## txt = txt.replace("<"," ") # Seems < > chars are expected to be xml tags ...
## txt = txt.replace(">"," ")
## return txt
dlg_txt = ""
if local_extra:
dlg_txt += _('%d local extra revision(s). \n') % (len(local_extra),)
## NOTE: We do not want such ugly info about missing revisions
## Revision Browser should be used there
## max_revisions = 10
## for log_revision in iter_log_revisions(local_extra, local_branch.repository, verbose=1):
## dlg_txt += log_revision_one_line_text(log_revision)
## if max_revisions <= 0:
## dlg_txt += _("more ... \n")
## break
## max_revisions -= 1
## dlg_txt += "\n"
if remote_extra:
dlg_txt += _('%d local missing revision(s).\n') % (len(remote_extra),)
## max_revisions = 10
## for log_revision in iter_log_revisions(remote_extra, parent_branch.repository, verbose=1):
## dlg_txt += log_revision_one_line_text(log_revision)
## if max_revisions <= 0:
## dlg_txt += _("more ... \n")
## break
## max_revisions -= 1
info_dialog(_('There are missing revisions'),
dlg_txt)
else:
info_dialog(_('Local branch up to date'),
_('There are no missing revisions.'))
@show_bzr_error
def on_menuitem_branch_pull_activate(self, widget):
""" Branch/Pull menu handler. """
branch_to = self.wt.branch
location = branch_to.get_parent()
if location is None:
error_dialog(_('Parent location is unknown'),
_('Pulling is not possible until there is a parent location.'))
return
branch_from = Branch.open(location)
if branch_to.get_parent() is None:
branch_to.set_parent(branch_from.base)
ret = branch_to.pull(branch_from)
info_dialog(_('Pull successful'), _('%d revision(s) pulled.') % ret)
@show_bzr_error
def on_menuitem_branch_update_activate(self, widget):
""" Brranch/checkout update menu handler. """
ret = self.wt.update()
conflicts = self.wt.conflicts()
if conflicts:
info_dialog(_('Update successful but conflicts generated'), _('Number of conflicts generated: %d.') % (len(conflicts),) )
else:
info_dialog(_('Update successful'), _('No conflicts generated.') )
def on_menuitem_branch_push_activate(self, widget):
""" Branch/Push... menu handler. """
push = PushDialog(repository=None,revid=None,branch=self.wt.branch, parent=self.window)
response = push.run()
if response != gtk.RESPONSE_NONE:
push.destroy()
@show_bzr_error
def on_menuitem_branch_revert_activate(self, widget):
""" Branch/Revert all changes menu handler. """
ret = self.wt.revert([])
if ret:
warning_dialog(_('Conflicts detected'),
_('Please have a look at the working tree before continuing.'))
else:
info_dialog(_('Revert successful'),
_('All files reverted to last revision.'))
self.refresh_right()
def on_menuitem_branch_status_activate(self, widget):
""" Branch/Status... menu handler. """
from bzrlib.plugins.gtk.status import StatusDialog
status = StatusDialog(self.wt, self.wtpath)
response = status.run()
if response != gtk.RESPONSE_NONE:
status.destroy()
def on_menuitem_branch_initialize_activate(self, widget):
""" Initialize current directory. """
init = InitDialog(self.path, self.window)
response = init.run()
if response != gtk.RESPONSE_NONE:
init.hide()
if response == gtk.RESPONSE_OK:
self.refresh_right()
init.destroy()
def on_menuitem_branch_tags_activate(self, widget):
""" Branch/Tags... menu handler. """
from bzrlib.plugins.gtk.tags import TagsWindow
if not self.remote:
window = TagsWindow(self.wt.branch, self.window)
else:
window = TagsWindow(self.remote_branch, self.window)
window.show()
def on_menuitem_file_annotate_activate(self, widget):
""" File/Annotate... menu handler. """
if self.get_selected_right() is None:
error_dialog(_('No file was selected'),
_('Please select a file from the list.'))
return
branch = self.wt.branch
file_id = self.wt.path2id(self.wt.relpath(os.path.join(self.path, self.get_selected_right())))
window = GAnnotateWindow(all=False, plain=False, parent=self.window)
window.set_title(os.path.join(self.path, self.get_selected_right()) + " - Annotate")
config = GAnnotateConfig(window)
window.show()
branch.lock_read()
try:
window.annotate(self.wt, branch, file_id)
finally:
branch.unlock()
def on_menuitem_file_make_directory_activate(self, widget):
""" File/Make directory... menu handler. """
from mkdir import OliveMkdir
mkdir = OliveMkdir(self.wt, self.wtpath)
mkdir.display()
def on_menuitem_file_move_activate(self, widget):
""" File/Move... menu handler. """
from move import OliveMove
move = OliveMove(self.wt, self.wtpath, self.get_selected_right())
move.display()
def on_menuitem_file_rename_activate(self, widget):
""" File/Rename... menu handler. """
from rename import OliveRename
rename = OliveRename(self.wt, self.wtpath, self.get_selected_right())
rename.display()
def on_menuitem_remove_file_activate(self, widget):
""" Remove (unversion) selected file. """
from remove import OliveRemoveDialog
remove = OliveRemoveDialog(self.wt, self.wtpath,
selected=self.get_selected_right(),
parent=self.window)
response = remove.run()
if response != gtk.RESPONSE_NONE:
remove.hide()
if response == gtk.RESPONSE_OK:
self.set_path(self.path)
self.refresh_right()
remove.destroy()
def on_menuitem_stats_diff_activate(self, widget):
""" Statistics/Differences... menu handler. """
window = DiffWindow()
parent_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
window.set_diff(self.wt.branch.nick, self.wt, parent_tree)
window.show()
def on_menuitem_stats_infos_activate(self, widget):
""" Statistics/Informations... menu handler. """
from info import OliveInfo
if self.remote:
info = OliveInfo(self.remote_branch)
else:
info = OliveInfo(self.wt.branch)
info.display()
def on_menuitem_stats_log_activate(self, widget):
""" Statistics/Log... menu handler. """
window = branchwin.BranchWindow(parent=self.window)
if not self.remote:
window.set_branch(self.wt.branch, self.wt.branch.last_revision(), None)
else:
window.set_branch(self.remote_branch, self.remote_branch.last_revision(), None)
window.show()
def on_menuitem_view_refresh_activate(self, widget):
""" View/Refresh menu handler. """
# Refresh the left pane
self.refresh_left()
# Refresh the right pane
self.refresh_right()
def on_menuitem_view_show_hidden_files_activate(self, widget):
""" View/Show hidden files menu handler. """
self.pref.set_preference('dotted_files', widget.get_active())
if self.path is not None:
self.refresh_right()
def on_menuitem_view_show_ignored_files_activate(self, widget):
""" Hide/Show ignored files menu handler. """
self.pref.set_preference('ignored_files', widget.get_active())
if self.path is not None:
self.refresh_right()
def on_treeview_left_button_press_event(self, widget, event):
""" Occurs when somebody right-clicks in the bookmark list. """
if event.button == 3:
# Don't show context with nothing selected
if self.get_selected_left() == None:
return
# Create a menu
from menu import OliveMenu
menu = OliveMenu(path=self.get_path(),
selected=self.get_selected_left(),
app=self)
menu.left_context_menu().popup(None, None, None, 0,
event.time)
def on_treeview_left_row_activated(self, treeview, path, view_column):
""" Occurs when somebody double-clicks or enters an item in the
bookmark list. """
newdir = self.get_selected_left()
if newdir == None:
return
if self.set_path(newdir):
self.refresh_right()
def on_treeview_right_button_press_event(self, widget, event):
""" Occurs when somebody right-clicks in the file list. """
if event.button == 3:
# Create a menu
from menu import OliveMenu
menu = OliveMenu(path=self.get_path(),
selected=self.get_selected_right(),
app=self)
# get the menu items
m_open = menu.ui.get_widget('/context_right/open')
m_add = menu.ui.get_widget('/context_right/add')
m_remove = menu.ui.get_widget('/context_right/remove')
m_rename = menu.ui.get_widget('/context_right/rename')
m_revert = menu.ui.get_widget('/context_right/revert')
m_commit = menu.ui.get_widget('/context_right/commit')
m_annotate = menu.ui.get_widget('/context_right/annotate')
m_diff = menu.ui.get_widget('/context_right/diff')
# check if we're in a branch
try:
from bzrlib.branch import Branch
Branch.open_containing(self.get_path())
if self.remote:
m_open.set_sensitive(False)
m_add.set_sensitive(False)
m_remove.set_sensitive(False)
m_rename.set_sensitive(False)
m_revert.set_sensitive(False)
m_commit.set_sensitive(False)
m_annotate.set_sensitive(False)
m_diff.set_sensitive(False)
else:
m_open.set_sensitive(True)
m_add.set_sensitive(True)
m_remove.set_sensitive(True)
m_rename.set_sensitive(True)
m_revert.set_sensitive(True)
m_commit.set_sensitive(True)
m_annotate.set_sensitive(True)
m_diff.set_sensitive(True)
except bzrerrors.NotBranchError:
m_open.set_sensitive(True)
m_add.set_sensitive(False)
m_remove.set_sensitive(False)
m_rename.set_sensitive(False)
m_revert.set_sensitive(False)
m_commit.set_sensitive(False)
m_annotate.set_sensitive(False)
m_diff.set_sensitive(False)
if not self.remote:
menu.right_context_menu().popup(None, None, None, 0,
event.time)
else:
menu.remote_context_menu().popup(None, None, None, 0,
event.time)
def on_treeview_right_row_activated(self, treeview, path, view_column):
""" Occurs when somebody double-clicks or enters an item in the
file list. """
from launch import launch
newdir = self.get_selected_right()
if not self.remote:
# We're local
if newdir == '..':
self.set_path(os.path.split(self.get_path())[0])
else:
fullpath = os.path.join(self.get_path(), newdir)
if os.path.isdir(fullpath):
# selected item is an existant directory
self.set_path(fullpath)
else:
launch(fullpath)
else:
# We're remote
if self._is_remote_dir(self.get_path() + newdir):
self.set_path(self.get_path() + newdir)
self.refresh_right()
def on_window_main_delete_event(self, widget, event=None):
""" Do some stuff before exiting. """
width, height = self.window_main.get_size()
self.pref.set_preference('window_width', width)
self.pref.set_preference('window_height', height)
x, y = self.window_main.get_position()
self.pref.set_preference('window_x', x)
self.pref.set_preference('window_y', y)
self.pref.set_preference('paned_position',
self.hpaned_main.get_position())
self.pref.write()
self.window_main.destroy()
def _load_left(self):
""" Load data into the left panel. (Bookmarks) """
# Create TreeStore
treestore = gtk.TreeStore(str, str)
# Get bookmarks
bookmarks = self.pref.get_bookmarks()
# Add them to the TreeStore
titer = treestore.append(None, [_('Bookmarks'), None])
for item in bookmarks:
title = self.pref.get_bookmark_title(item)
treestore.append(titer, [title, item])
# Create the column and add it to the TreeView
self.treeview_left.set_model(treestore)
tvcolumn_bookmark = gtk.TreeViewColumn(_('Bookmark'))
self.treeview_left.append_column(tvcolumn_bookmark)
# Set up the cells
cell = gtk.CellRendererText()
tvcolumn_bookmark.pack_start(cell, True)
tvcolumn_bookmark.add_attribute(cell, 'text', 0)
# Expand the tree
self.treeview_left.expand_all()
def _load_right(self):
""" Load data into the right panel. (Filelist) """
# Create ListStore
# Model: [ icon, dir, name, status text, status, size (int), size (human), mtime (int), mtime (local), fileid ]
liststore = gtk.ListStore(gobject.TYPE_STRING,
gobject.TYPE_BOOLEAN,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_STRING,
gobject.TYPE_INT,
gobject.TYPE_STRING,
gobject.TYPE_STRING)
dirs = []
files = []
# Fill the appropriate lists
dotted_files = self.pref.get_preference('dotted_files', 'bool')
for item in os.listdir(self.path):
if not dotted_files and item[0] == '.':
continue
if os.path.isdir(self.path + os.sep + item):
dirs.append(item)
else:
files.append(item)
if not self.notbranch:
branch = self.wt.branch
tree2 = self.wt.branch.repository.revision_tree(branch.last_revision())
delta = self.wt.changes_from(tree2, want_unchanged=True)
# Add'em to the ListStore
for item in dirs:
statinfo = os.stat(self.path + os.sep + item)
liststore.append([ gtk.STOCK_DIRECTORY,
True,
item,
'',
'',
"<DIR>",
"<DIR>",
statinfo.st_mtime,
self._format_date(statinfo.st_mtime),
''])
for item in files:
status = 'unknown'
fileid = ''
if not self.notbranch:
filename = self.wt.relpath(self.path + os.sep + item)
try:
self.wt.lock_read()
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
if rpathnew == filename:
status = 'renamed'
fileid = id
for rpath, id, kind in delta.added:
if rpath == filename:
status = 'added'
fileid = id
for rpath, id, kind in delta.removed:
if rpath == filename:
status = 'removed'
fileid = id
for rpath, id, kind, text_modified, meta_modified in delta.modified:
if rpath == filename:
status = 'modified'
fileid = id
for rpath, id, kind in delta.unchanged:
if rpath == filename:
status = 'unchanged'
fileid = id
for rpath, file_class, kind, id, entry in self.wt.list_files():
if rpath == filename and file_class == 'I':
status = 'ignored'
finally:
self.wt.unlock()
if status == 'renamed':
st = _('renamed')
elif status == 'removed':
st = _('removed')
elif status == 'added':
st = _('added')
elif status == 'modified':
st = _('modified')
elif status == 'unchanged':
st = _('unchanged')
elif status == 'ignored':
st = _('ignored')
else:
st = _('unknown')
statinfo = os.stat(self.path + os.sep + item)
liststore.append([gtk.STOCK_FILE,
False,
item,
st,
status,
str(statinfo.st_size), # NOTE: if int used there it will fail for large files (size expressed as long int)
self._format_size(statinfo.st_size),
statinfo.st_mtime,
self._format_date(statinfo.st_mtime),
fileid])
# Create the columns and add them to the TreeView
self.treeview_right.set_model(liststore)
self._tvcolumn_filename = gtk.TreeViewColumn(_('Filename'))
self._tvcolumn_status = gtk.TreeViewColumn(_('Status'))
self._tvcolumn_size = gtk.TreeViewColumn(_('Size'))
self._tvcolumn_mtime = gtk.TreeViewColumn(_('Last modified'))
self.treeview_right.append_column(self._tvcolumn_filename)
self.treeview_right.append_column(self._tvcolumn_status)
self.treeview_right.append_column(self._tvcolumn_size)
self.treeview_right.append_column(self._tvcolumn_mtime)
# Set up the cells
cellpb = gtk.CellRendererPixbuf()
cell = gtk.CellRendererText()
self._tvcolumn_filename.pack_start(cellpb, False)
self._tvcolumn_filename.pack_start(cell, True)
self._tvcolumn_filename.set_attributes(cellpb, stock_id=0)
self._tvcolumn_filename.add_attribute(cell, 'text', 2)
self._tvcolumn_status.pack_start(cell, True)
self._tvcolumn_status.add_attribute(cell, 'text', 3)
self._tvcolumn_size.pack_start(cell, True)
self._tvcolumn_size.add_attribute(cell, 'text', 6)
self._tvcolumn_mtime.pack_start(cell, True)
self._tvcolumn_mtime.add_attribute(cell, 'text', 8)
# Set up the properties of the TreeView
self.treeview_right.set_headers_visible(True)
self.treeview_right.set_headers_clickable(True)
self.treeview_right.set_search_column(1)
self._tvcolumn_filename.set_resizable(True)
self._tvcolumn_status.set_resizable(True)
self._tvcolumn_size.set_resizable(True)
self._tvcolumn_mtime.set_resizable(True)
# Set up sorting
liststore.set_sort_func(13, self._sort_filelist_callback, None)
liststore.set_sort_column_id(13, gtk.SORT_ASCENDING)
self._tvcolumn_filename.set_sort_column_id(13)
self._tvcolumn_status.set_sort_column_id(3)
self._tvcolumn_size.set_sort_column_id(5)
self._tvcolumn_mtime.set_sort_column_id(7)
# Set sensitivity
self.set_sensitivity()
def get_selected_fileid(self):
""" Get the file_id of the selected file. """
treeselection = self.treeview_right.get_selection()
(model, iter) = treeselection.get_selected()
if iter is None:
return None
else:
return model.get_value(iter, 9)
def get_selected_right(self):
""" Get the selected filename. """
treeselection = self.treeview_right.get_selection()
(model, iter) = treeselection.get_selected()
if iter is None:
return None
else:
return model.get_value(iter, 2)
def get_selected_left(self):
""" Get the selected bookmark. """
treeselection = self.treeview_left.get_selection()
(model, iter) = treeselection.get_selected()
if iter is None:
return None
else:
return model.get_value(iter, 1)
def set_statusbar(self, message):
""" Set the statusbar message. """
self.statusbar.push(self.context_id, message)
def clear_statusbar(self):
""" Clean the last message from the statusbar. """
self.statusbar.pop(self.context_id)
def set_sensitivity(self):
""" Set menu and toolbar sensitivity. """
if not self.remote:
# We're local
self.menuitem_branch_init.set_sensitive(self.notbranch)
self.menuitem_branch_get.set_sensitive(self.notbranch)
self.menuitem_branch_checkout.set_sensitive(self.notbranch)
self.menuitem_branch_pull.set_sensitive(not self.notbranch)
self.menuitem_branch_push.set_sensitive(not self.notbranch)
self.menuitem_branch_update.set_sensitive(not self.notbranch)
self.menuitem_branch_revert.set_sensitive(not self.notbranch)
self.menuitem_branch_merge.set_sensitive(not self.notbranch)
self.menuitem_branch_commit.set_sensitive(not self.notbranch)
self.menuitem_branch_tags.set_sensitive(not self.notbranch)
self.menuitem_branch_status.set_sensitive(not self.notbranch)
self.menuitem_branch_missing.set_sensitive(not self.notbranch)
self.menuitem_branch_conflicts.set_sensitive(not self.notbranch)
self.menuitem_stats.set_sensitive(not self.notbranch)
self.menuitem_stats_diff.set_sensitive(not self.notbranch)
self.menuitem_add_files.set_sensitive(not self.notbranch)
self.menuitem_remove_files.set_sensitive(not self.notbranch)
self.menuitem_file_make_directory.set_sensitive(not self.notbranch)
self.menuitem_file_rename.set_sensitive(not self.notbranch)
self.menuitem_file_move.set_sensitive(not self.notbranch)
self.menuitem_file_annotate.set_sensitive(not self.notbranch)
#self.menutoolbutton_diff.set_sensitive(True)
self.toolbutton_diff.set_sensitive(not self.notbranch)
self.toolbutton_log.set_sensitive(not self.notbranch)
self.toolbutton_commit.set_sensitive(not self.notbranch)
self.toolbutton_pull.set_sensitive(not self.notbranch)
self.toolbutton_push.set_sensitive(not self.notbranch)
self.toolbutton_update.set_sensitive(not self.notbranch)
else:
# We're remote
self.menuitem_branch_init.set_sensitive(False)
self.menuitem_branch_get.set_sensitive(True)
self.menuitem_branch_checkout.set_sensitive(True)
self.menuitem_branch_pull.set_sensitive(False)
self.menuitem_branch_push.set_sensitive(False)
self.menuitem_branch_update.set_sensitive(False)
self.menuitem_branch_revert.set_sensitive(False)
self.menuitem_branch_merge.set_sensitive(False)
self.menuitem_branch_commit.set_sensitive(False)
self.menuitem_branch_tags.set_sensitive(True)
self.menuitem_branch_status.set_sensitive(False)
self.menuitem_branch_missing.set_sensitive(False)
self.menuitem_branch_conflicts.set_sensitive(False)
self.menuitem_stats.set_sensitive(True)
self.menuitem_stats_diff.set_sensitive(False)
self.menuitem_add_files.set_sensitive(False)
self.menuitem_remove_files.set_sensitive(False)
self.menuitem_file_make_directory.set_sensitive(False)
self.menuitem_file_rename.set_sensitive(False)
self.menuitem_file_move.set_sensitive(False)
self.menuitem_file_annotate.set_sensitive(False)
#self.menutoolbutton_diff.set_sensitive(True)
self.toolbutton_diff.set_sensitive(False)
self.toolbutton_log.set_sensitive(True)
self.toolbutton_commit.set_sensitive(False)
self.toolbutton_pull.set_sensitive(False)
self.toolbutton_push.set_sensitive(False)
self.toolbutton_update.set_sensitive(False)
def refresh_left(self):
""" Refresh the bookmark list. """
# Get TreeStore and clear it
treestore = self.treeview_left.get_model()
treestore.clear()
# Re-read preferences
self.pref.read()
# Get bookmarks
bookmarks = self.pref.get_bookmarks()
# Add them to the TreeStore
titer = treestore.append(None, [_('Bookmarks'), None])
for item in bookmarks:
title = self.pref.get_bookmark_title(item)
treestore.append(titer, [title, item])
# Add the TreeStore to the TreeView
self.treeview_left.set_model(treestore)
# Expand the tree
self.treeview_left.expand_all()
def refresh_right(self, path=None):
""" Refresh the file list. """
if not self.remote:
# We're local
from bzrlib.workingtree import WorkingTree
if path is None:
path = self.get_path()
# A workaround for double-clicking Bookmarks
if not os.path.exists(path):
return
# Get ListStore and clear it
liststore = self.treeview_right.get_model()
liststore.clear()
# Show Status column
self._tvcolumn_status.set_visible(True)
dirs = []
files = []
# Fill the appropriate lists
dotted_files = self.pref.get_preference('dotted_files', 'bool')
ignored_files = self.pref.get_preference('ignored_files', 'bool')
for item in os.listdir(path):
if not dotted_files and item[0] == '.':
continue
if os.path.isdir(path + os.sep + item):
dirs.append(item)
else:
files.append(item)
# Try to open the working tree
notbranch = False
try:
tree1 = WorkingTree.open_containing(path)[0]
except (bzrerrors.NotBranchError, bzrerrors.NoWorkingTree):
notbranch = True
if not notbranch:
branch = tree1.branch
tree2 = tree1.branch.repository.revision_tree(branch.last_revision())
delta = tree1.changes_from(tree2, want_unchanged=True)
# Add'em to the ListStore
for item in dirs:
statinfo = os.stat(self.path + os.sep + item)
liststore.append([gtk.STOCK_DIRECTORY,
True,
item,
'',
'',
"<DIR>",
"<DIR>",
statinfo.st_mtime,
self._format_date(statinfo.st_mtime),
''])
for item in files:
status = 'unknown'
fileid = ''
if not notbranch:
filename = tree1.relpath(path + os.sep + item)
try:
self.wt.lock_read()
for rpath, rpathnew, id, kind, text_modified, meta_modified in delta.renamed:
if rpathnew == filename:
status = 'renamed'
fileid = id
for rpath, id, kind in delta.added:
if rpath == filename:
status = 'added'
fileid = id
for rpath, id, kind in delta.removed:
if rpath == filename:
status = 'removed'
fileid = id
for rpath, id, kind, text_modified, meta_modified in delta.modified:
if rpath == filename:
status = 'modified'
fileid = id
for rpath, id, kind in delta.unchanged:
if rpath == filename:
status = 'unchanged'
fileid = id
for rpath, file_class, kind, id, entry in self.wt.list_files():
if rpath == filename and file_class == 'I':
status = 'ignored'
finally:
self.wt.unlock()
if status == 'renamed':
st = _('renamed')
elif status == 'removed':
st = _('removed')
elif status == 'added':
st = _('added')
elif status == 'modified':
st = _('modified')
elif status == 'unchanged':
st = _('unchanged')
elif status == 'ignored':
st = _('ignored')
if not ignored_files:
continue
else:
st = _('unknown')
statinfo = os.stat(self.path + os.sep + item)
liststore.append([gtk.STOCK_FILE,
False,
item,
st,
status,
str(statinfo.st_size),
self._format_size(statinfo.st_size),
statinfo.st_mtime,
self._format_date(statinfo.st_mtime),
fileid])
else:
# We're remote
# Get ListStore and clear it
liststore = self.treeview_right.get_model()
liststore.clear()
# Hide Status column
self._tvcolumn_status.set_visible(False)
dirs = []
files = []
self._show_stock_image(gtk.STOCK_REFRESH)
for (name, type) in self.remote_entries:
if type.kind == 'directory':
dirs.append(type)
elif type.kind == 'file':
files.append(type)
class HistoryCache:
""" Cache based on revision history. """
def __init__(self, history):
self._history = history
def _lookup_revision(self, revid):
for r in self._history:
if r.revision_id == revid:
return r
rev = repo.get_revision(revid)
self._history.append(rev)
return rev
repo = self.remote_branch.repository
revhistory = self.remote_branch.revision_history()
try:
revs = repo.get_revisions(revhistory)
cache = HistoryCache(revs)
except bzrerrors.InvalidHttpResponse:
# Fallback to dummy algorithm, because of LP: #115209
cache = HistoryCache([])
for item in dirs:
if item.parent_id == self.remote_parent:
rev = cache._lookup_revision(item.revision)
liststore.append([ gtk.STOCK_DIRECTORY,
True,
item.name,
'',
'',
"<DIR>",
"<DIR>",
rev.timestamp,
self._format_date(rev.timestamp),
''
])
while gtk.events_pending():
gtk.main_iteration()
for item in files:
if item.parent_id == self.remote_parent:
rev = cache._lookup_revision(item.revision)
liststore.append([ gtk.STOCK_FILE,
False,
item.name,
'',
'',
str(item.text_size),
self._format_size(item.text_size),
rev.timestamp,
self._format_date(rev.timestamp),
item.file_id
])
while gtk.events_pending():
gtk.main_iteration()
self.image_location_error.destroy()
# Columns should auto-size
self.treeview_right.columns_autosize()
# Set sensitivity
self.set_sensitivity()
def _harddisks(self):
""" Returns hard drive letters under Win32. """
try:
import win32file
import string
except ImportError:
if sys.platform == 'win32':
print "pyWin32 modules needed to run Olive on Win32."
sys.exit(1)
else:
pass
driveletters = []
for drive in string.ascii_uppercase:
if win32file.GetDriveType(drive+':') == win32file.DRIVE_FIXED:
driveletters.append(drive+':')
return driveletters
def gen_hard_selector(self):
""" Generate the hard drive selector under Win32. """
drives = self._harddisks()
for drive in drives:
self.combobox_drive.append_text(drive)
self.combobox_drive.set_active(drives.index(os.getcwd()[0:2]))
def _refresh_drives(self, combobox):
if self._just_started:
return
model = combobox.get_model()
active = combobox.get_active()
if active >= 0:
drive = model[active][0]
self.set_path(drive + '\\')
self.refresh_right(drive + '\\')
def check_for_changes(self):
""" Check whether there were changes in the current working tree. """
old_tree = self.wt.branch.repository.revision_tree(self.wt.branch.last_revision())
delta = self.wt.changes_from(old_tree)
changes = False
if len(delta.added) or len(delta.removed) or len(delta.renamed) or len(delta.modified):
changes = True
return changes
def _sort_filelist_callback(self, model, iter1, iter2, data):
""" The sort callback for the file list, return values:
-1: iter1 < iter2
0: iter1 = iter2
1: iter1 > iter2
"""
name1 = model.get_value(iter1, 2)
name2 = model.get_value(iter2, 2)
if model.get_value(iter1, 1):
# item1 is a directory
if not model.get_value(iter2, 1):
# item2 isn't
return -1
else:
# both of them are directories, we compare their names
if name1 < name2:
return -1
elif name1 == name2:
return 0
else:
return 1
else:
# item1 is not a directory
if model.get_value(iter2, 1):
# item2 is
return 1
else:
# both of them are files, compare them
if name1 < name2:
return -1
elif name1 == name2:
return 0
else:
return 1
def _format_size(self, size):
""" Format size to a human readable format. """
if size < 1000:
return "%d[B]" % (size,)
size = size / 1000.0
for metric in ["kB","MB","GB","TB"]:
if size < 1000:
break
size = size / 1000.0
return "%.1f[%s]" % (size,metric)
def _format_date(self, timestamp):
""" Format the time (given in secs) to a human readable format. """
return time.ctime(timestamp)
def _is_remote_dir(self, location):
""" Determine whether the given location is a directory or not. """
if not self.remote:
# We're in local mode
return False
else:
branch, path = Branch.open_containing(location)
for (name, type) in self.remote_entries:
if name == path and type.kind == 'directory':
# We got it
return True
# Either it's not a directory or not in the inventory
return False
def _show_stock_image(self, stock_id):
""" Show a stock image next to the location entry. """
self.image_location_error.destroy()
self.image_location_error = gtk.image_new_from_stock(stock_id, gtk.ICON_SIZE_BUTTON)
self.hbox_location.pack_start(self.image_location_error, False, False, 0)
if sys.platform == 'win32':
self.hbox_location.reorder_child(self.image_location_error, 2)
else:
self.hbox_location.reorder_child(self.image_location_error, 1)
self.image_location_error.show()
while gtk.events_pending():
gtk.main_iteration()
import ConfigParser
class Preferences:
""" A class which handles Olive's preferences. """
def __init__(self, path=None):
""" Initialize the Preferences class. """
# Some default options
self.defaults = { 'strict_commit' : False,
'dotted_files' : False,
'ignored_files' : True,
'window_width' : 700,
'window_height' : 400,
'window_x' : 40,
'window_y' : 40,
'paned_position': 200 }
# Create a config parser object
self.config = ConfigParser.RawConfigParser()
# Set filename
if path is None:
if sys.platform == 'win32':
# Windows - no dotted files
self._filename = os.path.expanduser('~/olive.conf')
else:
self._filename = os.path.expanduser('~/.olive.conf')
else:
self._filename = path
# Load the configuration
self.read()
def _get_default(self, option):
""" Get the default option for a preference. """
try:
ret = self.defaults[option]
except KeyError:
return None
else:
return ret
def refresh(self):
""" Refresh the configuration. """
# First write out the changes
self.write()
# Then load the configuration again
self.read()
def read(self):
""" Just read the configuration. """
# Re-initialize the config parser object to avoid some bugs
self.config = ConfigParser.RawConfigParser()
self.config.read([self._filename])
def write(self):
""" Write the configuration to the appropriate files. """
fp = open(self._filename, 'w')
self.config.write(fp)
fp.close()
def get_bookmarks(self):
""" Return the list of bookmarks. """
bookmarks = self.config.sections()
if self.config.has_section('preferences'):
bookmarks.remove('preferences')
return bookmarks
def add_bookmark(self, path):
""" Add bookmark. """
try:
self.config.add_section(path)
except ConfigParser.DuplicateSectionError:
return False
else:
return True
def get_bookmark_title(self, path):
""" Get bookmark title. """
try:
ret = self.config.get(path, 'title')
except ConfigParser.NoOptionError:
ret = path
return ret
def set_bookmark_title(self, path, title):
""" Set bookmark title. """
# FIXME: What if path isn't listed yet?
# FIXME: Canonicalize paths first?
self.config.set(path, 'title', title)
def remove_bookmark(self, path):
""" Remove bookmark. """
return self.config.remove_section(path)
def set_preference(self, option, value):
""" Set the value of the given option. """
if value is True:
value = 'yes'
elif value is False:
value = 'no'
if self.config.has_section('preferences'):
self.config.set('preferences', option, value)
else:
self.config.add_section('preferences')
self.config.set('preferences', option, value)
def get_preference(self, option, kind='str'):
""" Get the value of the given option.
:param kind: str/bool/int/float. default: str
"""
if self.config.has_option('preferences', option):
if kind == 'bool':
return self.config.getboolean('preferences', option)
elif kind == 'int':
return self.config.getint('preferences', option)
elif kind == 'float':
return self.config.getfloat('preferences', option)
else:
return self.config.get('preferences', option)
else:
try:
return self._get_default(option)
except KeyError:
return None
syntax highlighted by Code2HTML, v. 0.9.1