[meld] Split MeldApp and MeldWindow into separate files



commit 866c777cad5f9a5e9a5519583ccc5f92f767bc44
Author: Kai Willadsen <kai willadsen gmail com>
Date:   Wed Feb 9 06:17:51 2011 +1000

    Split MeldApp and MeldWindow into separate files

 meld/meldapp.py    |  564 +--------------------------------------------------
 meld/meldwindow.py |  577 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 584 insertions(+), 557 deletions(-)
---
diff --git a/meld/meldapp.py b/meld/meldapp.py
index 91dc558..302fd84 100644
--- a/meld/meldapp.py
+++ b/meld/meldapp.py
@@ -1,4 +1,5 @@
 ### Copyright (C) 2002-2006 Stephen Kennedy <stevek gnome org>
+### Copyright (C) 2010-2011 Kai Willadsen <kai willadsen 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
@@ -14,578 +15,29 @@
 ### along with this program; if not, write to the Free Software
 ### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-# system
-import sys
-import os
+
 import optparse
+import os
 from gettext import gettext as _
 
-import gio
-import gtk
 import gobject
+import gtk
 
-# project
-import paths
-from ui import gnomeglade
-import misc
-from ui import notebooklabel
 import preferences
-import task
-import vc
-
-from util.sourceviewer import srcviewer
 
 version = "1.5.0"
 
-################################################################################
-#
-# NewDocDialog
-#
-################################################################################
-
-class NewDocDialog(gnomeglade.Component):
-    def __init__(self, parentapp):
-        gnomeglade.Component.__init__(self, paths.ui_dir("meldapp.ui"), "newdialog")
-        self.map_widgets_into_lists(["fileentry", "direntry", "vcentry", "three_way_compare"])
-        self.entrylists = self.fileentry, self.direntry, self.vcentry
-        self.widget.set_transient_for(parentapp.widget)
-        self.fileentry[0].set_sensitive(self.three_way_compare[0].get_active())
-        self.direntry[0].set_sensitive(self.three_way_compare[1].get_active())
-        self.diff_methods = (parentapp.append_filediff,
-                             parentapp.append_dirdiff,
-                             parentapp.append_vcview)
-        self.widget.show_all()
-
-    def on_entry_activate(self, entry):
-        for el in self.entrylists:
-            if entry in el:
-                i = el.index(entry)
-                if i == len(el) - 1:
-                    self.button_ok.grab_focus()
-                else:
-                    el[i+1].focus_entry()
-
-    def on_three_way_toggled(self, button):
-        page = self.three_way_compare.index(button)
-        self.entrylists[page][0].set_sensitive( button.get_active() )
-        self.entrylists[page][not button.get_active()].focus_entry()
-
-    def on_response(self, dialog, arg):
-        if arg == gtk.RESPONSE_OK:
-            page = self.type_notebook.get_current_page()
-            paths = [e.get_full_path() or "" for e in self.entrylists[page]]
-            if page < 2 and not self.three_way_compare[page].get_active():
-                paths.pop(0)
-            for path in paths:
-                self.entrylists[page][0].prepend_history(path)
-            if page == 2:
-                self.diff_methods[page](paths[0])
-            else:
-                self.diff_methods[page](paths)
-        self.widget.destroy()
-
-
-################################################################################
-#
-# MeldStatusBar
-#
-################################################################################
-
-class MeldStatusBar(object):
-
-    def __init__(self, task_progress, task_status, doc_status):
-        self.task_progress = task_progress
-        self.task_status = task_status
-        self.doc_status = doc_status
-
-    def set_task_status(self, status):
-        self.task_status.pop(1)
-        self.task_status.push(1, status)
-
-    def set_doc_status(self, status):
-        self.doc_status.pop(1)
-        self.doc_status.push(1, status)
-
-
-################################################################################
-#
-# MeldApp
-#
-################################################################################
-
-class MeldWindow(gnomeglade.Component):
-
-    #
-    # init
-    #
-    def __init__(self):
-        gladefile = paths.ui_dir("meldapp.ui")
-        gnomeglade.Component.__init__(self, gladefile, "meldapp")
-
-        actions = (
-            ("FileMenu", None, _("_File")),
-            ("New",     gtk.STOCK_NEW,      _("_New..."), "<control>N", _("Start a new comparison"), self.on_menu_file_new_activate),
-            ("Save",    gtk.STOCK_SAVE,     None, None, _("Save the current file"), self.on_menu_save_activate),
-            ("SaveAs",  gtk.STOCK_SAVE_AS,  None, "<control><shift>S", "Save the current file with a different name", self.on_menu_save_as_activate),
-            ("Close",   gtk.STOCK_CLOSE,    None, None, _("Close the current file"), self.on_menu_close_activate),
-            ("Quit",    gtk.STOCK_QUIT,     None, None, _("Quit the program"), self.on_menu_quit_activate),
-
-            ("EditMenu", None, _("_Edit")),
-            ("Undo",    gtk.STOCK_UNDO,     None, "<control>Z", _("Undo the last action"), self.on_menu_undo_activate),
-            ("Redo",    gtk.STOCK_REDO,     None, "<control><shift>Z", _("Redo the last undone action"), self.on_menu_redo_activate),
-            ("Cut",     gtk.STOCK_CUT,      None, None, _("Cut the selection"), self.on_menu_cut_activate),
-            ("Copy",    gtk.STOCK_COPY,     None, None, _("Copy the selection"), self.on_menu_copy_activate),
-            ("Paste",   gtk.STOCK_PASTE,    None, None, _("Paste the clipboard"), self.on_menu_paste_activate),
-            ("Find",    gtk.STOCK_FIND,     None, None, _("Search for text"), self.on_menu_find_activate),
-            ("FindNext", None,              _("Find Ne_xt"), "<control>G", _("Search forwards for the same text"), self.on_menu_find_next_activate),
-            ("Replace", gtk.STOCK_FIND_AND_REPLACE, _("_Replace"), "<control>H", _("Find and replace text"), self.on_menu_replace_activate),
-            ("Preferences", gtk.STOCK_PREFERENCES, _("Prefere_nces"), None, _("Configure the application"), self.on_menu_preferences_activate),
-
-            ("ChangesMenu", None, _("_Changes")),
-            ("NextChange", gtk.STOCK_GO_DOWN, _("Next change"), "<Alt>Down", _("Go to the next change"), self.on_menu_edit_down_activate),
-            ("PrevChange", gtk.STOCK_GO_UP,   _("Previous change"), "<Alt>Up", _("Go to the previous change"), self.on_menu_edit_up_activate),
-
-            ("ViewMenu", None, _("_View")),
-            ("FileStatus",  None, _("File status")),
-            ("VcStatus",    None, _("Version status")),
-            ("FileFilters",  None, _("File filters")),
-            ("Stop",    gtk.STOCK_STOP,     None, "Escape", _("Stop the current action"), self.on_toolbar_stop_clicked),
-            ("Refresh", gtk.STOCK_REFRESH,  None, "<control>R", _("Refresh the view"), self.on_menu_refresh_activate),
-            ("Reload",  gtk.STOCK_REFRESH,  _("Reload"), "<control><shift>R", _("Reload the comparison"), self.on_menu_reload_activate),
-
-            ("HelpMenu", None, _("_Help")),
-            ("Help",        gtk.STOCK_HELP,  _("_Contents"), "F1", _("Open the Meld manual"), self.on_menu_help_activate),
-            ("BugReport",   gtk.STOCK_DIALOG_WARNING, _("Report _Bug"), None, _("Report a bug in Meld"), self.on_menu_help_bug_activate),
-            ("About",       gtk.STOCK_ABOUT, None, None, _("About this program"), self.on_menu_about_activate),
-        )
-        toggleactions = (
-            ("Fullscreen",       None, _("Full Screen"), "F11", _("View the comparison in full screen"), self.on_action_fullscreen_toggled, False),
-            ("ToolbarVisible",   None, _("_Toolbar"),   None, _("Show or hide the toolbar"),   self.on_menu_toolbar_toggled,   app.prefs.toolbar_visible),
-            ("StatusbarVisible", None, _("_Statusbar"), None, _("Show or hide the statusbar"), self.on_menu_statusbar_toggled, app.prefs.statusbar_visible)
-        )
-        ui_file = paths.ui_dir("meldapp-ui.xml")
-        self.actiongroup = gtk.ActionGroup('MainActions')
-        self.actiongroup.set_translation_domain("meld")
-        self.actiongroup.add_actions(actions)
-        self.actiongroup.add_toggle_actions(toggleactions)
-        self.ui = gtk.UIManager()
-        self.ui.insert_action_group(self.actiongroup, 0)
-        self.ui.add_ui_from_file(ui_file)
-        self.ui.connect("connect-proxy", self._on_uimanager_connect_proxy)
-        self.ui.connect("disconnect-proxy", self._on_uimanager_disconnect_proxy)
-
-        for menuitem in ("Save", "Undo"):
-            self.actiongroup.get_action(menuitem).props.is_important = True
-        self.widget.add_accel_group(self.ui.get_accel_group())
-        self.menubar = self.ui.get_widget('/Menubar')
-        self.toolbar = self.ui.get_widget('/Toolbar')
-
-        # Add alternate keybindings for Prev/Next Change
-        accels = self.ui.get_accel_group()
-        (keyval, mask) = gtk.accelerator_parse("<Ctrl>D")
-        accels.connect_group(keyval, mask, 0, self.on_menu_edit_down_activate)
-        (keyval, mask) = gtk.accelerator_parse("<Ctrl>E")
-        accels.connect_group(keyval, mask, 0, self.on_menu_edit_up_activate)
-
-        self.appvbox.pack_start(self.menubar, expand=False)
-        self.appvbox.pack_start(self.toolbar, expand=False)
-        # TODO: should possibly use something other than doc_status
-        self._menu_context = self.doc_status.get_context_id("Tooltips")
-        self.statusbar = MeldStatusBar(self.task_progress, self.task_status, self.doc_status)
-        self.widget.drag_dest_set(
-            gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
-            [ ('text/uri-list', 0, 0) ],
-            gtk.gdk.ACTION_COPY)
-        self.widget.connect("drag_data_received",
-                            self.on_widget_drag_data_received)
-        self.toolbar.set_style(app.prefs.get_toolbar_style())
-        self.toolbar.props.visible = app.prefs.toolbar_visible
-        self.status_box.props.visible = app.prefs.statusbar_visible
-        app.prefs.notify_add(self.on_preference_changed)
-        self.idle_hooked = 0
-        self.scheduler = task.LifoScheduler()
-        self.scheduler.connect("runnable", self.on_scheduler_runnable )
-        self.widget.set_default_size(app.prefs.window_size_x, app.prefs.window_size_y)
-        self.ui.ensure_update()
-        self.widget.show()
-        self.diff_handler = None
-        self.widget.connect('focus_in_event', self.on_focus_change)
-        self.widget.connect('focus_out_event', self.on_focus_change)
-
-    def on_focus_change(self, widget, event, callback_data = None):
-        for idx in range(self.notebook.get_n_pages()):
-            w = self.notebook.get_nth_page(idx)
-            if hasattr(w.get_data("pyobject"), 'on_focus_change'):
-                w.get_data("pyobject").on_focus_change()
-        # Let the rest of the stack know about this event
-        return False
-
-    def on_widget_drag_data_received(self, wid, context, x, y, selection_data, info, time):
-        if len(selection_data.get_uris()) != 0:
-            paths = []
-            for uri in selection_data.get_uris():
-                paths.append(gio.File(uri=uri).get_path())
-            self.open_paths(paths)
-            return True
-
-    def _on_uimanager_connect_proxy(self, ui, action, widget):
-        tooltip = action.props.tooltip
-        if not tooltip:
-            return
-        if isinstance(widget, gtk.MenuItem):
-            cid = widget.connect("select", self._on_action_item_select_enter, tooltip)
-            cid2 = widget.connect("deselect", self._on_action_item_deselect_leave)
-            widget.set_data("meldapp::proxy-signal-ids", (cid, cid2))
-        elif isinstance(widget, gtk.ToolButton):
-            cid = widget.child.connect("enter", self._on_action_item_select_enter, tooltip)
-            cid2 = widget.child.connect("leave", self._on_action_item_deselect_leave)
-            widget.set_data("meldapp::proxy-signal-ids", (cid, cid2))
-
-    def _on_uimanager_disconnect_proxy(self, ui, action, widget):
-        cids = widget.get_data("meldapp::proxy-signal-ids")
-        if not cids:
-            return
-        if isinstance(widget, gtk.ToolButton):
-            widget = widget.child
-        for cid in cids:
-            widget.disconnect(cid)
-
-    def _on_action_item_select_enter(self, item, tooltip):
-        self.statusbar.doc_status.push(self._menu_context, tooltip)
-
-    def _on_action_item_deselect_leave(self, item):
-        self.statusbar.doc_status.pop(self._menu_context)
-
-    def on_idle(self):
-        ret = self.scheduler.iteration()
-        if ret:
-            if type(ret) in (type(""), type(u"")):
-                self.statusbar.set_task_status(ret)
-            elif type(ret) == type(0.0):
-                self.statusbar.task_progress.set_fraction(ret)
-            else:
-                self.statusbar.task_progress.pulse()
-        else:
-            self.statusbar.task_progress.set_fraction(0)
-        if self.scheduler.tasks_pending():
-            self.actiongroup.get_action("Stop").set_sensitive(True)
-            return 1
-        else:
-            self.statusbar.set_task_status("")
-            self.idle_hooked = 0
-            self.actiongroup.get_action("Stop").set_sensitive(False)
-            return 0
-
-    def on_scheduler_runnable(self, sched):
-        if not self.idle_hooked:
-            self.idle_hooked = 1
-            gobject.idle_add( self.on_idle )
-
-    def on_preference_changed(self, key, value):
-        if key == "toolbar_style":
-            self.toolbar.set_style(app.prefs.get_toolbar_style())
-        elif key == "statusbar_visible":
-            self.status_box.props.visible = app.prefs.statusbar_visible
-        elif key == "toolbar_visible":
-            self.toolbar.props.visible = app.prefs.toolbar_visible
-
-    #
-    # General events and callbacks
-    #
-    def on_delete_event(self, *extra):
-        return self.on_menu_quit_activate()
-
-    def on_switch_page(self, notebook, page, which):
-        newdoc = notebook.get_nth_page(which).get_data("pyobject")
-        newseq = newdoc.undosequence
-        oldidx = notebook.get_current_page()
-        if oldidx >= 0:
-            olddoc = notebook.get_nth_page(oldidx).get_data("pyobject")
-            olddoc.disconnect(self.diff_handler)
-            olddoc.on_container_switch_out_event(self.ui)
-        self.actiongroup.get_action("Undo").set_sensitive(newseq.can_undo())
-        self.actiongroup.get_action("Redo").set_sensitive(newseq.can_redo())
-        nbl = self.notebook.get_tab_label( newdoc.widget )
-        self.widget.set_title(nbl.get_label_text() + " - Meld")
-        self.statusbar.set_doc_status("")
-        self.diff_handler = newdoc.connect("next-diff-changed",
-                                           self.on_next_diff_changed)
-        newdoc.on_container_switch_in_event(self.ui)
-        self.scheduler.add_task( newdoc.scheduler )
-
-    def on_notebook_label_changed(self, component, text, tooltip):
-        nbl = self.notebook.get_tab_label( component.widget )
-        nbl.set_label_text(text)
-        nbl.set_tooltip_text(tooltip)
-        self.widget.set_title(text + " - Meld")
-        self.notebook.child_set_property(component.widget, "menu-label", text)
-
-    def on_can_undo(self, undosequence, can):
-        self.actiongroup.get_action("Undo").set_sensitive(can)
-
-    def on_can_redo(self, undosequence, can):
-        self.actiongroup.get_action("Redo").set_sensitive(can)
-
-    def on_next_diff_changed(self, doc, have_prev, have_next):
-        self.actiongroup.get_action("PrevChange").set_sensitive(have_prev)
-        self.actiongroup.get_action("NextChange").set_sensitive(have_next)
-
-    def on_size_allocate(self, window, rect):
-        app.prefs.window_size_x = rect.width
-        app.prefs.window_size_y = rect.height
-
-    #
-    # Toolbar and menu items (file)
-    #
-    def on_menu_file_new_activate(self, menuitem):
-        NewDocDialog(self)
-
-    def on_menu_save_activate(self, menuitem):
-        self.current_doc().save()
-
-    def on_menu_save_as_activate(self, menuitem):
-        self.current_doc().save_as()
-
-    def on_menu_close_activate(self, *extra):
-        i = self.notebook.get_current_page()
-        if i >= 0:
-            page = self.notebook.get_nth_page(i).get_data("pyobject")
-            self.try_remove_page(page)
-
-    def on_menu_quit_activate(self, *extra):
-        # Delete pages from right-to-left.  This ensures that if a version
-        # control page is open in the far left page, it will be closed last.
-        for c in reversed(self.notebook.get_children()):
-            page = c.get_data("pyobject")
-            self.notebook.set_current_page(self.notebook.page_num(page.widget))
-            response = self.try_remove_page(page, appquit=1)
-            if response == gtk.RESPONSE_CANCEL:
-                return gtk.RESPONSE_CANCEL
-        for c in self.notebook.get_children():
-            c.get_data("pyobject").on_quit_event()
-        gtk.main_quit()
-        return gtk.RESPONSE_CLOSE
-
-    #
-    # Toolbar and menu items (edit)
-    #
-    def on_menu_undo_activate(self, *extra):
-        self.current_doc().on_undo_activate()
-
-    def on_menu_redo_activate(self, *extra):
-        self.current_doc().on_redo_activate()
-
-    def on_menu_refresh_activate(self, *extra):
-        self.current_doc().on_refresh_activate()
-
-    def on_menu_reload_activate(self, *extra):
-        self.current_doc().on_reload_activate()
-  
-    def on_menu_find_activate(self, *extra):
-        self.current_doc().on_find_activate()
-
-    def on_menu_find_next_activate(self, *extra):
-        self.current_doc().on_find_next_activate()
-
-    def on_menu_replace_activate(self, *extra):
-        self.current_doc().on_replace_activate()
-
-    def on_menu_copy_activate(self, *extra):
-        widget = self.widget.get_focus()
-        if isinstance(widget, gtk.Editable):
-            widget.copy_clipboard()
-        elif isinstance(widget, gtk.TextView):
-            widget.emit("copy-clipboard")
-
-    def on_menu_cut_activate(self, *extra):
-        widget = self.widget.get_focus()
-        if isinstance(widget, gtk.Editable):
-            widget.cut_clipboard()
-        elif isinstance(widget, gtk.TextView):
-            widget.emit("cut-clipboard")
-
-    def on_menu_paste_activate(self, *extra):
-        widget = self.widget.get_focus()
-        if isinstance(widget, gtk.Editable):
-            widget.paste_clipboard()
-        elif isinstance(widget, gtk.TextView):
-            widget.emit("paste-clipboard")
-
-    #
-    # Toolbar and menu items (settings)
-    #
-    def on_menu_preferences_activate(self, item):
-        preferences.PreferencesDialog(self)
-
-    def on_action_fullscreen_toggled(self, widget):
-        is_full = self.widget.window.get_state() & gtk.gdk.WINDOW_STATE_FULLSCREEN
-        if widget.get_active() and not is_full:
-            self.widget.fullscreen()
-        elif is_full:
-            self.widget.unfullscreen()
-
-    def on_menu_toolbar_toggled(self, widget):
-        app.prefs.toolbar_visible = widget.get_active()
-
-    def on_menu_statusbar_toggled(self, widget):
-        app.prefs.statusbar_visible = widget.get_active()
-
-    #
-    # Toolbar and menu items (help)
-    #
-    def on_menu_help_activate(self, button):
-        misc.open_uri("ghelp:///"+os.path.abspath(paths.help_dir("C/meld.xml")))
-
-    def on_menu_help_bug_activate(self, button):
-        misc.open_uri("http://bugzilla.gnome.org/buglist.cgi?query=product%3Ameld";)
-
-    def on_menu_about_activate(self, *extra):
-        gtk.about_dialog_set_url_hook(lambda dialog, uri: misc.open_uri(uri))
-        builder = gtk.Builder()
-        # FIXME: domain literal duplicated from bin/meld
-        builder.set_translation_domain("meld")
-        builder.add_objects_from_file(paths.ui_dir("meldapp.ui"), ["about"])
-        about = builder.get_object("about")
-        about.props.version = version
-        about.set_transient_for(self.widget)
-        about.run()
-        about.hide()
-
-    #
-    # Toolbar and menu items (misc)
-    #
-    def on_menu_edit_down_activate(self, *args):
-        self.current_doc().next_diff(gtk.gdk.SCROLL_DOWN)
-
-    def on_menu_edit_up_activate(self, *args):
-        self.current_doc().next_diff(gtk.gdk.SCROLL_UP)
-
-    def on_toolbar_stop_clicked(self, *args):
-        self.current_doc().stop()
-
-    def try_remove_page(self, page, appquit=0):
-        "See if a page will allow itself to be removed"
-        response = page.on_delete_event(appquit)
-        if response != gtk.RESPONSE_CANCEL:
-            self.scheduler.remove_scheduler( page.scheduler )
-            i = self.notebook.page_num( page.widget )
-            assert(i>=0)
-            # If the page we're removing is the current page, we need to trigger a switch out
-            if self.notebook.get_current_page() == i:
-                page.on_container_switch_out_event(self.ui)
-            self.notebook.remove_page(i)
-            if self.notebook.get_n_pages() == 0:
-                self.widget.set_title("Meld")
-        return response
-
-    def on_file_changed(self, srcpage, filename):
-        for c in self.notebook.get_children():
-            page = c.get_data("pyobject")
-            if page != srcpage:
-                page.on_file_changed(filename)
-
-    def _append_page(self, page, icon):
-        nbl = notebooklabel.NotebookLabel(icon, "", lambda b: self.try_remove_page(page))
-        self.notebook.append_page( page.widget, nbl)
-        self.notebook.set_current_page( self.notebook.page_num(page.widget) )
-        self.scheduler.add_scheduler(page.scheduler)
-        page.connect("label-changed", self.on_notebook_label_changed)
-        page.connect("file-changed", self.on_file_changed)
-        page.connect("create-diff", lambda obj,arg: self.append_diff(arg) )
-        page.connect("status-changed", lambda junk,arg: self.statusbar.set_doc_status(arg) )
-
-        # Allow reordering of tabs
-        self.notebook.set_tab_reorderable(page.widget, True);
-
-    def append_dirdiff(self, dirs, auto_compare=False):
-        assert len(dirs) in (1,2,3)
-        doc = dirdiff.DirDiff(app.prefs, len(dirs))
-        self._append_page(doc, "folder")
-        doc.set_locations(dirs)
-        # FIXME: This doesn't work, as dirdiff behaves differently to vcview
-        if auto_compare:
-            doc.on_button_diff_clicked(None)
-        return doc
-
-    def append_filediff(self, files):
-        assert len(files) in (1, 2, 3, 4)
-        if len(files) == 4:
-            doc = filemerge.FileMerge(app.prefs, 3)
-        else:
-            doc = filediff.FileDiff(app.prefs, len(files))
-        seq = doc.undosequence
-        seq.clear()
-        seq.connect("can-undo", self.on_can_undo)
-        seq.connect("can-redo", self.on_can_redo)
-        self._append_page(doc, "text-x-generic")
-        doc.set_files(files)
-        return doc
-
-    def append_diff(self, paths, auto_compare=False):
-        dirslist = [p for p in paths if os.path.isdir(p)]
-        fileslist = [p for p in paths if os.path.isfile(p)]
-        if dirslist and fileslist:
-            # build new file list appending previous found filename to dirs (like diff)
-            lastfilename = fileslist[0]
-            builtfilelist = []
-            for elem in paths:
-                if os.path.isdir(elem):
-                    builtfilename = os.path.join(elem, lastfilename)
-                    if os.path.isfile(builtfilename):
-                        elem = builtfilename
-                    else:
-                        # exit at first non found directory + file
-                        misc.run_dialog(_("Cannot compare a mixture of files and directories.\n"),
-                                          parent=self,
-                                          buttonstype=gtk.BUTTONS_OK)
-                        return
-                else:
-                     lastfilename = os.path.basename(elem)
-                builtfilelist.append(elem)
-            return self.append_filediff(builtfilelist)
-        elif dirslist:
-            return self.append_dirdiff(paths, auto_compare)
-        else:
-            return self.append_filediff(paths)
-
-    def append_vcview(self, location, auto_compare=False):
-        doc = vcview.VcView(app.prefs)
-        # FIXME: need a good themed VC icon
-        self._append_page(doc, "vc-icon")
-        doc.set_location(location)
-        if auto_compare:
-            doc.on_button_diff_clicked(None)
-        return doc
-
-    def _single_file_open(self, path):
-        doc = vcview.VcView(app.prefs)
-        def cleanup():
-            self.scheduler.remove_scheduler(doc.scheduler)
-        self.scheduler.add_task(cleanup)
-        self.scheduler.add_scheduler(doc.scheduler)
-        doc.set_location(path)
-        doc.connect("create-diff", lambda obj,arg: self.append_diff(arg))
-        doc.run_diff([path])
-
-    def current_doc(self):
-        "Get the current doc or a dummy object if there is no current"
-        index = self.notebook.get_current_page()
-        if index >= 0:
-            return self.notebook.get_nth_page(index).get_data("pyobject")
-        class DummyDoc(object):
-            def __getattr__(self, a): return lambda *x: None
-        return DummyDoc()
-
 
 class MeldApp(object):
 
     def __init__(self):
         gobject.set_application_name("Meld")
         gtk.window_set_default_icon_name("meld")
+        self.version = version
         self.prefs = preferences.MeldPreferences()
 
     def create_window(self):
-        self.window = MeldWindow()
+        self.window = meldwindow.MeldWindow()
         return self.window
 
     def diff_files_callback(self, option, opt_str, value, parser):
@@ -661,7 +113,5 @@ class MeldApp(object):
 app = MeldApp()
 
 import filediff
-import filemerge
-import vcview
-import dirdiff
+import meldwindow
 
diff --git a/meld/meldwindow.py b/meld/meldwindow.py
new file mode 100644
index 0000000..80b3d64
--- /dev/null
+++ b/meld/meldwindow.py
@@ -0,0 +1,577 @@
+### Copyright (C) 2002-2006 Stephen Kennedy <stevek gnome org>
+### Copyright (C) 2010-2011 Kai Willadsen <kai willadsen 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
+from gettext import gettext as _
+
+import gio
+import gtk
+import gobject
+
+import dirdiff
+import filediff
+import filemerge
+import paths
+from ui import gnomeglade
+import misc
+from ui import notebooklabel
+import preferences
+import task
+import vcview
+
+from util.sourceviewer import srcviewer
+from meldapp import app
+
+################################################################################
+#
+# NewDocDialog
+#
+################################################################################
+
+class NewDocDialog(gnomeglade.Component):
+    def __init__(self, parentapp):
+        gnomeglade.Component.__init__(self, paths.ui_dir("meldapp.ui"), "newdialog")
+        self.map_widgets_into_lists(["fileentry", "direntry", "vcentry", "three_way_compare"])
+        self.entrylists = self.fileentry, self.direntry, self.vcentry
+        self.widget.set_transient_for(parentapp.widget)
+        self.fileentry[0].set_sensitive(self.three_way_compare[0].get_active())
+        self.direntry[0].set_sensitive(self.three_way_compare[1].get_active())
+        self.diff_methods = (parentapp.append_filediff,
+                             parentapp.append_dirdiff,
+                             parentapp.append_vcview)
+        self.widget.show_all()
+
+    def on_entry_activate(self, entry):
+        for el in self.entrylists:
+            if entry in el:
+                i = el.index(entry)
+                if i == len(el) - 1:
+                    self.button_ok.grab_focus()
+                else:
+                    el[i+1].focus_entry()
+
+    def on_three_way_toggled(self, button):
+        page = self.three_way_compare.index(button)
+        self.entrylists[page][0].set_sensitive( button.get_active() )
+        self.entrylists[page][not button.get_active()].focus_entry()
+
+    def on_response(self, dialog, arg):
+        if arg == gtk.RESPONSE_OK:
+            page = self.type_notebook.get_current_page()
+            paths = [e.get_full_path() or "" for e in self.entrylists[page]]
+            if page < 2 and not self.three_way_compare[page].get_active():
+                paths.pop(0)
+            for path in paths:
+                self.entrylists[page][0].prepend_history(path)
+            if page == 2:
+                self.diff_methods[page](paths[0])
+            else:
+                self.diff_methods[page](paths)
+        self.widget.destroy()
+
+
+################################################################################
+#
+# MeldStatusBar
+#
+################################################################################
+
+class MeldStatusBar(object):
+
+    def __init__(self, task_progress, task_status, doc_status):
+        self.task_progress = task_progress
+        self.task_status = task_status
+        self.doc_status = doc_status
+
+    def set_task_status(self, status):
+        self.task_status.pop(1)
+        self.task_status.push(1, status)
+
+    def set_doc_status(self, status):
+        self.doc_status.pop(1)
+        self.doc_status.push(1, status)
+
+
+################################################################################
+#
+# MeldApp
+#
+################################################################################
+
+class MeldWindow(gnomeglade.Component):
+
+    #
+    # init
+    #
+    def __init__(self):
+        gladefile = paths.ui_dir("meldapp.ui")
+        gnomeglade.Component.__init__(self, gladefile, "meldapp")
+
+        actions = (
+            ("FileMenu", None, _("_File")),
+            ("New",     gtk.STOCK_NEW,      _("_New..."), "<control>N", _("Start a new comparison"), self.on_menu_file_new_activate),
+            ("Save",    gtk.STOCK_SAVE,     None, None, _("Save the current file"), self.on_menu_save_activate),
+            ("SaveAs",  gtk.STOCK_SAVE_AS,  None, "<control><shift>S", "Save the current file with a different name", self.on_menu_save_as_activate),
+            ("Close",   gtk.STOCK_CLOSE,    None, None, _("Close the current file"), self.on_menu_close_activate),
+            ("Quit",    gtk.STOCK_QUIT,     None, None, _("Quit the program"), self.on_menu_quit_activate),
+
+            ("EditMenu", None, _("_Edit")),
+            ("Undo",    gtk.STOCK_UNDO,     None, "<control>Z", _("Undo the last action"), self.on_menu_undo_activate),
+            ("Redo",    gtk.STOCK_REDO,     None, "<control><shift>Z", _("Redo the last undone action"), self.on_menu_redo_activate),
+            ("Cut",     gtk.STOCK_CUT,      None, None, _("Cut the selection"), self.on_menu_cut_activate),
+            ("Copy",    gtk.STOCK_COPY,     None, None, _("Copy the selection"), self.on_menu_copy_activate),
+            ("Paste",   gtk.STOCK_PASTE,    None, None, _("Paste the clipboard"), self.on_menu_paste_activate),
+            ("Find",    gtk.STOCK_FIND,     None, None, _("Search for text"), self.on_menu_find_activate),
+            ("FindNext", None,              _("Find Ne_xt"), "<control>G", _("Search forwards for the same text"), self.on_menu_find_next_activate),
+            ("Replace", gtk.STOCK_FIND_AND_REPLACE, _("_Replace"), "<control>H", _("Find and replace text"), self.on_menu_replace_activate),
+            ("Preferences", gtk.STOCK_PREFERENCES, _("Prefere_nces"), None, _("Configure the application"), self.on_menu_preferences_activate),
+
+            ("ChangesMenu", None, _("_Changes")),
+            ("NextChange", gtk.STOCK_GO_DOWN, _("Next change"), "<Alt>Down", _("Go to the next change"), self.on_menu_edit_down_activate),
+            ("PrevChange", gtk.STOCK_GO_UP,   _("Previous change"), "<Alt>Up", _("Go to the previous change"), self.on_menu_edit_up_activate),
+
+            ("ViewMenu", None, _("_View")),
+            ("FileStatus",  None, _("File status")),
+            ("VcStatus",    None, _("Version status")),
+            ("FileFilters",  None, _("File filters")),
+            ("Stop",    gtk.STOCK_STOP,     None, "Escape", _("Stop the current action"), self.on_toolbar_stop_clicked),
+            ("Refresh", gtk.STOCK_REFRESH,  None, "<control>R", _("Refresh the view"), self.on_menu_refresh_activate),
+            ("Reload",  gtk.STOCK_REFRESH,  _("Reload"), "<control><shift>R", _("Reload the comparison"), self.on_menu_reload_activate),
+
+            ("HelpMenu", None, _("_Help")),
+            ("Help",        gtk.STOCK_HELP,  _("_Contents"), "F1", _("Open the Meld manual"), self.on_menu_help_activate),
+            ("BugReport",   gtk.STOCK_DIALOG_WARNING, _("Report _Bug"), None, _("Report a bug in Meld"), self.on_menu_help_bug_activate),
+            ("About",       gtk.STOCK_ABOUT, None, None, _("About this program"), self.on_menu_about_activate),
+        )
+        toggleactions = (
+            ("Fullscreen",       None, _("Full Screen"), "F11", _("View the comparison in full screen"), self.on_action_fullscreen_toggled, False),
+            ("ToolbarVisible",   None, _("_Toolbar"),   None, _("Show or hide the toolbar"),   self.on_menu_toolbar_toggled,   app.prefs.toolbar_visible),
+            ("StatusbarVisible", None, _("_Statusbar"), None, _("Show or hide the statusbar"), self.on_menu_statusbar_toggled, app.prefs.statusbar_visible)
+        )
+        ui_file = paths.ui_dir("meldapp-ui.xml")
+        self.actiongroup = gtk.ActionGroup('MainActions')
+        self.actiongroup.set_translation_domain("meld")
+        self.actiongroup.add_actions(actions)
+        self.actiongroup.add_toggle_actions(toggleactions)
+        self.ui = gtk.UIManager()
+        self.ui.insert_action_group(self.actiongroup, 0)
+        self.ui.add_ui_from_file(ui_file)
+        self.ui.connect("connect-proxy", self._on_uimanager_connect_proxy)
+        self.ui.connect("disconnect-proxy", self._on_uimanager_disconnect_proxy)
+
+        for menuitem in ("Save", "Undo"):
+            self.actiongroup.get_action(menuitem).props.is_important = True
+        self.widget.add_accel_group(self.ui.get_accel_group())
+        self.menubar = self.ui.get_widget('/Menubar')
+        self.toolbar = self.ui.get_widget('/Toolbar')
+
+        # Add alternate keybindings for Prev/Next Change
+        accels = self.ui.get_accel_group()
+        (keyval, mask) = gtk.accelerator_parse("<Ctrl>D")
+        accels.connect_group(keyval, mask, 0, self.on_menu_edit_down_activate)
+        (keyval, mask) = gtk.accelerator_parse("<Ctrl>E")
+        accels.connect_group(keyval, mask, 0, self.on_menu_edit_up_activate)
+
+        self.appvbox.pack_start(self.menubar, expand=False)
+        self.appvbox.pack_start(self.toolbar, expand=False)
+        # TODO: should possibly use something other than doc_status
+        self._menu_context = self.doc_status.get_context_id("Tooltips")
+        self.statusbar = MeldStatusBar(self.task_progress, self.task_status, self.doc_status)
+        self.widget.drag_dest_set(
+            gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
+            [ ('text/uri-list', 0, 0) ],
+            gtk.gdk.ACTION_COPY)
+        self.widget.connect("drag_data_received",
+                            self.on_widget_drag_data_received)
+        self.toolbar.set_style(app.prefs.get_toolbar_style())
+        self.toolbar.props.visible = app.prefs.toolbar_visible
+        self.status_box.props.visible = app.prefs.statusbar_visible
+        app.prefs.notify_add(self.on_preference_changed)
+        self.idle_hooked = 0
+        self.scheduler = task.LifoScheduler()
+        self.scheduler.connect("runnable", self.on_scheduler_runnable )
+        self.widget.set_default_size(app.prefs.window_size_x, app.prefs.window_size_y)
+        self.ui.ensure_update()
+        self.widget.show()
+        self.diff_handler = None
+        self.widget.connect('focus_in_event', self.on_focus_change)
+        self.widget.connect('focus_out_event', self.on_focus_change)
+
+    def on_focus_change(self, widget, event, callback_data = None):
+        for idx in range(self.notebook.get_n_pages()):
+            w = self.notebook.get_nth_page(idx)
+            if hasattr(w.get_data("pyobject"), 'on_focus_change'):
+                w.get_data("pyobject").on_focus_change()
+        # Let the rest of the stack know about this event
+        return False
+
+    def on_widget_drag_data_received(self, wid, context, x, y, selection_data, info, time):
+        if len(selection_data.get_uris()) != 0:
+            paths = []
+            for uri in selection_data.get_uris():
+                paths.append(gio.File(uri=uri).get_path())
+            self.open_paths(paths)
+            return True
+
+    def _on_uimanager_connect_proxy(self, ui, action, widget):
+        tooltip = action.props.tooltip
+        if not tooltip:
+            return
+        if isinstance(widget, gtk.MenuItem):
+            cid = widget.connect("select", self._on_action_item_select_enter, tooltip)
+            cid2 = widget.connect("deselect", self._on_action_item_deselect_leave)
+            widget.set_data("meldapp::proxy-signal-ids", (cid, cid2))
+        elif isinstance(widget, gtk.ToolButton):
+            cid = widget.child.connect("enter", self._on_action_item_select_enter, tooltip)
+            cid2 = widget.child.connect("leave", self._on_action_item_deselect_leave)
+            widget.set_data("meldapp::proxy-signal-ids", (cid, cid2))
+
+    def _on_uimanager_disconnect_proxy(self, ui, action, widget):
+        cids = widget.get_data("meldapp::proxy-signal-ids")
+        if not cids:
+            return
+        if isinstance(widget, gtk.ToolButton):
+            widget = widget.child
+        for cid in cids:
+            widget.disconnect(cid)
+
+    def _on_action_item_select_enter(self, item, tooltip):
+        self.statusbar.doc_status.push(self._menu_context, tooltip)
+
+    def _on_action_item_deselect_leave(self, item):
+        self.statusbar.doc_status.pop(self._menu_context)
+
+    def on_idle(self):
+        ret = self.scheduler.iteration()
+        if ret:
+            if type(ret) in (type(""), type(u"")):
+                self.statusbar.set_task_status(ret)
+            elif type(ret) == type(0.0):
+                self.statusbar.task_progress.set_fraction(ret)
+            else:
+                self.statusbar.task_progress.pulse()
+        else:
+            self.statusbar.task_progress.set_fraction(0)
+        if self.scheduler.tasks_pending():
+            self.actiongroup.get_action("Stop").set_sensitive(True)
+            return 1
+        else:
+            self.statusbar.set_task_status("")
+            self.idle_hooked = 0
+            self.actiongroup.get_action("Stop").set_sensitive(False)
+            return 0
+
+    def on_scheduler_runnable(self, sched):
+        if not self.idle_hooked:
+            self.idle_hooked = 1
+            gobject.idle_add( self.on_idle )
+
+    def on_preference_changed(self, key, value):
+        if key == "toolbar_style":
+            self.toolbar.set_style(app.prefs.get_toolbar_style())
+        elif key == "statusbar_visible":
+            self.status_box.props.visible = app.prefs.statusbar_visible
+        elif key == "toolbar_visible":
+            self.toolbar.props.visible = app.prefs.toolbar_visible
+
+    #
+    # General events and callbacks
+    #
+    def on_delete_event(self, *extra):
+        return self.on_menu_quit_activate()
+
+    def on_switch_page(self, notebook, page, which):
+        newdoc = notebook.get_nth_page(which).get_data("pyobject")
+        newseq = newdoc.undosequence
+        oldidx = notebook.get_current_page()
+        if oldidx >= 0:
+            olddoc = notebook.get_nth_page(oldidx).get_data("pyobject")
+            olddoc.disconnect(self.diff_handler)
+            olddoc.on_container_switch_out_event(self.ui)
+        self.actiongroup.get_action("Undo").set_sensitive(newseq.can_undo())
+        self.actiongroup.get_action("Redo").set_sensitive(newseq.can_redo())
+        nbl = self.notebook.get_tab_label( newdoc.widget )
+        self.widget.set_title(nbl.get_label_text() + " - Meld")
+        self.statusbar.set_doc_status("")
+        self.diff_handler = newdoc.connect("next-diff-changed",
+                                           self.on_next_diff_changed)
+        newdoc.on_container_switch_in_event(self.ui)
+        self.scheduler.add_task( newdoc.scheduler )
+
+    def on_notebook_label_changed(self, component, text, tooltip):
+        nbl = self.notebook.get_tab_label( component.widget )
+        nbl.set_label_text(text)
+        nbl.set_tooltip_text(tooltip)
+        self.widget.set_title(text + " - Meld")
+        self.notebook.child_set_property(component.widget, "menu-label", text)
+
+    def on_can_undo(self, undosequence, can):
+        self.actiongroup.get_action("Undo").set_sensitive(can)
+
+    def on_can_redo(self, undosequence, can):
+        self.actiongroup.get_action("Redo").set_sensitive(can)
+
+    def on_next_diff_changed(self, doc, have_prev, have_next):
+        self.actiongroup.get_action("PrevChange").set_sensitive(have_prev)
+        self.actiongroup.get_action("NextChange").set_sensitive(have_next)
+
+    def on_size_allocate(self, window, rect):
+        app.prefs.window_size_x = rect.width
+        app.prefs.window_size_y = rect.height
+
+    #
+    # Toolbar and menu items (file)
+    #
+    def on_menu_file_new_activate(self, menuitem):
+        NewDocDialog(self)
+
+    def on_menu_save_activate(self, menuitem):
+        self.current_doc().save()
+
+    def on_menu_save_as_activate(self, menuitem):
+        self.current_doc().save_as()
+
+    def on_menu_close_activate(self, *extra):
+        i = self.notebook.get_current_page()
+        if i >= 0:
+            page = self.notebook.get_nth_page(i).get_data("pyobject")
+            self.try_remove_page(page)
+
+    def on_menu_quit_activate(self, *extra):
+        # Delete pages from right-to-left.  This ensures that if a version
+        # control page is open in the far left page, it will be closed last.
+        for c in reversed(self.notebook.get_children()):
+            page = c.get_data("pyobject")
+            self.notebook.set_current_page(self.notebook.page_num(page.widget))
+            response = self.try_remove_page(page, appquit=1)
+            if response == gtk.RESPONSE_CANCEL:
+                return gtk.RESPONSE_CANCEL
+        for c in self.notebook.get_children():
+            c.get_data("pyobject").on_quit_event()
+        gtk.main_quit()
+        return gtk.RESPONSE_CLOSE
+
+    #
+    # Toolbar and menu items (edit)
+    #
+    def on_menu_undo_activate(self, *extra):
+        self.current_doc().on_undo_activate()
+
+    def on_menu_redo_activate(self, *extra):
+        self.current_doc().on_redo_activate()
+
+    def on_menu_refresh_activate(self, *extra):
+        self.current_doc().on_refresh_activate()
+
+    def on_menu_reload_activate(self, *extra):
+        self.current_doc().on_reload_activate()
+  
+    def on_menu_find_activate(self, *extra):
+        self.current_doc().on_find_activate()
+
+    def on_menu_find_next_activate(self, *extra):
+        self.current_doc().on_find_next_activate()
+
+    def on_menu_replace_activate(self, *extra):
+        self.current_doc().on_replace_activate()
+
+    def on_menu_copy_activate(self, *extra):
+        widget = self.widget.get_focus()
+        if isinstance(widget, gtk.Editable):
+            widget.copy_clipboard()
+        elif isinstance(widget, gtk.TextView):
+            widget.emit("copy-clipboard")
+
+    def on_menu_cut_activate(self, *extra):
+        widget = self.widget.get_focus()
+        if isinstance(widget, gtk.Editable):
+            widget.cut_clipboard()
+        elif isinstance(widget, gtk.TextView):
+            widget.emit("cut-clipboard")
+
+    def on_menu_paste_activate(self, *extra):
+        widget = self.widget.get_focus()
+        if isinstance(widget, gtk.Editable):
+            widget.paste_clipboard()
+        elif isinstance(widget, gtk.TextView):
+            widget.emit("paste-clipboard")
+
+    #
+    # Toolbar and menu items (settings)
+    #
+    def on_menu_preferences_activate(self, item):
+        preferences.PreferencesDialog(self)
+
+    def on_action_fullscreen_toggled(self, widget):
+        is_full = self.widget.window.get_state() & gtk.gdk.WINDOW_STATE_FULLSCREEN
+        if widget.get_active() and not is_full:
+            self.widget.fullscreen()
+        elif is_full:
+            self.widget.unfullscreen()
+
+    def on_menu_toolbar_toggled(self, widget):
+        app.prefs.toolbar_visible = widget.get_active()
+
+    def on_menu_statusbar_toggled(self, widget):
+        app.prefs.statusbar_visible = widget.get_active()
+
+    #
+    # Toolbar and menu items (help)
+    #
+    def on_menu_help_activate(self, button):
+        misc.open_uri("ghelp:///"+os.path.abspath(paths.help_dir("C/meld.xml")))
+
+    def on_menu_help_bug_activate(self, button):
+        misc.open_uri("http://bugzilla.gnome.org/buglist.cgi?query=product%3Ameld";)
+
+    def on_menu_about_activate(self, *extra):
+        gtk.about_dialog_set_url_hook(lambda dialog, uri: misc.open_uri(uri))
+        builder = gtk.Builder()
+        # FIXME: domain literal duplicated from bin/meld
+        builder.set_translation_domain("meld")
+        builder.add_objects_from_file(paths.ui_dir("meldapp.ui"), ["about"])
+        about = builder.get_object("about")
+        about.props.version = app.version
+        about.set_transient_for(self.widget)
+        about.run()
+        about.hide()
+
+    #
+    # Toolbar and menu items (misc)
+    #
+    def on_menu_edit_down_activate(self, *args):
+        self.current_doc().next_diff(gtk.gdk.SCROLL_DOWN)
+
+    def on_menu_edit_up_activate(self, *args):
+        self.current_doc().next_diff(gtk.gdk.SCROLL_UP)
+
+    def on_toolbar_stop_clicked(self, *args):
+        self.current_doc().stop()
+
+    def try_remove_page(self, page, appquit=0):
+        "See if a page will allow itself to be removed"
+        response = page.on_delete_event(appquit)
+        if response != gtk.RESPONSE_CANCEL:
+            self.scheduler.remove_scheduler( page.scheduler )
+            i = self.notebook.page_num( page.widget )
+            assert(i>=0)
+            # If the page we're removing is the current page, we need to trigger a switch out
+            if self.notebook.get_current_page() == i:
+                page.on_container_switch_out_event(self.ui)
+            self.notebook.remove_page(i)
+            if self.notebook.get_n_pages() == 0:
+                self.widget.set_title("Meld")
+        return response
+
+    def on_file_changed(self, srcpage, filename):
+        for c in self.notebook.get_children():
+            page = c.get_data("pyobject")
+            if page != srcpage:
+                page.on_file_changed(filename)
+
+    def _append_page(self, page, icon):
+        nbl = notebooklabel.NotebookLabel(icon, "", lambda b: self.try_remove_page(page))
+        self.notebook.append_page( page.widget, nbl)
+        self.notebook.set_current_page( self.notebook.page_num(page.widget) )
+        self.scheduler.add_scheduler(page.scheduler)
+        page.connect("label-changed", self.on_notebook_label_changed)
+        page.connect("file-changed", self.on_file_changed)
+        page.connect("create-diff", lambda obj,arg: self.append_diff(arg) )
+        page.connect("status-changed", lambda junk,arg: self.statusbar.set_doc_status(arg) )
+
+        # Allow reordering of tabs
+        self.notebook.set_tab_reorderable(page.widget, True);
+
+    def append_dirdiff(self, dirs, auto_compare=False):
+        assert len(dirs) in (1,2,3)
+        doc = dirdiff.DirDiff(app.prefs, len(dirs))
+        self._append_page(doc, "folder")
+        doc.set_locations(dirs)
+        # FIXME: This doesn't work, as dirdiff behaves differently to vcview
+        if auto_compare:
+            doc.on_button_diff_clicked(None)
+        return doc
+
+    def append_filediff(self, files):
+        assert len(files) in (1, 2, 3, 4)
+        if len(files) == 4:
+            doc = filemerge.FileMerge(app.prefs, 3)
+        else:
+            doc = filediff.FileDiff(app.prefs, len(files))
+        seq = doc.undosequence
+        seq.clear()
+        seq.connect("can-undo", self.on_can_undo)
+        seq.connect("can-redo", self.on_can_redo)
+        self._append_page(doc, "text-x-generic")
+        doc.set_files(files)
+        return doc
+
+    def append_diff(self, paths, auto_compare=False):
+        dirslist = [p for p in paths if os.path.isdir(p)]
+        fileslist = [p for p in paths if os.path.isfile(p)]
+        if dirslist and fileslist:
+            # build new file list appending previous found filename to dirs (like diff)
+            lastfilename = fileslist[0]
+            builtfilelist = []
+            for elem in paths:
+                if os.path.isdir(elem):
+                    builtfilename = os.path.join(elem, lastfilename)
+                    if os.path.isfile(builtfilename):
+                        elem = builtfilename
+                    else:
+                        # exit at first non found directory + file
+                        misc.run_dialog(_("Cannot compare a mixture of files and directories.\n"),
+                                          parent=self,
+                                          buttonstype=gtk.BUTTONS_OK)
+                        return
+                else:
+                     lastfilename = os.path.basename(elem)
+                builtfilelist.append(elem)
+            return self.append_filediff(builtfilelist)
+        elif dirslist:
+            return self.append_dirdiff(paths, auto_compare)
+        else:
+            return self.append_filediff(paths)
+
+    def append_vcview(self, location, auto_compare=False):
+        doc = vcview.VcView(app.prefs)
+        # FIXME: need a good themed VC icon
+        self._append_page(doc, "vc-icon")
+        doc.set_location(location)
+        if auto_compare:
+            doc.on_button_diff_clicked(None)
+        return doc
+
+    def _single_file_open(self, path):
+        doc = vcview.VcView(app.prefs)
+        def cleanup():
+            self.scheduler.remove_scheduler(doc.scheduler)
+        self.scheduler.add_task(cleanup)
+        self.scheduler.add_scheduler(doc.scheduler)
+        doc.set_location(path)
+        doc.connect("create-diff", lambda obj,arg: self.append_diff(arg))
+        doc.run_diff([path])
+
+    def current_doc(self):
+        "Get the current doc or a dummy object if there is no current"
+        index = self.notebook.get_current_page()
+        if index >= 0:
+            return self.notebook.get_nth_page(index).get_data("pyobject")
+        class DummyDoc(object):
+            def __getattr__(self, a): return lambda *x: None
+        return DummyDoc()
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]