[glom/gtkapplication] Add missing files



commit 5eea754fd575f32d9b0afcc590c1a42db44f05d7
Author: Murray Cumming <murrayc murrayc com>
Date:   Mon Feb 13 12:18:23 2012 +0100

    Add missing files

 glom/appwindow.cc                    | 2926 ++++++++++++++++++++++++++++++++++
 glom/appwindow.h                     |  305 ++++
 glom/bakery/appwindow.cc             |  140 ++
 glom/bakery/appwindow.h              |  138 ++
 glom/bakery/appwindow_withdoc.cc     |  496 ++++++
 glom/bakery/appwindow_withdoc.h      |  180 +++
 glom/bakery/appwindow_withdoc_gtk.cc |  661 ++++++++
 glom/bakery/appwindow_withdoc_gtk.h  |  126 ++
 8 files changed, 4972 insertions(+), 0 deletions(-)
---
diff --git a/glom/appwindow.cc b/glom/appwindow.cc
new file mode 100644
index 0000000..868b4b5
--- /dev/null
+++ b/glom/appwindow.cc
@@ -0,0 +1,2926 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2010 Murray Cumming
+ *
+ * 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.
+ */
+
+#include "config.h" //For VERSION, GLOM_ENABLE_CLIENT_ONLY, GLOM_ENABLE_SQLITE
+
+#include <glom/appwindow.h>
+#include <glom/dialog_existing_or_new.h>
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+#include <glom/mode_design/translation/dialog_change_language.h>
+#include <glom/mode_design/translation/window_translations.h>
+#include <glom/utility_widgets/filechooserdialog_saveextras.h>
+#include <glom/glade_utils.h>
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+#include <glom/utils_ui.h>
+#include <glom/glade_utils.h>
+#include <libglom/db_utils.h>
+#include <libglom/privs.h>
+#include <glom/python_embed/python_ui_callbacks.h>
+#include <glom/python_embed/glom_python.h>
+#include <libglom/spawn_with_feedback.h>
+
+#include <gtkmm/radioaction.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/main.h>
+
+#include <cstdio>
+#include <memory> //For std::auto_ptr<>
+#include <giomm/file.h>
+#include <glibmm/spawn.h>
+#include <glibmm/convert.h>
+#include <sstream> //For stringstream.
+
+#ifndef G_OS_WIN32
+#include <libepc/consumer.h>
+#include <libsoup/soup-status.h>
+#endif // !G_OS_WIN32
+
+#ifndef G_OS_WIN32
+# include <netdb.h> //For gethostbyname().
+#endif
+
+#include <glibmm/i18n.h>
+
+namespace Glom
+{
+
+//static const int GLOM_RESPONSE_BROWSE_NETWORK = 1;
+
+// Global application variable
+AppWindow* global_appwindow = 0;
+
+Glib::ustring AppWindow::m_current_locale;
+Glib::ustring AppWindow::m_original_locale;
+
+const char* AppWindow::glade_id("window_main");
+const bool AppWindow::glade_developer(false);
+
+AppWindow::AppWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
+: type_base(cobject, "Glom"),
+  m_pBoxTop(0),
+  m_pFrame(0),
+  m_bAboutShown(false),
+  m_pAbout(false),
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  m_window_translations(0),
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+  m_menu_tables_ui_merge_id(0),
+  m_menu_reports_ui_merge_id(0),
+  m_menu_print_layouts_ui_merge_id(0),
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  m_ui_save_extra_showextras(false),
+  m_ui_save_extra_newdb_hosting_mode(Document::HOSTING_MODE_DEFAULT),
+  m_avahi_progress_dialog(0),
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+  m_show_sql_debug(false)
+{
+  Gtk::Window::set_default_icon_name("glom");
+
+  //Load widgets from glade file:
+  builder->get_widget("bakery_vbox", m_pBoxTop);
+  builder->get_widget_derived("vbox_frame", m_pFrame); //This one is derived. There's a lot happening here.
+  builder->get_widget_derived("infobar_progress", m_infobar_progress);
+
+  add_mime_type("application/x-glom"); //TODO: make this actually work - we need to register it properly.
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+#ifndef G_OS_WIN32
+  //Install UI hooks for this:
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  if(!connection_pool)
+    connection_pool->set_avahi_publish_callbacks(
+      sigc::mem_fun(*this, &AppWindow::on_connection_avahi_begin),
+      sigc::mem_fun(*this, &AppWindow::on_connection_avahi_progress),
+      sigc::mem_fun(*this, &AppWindow::on_connection_avahi_done) );
+#endif
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  global_appwindow = this;
+}
+
+AppWindow::~AppWindow()
+{
+  #ifndef GLOM_ENABLE_CLIENT_ONLY
+  if(m_window_translations)
+  {
+    m_pFrame->remove_view(m_window_translations);
+    delete m_window_translations;
+  }
+
+  delete m_avahi_progress_dialog;
+  m_avahi_progress_dialog = 0;
+
+  #endif // !GLOM_ENABLE_CLIENT_ONLY
+  
+  delete m_pAbout;
+  m_pAbout = 0;
+
+  //This was set in the constructor:
+  global_appwindow = 0;
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void AppWindow::on_connection_avahi_begin()
+{
+  //Create the dialog:
+  delete m_avahi_progress_dialog;
+  m_avahi_progress_dialog = 0;
+
+  m_avahi_progress_dialog = new Gtk::MessageDialog(Utils::bold_message(_("Glom: Generating Encryption Certificates")), true, Gtk::MESSAGE_INFO);
+  m_avahi_progress_dialog->set_secondary_text(_("Please wait while Glom prepares your system for publishing over the network."));
+  m_avahi_progress_dialog->set_transient_for(*this);
+  m_avahi_progress_dialog->show();
+}
+
+void AppWindow::on_connection_avahi_progress()
+{
+  //Allow GTK+ to process events, so that the UI is responsive:
+  while(Gtk::Main::events_pending())
+   Gtk::Main::iteration();
+}
+
+void AppWindow::on_connection_avahi_done()
+{
+  //Delete the dialog:
+  delete m_avahi_progress_dialog;
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+bool AppWindow::init(const Glib::ustring& document_uri)
+{
+  return init(document_uri, false);
+}
+
+bool AppWindow::init(const Glib::ustring& document_uri, bool restore)
+{
+  type_base::init(); //calls init_menus() and init_toolbars()
+
+  //m_pFrame->set_shadow_type(Gtk::SHADOW_IN);
+
+  if(document_uri.empty())
+  {
+    Document* pDocument = static_cast<Document*>(get_document());
+    if(pDocument && pDocument->get_connection_database().empty()) //If it is a new (default) document.
+    {
+        return offer_new_or_existing();
+    }
+  }
+  else
+  {
+    if(restore)
+      return do_restore_backup(document_uri);
+    else
+    {
+      const bool test = open_document(document_uri);
+      if(!test)
+        return offer_new_or_existing();
+    }
+  }
+
+  return true;
+  //show_all();
+}
+
+bool AppWindow::get_show_sql_debug() const
+{
+  return m_show_sql_debug;
+}
+
+void AppWindow::set_show_sql_debug(bool val)
+{
+  m_show_sql_debug = val;
+}
+
+void AppWindow::set_stop_auto_server_shutdown(bool val)
+{
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  if(connection_pool)
+    connection_pool->set_auto_server_shutdown(!val);
+}
+
+void AppWindow::init_layout()
+{
+  //We override this method so that we can put everything in the vbox from the glade file, instead of the vbox from AppWindow_Gtk.
+
+  //Add menu bar at the top:
+  //These were defined in init_uimanager().
+  Gtk::MenuBar* pMenuBar = static_cast<Gtk::MenuBar*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
+  m_pBoxTop->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+  add_accel_group(m_refUIManager->get_accel_group());
+
+  //Add placeholder, to be used by add():
+  //m_pBoxTop->pack_start(m_VBox_PlaceHolder);
+  //m_VBox_PlaceHolder.show();
+}
+
+void AppWindow::init_toolbars()
+{
+  //We override this because
+  //a) We don't want a toolbar, and
+  //b) The default toolbar layout has actions that we don't have.
+
+/*
+  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <toolbar name='Bakery_ToolBar'>"
+    "    <placeholder name='Bakery_ToolBarItemsPH'>"
+    "      <toolitem action='BakeryAction_File_New' />"
+    "      <toolitem action='BakeryAction_File_Open' />"
+    "      <toolitem action='BakeryAction_File_Save' />"
+    "    </placeholder>"
+    "  </toolbar>"
+    "</ui>";
+
+  add_ui_from_string(ui_description);
+*/
+}
+
+void AppWindow::init_menus_file()
+{
+  //Overridden to remove the Save and Save-As menu items,
+  //because all changes are saved immediately and automatically.
+
+  // File menu
+
+  //Build actions:
+  m_refFileActionGroup = Gtk::ActionGroup::create("BakeryFileActions");
+
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File", _("_File")));
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File_RecentFiles", _("_Recent Files")));
+
+  //File actions
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_New", Gtk::Stock::NEW),
+                        sigc::mem_fun((AppWindow&)*this, &AppWindow::on_menu_file_new));
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Open", Gtk::Stock::OPEN),
+                        sigc::mem_fun((AppWindow_WithDoc&)*this, &AppWindow_WithDoc::on_menu_file_open));
+
+  Glib::RefPtr<Gtk::Action> action = Gtk::Action::create("BakeryAction_File_SaveAsExample", _("_Save as Example"));
+  m_listDeveloperActions.push_back(action);
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  m_refFileActionGroup->add(action,
+                        sigc::mem_fun((AppWindow&)*this, &AppWindow::on_menu_file_save_as_example));
+
+  action = Gtk::Action::create("BakeryAction_Menu_File_Export", _("_Export"));
+  m_refFileActionGroup->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_file_export));
+  m_listTableSensitiveActions.push_back(action);
+
+  action = Gtk::Action::create("BakeryAction_Menu_File_Import", _("I_mport"));
+  m_refFileActionGroup->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_file_import));
+  m_listTableSensitiveActions.push_back(action);
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  m_toggleaction_network_shared = Gtk::ToggleAction::create("BakeryAction_Menu_File_Share", _("S_hared on Network"));
+  m_refFileActionGroup->add(m_toggleaction_network_shared);
+  m_listTableSensitiveActions.push_back(m_toggleaction_network_shared);
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  m_connection_toggleaction_network_shared =
+    m_toggleaction_network_shared->signal_toggled().connect(
+      sigc::mem_fun(*this, &AppWindow::on_menu_file_toggle_share) );
+  m_listDeveloperActions.push_back(m_toggleaction_network_shared);
+#endif //!GLOM_ENABLE_CLIENT_ONLY
+
+  action = Gtk::Action::create("GlomAction_Menu_File_Print", Gtk::Stock::PRINT);
+  m_refFileActionGroup->add(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refFileActionGroup->add(Gtk::Action::create("GlomAction_File_Print", _("_Standard")),
+                        sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_file_print) );
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  Glib::RefPtr<Gtk::Action> action_print_edit = Gtk::Action::create("GlomAction_File_PrintEdit", _("_Edit Print Layouts"));
+  m_refFileActionGroup->add(action_print_edit, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_file_print_edit_layouts));
+  m_listDeveloperActions.push_back(action_print_edit);
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Close", Gtk::Stock::CLOSE),
+                        sigc::mem_fun((AppWindow_WithDoc&)*this, &AppWindow_WithDoc::on_menu_file_close));
+
+  m_refUIManager->insert_action_group(m_refFileActionGroup);
+
+  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_File'>"
+    "      <menu action='BakeryAction_Menu_File'>"
+    "        <menuitem action='BakeryAction_File_New' />"
+    "        <menuitem action='BakeryAction_File_Open' />"
+    "        <menu action='BakeryAction_Menu_File_RecentFiles'>"
+    "        </menu>"
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    "        <menuitem action='BakeryAction_File_SaveAsExample' />"
+    "        <separator/>"
+    "        <menuitem action='BakeryAction_Menu_File_Export' />"
+    "        <menuitem action='BakeryAction_Menu_File_Import' />"
+    "        <menuitem action='BakeryAction_Menu_File_Share' />"
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+    "        <separator/>"
+    "        <menu action='GlomAction_Menu_File_Print'>"
+    "          <menuitem action='GlomAction_File_Print' />"
+    "          <placeholder name='Menu_PrintLayouts_Dynamic' />"
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    "          <menuitem action='GlomAction_File_PrintEdit' />"
+#endif //GLOM_ENABLE_CLIENT_ONLY
+    "        </menu>"
+    "        <separator/>"
+    "        <menuitem action='BakeryAction_File_Close' />"
+    "      </menu>"
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+
+  //Add menu:
+  add_ui_from_string(ui_description);
+
+  //Add recent-files submenu:
+  init_menus_file_recentfiles("/Bakery_MainMenu/Bakery_MenuPH_File/BakeryAction_Menu_File/BakeryAction_Menu_File_RecentFiles");
+}
+
+void AppWindow::init_menus()
+{
+  init_menus_file();
+  init_menus_edit();
+
+  //Build actions:
+  m_refActionGroup_Others = Gtk::ActionGroup::create("GlomOthersActions");
+
+  //"Tables" menu:
+  m_refActionGroup_Others->add( Gtk::Action::create("Glom_Menu_Tables", _("_Tables")) );
+
+//  Glib::RefPtr<Gtk::Action> action = Gtk::Action::create("GlomAction_Menu_Navigate_Database", _("_Database"));
+//  m_listDeveloperActions.push_back(action);
+//  m_refActionGroup_Others->add(action,
+//                        sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_Navigate_Database) );
+
+  Glib::RefPtr<Gtk::Action> action;
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  action = Gtk::Action::create("GlomAction_Menu_EditTables", _("_Edit Tables"));
+  m_refActionGroup_Others->add(action,
+                        sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_Tables_EditTables) );
+  m_listDeveloperActions.push_back(action);
+
+/* Commented out because it is useful but confusing to new users:
+  action = Gtk::Action::create("GlomAction_Menu_AddRelatedTable", _("Add _Related Table"));
+  m_refActionGroup_Others->add(action,
+                        sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_Tables_AddRelatedTable) );
+  m_listDeveloperActions.push_back(action);
+*/
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  //"Reports" menu:
+  m_refActionGroup_Others->add( Gtk::Action::create("Glom_Menu_Reports", _("_Reports")) );
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  action = Gtk::Action::create("GlomAction_Menu_EditReports", _("_Edit Reports"));
+  m_refActionGroup_Others->add(action,
+                        sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_Reports_EditReports) );
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+#endif
+
+  //We remember this action, so that it can be explicitly activated later.
+  m_action_mode_find = Gtk::ToggleAction::create("GlomAction_Menu_Edit_Find", _("_Find"), "", false);
+  m_refActionGroup_Others->add(m_action_mode_find,  Gtk::AccelKey("<control>F"),
+                        sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_Edit_Find) );
+  m_listTableSensitiveActions.push_back(m_action_mode_find);
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  action = Gtk::Action::create("Glom_Menu_Developer", C_("Developer menu title", "_Developer"));
+  m_refActionGroup_Others->add(action);
+
+
+  Gtk::RadioAction::Group group_userlevel;
+
+  m_action_menu_developer_developer = Gtk::RadioAction::create(group_userlevel, "GlomAction_Menu_Developer_Developer", _("_Developer Mode"));
+  m_refActionGroup_Others->add(m_action_menu_developer_developer,
+                        sigc::mem_fun(*this, &AppWindow::on_menu_developer_developer) );
+
+  m_action_menu_developer_operator =  Gtk::RadioAction::create(group_userlevel, "GlomAction_Menu_Developer_Operator", _("_Operator Mode"));
+  m_refActionGroup_Others->add(m_action_menu_developer_operator,
+                          sigc::mem_fun(*this, &AppWindow::on_menu_developer_operator) );
+
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Database_Preferences", _("_Database Preferences"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_database_preferences) );
+
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Fields", _("_Fields"));
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_fields) );
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_RelationshipsOverview", _("Relationships _Overview"));
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_relationships_overview) );
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Relationships", _("_Relationships for this Table"));
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_relationships) );
+
+  m_action_developer_users = Gtk::Action::create("GlomAction_Menu_Developer_Users", _("_Users"));
+  m_listDeveloperActions.push_back(m_action_developer_users);
+  m_refActionGroup_Others->add(m_action_developer_users, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_users));
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_PrintLayouts", _("_Print Layouts")); //TODO: Rename? This looks like an action rather than a noun. It won't actually start printing.
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_print_layouts));
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Reports", _("R_eports"));
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_reports));
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Script_Library", _("Script _Library"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_script_library));
+
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Layout", _("_Layout"));
+  m_listDeveloperActions.push_back(action);
+  m_listTableSensitiveActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_developer_layout));
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_ChangeLanguage", _("Test Tra_nslation"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &AppWindow::on_menu_developer_changelanguage));
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_Translations", _("_Translations"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &AppWindow::on_menu_developer_translations));
+
+
+  //"Active Platform" menu:
+  action =  Gtk::Action::create("Glom_Menu_Developer_ActivePlatform", _("_Active Platform"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action);
+  Gtk::RadioAction::Group group_active_platform;
+
+  action = Gtk::RadioAction::create(group_active_platform, "GlomAction_Menu_Developer_ActivePlatform_Normal",
+    _("_Normal"), _("The layout to use for normal desktop environments."));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &AppWindow::on_menu_developer_active_platform_normal));
+
+  action = Gtk::RadioAction::create(group_active_platform, "GlomAction_Menu_Developer_ActivePlatform_Maemo",
+    _("_Maemo"), _("The layout to use for Maemo devices."));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &AppWindow::on_menu_developer_active_platform_maemo));
+
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_ExportBackup", _("_Export Backup"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &AppWindow::on_menu_developer_export_backup));
+
+  action = Gtk::Action::create("GlomAction_Menu_Developer_RestoreBackup", _("_Restore Backup"));
+  m_listDeveloperActions.push_back(action);
+  m_refActionGroup_Others->add(action, sigc::mem_fun(*this, &AppWindow::on_menu_developer_restore_backup));
+
+  //TODO: Think of a better name for this menu item,
+  //though it mostly only exists because it is not quite ready to be on by default:
+  //Note to translators: Drag and Drop is part of the name, not a verb or action:
+  m_action_enable_layout_drag_and_drop = Gtk::ToggleAction::create("GlomAction_Menu_Developer_EnableLayoutDragAndDrop", _("_Drag and Drop Layout"));
+  m_listDeveloperActions.push_back(m_action_enable_layout_drag_and_drop);
+  m_refActionGroup_Others->add(m_action_enable_layout_drag_and_drop, sigc::mem_fun(*this, &AppWindow::on_menu_developer_enable_layout_drag_and_drop));
+
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  m_refUIManager->insert_action_group(m_refActionGroup_Others);
+  
+  action = Gtk::Action::create("Glom_Menu_Help", C_("Help menu title", "_Help"));
+  m_refActionGroup_Others->add(action);
+  
+  m_refHelpActionGroup = Gtk::ActionGroup::create("GlomHelpActions");
+  m_refHelpActionGroup->add(Gtk::Action::create("GlomAction_Menu_Help", _("_Help")));
+  
+  m_refHelpActionGroup->add( Gtk::Action::create("GlomAction_Menu_Help_About",
+                        _("_About"), _("About the application")),
+                        sigc::mem_fun(*this, &AppWindow::on_menu_help_about) );
+  m_refHelpActionGroup->add( Gtk::Action::create("GlomAction_Menu_Help_Contents",
+                        _("_Contents"), _("Help with the application")),
+                        sigc::mem_fun(*this, &AppWindow::on_menu_help_contents) );
+  m_refUIManager->insert_action_group(m_refHelpActionGroup);                       
+
+  //Build part of the menu structure, to be merged in by using the "Bakery_MenuPH_Others" placeholder:
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_Edit'>"
+    "      <menu action='BakeryAction_Menu_Edit'>"
+    "        <menuitem action='BakeryAction_Edit_Cut' />"
+    "        <menuitem action='BakeryAction_Edit_Copy' />"
+    "        <menuitem action='BakeryAction_Edit_Paste' />"
+    "        <menuitem action='BakeryAction_Edit_Clear' />"
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_Edit_Find' />"
+    "      </menu>"
+    "    </placeholder>"
+    "    <placeholder name='Bakery_MenuPH_Others'>"
+    "      <menu action='Glom_Menu_Tables'>"
+    "        <placeholder name='Menu_Tables_Dynamic' />"
+    "        <separator />"
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    "        <menuitem action='GlomAction_Menu_EditTables' />"
+/* Commented out because it is useful but confusing to new users:
+    "        <menuitem action='GlomAction_Menu_AddRelatedTable' />"
+*/
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+    "     </menu>"
+    "     <menu action='Glom_Menu_Reports'>"
+    "        <placeholder name='Menu_Reports_Dynamic' />"
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_EditReports' />"
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+    "     </menu>"
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    "      <menu action='Glom_Menu_Developer'>"
+    "        <menuitem action='GlomAction_Menu_Developer_Operator' />"
+    "        <menuitem action='GlomAction_Menu_Developer_Developer' />"
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_Developer_Fields' />"
+    "        <menuitem action='GlomAction_Menu_Developer_Relationships' />"
+    "        <menuitem action='GlomAction_Menu_Developer_RelationshipsOverview' />"
+    "        <menuitem action='GlomAction_Menu_Developer_Layout' />"
+    "        <menuitem action='GlomAction_Menu_Developer_PrintLayouts' />"
+    "        <menuitem action='GlomAction_Menu_Developer_Reports' />"
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_Developer_Database_Preferences' />"
+    "        <menuitem action='GlomAction_Menu_Developer_Users' />"
+    "        <menuitem action='GlomAction_Menu_Developer_Script_Library' />"
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_Developer_Translations' />"
+    "        <menuitem action='GlomAction_Menu_Developer_ChangeLanguage' />"
+    "        <separator />"
+    "        <menu action='Glom_Menu_Developer_ActivePlatform'>"
+    "          <menuitem action='GlomAction_Menu_Developer_ActivePlatform_Normal' />"
+    "          <menuitem action='GlomAction_Menu_Developer_ActivePlatform_Maemo' />"
+    "        </menu>"
+    "        <menuitem action='GlomAction_Menu_Developer_EnableLayoutDragAndDrop' />"
+    "        <separator />"
+    "        <menuitem action='GlomAction_Menu_Developer_ExportBackup' />"
+    "        <menuitem action='GlomAction_Menu_Developer_RestoreBackup' />"
+    "      </menu>"
+    "      <menu action='Glom_Menu_Help'>"
+    "        <menuitem action='GlomAction_Menu_Help_About' />"
+    "        <menuitem action='GlomAction_Menu_Help_Contents' />"
+    "      </menu>"
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+
+/*  "        <menuitem action='GlomAction_Menu_Developer_RelationshipsOverview' />" */
+
+  //Add menu:
+  add_ui_from_string(ui_description);
+
+  update_table_sensitive_ui();
+
+  fill_menu_tables();
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+
+void AppWindow::on_menu_help_about()
+{
+  if(m_pAbout && m_bAboutShown) // "About" box hasn't been closed, so just raise it
+  {
+    m_pAbout->set_transient_for(*this);
+
+    Glib::RefPtr<Gdk::Window> about_win = m_pAbout->get_window();
+    about_win->show();
+    about_win->raise();
+  }
+  else
+  {
+    //Re-create About box:
+    delete m_pAbout;
+    m_pAbout = 0;
+
+    m_pAbout = new Gtk::AboutDialog;
+
+    m_pAbout->set_program_name(m_strAppName);
+    m_pAbout->set_version(m_strVersion);
+    m_pAbout->set_comments(_("A Database GUI"));
+    m_pAbout->set_version(PACKAGE_VERSION);
+    m_pAbout->set_copyright(_("Â 2000-2011 Murray Cumming, Openismus GmbH"));
+    std::vector<Glib::ustring> vecAuthors;
+    vecAuthors.push_back("Murray Cumming <murrayc murrayc com>");
+    m_pAbout->set_authors(vecAuthors);
+    
+    Glib::RefPtr<Gdk::Pixbuf> logo = Gdk::Pixbuf::create_from_file(GLOM_ICONPATH);
+    if(logo)
+      m_pAbout->set_logo(logo);
+    else
+      std::cout << G_STRFUNC << ": Could not load icon from path=" << GLOM_ICONPATH << std::endl;
+
+    m_pAbout->signal_hide().connect( sigc::mem_fun(*this, &AppWindow::on_about_close) );
+    m_bAboutShown = true;
+    static_cast<Gtk::Dialog*>(m_pAbout)->run(); //show() would be better. see below:
+    m_pAbout->hide();
+    //m_pAbout->show(); //TODO: respond to the OK button.
+  }
+}
+
+void AppWindow::on_about_close()
+{
+  m_bAboutShown = false;
+}
+
+void AppWindow::on_menu_file_toggle_share()
+{
+  if(!m_pFrame)
+    return;
+
+  m_pFrame->on_menu_file_toggle_share(m_toggleaction_network_shared);
+}
+
+void AppWindow::on_menu_developer_developer()
+{
+  if(!m_pFrame)
+    return;
+
+  m_pFrame->on_menu_developer_developer(m_action_menu_developer_developer, m_action_menu_developer_operator);
+  m_pFrame->set_enable_layout_drag_and_drop(m_action_enable_layout_drag_and_drop->get_active());
+}
+
+void AppWindow::on_menu_developer_operator()
+{
+  if(m_pFrame)
+  {
+    m_pFrame->on_menu_developer_operator(m_action_menu_developer_operator);
+    m_pFrame->set_enable_layout_drag_and_drop(false);
+  }
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+static bool hostname_is_localhost(const Glib::ustring& hostname)
+{
+  if(hostname.empty())
+    return false;
+
+  //Quick short cut:
+  if(hostname == "localhost")
+    return true;
+  else if(hostname == "localhost.localdomain")
+    return true;
+  else if(hostname == "127.0.0.1") //Standard IP address for localhost.
+    return true;
+
+  //TODO: Is there some way to compare hostents?
+  /*
+  hostent* a = gethostbyname("localhost");
+  hostent* b = gethostbyname(hostname.c_str());
+  if(!a || !b)
+  {
+    return a == b;
+  }
+
+  //TODO: return are_equal(a, b);
+  */
+
+  return true;
+}
+
+void AppWindow::ui_warning_load_failed(int failure_code)
+{
+  if(failure_code == Document::LOAD_FAILURE_CODE_NOT_FOUND)
+  {
+    //TODO: Put this in the generic bakery code.
+    ui_warning(_("Open Failed"),
+      _("The document could not be found."));
+
+    //TODO: Glom::Bakery::AppWindow_WithDoc removes the file from the recent history,
+    //but the initial/welcome dialog doesn't yet update its list when the
+    //recent history changes.
+  }
+  else if(failure_code == Document::LOAD_FAILURE_CODE_FILE_VERSION_TOO_NEW)
+  {
+    ui_warning(_("Open Failed"),
+      _("The document could not be opened because it was created or modified by a newer version of Glom."));
+  }
+  else
+    GlomBakery::AppWindow_WithDoc_Gtk::ui_warning_load_failed();
+}
+
+
+#ifndef G_OS_WIN32
+void AppWindow::open_browsed_document(const EpcServiceInfo* server, const Glib::ustring& service_name)
+{
+  gsize length = 0;
+  gchar *document_contents = 0;
+
+  bool keep_trying = true;
+  while(keep_trying)
+  {
+    //Request a password to attempt retrieval of the document over the network:
+    Dialog_Connection* dialog_connection = 0;
+    //Load the Glade file and instantiate its widgets to get the dialog stuff:
+    Utils::get_glade_widget_derived_with_warning(dialog_connection);
+    dialog_connection->set_transient_for(*this);
+    dialog_connection->set_connect_to_browsed();
+    dialog_connection->set_database_name(service_name);
+    const int response = Glom::Utils::dialog_run_with_help(dialog_connection);
+    dialog_connection->hide();
+    if(response != Gtk::RESPONSE_OK)
+      keep_trying = false;
+    else
+    {
+      //Open the document supplied by the other glom instance on the network:
+      EpcConsumer* consumer = epc_consumer_new(server);
+
+      Glib::ustring username, password;
+      dialog_connection->get_username_and_password(username, password);
+      epc_consumer_set_username(consumer, username.c_str());
+      epc_consumer_set_password(consumer, password.c_str());
+
+      GError *error = 0;
+      document_contents = (gchar*)epc_consumer_lookup(consumer, "document", &length, &error);
+      if(error)
+      {
+        std::cout << "debug: " << G_STRFUNC << ": " << std::endl << "  " << error->message << std::endl;
+        const int error_code = error->code;
+        g_clear_error(&error);
+
+        if(error_code == SOUP_STATUS_FORBIDDEN ||
+           error_code == SOUP_STATUS_UNAUTHORIZED)
+        {
+          //std::cout << "   SOUP_STATUS_FORBIDDEN or SOUP_STATUS_UNAUTHORIZED" << std::endl;
+
+          Utils::show_ok_dialog(_("Connection Failed"), _("Glom could not connect to the database server. Maybe you entered an incorrect user name or password, or maybe the postgres database server is not running."), *this, Gtk::MESSAGE_ERROR); //TODO: Add help button.
+        }
+      }
+      else
+      {
+        //Store the username and password (now known to be correct) temporarily,
+        //so we can use them when connecting directly to the database later:
+        //(We can't just put them in the temp document instance, because these are not saved to disk.)
+        m_temp_username = username;
+        m_temp_password = password;
+
+        keep_trying = false; //Finished.
+      }
+    }
+
+    delete dialog_connection;
+    dialog_connection = 0;
+
+  }
+
+
+  if(document_contents && length)
+  {
+    //Create a temporary Document instance, so we can manipulate the data:
+    Document document_temp;
+    int failure_code = 0;
+    const bool loaded = document_temp.load_from_data((const guchar*)document_contents, length, failure_code);
+    if(loaded)
+    {
+      // Connection is always remote-hosted in client only mode:
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+#ifdef GLOM_ENABLE_POSTGRESQL
+      //Stop the document from being self-hosted (it's already hosted by the other networked Glom instance):
+      if(document_temp.get_hosting_mode() == Document::HOSTING_MODE_POSTGRES_SELF)
+        document_temp.set_hosting_mode(Document::HOSTING_MODE_POSTGRES_CENTRAL);
+#endif //GLOM_ENABLE_POSTGRESQL
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+      // TODO: Error out in case this is a sqlite database, since we probably
+      // can't access it from this host?
+
+      //If the publisher thinks that it's using a postgres database on localhost,
+      //then we need to use a host name that means the same thing from the client's PC:
+      const Glib::ustring host = document_temp.get_connection_server();
+      if(hostname_is_localhost(host))
+        document_temp.set_connection_server( epc_service_info_get_host(server) );
+
+      //Make sure that we only use the specified port instead of connecting to some other postgres instance
+      //on the same server:
+      document_temp.set_connection_try_other_ports(false);
+    }
+    else
+    {
+      std::cerr << "Could not parse the document that was retrieved over the network: failure_code=" << failure_code << std::endl;
+    }
+
+    g_free(document_contents);
+    document_contents = 0;
+
+    //TODO_Performance: Horribly inefficient, but happens rarely:
+    const Glib::ustring temp_document_contents = document_temp.build_and_get_contents();
+
+    //This loads the document and connects to the database (using m_temp_username and m_temp_password):
+    open_document_from_data((const guchar*)temp_document_contents.c_str(), temp_document_contents.bytes());
+
+    //Mark the document as opened-from-browse
+    //so we don't think that opening has failed because it has no URI,
+    //and to stop us from allowing developer mode
+    //(that would require changes to the original document).
+    Document* document = dynamic_cast<Document*>(get_document());
+    if(document)
+    {
+      document->set_opened_from_browse();
+      document->set_userlevel(AppState::USERLEVEL_OPERATOR); //TODO: This should happen automatically.
+
+      document->set_network_shared(true); //It is shared by the computer that we opened this from.
+      update_network_shared_ui();
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+
+      update_userlevel_ui();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+    }
+  }
+}
+#endif // !G_OS_WIN32
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+//Copied from bakery:
+static bool uri_is_writable(const Glib::RefPtr<const Gio::File>& uri)
+{
+  if(!uri)
+    return false;
+
+  Glib::RefPtr<const Gio::FileInfo> file_info;
+
+  try
+  {
+    file_info = uri->query_info(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+  }
+  catch(const Glib::Error& /* ex */)
+  {
+    return false;
+  }
+
+  if(file_info)
+  {
+    return file_info->get_attribute_boolean(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+  }
+  else
+    return true; //Not every URI protocol supports access rights, so assume that it's writable and complain later.
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+//TODO: Use Gio::AppWindow? Is this even used?
+void AppWindow::new_instance(const Glib::ustring& uri) //Override
+{
+  Glib::ustring command = "glom";
+  if(!uri.empty())
+    command += ' ' + uri;
+
+  try
+  {
+    Glib::spawn_command_line_sync(
+      command.c_str());
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": " << ex.what() << std::endl;
+  }
+}
+
+void AppWindow::init_create_document()
+{
+  if(!m_pDocument)
+  {
+    Document* document_glom = new Document();
+
+    //By default, we assume that the original is in the current locale.
+    document_glom->set_translation_original_locale(AppWindow::get_current_locale());
+
+    m_pDocument = document_glom;
+    //document_glom->set_parent_window(this); //So that it can show a BusyCursor when loading and saving.
+
+
+    //Tell document about view:
+    m_pDocument->set_view(m_pFrame);
+
+    //Tell view about document:
+    //(This calls set_document() in the child views too.)
+    m_pFrame->set_document(static_cast<Document*>(m_pDocument));
+  }
+
+  type_base::init_create_document(); //Sets window title. Doesn't recreate doc.
+}
+
+bool AppWindow::check_document_hosting_mode_is_supported(Document* document)
+{
+  //If it's an example then the document's hosting mode doesn't matter,
+  //because the user will be asked to choose one when saving anyway.
+  if(document->get_is_example_file())
+    return true;
+
+  //Check that the file's hosting mode is supported by this build:
+  Glib::ustring error_message;
+  switch(document->get_hosting_mode())
+  {
+    case Document::HOSTING_MODE_POSTGRES_SELF:
+    {
+      #ifdef GLOM_ENABLE_CLIENT_ONLY
+      error_message = _("The file cannot be opened because this version of Glom does not support self-hosting of databases.");
+      break;
+      #endif //GLOM_ENABLE_CLIENT_ONLY
+
+      #ifndef GLOM_ENABLE_POSTGRESQL
+      error_message = _("The file cannot be opened because this version of Glom does not support PostgreSQL databases.");
+      break;
+      #endif //GLOM_ENABLE_POSTGRESQL
+
+      break;
+    }
+    case Document::HOSTING_MODE_POSTGRES_CENTRAL:
+    {
+      #ifndef GLOM_ENABLE_POSTGRESQL
+      error_message = _("The file cannot be opened because this version of Glom does not support PostgreSQL databases.");
+      #endif //GLOM_ENABLE_POSTGRESQL
+
+      break;
+    }
+    case Document::HOSTING_MODE_SQLITE:
+    {
+      #ifndef GLOM_ENABLE_SQLITE
+      error_message = _("The file cannot be opened because this version of Glom does not support SQLite databases.");
+      #endif //GLOM_ENABLE_SQLITE
+
+      break;
+    }
+    default:
+    {
+      //on_document_load() should have checked for this already, informing the user.
+      std::cerr << G_STRFUNC << ": Unhandled hosting mode: " << document->get_hosting_mode() << std::endl;
+     g_assert_not_reached();
+     break;
+    }
+  }
+
+  if(error_message.empty())
+    return true;
+
+  //Warn the user.
+  Frame_Glom::show_ok_dialog(_("File Uses Unsupported Database Backend"), error_message, *this, Gtk::MESSAGE_ERROR);
+  return false;
+}
+
+bool AppWindow::on_document_load()
+{
+  //Link to the database described in the document.
+  //Need to ask user for user/password:
+  //m_pFrame->load_from_document();
+  Document* pDocument = static_cast<Document*>(get_document());
+  if(!pDocument)
+    return false;
+
+  //Set this so that AppWindow::get_current_locale() works as expected:
+  AppWindow::set_original_locale(pDocument->get_translation_original_locale());
+
+  if(!pDocument->get_is_new() && !check_document_hosting_mode_is_supported(pDocument))
+    return false;
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  //Connect signals:
+  pDocument->signal_userlevel_changed().connect( sigc::mem_fun(*this, &AppWindow::on_userlevel_changed) );
+
+  //Disable/Enable actions, depending on userlevel:
+  pDocument->emit_userlevel_changed();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  if(pDocument->get_connection_database().empty()) //If it is a new (default) document.
+  {
+    //offer_new_or_existing();
+  }
+  else
+  {
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    //Prevent saving until we are sure that everything worked.
+    //This also stops us from losing the example data as soon as we say the new file (created from the example) is not an example.
+    pDocument->set_allow_autosave(false);
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+    // Example files and backup files are not supported in client only mode because they
+    // would need to be saved, but saving support is disabled.
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    const bool is_example = pDocument->get_is_example_file();
+    const bool is_backup = pDocument->get_is_backup_file();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+    const std::string original_uri = pDocument->get_file_uri();
+
+    if(is_example || is_backup)
+    {
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+      // Remember the URI to the example file to be able to prevent
+      // adding the URI to the recently used files in document_history_add.
+      // We want to add the document that is created from the example
+      // instead of the example itself.
+      // TODO: This is a weird hack. Find a nicer way. murrayc.
+      m_example_uri = original_uri;
+
+      pDocument->set_file_uri(Glib::ustring()); //Prevent it from defaulting to the read-only examples directory when offering saveas.
+      //m_ui_save_extra_* are used by offer_saveas() if it's not empty:
+      m_ui_save_extra_showextras = true;
+
+      if(is_example)
+      {
+        m_ui_save_extra_title = _("Creating From Example File");
+        m_ui_save_extra_message = _("To use this example file you must save an editable copy of the file. A new database will also be created on the server.");
+      }
+      else if(is_backup)
+      {
+        m_ui_save_extra_title = _("Creating From Backup File");
+        m_ui_save_extra_message = _("To use this backup file you must save an editable copy of the file. A new database will also be created on the server.");
+      }
+
+      m_ui_save_extra_newdb_title = "TODO";
+      m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT;
+
+
+      // Reinit cancelled state
+      set_operation_cancelled(false);
+
+      offer_saveas();
+      // Note that bakery will try to add the example file itself to the
+      // recently used documents, which is not what we want.
+      m_ui_save_extra_message.clear();
+      m_ui_save_extra_title.clear();
+
+      if(!get_operation_cancelled())
+      {
+        //Get the results from the extended save dialog:
+        pDocument->set_database_title_original(m_ui_save_extra_newdb_title);
+        pDocument->set_hosting_mode(m_ui_save_extra_newdb_hosting_mode);
+        m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT;
+        pDocument->set_is_example_file(false);
+        pDocument->set_is_backup_file(false);
+
+        // For self-hosting, we will choose a port later. For central
+        // hosting, try several default ports. Don't use the values that
+        // are set in the example file.
+        pDocument->set_connection_port(0);
+        pDocument->set_connection_try_other_ports(true);
+
+        // We have a valid uri, so we can set it to !new and modified here
+      }
+
+      m_ui_save_extra_newdb_title.clear();
+      m_ui_save_extra_showextras = false;
+
+      if(get_operation_cancelled())
+      {
+        pDocument->set_modified(false);
+        pDocument->set_is_new(true);
+        pDocument->set_allow_autosave(true); //Turn this back on.
+        std::cout << "debug: user cancelled creating database" << std::endl;
+        return false;
+      }
+
+#else // !GLOM_ENABLE_CLIENT_ONLY
+      // TODO_clientonly: Tell the user that opening example files is
+      // not supported. This could alternatively also be done in
+      // Document_after::load_after, I am not sure which is better.
+      ui_warning_load_failed(0);
+      return false;
+#endif // GLOM_ENABLE_CLIENT_ONLY
+    }
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+    //Warn about read-only files, because users will otherwise wonder why they can't use Developer mode:
+    Document::userLevelReason reason = Document::USER_LEVEL_REASON_UNKNOWN;
+    const AppState::userlevels userlevel = pDocument->get_userlevel(reason);
+    if( (userlevel == AppState::USERLEVEL_OPERATOR) && (reason == Document::USER_LEVEL_REASON_FILE_READ_ONLY) )
+    {
+      Gtk::MessageDialog dialog(Utils::bold_message(_("Opening Read-Only File.")), true,  Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE);
+      dialog.set_secondary_text(_("This file is read only, so you will not be able to enter Developer mode to make design changes."));
+      dialog.set_transient_for(*this);
+      dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+      dialog.add_button(_("Continue without Developer Mode"), Gtk::RESPONSE_OK); //arbitrary response code.
+
+      const int response = dialog.run();
+      dialog.hide();
+      if((response == Gtk::RESPONSE_CANCEL)  || (response == Gtk::RESPONSE_DELETE_EVENT))
+        return false;
+    }
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+    //Read the connection information from the document:
+    ConnectionPool* connection_pool = ConnectionPool::get_instance();
+    if(!connection_pool)
+      return false; //Impossible anyway.
+    else
+    {
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+      connection_pool->set_get_document_func( sigc::mem_fun(*this, &AppWindow::on_connection_pool_get_document) );
+#endif
+
+      connection_pool->set_ready_to_connect(true); //connect_to_server() will now attempt the connection-> Shared instances of m_Connection will also be usable.
+
+      //Attempt to connect to the specified database:
+      bool test = false;
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+      if(is_example || is_backup)
+      {
+        //The user has already had the chance to specify a new filename and database name.
+        test = m_pFrame->connection_request_password_and_choose_new_database_name();
+      }
+      else
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+      {
+        //Ask for the username/password and connect:
+        //Note that m_temp_username and m_temp_password are set if
+        //we already asked for them when getting the document over the network:
+
+        //Use the default username/password if opening as non network-shared:
+        if(!(pDocument->get_network_shared()))
+        {
+          // If the document is centrally hosted, don't pretend to know the
+          // username or password, because we don't. The user will enter
+          // the login credentials in a dialog.
+          if(pDocument->get_hosting_mode() != Document::HOSTING_MODE_POSTGRES_CENTRAL)
+            m_temp_username = Privs::get_default_developer_user_name(m_temp_password);
+        }
+
+        bool database_not_found = false;
+        test = m_pFrame->connection_request_password_and_attempt(database_not_found, m_temp_username, m_temp_password);
+        m_temp_username = Glib::ustring();
+        m_temp_password = Glib::ustring();
+
+        if(!test && database_not_found)
+        {
+          #ifndef GLOM_ENABLE_CLIENT_ONLY
+          if(!is_example)
+          {
+            //The connection to the server is OK, but the database is not there yet.
+            Frame_Glom::show_ok_dialog(_("Database Not Found On Server"), _("The database could not be found on the server. Please consult your system administrator."), *this, Gtk::MESSAGE_ERROR);
+          }
+          else
+          #endif // !GLOM_ENABLE_CLIENT_ONLY
+            std::cerr << G_STRFUNC << ": unexpected database_not_found error when opening example." << std::endl;
+        }
+        else if(!test)
+        {
+          //std::cerr might show some hints, but we don't want to confront the user with them:
+          //TODO: Actually complain about specific stuff such as missing data, because the user might really play with the file system.
+          Frame_Glom::show_ok_dialog(_("Problem Loading Document"), _("Glom could not load the document."), *this, Gtk::MESSAGE_ERROR);
+          std::cerr << G_STRFUNC << ": unexpected error." << std::endl;
+        }
+      }
+
+      if(!test)
+        return false; //Failed. Close the document.
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+      if(is_example || is_backup)
+      {
+        //Create the example database:
+        //connection_request_password_and_choose_new_database_name() has already change the database name to a new unused one:
+
+        bool user_cancelled = false;
+        bool test = false;
+        if(is_example)
+          test = recreate_database_from_example(user_cancelled);
+        else
+          test = recreate_database_from_backup(original_uri, user_cancelled);
+
+        if(!test)
+        {
+          // TODO: Do we need to call connection_pool->cleanup() here, for
+          // stopping self-hosted databases? armin.
+          connection_pool->cleanup( sigc::mem_fun(*this, &AppWindow::on_connection_close_progress) );
+          //If the database was not successfully recreated:
+          return false;
+        }
+        else
+        {
+          //Make sure that the changes (mark as non example, and save the new database name) are really saved:
+          //Change the user level temporarily so that save_changes() actually saves:
+          const AppState::userlevels user_level = pDocument->get_userlevel();
+          pDocument->set_userlevel(AppState::USERLEVEL_DEVELOPER);
+          pDocument->set_modified(true);
+          pDocument->set_allow_autosave(true); //Turn this back on.
+          pDocument->set_userlevel(user_level); //Change it back.
+        }
+      }
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+      //Switch to operator mode when opening new documents:
+      pDocument->set_userlevel(AppState::USERLEVEL_OPERATOR);
+
+      //Make sure that it's saved in history, even if it was saved from an example file:
+      document_history_add(pDocument->get_file_uri());
+
+      //Open default table, or show list of tables instead:
+      m_pFrame->do_menu_Navigate_Table(true /* open the default if there is one */);
+    }
+  }
+
+  //List the non-hidden tables in the menu:
+  fill_menu_tables();
+
+  update_network_shared_ui();
+
+  //Run any startup script:
+  const Glib::ustring script = pDocument->get_startup_script();
+  if(!script.empty())
+  {
+    Glib::ustring error_message; //TODO: Check this and tell the user.
+    ConnectionPool* connection_pool = ConnectionPool::get_instance();
+    sharedptr<SharedConnection> sharedconnection = connection_pool->connect();
+    AppPythonUICallbacks callbacks;
+    glom_execute_python_function_implementation(script,
+      type_map_fields(), //only used when there is a current table and record.
+      pDocument,
+      Glib::ustring() /* table_name */,
+      sharedptr<Field>(), Gnome::Gda::Value(), // primary key - only used when there is a current table and record.
+      sharedconnection->get_gda_connection(),
+      callbacks,
+      error_message);
+
+    if(!error_message.empty())
+    {
+      std::cerr << "Python Error: " << error_message << std::endl;
+    }
+  }
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  pDocument->set_allow_autosave(true);
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  return true; //Loading of the document into the application succeeded.
+}
+
+void AppWindow::on_connection_close_progress()
+{
+  //TODO_murrayc
+}
+
+void AppWindow::on_connection_save_backup_progress()
+{
+  pulse_progress_message();
+}
+
+void AppWindow::on_connection_convert_backup_progress()
+{
+  pulse_progress_message();
+}
+
+void AppWindow::on_document_close()
+{
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  //TODO: It would be better to do this in a AppWindow::on_document_closed() virtual method,
+  //but that would need an ABI break in Bakery:
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  if(!connection_pool)
+    return;
+
+  connection_pool->cleanup( sigc::mem_fun(*this, &AppWindow::on_connection_close_progress) );
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+}
+
+/*
+void AppWindow::statusbar_set_text(const Glib::ustring& strText)
+{
+  m_pStatus->set_text(strText);
+}
+
+void AppWindow::statusbar_clear()
+{
+  statusbar_set_text("");
+}
+*/
+
+
+void AppWindow::update_network_shared_ui()
+{
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document)
+    return;
+
+  if(!m_connection_toggleaction_network_shared)
+    return;
+
+  //Show the status in the UI:
+  //(get_network_shared() already enforces constraints).
+  const bool shared = document->get_network_shared();
+  //TODO: Our use of block() does not seem to work. The signal actually seems to be emitted some time later instead.
+  m_connection_toggleaction_network_shared.block(); //Prevent signal handling.
+  m_toggleaction_network_shared->set_active(shared);
+
+  //Do not allow impossible changes:
+  const Document::HostingMode hosting_mode = document->get_hosting_mode();
+  if( (hosting_mode == Document::HOSTING_MODE_POSTGRES_CENTRAL) //Central hosting means that it must be shared on the network.
+    || (hosting_mode == Document::HOSTING_MODE_SQLITE) ) //sqlite does not allow network sharing.
+  {
+    m_toggleaction_network_shared->set_sensitive(false);
+  }
+
+  m_connection_toggleaction_network_shared.unblock();
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void AppWindow::on_userlevel_changed(AppState::userlevels /* userlevel */)
+{
+  update_userlevel_ui();
+}
+
+void AppWindow::update_table_sensitive_ui()
+{
+  AppState::userlevels userlevel = get_userlevel();
+
+  bool has_table = false;
+
+  if(m_pFrame)
+    has_table = !m_pFrame->get_shown_table_name().empty();
+
+  for(type_listActions::iterator iter = m_listTableSensitiveActions.begin(); iter != m_listTableSensitiveActions.end(); ++iter)
+  {
+    Glib::RefPtr<Gtk::Action> action = *iter;
+ 
+    bool sensitive = has_table;
+
+    const bool is_developer_item = 
+      (std::find(m_listDeveloperActions.begin(), m_listDeveloperActions.end(), action) != m_listDeveloperActions.end());
+    if(is_developer_item)
+      sensitive = sensitive && (userlevel == AppState::USERLEVEL_DEVELOPER);
+
+    action->set_sensitive(sensitive);
+  }
+}
+
+void AppWindow::update_userlevel_ui()
+{
+  AppState::userlevels userlevel = get_userlevel();
+
+  //Disable/Enable developer actions:
+  for(type_listActions::iterator iter = m_listDeveloperActions.begin(); iter != m_listDeveloperActions.end(); ++iter)
+  {
+    Glib::RefPtr<Gtk::Action> action = *iter;
+     action->set_sensitive( userlevel == AppState::USERLEVEL_DEVELOPER );
+  }
+
+  //Ensure table sensitive menus stay disabled if necessary.
+  update_table_sensitive_ui();
+
+  // Hide users entry from developer menu for connections that don't
+  // support users
+  if(userlevel == AppState::USERLEVEL_DEVELOPER)
+  {
+    if(ConnectionPool::get_instance_is_ready())
+    {
+      sharedptr<SharedConnection> connection = ConnectionPool::get_and_connect();
+      if(connection && !connection->get_gda_connection()->supports_feature(Gnome::Gda::CONNECTION_FEATURE_USERS))
+        m_action_developer_users->set_sensitive(false);
+    }
+  }
+
+  //Make sure that the correct radio menu item is activated (the userlevel might have been set programmatically):
+  //We only need to set/unset one, because the others are in the same radio group.
+  if(userlevel == AppState::USERLEVEL_DEVELOPER)
+  {
+    if(!m_action_menu_developer_developer->get_active())
+      m_action_menu_developer_developer->set_active();
+  }
+  else if(userlevel ==  AppState::USERLEVEL_OPERATOR)
+  {
+    if(!m_action_menu_developer_operator->get_active())
+      m_action_menu_developer_operator->set_active();
+    // Remove the drag layout toolbar
+  }
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+Glib::RefPtr<Gtk::UIManager> AppWindow::get_ui_manager()
+{
+  return m_refUIManager;
+}
+
+
+bool AppWindow::offer_new_or_existing()
+{
+  //Offer to load an existing document, or start a new one.
+  Dialog_ExistingOrNew* dialog_raw = 0;
+  Utils::get_glade_widget_derived_with_warning(dialog_raw);
+  std::auto_ptr<Dialog_ExistingOrNew> dialog(dialog_raw);
+  dialog->set_transient_for(*this);
+/*
+  dialog->signal_new().connect(sigc::mem_fun(*this, &AppWindow::on_existing_or_new_new));
+  dialog->signal_open_from_uri().connect(sigc::mem_fun(*this, &AppWindow::on_existing_or_new_open_from_uri));
+  dialog->signal_open_from_remote().connect(sigc::mem_fun(*this, &AppWindow::on_existing_or_new_open_from_remote));
+*/
+  bool ask_again = true;
+  while(ask_again)
+  {
+    const int response_id = Utils::dialog_run_with_help(dialog_raw);
+    dialog->hide();
+
+    if(response_id == Gtk::RESPONSE_ACCEPT)
+    {
+      switch(dialog->get_action())
+      {
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+      case Dialog_ExistingOrNew::NEW_EMPTY:
+        existing_or_new_new();
+        break;
+      case Dialog_ExistingOrNew::NEW_FROM_TEMPLATE:
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+      case Dialog_ExistingOrNew::OPEN_URI:
+        open_document(dialog->get_uri());
+        break;
+#ifndef G_OS_WIN32
+      case Dialog_ExistingOrNew::OPEN_REMOTE:
+        open_browsed_document(dialog->get_service_info(), dialog->get_service_name());
+        break;
+#endif
+      case Dialog_ExistingOrNew::NONE:
+      default:
+	std::cerr << G_STRFUNC << ": Unhandled action: " << dialog->get_action() << std::endl;
+        g_assert_not_reached();
+        break;
+      }
+
+      //Check that a document was opened:
+      Document* document = dynamic_cast<Document*>(get_document());
+      if(!document)
+      {
+        std::cerr << G_STRFUNC << ": document was NULL." << std::endl;
+        return false;
+      }
+
+      if(!document->get_file_uri().empty() || (document->get_opened_from_browse()))
+        ask_again = false;
+    }
+    else if((response_id == Gtk::RESPONSE_CLOSE)  || (response_id == Gtk::RESPONSE_DELETE_EVENT))
+    {
+      return false; //close the window to close the application, because they need to choose a new or existing document.
+    }
+    else if((response_id == Gtk::RESPONSE_NONE)
+     || (response_id == 0))
+    {
+       //For instance, the file-open dialog was cancelled after Dialog_ExistingOrNew opened it,
+       //so just ask again.
+       //TODO: Stop Dialog_ExistingOrNew from emitting a response in this case.
+    }
+    else
+    {
+      // This would mean that we got a unhandled response from the dialog
+      g_return_val_if_reached(false);
+    }
+  }
+
+  return true;
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void AppWindow::existing_or_new_new()
+{
+  // New empty document
+
+  //Each document must have a location, so ask the user for one.
+  //This will use an extended save dialog that also asks for the database title and some hosting details:
+  Glib::ustring db_title;
+
+  m_ui_save_extra_showextras = true; //Offer self-hosting or central hosting, and offer the database title.
+  m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT; /* Default to self-hosting */
+  m_ui_save_extra_newdb_title.clear();
+  offer_saveas();
+
+  //Check that the document was given a location:
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document->get_file_uri().empty())
+  {
+    //Get details from the extended save dialog:
+    const Glib::ustring db_title = m_ui_save_extra_newdb_title;
+    Document::HostingMode hosting_mode = m_ui_save_extra_newdb_hosting_mode;
+    m_ui_save_extra_newdb_title.clear();
+    m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT;
+
+    //Make sure that the user can do something with his new document:
+    document->set_userlevel(AppState::USERLEVEL_DEVELOPER);
+    // Try various ports if connecting to an existing database server instead
+    // of self-hosting one:
+    document->set_connection_try_other_ports(m_ui_save_extra_newdb_hosting_mode == Document::HOSTING_MODE_DEFAULT);
+
+    //Each new document must have an associated new database,
+    //so choose a name
+
+    //Create a database name based on the title.
+    //The user will (almost) never see this anyway but it's nicer than using a random number:
+    Glib::ustring db_name = Utils::create_name_from_title(db_title);
+
+    //Prefix glom_ to the database name, so it's more obvious
+    //for the system administrator.
+    //This database name should never be user-visible again, either prefixed or not prefixed.
+    db_name = "glom_" + db_name;
+
+    //Connect to the server and choose a variation of this db_name that does not exist yet:
+    document->set_connection_database(db_name);
+    document->set_hosting_mode(hosting_mode);
+
+   //Tell the connection pool about the document:
+   ConnectionPool* connection_pool = ConnectionPool::get_instance();
+   if(connection_pool)
+     connection_pool->set_get_document_func( sigc::mem_fun(*this, &AppWindow::on_connection_pool_get_document) );
+
+    const bool connected = m_pFrame->connection_request_password_and_choose_new_database_name();
+    if(!connected)
+    {
+      // Unset URI so that the offer_new_or_existing does not disappear
+      // so the user can make a different choice about what document to open.
+      // TODO: Show some error message?
+      document->set_file_uri("");
+    }
+    else
+    {
+      const bool db_created = m_pFrame->create_database(document->get_connection_database(), db_title);
+      if(db_created)
+      {
+        const Glib::ustring database_name_used = document->get_connection_database();
+        ConnectionPool::get_instance()->set_database(database_name_used);
+        document->set_database_title_original(db_title);
+        m_pFrame->set_databases_selected(database_name_used);
+
+        // Add the document to recent files
+	    document_history_add(document->get_file_uri());
+      }
+      else
+      {
+        // Unset URI so that the offer_new_or_existing does not disappear
+        // so the user can make a different choice about what document to open.
+        // TODO: Show some error message?
+        document->set_file_uri("");
+      }
+    }
+  }
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+void AppWindow::set_mode_data()
+{
+  if (!m_pFrame)
+    return;
+
+  if (m_pFrame->m_Mode == Frame_Glom::MODE_Find)
+    m_action_mode_find->activate();
+}
+
+void AppWindow::set_mode_find()
+{
+  if (!m_pFrame)
+    return;
+
+  if (m_pFrame->m_Mode == Frame_Glom::MODE_Data)
+    m_action_mode_find->activate();
+}
+
+void AppWindow::on_menu_help_contents()
+{
+  Glom::Utils::show_help();
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+
+void AppWindow::on_recreate_database_progress()
+{
+  //Show the user that something is happening, because the INSERTS might take time.
+  pulse_progress_message();
+
+  //Ensure that the infobar is shown, instead of waiting for the application to be idle.
+  while(Gtk::Main::instance()->events_pending())
+    Gtk::Main::instance()->iteration();
+}
+
+bool AppWindow::recreate_database_from_example(bool& user_cancelled)
+{
+  ShowProgressMessage progress_message(_("Creating Glom database from example file."));
+
+  //Create a database, based on the information in the current document:
+  Document* pDocument = static_cast<Document*>(get_document());
+  if(!pDocument)
+    return false;
+
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  if(!connection_pool)
+    return false; //Impossible anyway.
+
+  //Check whether the database exists already.
+  const Glib::ustring db_name = pDocument->get_connection_database();
+  if(db_name.empty())
+    return false;
+
+  connection_pool->set_database(db_name);
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+#else
+  std::auto_ptr<std::exception> error;
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+  {
+    connection_pool->set_ready_to_connect(); //This has succeeded already.
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    sharedptr<SharedConnection> sharedconnection = connection_pool->connect();
+#else
+    sharedptr<SharedConnection> sharedconnection = connection_pool->connect(error);
+    if(!error.get())
+    {
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+      g_warning("AppWindow::recreate_database_from_example(): Failed because database exists already.");
+
+      return false; //Connection to the database succeeded, because no exception was thrown. so the database exists already.
+#ifndef GLIBMM_EXCEPTIONS_ENABLED
+    }
+#endif // !GLIBMM_EXCEPTIONS_ENABLED
+  }
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  catch(const ExceptionConnection& ex)
+  {
+#else
+  if(error.get())
+  {
+    const ExceptionConnection* exptr = dynamic_cast<ExceptionConnection*>(error.get());
+    if(exptr)
+    {
+      const ExceptionConnection& ex = *exptr;
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+      if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
+      {
+        user_cancelled = true; //Eventually, the user will cancel after retrying.
+        g_warning("AppWindow::recreate_database_from_example(): Failed because connection to server failed, without specifying a database.");
+        return false;
+      }
+#ifndef GLIBMM_EXCEPTIONS_ENABLED
+    }
+#endif // !GLIBMM_EXCEPTIONS_ENABLED
+
+    //Otherwise continue, because we _expected_ connect() to fail if the db does not exist yet.
+  }
+
+  //Show the user that something is happening, because the INSERTS might take time.
+  pulse_progress_message();
+
+  //Ensure that the infobar is shown, instead of waiting for the application to be idle.
+  while(Gtk::Main::instance()->events_pending())
+    Gtk::Main::instance()->iteration();
+
+  //Create the database: (This will show a connection dialog)
+  connection_pool->set_database( Glib::ustring() );
+  const bool db_created = m_pFrame->create_database(db_name, pDocument->get_database_title_original());
+
+  if(!db_created)
+  {
+    return false;
+  }
+  else
+    connection_pool->set_database(db_name); //Specify the new database when connecting from now on.
+
+  pulse_progress_message();
+  BusyCursor busy_cursor(this);
+
+  sharedptr<SharedConnection> sharedconnection;
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+  {
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    sharedconnection = connection_pool->connect();
+#else
+    sharedconnection = connection_pool->connect(error);
+    if(!error.get())
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+      connection_pool->set_database(db_name); //The database was successfully created, so specify it when connecting from now on.
+  }
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  catch(const ExceptionConnection& ex)
+  {
+#else
+  if(error.get())
+  {
+    const std::exception& ex = *error.get();
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+    g_warning("AppWindow::recreate_database_from_example(): Failed to connect to the newly-created database.");
+    return false;
+  }
+
+  //Create the developer group, and make this user a member of it:
+  pulse_progress_message();
+  bool test = DbUtils::add_standard_groups(pDocument);
+  if(!test)
+    return false;
+
+  //Add any extra groups from the example file:
+  pulse_progress_message();
+  test = DbUtils::add_groups_from_document(pDocument);
+  if(!test)
+    return false;
+
+  //Create each table:
+  Document::type_listTableInfo tables = pDocument->get_tables();
+  for(Document::type_listTableInfo::const_iterator iter = tables.begin(); iter != tables.end(); ++iter)
+  {
+    sharedptr<const TableInfo> table_info = *iter;
+
+    //Create SQL to describe all fields in this table:
+    Glib::ustring sql_fields;
+    Document::type_vec_fields fields = pDocument->get_table_fields(table_info->get_name());
+
+    pulse_progress_message();
+    const bool table_creation_succeeded = DbUtils::create_table(table_info, fields);
+    pulse_progress_message();
+    if(!table_creation_succeeded)
+    {
+      g_warning("AppWindow::recreate_database_from_example(): CREATE TABLE failed with the newly-created database.");
+      return false;
+    }
+  }
+
+  pulse_progress_message();
+  DbUtils::add_standard_tables(pDocument); //Add internal, hidden, tables.
+
+  //Set table priviliges, using the groups we just added:
+  pulse_progress_message();
+  test = DbUtils::set_table_privileges_groups_from_document(pDocument);
+  if(!test)
+    return false;
+
+  for(Document::type_listTableInfo::const_iterator iter = tables.begin(); iter != tables.end(); ++iter)
+  {
+    sharedptr<const TableInfo> table_info = *iter;
+
+    //Add any example data to the table:
+    pulse_progress_message();
+
+    //try
+    //{
+      const bool table_insert_succeeded = DbUtils::insert_example_data(pDocument, table_info->get_name());
+
+      if(!table_insert_succeeded)
+      {
+        g_warning("AppWindow::recreate_database_from_example(): INSERT of example data failed with the newly-created database.");
+        return false;
+      }
+    //}
+    //catch(const std::exception& ex)
+    //{
+    //  std::cerr << "AppWindow::recreate_database_from_example(): exception: " << ex.what() << std::endl;
+      //HandleError(ex);
+    //}
+
+  } //for(tables)
+
+  return true; //All tables created successfully.
+}
+
+bool AppWindow::recreate_database_from_backup(const Glib::ustring& backup_uri, bool& user_cancelled)
+{
+  ShowProgressMessage progress_message(_("Creating Glom database from backup file."));
+
+  //Create a database, based on the information in the current document:
+  Document* pDocument = static_cast<Document*>(get_document());
+  if(!pDocument)
+    return false;
+
+  ConnectionPool* connection_pool = ConnectionPool::get_instance();
+  if(!connection_pool)
+    return false; //Impossible anyway.
+
+  //Check whether the database exists already.
+  const Glib::ustring db_name = pDocument->get_connection_database();
+  if(db_name.empty())
+    return false;
+
+  connection_pool->set_database(db_name);
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  try
+#else
+  std::auto_ptr<std::exception> error;
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+  {
+    connection_pool->set_ready_to_connect(); //This has succeeded already.
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+    sharedptr<SharedConnection> sharedconnection = connection_pool->connect();
+#else
+    sharedptr<SharedConnection> sharedconnection = connection_pool->connect(error);
+    if(!error.get())
+    {
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+      g_warning("AppWindow::recreate_database_from_example(): Failed because database exists already.");
+
+      return false; //Connection to the database succeeded, because no exception was thrown. so the database exists already.
+#ifndef GLIBMM_EXCEPTIONS_ENABLED
+    }
+#endif // !GLIBMM_EXCEPTIONS_ENABLED
+  }
+#ifdef GLIBMM_EXCEPTIONS_ENABLED
+  catch(const ExceptionConnection& ex)
+  {
+#else
+  if(error.get())
+  {
+    const ExceptionConnection* exptr = dynamic_cast<ExceptionConnection*>(error.get());
+    if(exptr)
+    {
+      const ExceptionConnection& ex = *exptr;
+#endif // GLIBMM_EXCEPTIONS_ENABLED
+      if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
+      {
+        user_cancelled = true; //Eventually, the user will cancel after retrying.
+        g_warning("AppWindow::recreate_database_from_example(): Failed because connection to server failed, without specifying a database.");
+        return false;
+      }
+#ifndef GLIBMM_EXCEPTIONS_ENABLED
+    }
+#endif // !GLIBMM_EXCEPTIONS_ENABLED
+
+    //Otherwise continue, because we _expected_ connect() to fail if the db does not exist yet.
+  }
+
+  //Show the user that something is happening, because the INSERTS might take time.
+  pulse_progress_message();
+
+  //Ensure that the infobar is shown, instead of waiting for the application to be idle.
+  while(Gtk::Main::instance()->events_pending())
+    Gtk::Main::instance()->iteration();
+
+  pulse_progress_message();
+
+  //Create the database: (This will show a connection dialog)
+  connection_pool->set_database( Glib::ustring() );
+  try
+  {
+    ConnectionPool::get_instance()->create_database(db_name);
+  }
+  catch(const Glib::Exception& ex) // libgda does not set error domain
+  {
+    std::cerr << G_STRFUNC << ": Gnome::Gda::Connection::create_database(" << db_name << ") failed: " << ex.what() << std::endl;
+
+    //Tell the user:
+    Gtk::Dialog* dialog = 0;
+    Utils::get_glade_widget_with_warning("glom_developer.glade", "dialog_error_create_database", dialog);
+    dialog->set_transient_for(*this);
+    Glom::Utils::dialog_run_with_help(dialog, "dialog_error_create_database");
+    delete dialog;
+
+    return false;
+  }
+
+  connection_pool->set_database(db_name); //Specify the new database when connecting from now on.
+
+  //Create the developer group, and make this user a member of it:
+  pulse_progress_message();
+  bool test = DbUtils::add_standard_groups(pDocument);
+  if(!test)
+    return false;
+
+  //Add any extra groups from the example file:
+  pulse_progress_message();
+  test = DbUtils::add_groups_from_document(pDocument);
+  if(!test)
+    return false;
+
+  //m_pFrame->add_standard_tables(); //Add internal, hidden, tables.
+
+  //Restore the backup into the database:
+  std::string original_dir_path;
+
+  Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(backup_uri);
+  if(gio_file)
+  {
+    Glib::RefPtr<Gio::File> parent = gio_file->get_parent();
+    if(parent)
+    {
+      try
+      {
+        original_dir_path = Glib::filename_from_uri(parent->get_uri());
+      }
+      catch(const Glib::Error& ex)
+      {
+        std::cerr << G_STRFUNC << ": Glib::filename_from_uri() failed: " << ex.what() << std::endl;
+      }
+    }
+  }
+
+  if(original_dir_path.empty())
+  {
+    std::cerr << G_STRFUNC << ": original_dir_path is empty." << std::endl;
+    return false;
+  }
+
+  //Restore the database from the backup:
+  //std::cout << "DEBUG: original_dir_path=" << original_dir_path << std::endl;
+  const bool restored = connection_pool->convert_backup(
+    sigc::mem_fun(*this, &AppWindow::on_connection_convert_backup_progress), original_dir_path);
+
+  if(!restored)
+  {
+    std::cerr << G_STRFUNC << ": Restore failed." << std::endl;
+    return false;
+  }
+
+  return true; //Restore successfully.
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+AppState::userlevels AppWindow::get_userlevel() const
+{
+  const Document* document = dynamic_cast<const Document*>(get_document());
+  if(document)
+  {
+    return document->get_userlevel();
+  }
+  else
+    g_assert_not_reached();
+    //return AppState::USERLEVEL_DEVELOPER; //This should never happen.
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void AppWindow::add_developer_action(const Glib::RefPtr<Gtk::Action>& refAction)
+{
+  //Prevent it from being added twice:
+  remove_developer_action(refAction);
+
+  m_listDeveloperActions.push_back(refAction);
+}
+
+void AppWindow::remove_developer_action(const Glib::RefPtr<Gtk::Action>& refAction)
+{
+  for(type_listActions::iterator iter = m_listDeveloperActions.begin(); iter != m_listDeveloperActions.end(); ++iter)
+  {
+    if(*iter == refAction)
+    {
+      m_listDeveloperActions.erase(iter);
+      break;
+    }
+  }
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+void AppWindow::fill_menu_tables()
+{
+  //TODO: There must be a better way than building a ui_string like this:
+
+  m_listNavTableActions.clear();
+  if(m_menu_tables_ui_merge_id)
+    m_refUIManager->remove_ui(m_menu_tables_ui_merge_id);
+
+  if(m_refNavTablesActionGroup)
+  {
+    m_refUIManager->remove_action_group(m_refNavTablesActionGroup);
+    m_refNavTablesActionGroup.reset();
+  }
+
+  m_refNavTablesActionGroup = Gtk::ActionGroup::create("NavTablesActions");
+
+  Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_Others'>"
+    "      <menu action='Glom_Menu_Tables'>"
+    "        <placeholder name='Menu_Tables_Dynamic'>";
+
+  Document* document = dynamic_cast<Document*>(get_document());
+  const Document::type_listTableInfo tables = document->get_tables();
+  for(Document::type_listTableInfo::const_iterator iter = tables.begin(); iter != tables.end(); ++iter)
+  {
+    sharedptr<const TableInfo> table_info = *iter;
+    if(!table_info->get_hidden())
+    {
+      const Glib::ustring action_name = "NavTableAction_" + table_info->get_name();
+
+      ui_description += "<menuitem action='" + action_name + "' />";
+
+      Glib::RefPtr<Gtk::Action> refAction = Gtk::Action::create(action_name, Utils::string_escape_underscores(item_get_title_or_name(table_info)));
+      m_refNavTablesActionGroup->add(refAction,
+        sigc::bind( sigc::mem_fun(*m_pFrame, &Frame_Glom::on_box_tables_selected), table_info->get_name()) );
+
+      m_listNavTableActions.push_back(refAction);
+
+      //m_refUIManager->add_ui(merge_id, path, table_info->m_title, refAction, UI_MANAGER_MENUITEM);
+    }
+  }
+
+  m_refUIManager->insert_action_group(m_refNavTablesActionGroup);
+
+
+  ui_description +=
+    "     </placeholder>"
+    "    </menu>"
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+
+  //Add menus:
+  try
+  {
+    m_menu_tables_ui_merge_id = m_refUIManager->add_ui_from_string(ui_description);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": building menus failed: " <<  ex.what() << std::endl;
+    std::cerr << "   The ui_description was: " <<  ui_description << std::endl;
+  }
+}
+
+void AppWindow::fill_menu_reports(const Glib::ustring& table_name)
+{
+  //TODO: There must be a better way than building a ui_string like this:
+
+  m_listNavReportActions.clear();
+  if(m_menu_reports_ui_merge_id)
+    m_refUIManager->remove_ui(m_menu_reports_ui_merge_id);
+
+  if(m_refNavReportsActionGroup)
+  {
+    m_refUIManager->remove_action_group(m_refNavReportsActionGroup);
+    m_refNavReportsActionGroup.reset();
+  }
+
+  m_refNavReportsActionGroup = Gtk::ActionGroup::create("NavReportsActions");
+
+  Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_Others'>"
+    "     <menu action='Glom_Menu_Reports'>"
+    "        <placeholder name='Menu_Reports_Dynamic'>";
+
+  Document* document = dynamic_cast<Document*>(get_document());
+  const std::vector<Glib::ustring> reports = document->get_report_names(table_name);
+  for(std::vector<Glib::ustring>::const_iterator iter = reports.begin(); iter != reports.end(); ++iter)
+  {
+    sharedptr<Report> report = document->get_report(table_name, *iter);
+    if(report)
+    {
+      const Glib::ustring report_name = report->get_name();
+      if(!report_name.empty())
+      {
+        const Glib::ustring action_name = "NavReportAction_" + report_name;
+
+        ui_description += "<menuitem action='" + action_name + "' />";
+
+        Glib::RefPtr<Gtk::Action> refAction = Gtk::Action::create( action_name, Utils::string_escape_underscores(item_get_title_or_name(report)) );
+        m_refNavReportsActionGroup->add(refAction,
+          sigc::bind( sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_report_selected), report->get_name()) );
+
+        m_listNavReportActions.push_back(refAction);
+
+        //m_refUIManager->add_ui(merge_id, path, table_info->m_title, refAction, UI_MANAGER_MENUITEM);
+      }
+    }
+  }
+
+  m_refUIManager->insert_action_group(m_refNavReportsActionGroup);
+
+
+  ui_description +=
+    "     </placeholder>"
+    "    </menu>"
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+
+  //Add menus:
+  try
+  {
+    m_menu_reports_ui_merge_id = m_refUIManager->add_ui_from_string(ui_description);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": building menus failed: " <<  ex.what();
+  }
+}
+
+void AppWindow::fill_menu_print_layouts(const Glib::ustring& table_name)
+{
+  //TODO: This is copy/pasted from fill_menu_print_reports. Can we generalize it?
+
+  //TODO: There must be a better way than building a ui_string like this:
+
+  m_listNavPrintLayoutActions.clear();
+  if(m_menu_print_layouts_ui_merge_id)
+    m_refUIManager->remove_ui(m_menu_print_layouts_ui_merge_id);
+
+  //Only fill menu if we are in details mode,
+  //because this feature is not (yet) available for lists:
+  if(!m_pFrame || !m_pFrame->get_viewing_details())
+    return;
+
+  if(m_refNavPrintLayoutsActionGroup)
+  {
+    m_refUIManager->remove_action_group(m_refNavPrintLayoutsActionGroup);
+    m_refNavPrintLayoutsActionGroup.reset();
+  }
+
+  m_refNavPrintLayoutsActionGroup = Gtk::ActionGroup::create("NavPrintLayoutsActions");
+
+  Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_File'>"
+    "      <menu action='BakeryAction_Menu_File'>"
+    "        <menu action='GlomAction_Menu_File_Print'>"
+    "          <placeholder name='Menu_PrintLayouts_Dynamic'>";
+
+  Document* document = dynamic_cast<Document*>(get_document());
+  const std::vector<Glib::ustring> tables = document->get_print_layout_names(table_name);
+
+  // TODO_clientonly: Should this be available in client only mode? We need to
+  // depend on goocanvas in client only mode then:
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  for(std::vector<Glib::ustring>::const_iterator iter = tables.begin(); iter != tables.end(); ++iter)
+  {
+    sharedptr<PrintLayout> print_layout = document->get_print_layout(table_name, *iter);
+    if(print_layout)
+    {
+      const Glib::ustring name = print_layout->get_name();
+      if(!name.empty())
+      {
+        const Glib::ustring action_name = "NavPrintLayoutAction_" + name;
+
+        ui_description += "<menuitem action='" + action_name + "' />";
+
+        Glib::RefPtr<Gtk::Action> refAction = Gtk::Action::create( action_name, Utils::string_escape_underscores(item_get_title(print_layout)) );
+        m_refNavPrintLayoutsActionGroup->add(refAction,
+          sigc::bind( sigc::mem_fun(*m_pFrame, &Frame_Glom::on_menu_print_layout_selected), print_layout->get_name()) );
+
+        m_listNavPrintLayoutActions.push_back(refAction);
+
+        //m_refUIManager->add_ui(merge_id, path, table_info->m_title, refAction, UI_MANAGER_MENUITEM);
+      }
+    }
+  }
+#endif
+
+  m_refUIManager->insert_action_group(m_refNavPrintLayoutsActionGroup);
+
+
+  ui_description +=
+    "       </placeholder>"
+    "      </menu>"
+    "    </menu>"
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+
+  //Add menus:
+  try
+  {
+    m_menu_print_layouts_ui_merge_id = m_refUIManager->add_ui_from_string(ui_description);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << G_STRFUNC << ": building menus failed: " <<  ex.what();
+  }
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void AppWindow::on_menu_file_save_as_example()
+{
+  //Based on the implementation of GlomBakery::AppWindow_WithDoc::on_menu_file_saveas()
+
+  //Display File Save dialog and respond to choice:
+
+  //Bring document window to front, to make it clear which document is being saved:
+  //This doesn't work: TODO.
+  ui_bring_to_front();
+
+  //Show the save dialog:
+  Document* document = dynamic_cast<Document*>(get_document());
+  const Glib::ustring& file_uriOld = document->get_file_uri();
+
+  m_ui_save_extra_showextras = false;
+  m_ui_save_extra_title.clear();
+  m_ui_save_extra_message.clear();
+  m_ui_save_extra_newdb_title.clear();
+  m_ui_save_extra_newdb_hosting_mode = Document::HOSTING_MODE_DEFAULT;
+
+  Glib::ustring file_uri = ui_file_select_save(file_uriOld); //Also asks for overwrite confirmation.
+  if(!file_uri.empty())
+  {
+    //Enforce the file extension:
+    file_uri = document->get_file_uri_with_extension(file_uri);
+
+    bool bUseThisFileUri = true;
+    //We previously checked whether the file existed,
+    //but The FileChooser checks that already,
+    //so Bakery doesn't bother checking anymore,
+    //and our old test always set bUseThisFileUri to true anyway. murryac.
+    //TODO: So remove this bool. murrayc.
+
+    //Save to this filepath:
+    if(bUseThisFileUri)
+    {
+      //Prevent saving while we modify the document:
+      document->set_allow_autosave(false);
+
+      document->set_file_uri(file_uri, true); //true = enforce file extension
+      document->set_is_example_file();
+
+      //Save all data from all tables into the document:
+      Document::type_listTableInfo list_table_info = document->get_tables();
+      for(Document::type_listTableInfo::const_iterator iter = list_table_info.begin(); iter != list_table_info.end(); ++iter)
+      {
+        const Glib::ustring table_name = (*iter)->get_name();
+
+        //const type_vec_fields vec_fields = document->get_table_fields(table_name);
+
+        //export_data_to_*() needs a type_list_layout_groups;
+        Document::type_list_layout_groups sequence = document->get_data_layout_groups_default("list", table_name, "" /* layout_platform */);
+
+        //std::cout << "debug: table_name=" << table_name << std::endl;
+
+        Document::type_example_rows example_rows;
+        FoundSet found_set;
+        found_set.m_table_name = table_name;
+        m_pFrame->export_data_to_vector(example_rows, found_set, sequence);
+        //std::cout << "  debug after row_text=" << row_text << std::endl;
+
+        document->set_table_example_data(table_name, example_rows);
+      }
+
+      const bool bTest = document->save();
+      document->set_is_example_file(false);
+      document->set_is_backup_file(false);
+      document->set_file_uri(file_uriOld);
+      document->set_allow_autosave(true);
+
+      if(!bTest)
+      {
+        ui_warning(_("Save failed."), _("There was an error while saving the example file."));
+      }
+      else
+      {
+        //Disable Save and SaveAs menu items:
+        after_successful_save();
+      }
+
+      update_window_title();
+
+
+      //Close if this save was a result of a File|Close or File|Exit:.
+      //if(bTest && m_bCloseAfterSave) //Don't close if the save failed.
+      //{
+      //  on_menu_file_close(); //This could be the second time, but now there are no unsaved changes.
+      //}
+    }
+    else
+    {
+      //Let the user choose a different file path,
+      //because he decided not to overwrite the 1st one.
+      on_menu_file_save_as_example(); //recursive.
+    }
+  }
+  else
+  {
+    cancel_close_or_exit();
+  }
+}
+
+Glib::ustring AppWindow::ui_file_select_save(const Glib::ustring& old_file_uri) //override
+{
+  //Reimplement this whole function, just so we can use our custom FileChooserDialog class:
+  AppWindow& app = *this;
+
+  std::auto_ptr<Gtk::FileChooserDialog> fileChooser_Save;
+  Glom::FileChooserDialog_SaveExtras* fileChooser_SaveExtras = 0;
+
+  //Create the appropriate dialog, depending on how the caller set m_ui_save_extra_showextras:
+  if(m_ui_save_extra_showextras)
+  {
+    fileChooser_SaveExtras = new Glom::FileChooserDialog_SaveExtras(_("Save Document"), Gtk::FILE_CHOOSER_ACTION_SAVE);
+    fileChooser_Save.reset(fileChooser_SaveExtras);
+  }
+  else
+  {
+    fileChooser_Save.reset(new Gtk::FileChooserDialog(gettext("Save Document"), Gtk::FILE_CHOOSER_ACTION_SAVE));
+  }
+
+  fileChooser_Save->set_do_overwrite_confirmation(); //Ask the user if the file already exists.
+
+  Gtk::Window* pWindow = dynamic_cast<Gtk::Window*>(&app);
+  if(pWindow)
+    fileChooser_Save->set_transient_for(*pWindow);
+
+  fileChooser_Save->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  fileChooser_Save->add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+  fileChooser_Save->set_default_response(Gtk::RESPONSE_OK);
+
+  //This is the reason that we override this method:
+  if(!m_ui_save_extra_title.empty())
+    fileChooser_Save->set_title(m_ui_save_extra_title);
+
+  if(fileChooser_SaveExtras)
+  {
+    fileChooser_SaveExtras->set_extra_message(m_ui_save_extra_message);
+
+
+    //Start with something suitable:
+    Document* document = dynamic_cast<Document*>(get_document());
+    g_assert(document);
+    const Glib::ustring filename = document->get_name(); //Get the filename without the path and extension.
+
+    //Avoid ".". TODO: Find out why it happens:
+    if(filename == ".")
+      m_ui_save_extra_newdb_title = Glib::ustring();
+    else
+      m_ui_save_extra_newdb_title = Utils::title_from_string( filename ); //Start with something suitable.
+
+    fileChooser_SaveExtras->set_extra_newdb_title(m_ui_save_extra_newdb_title);
+    fileChooser_SaveExtras->set_extra_newdb_hosting_mode(m_ui_save_extra_newdb_hosting_mode);
+  }
+
+
+  //Make the save dialog show the existing filename, if any:
+  if(!old_file_uri.empty())
+  {
+    //Just start with the parent folder,
+    //instead of the whole name, to avoid overwriting:
+    Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(old_file_uri);
+    if(gio_file)
+    {
+      Glib::RefPtr<Gio::File> parent = gio_file->get_parent();
+      if(parent)
+      {
+        const Glib::ustring uri_parent = parent->get_uri();
+        fileChooser_Save->set_uri(uri_parent);
+      }
+    }
+  }
+
+
+  //bool tried_once_already = false;
+
+  bool try_again = true;
+  while(try_again)
+  {
+    try_again = false;
+
+    const int response_id = fileChooser_Save->run();
+    fileChooser_Save->hide();
+    if((response_id != Gtk::RESPONSE_CANCEL) && (response_id != Gtk::RESPONSE_DELETE_EVENT))
+    {
+      const Glib::ustring uri_chosen = fileChooser_Save->get_uri();
+
+      //Change the URI, to put the file (and its data folder) in a folder:
+      const Glib::ustring uri = Utils::get_file_uri_without_extension(uri_chosen);
+
+      //Check whether the file exists, and that we have rights to it:
+      Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
+      if(!file)
+        return Glib::ustring(); //Failure.
+
+
+      //If the file exists (the FileChooser offers a "replace?" dialog, so this is not possible.):
+      if(AppWindow_WithDoc::file_exists(uri))
+      {
+        //Check whether we have rights to the file to change it:
+        //Really, GtkFileChooser should do this for us.
+        if(!uri_is_writable(file))
+        {
+           //Warn the user:
+           ui_warning(gettext("Read-only File."), _("You may not overwrite the existing file, because you do not have sufficient access rights."));
+           try_again = true; //Try again.
+           continue;
+        }
+      }
+
+      //Check whether we have rights to the directory, to create a new file in it:
+      //Really, GtkFileChooser should do this for us.
+      Glib::RefPtr<const Gio::File> parent = file->get_parent();
+      if(parent)
+      {
+        if(!uri_is_writable(parent))
+        {
+          //Warn the user:
+           ui_warning(gettext("Read-only Directory."), _("You may not create a file in this directory, because you do not have sufficient access rights."));
+           try_again = true; //Try again.
+           continue;
+        }
+      }
+
+      if(!try_again && fileChooser_SaveExtras)
+      {
+        //Get the extra details from the extended save dialog:
+        m_ui_save_extra_newdb_title = fileChooser_SaveExtras->get_extra_newdb_title();
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+        m_ui_save_extra_newdb_hosting_mode = fileChooser_SaveExtras->get_extra_newdb_hosting_mode();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+        if(m_ui_save_extra_newdb_title.empty())
+        {
+          Frame_Glom::show_ok_dialog(_("Database Title missing"), _("You must specify a title for the new database."), *this, Gtk::MESSAGE_ERROR);
+
+          try_again = true;
+          continue;
+        }
+      }
+
+      bool is_self_hosted = false;
+
+#ifdef GLOM_ENABLE_POSTGRESQL
+      if(m_ui_save_extra_newdb_hosting_mode == Document::HOSTING_MODE_POSTGRES_SELF)
+        is_self_hosted = true;
+#endif //GLOM_ENABLE_POSTGRESQL
+
+#ifdef GLOM_ENABLE_SQLITE
+      if(m_ui_save_extra_newdb_hosting_mode == Document::HOSTING_MODE_SQLITE)
+        is_self_hosted = true;
+#endif // GLOM_ENABLE_SQLITE
+
+      // Create a directory for self-hosted databases (sqlite or self-hosted
+      // postgresql).
+      if(!try_again && fileChooser_SaveExtras && is_self_hosted)
+      {
+        //Check that the directory does not exist already.
+        //The GtkFileChooser could not check for that because it could not know that we would create a directory based on the filename:
+        //Note that uri has no extension at this point:
+        Glib::RefPtr<Gio::File> dir = Gio::File::create_for_uri(uri);
+        if(dir->query_exists())
+        {
+          ui_warning(_("Directory Already Exists"), _("There is an existing directory with the same name as the directory that should be created for the new database files. You should specify a different filename to use a new directory instead."));
+          try_again = true; //Try again.
+          continue;
+        }
+
+        //Create the directory, so that file creation can succeed later:
+        //0770 means "this user and his group can read and write this "executable" (can add child files) directory".
+        //The 0 prefix means that this is octal.
+        try
+        {
+          //TODO: ensure that we use 0770? murrayc.
+          dir->make_directory();
+        }
+        catch(const Gio::Error& ex)
+        {
+          std::cerr << G_STRFUNC << ": " << ex.what() << std::endl;
+        }
+
+        //Add the filename (Note that the caller will add the extension if necessary, so we don't do it here.)
+        Glib::RefPtr<Gio::File> file_with_ext = Gio::File::create_for_uri(uri_chosen);
+        const Glib::ustring filename_part = file_with_ext->get_basename();
+
+        //Add the filename part to the newly-created directory:
+        Glib::RefPtr<Gio::File> file_whole = dir->get_child(filename_part);
+        return file_whole->get_uri();
+      }
+
+      if(!try_again)
+      {
+        return uri_chosen;
+      }
+    }
+    else
+      return Glib::ustring(); //The user cancelled.
+  }
+
+  return Glib::ustring();
+}
+
+void AppWindow::stop_self_hosting_of_document_database()
+{
+  Document* pDocument = static_cast<Document*>(get_document());
+  if(pDocument)
+  {
+    ConnectionPool* connection_pool = ConnectionPool::get_instance();
+    if(!connection_pool)
+      return;
+
+    connection_pool->cleanup( sigc::mem_fun(*this, &AppWindow::on_connection_close_progress ));
+  }
+}
+
+void AppWindow::on_menu_developer_changelanguage()
+{
+  Dialog_ChangeLanguage* dialog = 0;
+  Utils::get_glade_widget_derived_with_warning(dialog);
+  if(!dialog) //Unlikely and it already warns on stderr.
+    return;
+    
+  dialog->set_transient_for(*this);
+  const int response = Glom::Utils::dialog_run_with_help(dialog);
+  dialog->hide();
+
+  if(response == Gtk::RESPONSE_OK)
+  {
+    AppWindow::set_current_locale(dialog->get_locale());
+
+    //Get the translations from the document (in Operator mode, we only load the necessary translations.)
+    //This also updates the UI, so we show all the translated titles:
+    int failure_code = 0;
+    get_document()->load(failure_code);
+
+    m_pFrame->show_table_refresh(); //load() doesn't seem to refresh the view.
+  }
+
+  delete dialog;
+}
+
+void AppWindow::on_menu_developer_translations()
+{
+  if(!m_window_translations)
+  {
+    Utils::get_glade_widget_derived_with_warning(m_window_translations);
+    if(m_window_translations)
+    {
+      m_pFrame->add_view(m_window_translations);
+      m_window_translations->set_transient_for(*this);
+      m_window_translations->set_document(static_cast<Document*>(m_pDocument));
+      m_window_translations->load_from_document();
+      m_window_translations->show();
+
+      m_window_translations->signal_hide().connect(sigc::mem_fun(*this, &AppWindow::on_window_translations_hide));
+    }
+  }
+  else
+  {
+    m_window_translations->show();
+    m_window_translations->load_from_document();
+  }
+}
+
+void AppWindow::on_menu_developer_active_platform_normal()
+{
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(document)
+   document->set_active_layout_platform("");
+
+  m_pFrame->show_table_refresh();
+}
+
+void AppWindow::on_menu_developer_active_platform_maemo()
+{
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(document)
+   document->set_active_layout_platform("maemo");
+
+  m_pFrame->show_table_refresh();
+}
+
+void AppWindow::on_menu_developer_export_backup()
+{
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document)
+    return;
+
+  // Ask the user to choose a new directory name.
+  // Start with a name based on the existing name.
+  const Glib::ustring fileuri_old = document->get_file_uri();
+  const Glib::RefPtr<const Gio::File> file_old =
+    Gio::File::create_for_uri( Utils::get_file_uri_without_extension(fileuri_old) );
+  const std::string old_basename = file_old->get_basename();
+  Glib::TimeVal timeval;
+  timeval.assign_current_time();
+  std::string starting_name = old_basename + "-backup-" + timeval.as_iso8601();
+  //Replace : because that confuses (makes it fail) tar (and file-roller) when opening the file,
+  //and --force-local is not relevant to opening files.
+  starting_name = Utils::string_replace(starting_name, ":", "-");
+
+  // This actually creates the directory:
+  Gtk::FileChooserDialog dialog(*this, _("Save Backup"), Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
+  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
+  dialog.set_local_only(); //Because pg_dump, pg_restore and tar can't use URIs.
+  dialog.set_current_name(starting_name);
+  const int result = dialog.run();
+  dialog.hide();
+  if(result != Gtk::RESPONSE_ACCEPT)
+    return;
+
+  //Get the path to the directory in which the .glom and data files will be created.
+  //The .tar.gz will then be created next to it:
+  const std::string& path_dir = dialog.get_filename();
+  if(path_dir.empty())
+    return;
+
+  ShowProgressMessage progress_message(_("Exporting backup"));
+  const Glib::ustring tarball_uri = document->save_backup_file(
+    Glib::filename_to_uri(path_dir),
+    sigc::mem_fun(*this, &AppWindow::on_connection_save_backup_progress));
+
+  if(tarball_uri.empty())
+    ui_warning(_("Export Backup failed."), _("There was an error while exporting the backup."));
+  //TODO: Offer to show the tarball file in the file manager?
+}
+
+void AppWindow::on_menu_developer_restore_backup()
+{
+  Gtk::FileChooserDialog file_dlg(_("Choose a backup file"), Gtk::FILE_CHOOSER_ACTION_OPEN);
+  file_dlg.set_transient_for(*this);
+  file_dlg.set_local_only(); //Because we can't untar remote files.
+
+  Glib::RefPtr<Gtk::FileFilter> filter = Gtk::FileFilter::create();
+  filter->set_name(_(".tar.gz Backup files"));
+  filter->add_pattern("*.tar.gz");
+  filter->add_pattern("*.tgz");
+  file_dlg.add_filter(filter);
+
+  file_dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  file_dlg.add_button(_("Restore"), Gtk::RESPONSE_OK);
+
+  const int result = file_dlg.run();
+  file_dlg.hide();
+  if(result != Gtk::RESPONSE_OK)
+    return;
+
+  const std::string uri_tarball = file_dlg.get_uri();
+  if(uri_tarball.empty())
+    return;
+
+  do_restore_backup(uri_tarball);
+}
+
+void AppWindow::do_print_layout(const Glib::ustring& print_layout_name, bool preview, Gtk::Window* transient_for)
+{
+  m_pFrame->do_print_layout(print_layout_name, preview, transient_for);
+}
+
+bool AppWindow::do_restore_backup(const Glib::ustring& backup_uri)
+{
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document)
+    return false;
+    
+  ShowProgressMessage progress_message(_("Restoring backup"));
+  const Glib::ustring restored_file = Glom::Document::restore_backup_file(
+    backup_uri,
+    sigc::mem_fun(*this, &AppWindow::on_connection_convert_backup_progress));
+
+  if(restored_file.empty())
+  {
+    ui_warning(_("Restore Backup failed."), _("There was an error while restoring the backup."));
+    return false;
+  }
+  
+  open_document(restored_file);
+
+  return true;
+}
+
+void AppWindow::on_menu_developer_enable_layout_drag_and_drop()
+{
+  m_pFrame->set_enable_layout_drag_and_drop(m_action_enable_layout_drag_and_drop->get_active());
+}
+
+
+void AppWindow::on_window_translations_hide()
+{
+  if(m_window_translations)
+  {
+    m_pFrame->on_developer_dialog_hide();
+  }
+}
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+AppWindow* AppWindow::get_appwindow()
+{
+  return global_appwindow;
+}
+
+void AppWindow::document_history_add(const Glib::ustring& file_uri)
+{
+  // We override this so we can prevent example files from being saved in the recently-used list:
+
+  bool prevent = false;
+  if(!file_uri.empty())
+  {
+    prevent = (file_uri == m_example_uri);
+    if(prevent)
+      return;
+  }
+
+  GlomBakery::AppWindow_WithDoc_Gtk::document_history_add(file_uri);
+}
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+void AppWindow::do_menu_developer_fields(Gtk::Window& parent, const Glib::ustring table_name)
+{
+  m_pFrame->do_menu_developer_fields(parent, table_name);
+}
+
+void AppWindow::do_menu_developer_relationships(Gtk::Window& parent, const Glib::ustring table_name)
+{
+  m_pFrame->do_menu_developer_relationships(parent, table_name);
+}
+
+Document* AppWindow::on_connection_pool_get_document()
+{
+  return dynamic_cast<Document*>(get_document());
+}
+#endif //GLOM_ENABLE_CLIENT_ONLY
+
+//overridden to show the current table name in the window's title:
+void AppWindow::update_window_title()
+{
+  //Set application's main window title:
+
+  Document* document = dynamic_cast<Document*>(get_document());
+  if(!document)
+    return;
+
+  if(!m_pFrame)
+    return;
+
+  //Show the table title:
+  const Glib::ustring table_name = m_pFrame->get_shown_table_name();
+  Glib::ustring table_label = document->get_table_title(table_name, AppWindow::get_current_locale());
+  if(!table_label.empty())
+  {
+    #ifndef GLOM_ENABLE_CLIENT_ONLY
+    if(document->get_userlevel() == AppState::USERLEVEL_DEVELOPER)
+      table_label += " (" + table_name + ")"; //Show the table name as well, if in developer mode.
+    #endif // GLOM_ENABLE_CLIENT_ONLY
+  }
+  else //Use the table name if there is no table title.
+    table_label = table_name;
+
+  Glib::ustring strTitle = document->get_name();
+  if(!table_label.empty())
+    strTitle += ": " + table_label;
+
+  #ifndef GLOM_ENABLE_CLIENT_ONLY
+  //Indicate unsaved changes:
+  if(document->get_modified())
+    strTitle += " *";
+
+  //Indicate read-only files:
+  if(document->get_read_only())
+    strTitle += _(" (read-only)");
+  #endif //GLOM_ENABLE_CLIENT_ONLY
+
+  strTitle +=  " - " + m_strAppName;
+
+  set_title(strTitle);
+}
+
+void AppWindow::show_table_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value)
+{
+  if(!m_pFrame)
+    return;
+
+  m_pFrame->show_table(table_name, primary_key_value);
+}
+
+void AppWindow::show_table_list(const Glib::ustring& table_name)
+{
+  if(!m_pFrame)
+    return;
+
+  m_pFrame->show_table(table_name);
+}
+
+
+void AppWindow::print_report(const Glib::ustring& report_name)
+{
+  if(!m_pFrame)
+    return;
+
+  m_pFrame->on_menu_report_selected(report_name);
+}
+
+void AppWindow::print_layout()
+{
+  if(!m_pFrame)
+    return;
+
+  m_pFrame->on_menu_file_print();
+}
+
+void AppWindow::start_new_record()
+{
+  m_pFrame->on_menu_add_record();
+}
+
+void AppWindow::set_progress_message(const Glib::ustring& message)
+{
+  const Glib::ustring title = _("Processing");
+  const std::string collate_key = (title + message).collate_key();
+
+  if(collate_key != m_progress_collate_key)
+  {
+    // New progress message.
+    m_progress_collate_key = collate_key;
+    m_infobar_progress->set_message(title, message);
+    m_infobar_progress->show();
+  }
+
+  // Pulse the progress bar regardless of whether the message is new or not.
+  m_infobar_progress->pulse();
+  
+  //Block interaction with the rest of the UI.
+  Gtk::MenuBar* pMenuBar = static_cast<Gtk::MenuBar*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
+  if(pMenuBar)
+    pMenuBar->set_sensitive(false);
+  m_pFrame->set_sensitive(false);
+}
+
+void AppWindow::pulse_progress_message()
+{
+  m_infobar_progress->pulse();
+}
+
+void AppWindow::clear_progress_message()
+{
+  m_progress_collate_key.clear();
+  m_infobar_progress->hide();
+
+  Gtk::MenuBar* pMenuBar = static_cast<Gtk::MenuBar*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
+  if(pMenuBar)
+    pMenuBar->set_sensitive();
+  m_pFrame->set_sensitive();
+}
+
+void AppWindow::set_current_locale(const Glib::ustring& locale)
+{
+  if(locale.empty())
+    return;
+
+  m_current_locale = locale;
+}
+
+void AppWindow::set_original_locale(const Glib::ustring& locale)
+{
+  if(locale.empty())
+    return;
+
+  m_original_locale = locale;
+}
+
+Glib::ustring AppWindow::get_original_locale()
+{
+  //Default to English:
+  if(m_original_locale.empty())
+    m_original_locale = "en";
+
+  return m_original_locale; 
+}
+
+bool AppWindow::get_current_locale_not_original()
+{
+  if(m_original_locale.empty())
+    get_original_locale();
+
+  if(m_current_locale.empty())
+    get_current_locale();
+
+  return m_original_locale != m_current_locale;
+}
+
+Glib::ustring AppWindow::get_current_locale()
+{
+  //Return a previously-set current locale, if any:
+  if(!m_current_locale.empty())
+    return m_current_locale;
+
+  //Get the user's current locale:
+  const char* cLocale = setlocale(LC_ALL, 0); //Passing NULL means query, instead of set.
+  if(cLocale)
+  {
+    //std::cout << "debug1: " << G_STRFUNC << ": locale=" << cLocale << std::endl;
+    return Utils::locale_simplify(cLocale);
+    //std::cout << "debug2: " << G_STRFUNC << ": m_current_locale=" << m_current_locale << std::endl;
+  }
+  else
+    return "C";
+}
+
+Glib::ustring item_get_title(const sharedptr<const TranslatableItem>& item)
+{
+  if(!item)
+    return Glib::ustring();
+
+  return item->get_title(AppWindow::get_current_locale());
+}
+
+Glib::ustring item_get_title_or_name(const sharedptr<const TranslatableItem>& item)
+{
+  if(!item)
+    return Glib::ustring();
+
+  return item->get_title_or_name(AppWindow::get_current_locale());
+}
+
+
+
+} //namespace Glom
diff --git a/glom/appwindow.h b/glom/appwindow.h
new file mode 100644
index 0000000..1ca8e88
--- /dev/null
+++ b/glom/appwindow.h
@@ -0,0 +1,305 @@
+/* Glom
+ *
+ * Copyright (C) 2001-2004 Murray Cumming
+ *
+ * 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.
+ */
+
+#ifndef GLOM_APP_WINDOW_H
+#define GLOM_APP_WINDOW_H
+
+#include "config.h" // For GLOM_ENABLE_CLIENT_ONLY
+
+#include <glom/bakery/appwindow_withdoc_gtk.h>
+#include <glom/frame_glom.h>
+#include <glom/show_progress_message.h>
+#include <glom/infobar_progress_creating.h>
+#include <gtkmm/aboutdialog.h>
+#include <gtkmm/messagedialog.h>
+
+//Avoid including the header here:
+extern "C"
+{
+typedef struct AvahiStringList AvahiStringList;
+
+typedef struct _EpcServiceInfo EpcServiceInfo;
+}
+
+namespace Glom
+{
+
+class Window_Translations;
+
+class AppWindow : public GlomBakery::AppWindow_WithDoc_Gtk
+{
+public:
+  static const char* glade_id;
+  static const bool glade_developer;
+
+  AppWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder);
+  virtual ~AppWindow();
+
+  virtual bool init(const Glib::ustring& document_uri = Glib::ustring()); //override
+
+  /**
+   * @param restore Whether @a document_uri is a .tar.gz backup file to restore.
+   */
+  bool init(const Glib::ustring& document_uri, bool restore);
+
+  //virtual void statusbar_set_text(const Glib::ustring& strText);
+  //virtual void statusbar_clear();
+
+  /// Get the UIManager so we can merge new menus in.
+  Glib::RefPtr<Gtk::UIManager> get_ui_manager();
+
+  /** Changes the mode to Data mode, as if the user had selected the Data Mode menu item.
+   */
+  void set_mode_data();
+  void set_mode_find();
+
+  /** Show in the UI whether the database is shared on the network.
+   */
+  void update_network_shared_ui();
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  void add_developer_action(const Glib::RefPtr<Gtk::Action>& refAction);
+  void remove_developer_action(const Glib::RefPtr<Gtk::Action>& refAction);
+
+  /** Show in the UI whether the document is in developer or operator mode.
+   */
+  void update_userlevel_ui();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  /** Enable/disable UI elements depending on whether a table is loaded.
+   */
+  void update_table_sensitive_ui();
+
+  AppState::userlevels get_userlevel() const;
+
+  void fill_menu_tables();
+  void fill_menu_reports(const Glib::ustring& table_name);
+  void fill_menu_print_layouts(const Glib::ustring& table_name);
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  void do_menu_developer_fields(Gtk::Window& parent, const Glib::ustring table_name);
+  void do_menu_developer_relationships(Gtk::Window& parent, const Glib::ustring table_name);
+  void do_print_layout(const Glib::ustring& print_layout_name, bool preview = false, Gtk::Window* transient_for = 0);
+  bool do_restore_backup(const Glib::ustring& backup_uri);
+#endif //GLOM_ENABLE_CLIENT_ONLY
+
+  ///Whether to show the generated SQL queries on stdout, for debugging.
+  bool get_show_sql_debug() const;
+
+  ///Whether to show the generated SQL queries on stdout, for debugging.
+  void set_show_sql_debug(bool val = true);
+
+  ///Whether to automatically shutdown the database server when Glom crashes.
+  void set_stop_auto_server_shutdown(bool val = true);
+
+  void show_table_details(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value);
+  void show_table_list(const Glib::ustring& table_name);
+
+  /** Print the named report for the current table.
+   */
+  void print_report(const Glib::ustring& report_name);
+
+  /** Print the current layout for the current table.
+   */
+  void print_layout();
+
+  /** Offer the user the UI to add a new record,
+   */
+  void start_new_record();
+
+  void set_progress_message(const Glib::ustring& message);
+  void pulse_progress_message();
+  void clear_progress_message();
+
+  /** Set the locale used for original text of titles. This 
+   * must usually be stored in the document. 
+   * Ideally, it would be English.
+   */
+  static void set_original_locale(const Glib::ustring& locale);
+
+  static Glib::ustring get_original_locale();
+
+  static bool get_current_locale_not_original();
+
+  /** Set the locale used for titles, to test translations.
+   * Usually the current locale is just the locale at startup.
+   */
+  static void set_current_locale(const Glib::ustring& locale);
+
+  /** Get the locale used by this program when it was started,
+   * or the locale set by set_current_locale().
+   */
+  static Glib::ustring get_current_locale();
+
+  static AppWindow* get_appwindow();
+
+protected:
+  virtual void ui_warning_load_failed(int failure_code = 0); //Override.
+
+private:
+  virtual void init_layout(); //override.
+  virtual void init_menus(); //override.
+  virtual void init_toolbars(); //override
+  virtual void init_create_document(); //override
+  virtual bool on_document_load(); //override.
+  virtual void on_document_close(); //override.
+  virtual void update_window_title(); //override.
+
+  virtual void init_menus_file(); //override.
+
+  bool offer_new_or_existing();
+
+  void on_menu_help_contents();
+
+  /** Check that the file's hosting mode is supported by this build and
+   * tell the user if necessary.
+   */
+  bool check_document_hosting_mode_is_supported(Document* document);
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  void existing_or_new_new();
+
+  void on_menu_file_toggle_share();
+  void on_menu_developer_developer();
+  void on_menu_developer_operator();
+  void on_menu_file_save_as_example();
+  void on_menu_developer_changelanguage();
+  void on_menu_developer_translations();
+  void on_menu_developer_active_platform_normal();
+  void on_menu_developer_active_platform_maemo();
+  void on_menu_developer_export_backup();
+  void on_menu_developer_restore_backup();
+  void on_menu_developer_enable_layout_drag_and_drop ();
+
+  void on_window_translations_hide();
+
+  virtual Glib::ustring ui_file_select_save(const Glib::ustring& old_file_uri); //overridden.
+  void on_userlevel_changed(AppState::userlevels userlevel);
+
+  Document* on_connection_pool_get_document();
+
+  bool recreate_database_from_example(bool& user_cancelled); //return indicates success.
+  bool recreate_database_from_backup(const Glib::ustring& backup_uri, bool& user_cancelled); //return indicates success.
+  void on_recreate_database_progress();
+
+  void stop_self_hosting_of_document_database();
+
+  void on_connection_avahi_begin();
+  void on_connection_avahi_progress();
+  void on_connection_avahi_done();
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  void on_menu_help_about();
+  void on_about_close();
+
+#ifndef G_OS_WIN32
+  /** Offer a file chooser dialog, with a Browse Network button.
+   * @param browsed This will be set to true if the user chose a networked glom instance to open.
+   * @browsed_server This will be filled with the server details if browsed was set to true.
+   */
+  Glib::ustring ui_file_select_open_with_browse(bool& browsed, EpcServiceInfo*& browsed_server, Glib::ustring& browsed_service_name, const Glib::ustring& starting_folder_uri = Glib::ustring());
+#endif // !G_OS_WIN32
+
+  virtual void document_history_add(const Glib::ustring& file_uri); //overridden.
+
+  virtual void new_instance(const Glib::ustring& uri = Glib::ustring()); //Override
+
+  void on_connection_close_progress();
+  void on_connection_save_backup_progress();
+  void on_connection_convert_backup_progress();
+
+#ifndef G_OS_WIN32
+  void open_browsed_document(const EpcServiceInfo* server, const Glib::ustring& service_name);
+#endif // !G_OS_WIN32
+
+  typedef GlomBakery::AppWindow_WithDoc_Gtk type_base;
+
+  //Widgets:
+
+  Glib::RefPtr<Gtk::ActionGroup> m_refActionGroup_Others;
+
+  typedef std::list< Glib::RefPtr<Gtk::Action> > type_listActions;
+  type_listActions m_listDeveloperActions; //Only enabled when in developer mode.
+  type_listActions m_listTableSensitiveActions; // Only enabled when a table is loaded.
+  Glib::RefPtr<Gtk::Action> m_action_mode_find;
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  Glib::RefPtr<Gtk::Action> m_action_developer_users;
+  Glib::RefPtr<Gtk::RadioAction> m_action_menu_developer_developer, m_action_menu_developer_operator;
+  Glib::RefPtr<Gtk::ToggleAction> m_action_enable_layout_drag_and_drop ;
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  Glib::RefPtr<Gtk::ToggleAction> m_toggleaction_network_shared;
+  sigc::connection m_connection_toggleaction_network_shared;
+
+  Gtk::Box* m_pBoxTop;
+  Frame_Glom* m_pFrame;
+  
+  bool m_bAboutShown;
+  Gtk::AboutDialog* m_pAbout; //About box.
+
+  Infobar_ProgressCreating* m_infobar_progress;
+  std::string m_progress_collate_key;
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  Window_Translations* m_window_translations;
+
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  Glib::RefPtr<Gtk::ActionGroup> m_refHelpActionGroup;
+  Glib::RefPtr<Gtk::ActionGroup> m_refNavTablesActionGroup, m_refNavReportsActionGroup, m_refNavPrintLayoutsActionGroup;
+  type_listActions m_listNavTableActions, m_listNavReportActions, m_listNavPrintLayoutActions;
+  Gtk::UIManager::ui_merge_id m_menu_tables_ui_merge_id, m_menu_reports_ui_merge_id, m_menu_print_layouts_ui_merge_id;
+
+#ifndef GLOM_ENABLE_CLIENT_ONLY
+  //Set these before calling offer_saveas() (which uses ui_file_select_save()), and clear it afterwards.
+  bool m_ui_save_extra_showextras;
+  Glib::ustring m_ui_save_extra_title;
+  Glib::ustring m_ui_save_extra_message;
+
+  Glib::ustring m_ui_save_extra_newdb_title;
+
+  Document::HostingMode m_ui_save_extra_newdb_hosting_mode;
+
+  Gtk::MessageDialog* m_avahi_progress_dialog;
+
+#endif // !GLOM_ENABLE_CLIENT_ONLY
+
+  // This is set to the URI of an example file that is loaded to be able to
+  // prevent adding it into the recently used resources in
+  // document_history_add().
+  Glib::ustring m_example_uri;
+
+  //A temporary store for the username/password if
+  //we already asked for them when getting the document over the network,
+  //so we can use them again when connecting directly to the database:
+  Glib::ustring m_temp_username, m_temp_password;
+
+  bool m_show_sql_debug;
+
+  static Glib::ustring m_current_locale, m_original_locale;
+};
+
+Glib::ustring item_get_title(const sharedptr<const TranslatableItem>& item);
+
+Glib::ustring item_get_title_or_name(const sharedptr<const TranslatableItem>& item);
+
+} //namespace Glom
+
+#endif // GLOM_APP_WINDOW_H
diff --git a/glom/bakery/appwindow.cc b/glom/bakery/appwindow.cc
new file mode 100644
index 0000000..1fd666a
--- /dev/null
+++ b/glom/bakery/appwindow.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2000 Murray Cumming
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glom/bakery/appwindow.h>
+#include <algorithm>
+
+
+namespace GlomBakery
+{
+
+//Initialize static member data:
+Glib::ustring AppWindow::m_strVersion;
+
+bool AppWindow::m_bOperationCancelled = false;
+Glib::ustring AppWindow::m_strCommandLine_0;
+Glib::ustring AppWindow::m_strAppName;
+
+AppWindow::AppWindow(const Glib::ustring& appname)
+{
+  init_app_name(appname);
+}
+
+AppWindow::~AppWindow()
+{
+
+}
+
+void AppWindow::init_app_name(const Glib::ustring& appname) //static
+{
+  m_strAppName = appname;
+}
+
+void AppWindow::init()
+{
+  //set_wmclass(m_strAppName, m_strTitle); //The docs say "Don't use this".
+
+  init_ui_manager();
+  init_menus();
+  init_toolbars();
+
+  //on_document_load(); //Show the document (even if it is empty).
+}
+
+void AppWindow::init_ui_manager()
+{
+  
+}
+
+void AppWindow::init_menus()
+{
+  init_menus_file();
+  init_menus_edit();
+
+  //create_menus(m_menu_UI_Infos);
+  //install_menu_hints();
+
+  //Override this to add more menus.
+}
+
+void AppWindow::init_toolbars()
+{
+  
+}
+
+void AppWindow::on_menu_file_new()
+{
+  new_instance();
+}
+
+void AppWindow::on_menu_file_close()
+{
+  ui_hide();
+}
+
+void AppWindow::on_menu_edit_cut()
+{
+  on_menu_edit_copy();
+  on_menu_edit_clear();
+}
+
+
+void AppWindow::on_menu_edit_copy()
+{
+  
+}
+
+void AppWindow::on_menu_edit_paste()
+{
+  
+}
+
+void AppWindow::on_menu_edit_clear()
+{
+  
+}
+
+Glib::ustring AppWindow::get_version() const
+{
+  return m_strVersion;
+}
+
+void AppWindow::set_operation_cancelled(bool bVal /* = true */)
+{
+  m_bOperationCancelled = bVal;
+}
+
+bool AppWindow::get_operation_cancelled()
+{
+  return m_bOperationCancelled;
+}
+
+void AppWindow::set_command_line_args(int argc, char **&argv)
+{
+  if( (argc > 0) && argv[0])
+    m_strCommandLine_0 = (char*)argv[0];
+}
+
+AppWindow::type_signal_hide AppWindow::ui_signal_hide()
+{
+  return m_signal_hide;
+}
+
+
+
+} //namespace
diff --git a/glom/bakery/appwindow.h b/glom/bakery/appwindow.h
new file mode 100644
index 0000000..9f844b8
--- /dev/null
+++ b/glom/bakery/appwindow.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000 Murray Cumming
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GLOM_BAKERY_APP_H
+#define GLOM_BAKERY_APP_H
+
+#include <glibmm/object.h>
+
+#include <vector>
+
+namespace GlomBakery
+{
+
+/** Bakery's Main Window.
+ * This is an abstract class. You must use a class such as AppWindow_Gtk, which implements
+ * the ui_* methods for a particular GUI toolkit.
+ *
+ * Features:
+ * - Override methods to add/change menus/toolbars/statusbar.
+ *   - Default is basic File, Edit, Help menus and toolbar icons.
+ *
+ 
+ *
+ * TODO:
+ * - Command-line args - wrap popt?
+ * - Session Management - need Command-line args.
+ *
+ */
+
+class AppWindow : virtual public Glib::ObjectBase
+{
+public:
+
+  //The constructor has a default argument so that there is a default constructor,
+  //so that derived classes do not need to call a specific constructor. This is
+  //a virtual base class so they would otherwise need to do that.
+
+  ///Don't forget to call init() too.
+  AppWindow(const Glib::ustring& appname = Glib::ustring()); 
+
+  virtual ~AppWindow();
+
+  virtual void init(); //Sets it up and shows it.
+
+  virtual Glib::ustring get_version() const;
+
+  static void set_command_line_args(int argc, char** &argv); //Needed for session management.
+
+  typedef sigc::signal<void> type_signal_hide;
+  type_signal_hide ui_signal_hide();
+
+protected:
+  static void init_app_name(const Glib::ustring& appname);
+
+  /** Builds the intial ui string, with placeholders.
+   * This allows us to merge in actual menus and toolbars in the other init_*() methods.
+   */
+  virtual void init_ui_manager();
+
+  /** Override this to add more menus or different menus.
+   */
+  virtual void init_menus();
+
+  /** Call this from init_menus() to add the standard file menu
+   */
+  virtual void init_menus_file() = 0;
+
+  /** Call this from init_menus() to add the standard edit menu
+   */ 
+  virtual void init_menus_edit() = 0;
+
+  virtual void init_toolbars();
+  
+  virtual void new_instance(const Glib::ustring& uri = Glib::ustring()) = 0; //Must override in order to new() the derived document class.
+
+//  virtual void close_window() = 0;
+//  virtual void bring_to_front() = 0;
+  //Signal handlers:
+
+public: // We can not take function pointers of these methods in a 
+        // derived class, if they are protected - for instance, with sigc::mem_fun() 
+  //Menus:
+  virtual void on_menu_file_new();
+  virtual void on_menu_file_close();
+
+  //Edit menu handlers overriden in AppWindow_WithDoc:
+  virtual void on_menu_edit_cut();
+  virtual void on_menu_edit_copy();
+  virtual void on_menu_edit_paste();
+  virtual void on_menu_edit_clear();
+
+  virtual void on_menu_help_about() = 0;
+
+protected:
+  //GUI abstractions:
+  virtual void ui_hide() = 0;
+  virtual void ui_bring_to_front() = 0;
+
+  //operation_cancelled:
+  //e.g. A File|Open tries to save existing data,
+  //but this needs to be cancelled if the save failed. 
+  static void set_operation_cancelled(bool bVal = true);
+  static bool get_operation_cancelled();
+
+  //Member data:
+  
+  //'About Box'/WM Class information:
+  static Glib::ustring m_strAppName;
+  static Glib::ustring m_strVersion;
+
+  //Instances
+
+  static bool m_bOperationCancelled; //see set/get_operation_cancelled().
+
+  //Command line args:
+  static Glib::ustring m_strCommandLine_0;
+
+  type_signal_hide m_signal_hide;
+};
+
+} //namespace
+
+#endif //BAKERY_APP_H
diff --git a/glom/bakery/appwindow_withdoc.cc b/glom/bakery/appwindow_withdoc.cc
new file mode 100644
index 0000000..1cb82a6
--- /dev/null
+++ b/glom/bakery/appwindow_withdoc.cc
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2000 Murray Cumming
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "config.h"
+#include <glom/bakery/appwindow_withdoc.h>
+#include <glom/bakery/dialog_offersave.h>
+#include <giomm/file.h>
+#include <algorithm>
+#include <glibmm/i18n-lib.h>
+
+namespace GlomBakery
+{
+
+AppWindow_WithDoc::type_list_strings AppWindow_WithDoc::m_mime_types;
+
+AppWindow_WithDoc::AppWindow_WithDoc(const Glib::ustring& appname)
+: AppWindow(appname),
+  m_pDocument(0),
+  m_bCloseAfterSave(false)
+{
+}
+
+AppWindow_WithDoc::~AppWindow_WithDoc()
+{
+  //Delete the document:
+  delete m_pDocument; //This will cause Document::signal_forget to be emitted, so the Views will then null their pointers as well. A smartpointer might be a better way to do this.
+  m_pDocument = 0;
+}
+
+//static
+void AppWindow_WithDoc::add_mime_type(const Glib::ustring& mime_type)
+{
+  if( std::find(m_mime_types.begin(), m_mime_types.end(), mime_type) == m_mime_types.end() )
+    m_mime_types.push_back(mime_type);
+}
+
+void AppWindow_WithDoc::on_menu_file_close()
+{
+  if(m_pDocument->get_modified())
+  {
+    //Offer to save changes:
+    m_bCloseAfterSave = true; //Checked in FileChooser signal handler.
+    offer_to_save_changes(); //If a File|Exit is in progress, this could cancel it.
+  }
+
+  if(!get_operation_cancelled())
+    ui_hide();
+
+  on_document_close();
+}
+
+bool AppWindow_WithDoc::open_document_from_data(const guchar* data, std::size_t length)
+{
+  int failure_code = 0;
+  const bool bTest = m_pDocument->load_from_data(data, length, failure_code);
+
+  bool bOpenFailed = false;
+  if(!bTest) //if open failed.
+    bOpenFailed = true;
+  else
+  {
+    //if open succeeded then let the App respond:
+    const bool test = on_document_load();
+    if(!test)
+      bOpenFailed = true; //The application didn't like something about the just-loaded document.
+    else
+    {
+      update_window_title();
+      set_document_modified(false); //disables menu and toolbar Save items.
+
+      return true; //success.
+    }
+  }
+
+  if(bOpenFailed)
+  {
+    ui_warning_load_failed(failure_code);
+
+    return false; //failed.
+  }
+  else
+    return true;
+}
+
+bool AppWindow_WithDoc::open_document(const Glib::ustring& file_uri)
+{
+  {
+    //Open it:
+
+    //Load it into a new instance unless the current document is just a default new.
+    if(!(get_document()->get_is_new())) //if it's not new.
+    {
+      //New instance:
+      new_instance(file_uri);
+      return true;
+    }
+
+    AppWindow_WithDoc* pApp = this; //Replace the default new document in this instance.
+
+    //Open it.
+    pApp->m_pDocument->set_file_uri(file_uri);
+    int failure_code = 0;
+    const bool bTest = pApp->m_pDocument->load(failure_code);
+
+    bool bOpenFailed = false;
+    bool bShowError = false;
+    if(!bTest) //if open failed.
+    {
+      bOpenFailed = true;
+      bShowError = true;
+    }
+    else
+    {
+      //if open succeeded then let the App respond:
+      const bool test = pApp->on_document_load();
+      if(!test)
+        bOpenFailed = true; //The application didn't like something about the just-loaded document.
+      else
+      {
+        pApp->update_window_title();
+        set_document_modified(false); //disables menu and toolbar Save items.
+
+        //Update document history list:
+        //(Getting the file URI again, in case it has changed while being opened,
+        //for instance if it was a template file that was saved as a real file.)
+        if(pApp->m_pDocument)
+          document_history_add(file_uri);
+
+        return true; //success.
+      }
+    }
+
+    if(bOpenFailed)
+    {
+      if (bShowError)
+        ui_warning_load_failed(failure_code);
+
+      //Make sure that non-existant files are removed from the history list:
+      if(failure_code == Document::LOAD_FAILURE_CODE_NOT_FOUND)
+        document_history_remove(file_uri);
+
+      //re-initialize document.
+      delete pApp->m_pDocument;
+      pApp->m_pDocument = 0;
+      pApp->init_create_document();
+
+      return false; //failed.
+    }
+
+  } //if already open.
+
+  return false; //failed.
+}
+
+void AppWindow_WithDoc::on_menu_file_open()
+{
+  //Display File Open dialog and respond to choice:
+
+  //Bring document window to front, to make it clear which document is being changed:
+  ui_bring_to_front();
+
+  //Ask user to choose file to open:
+  Glib::ustring file_uri = ui_file_select_open();
+  if(!file_uri.empty())
+    open_document(file_uri);
+}
+
+//This is for calling directly, use the other override for the signal handler:
+void AppWindow_WithDoc::offer_saveas()
+{
+  on_menu_file_saveas();
+}
+
+bool AppWindow_WithDoc::file_exists(const Glib::ustring& uri)
+{
+  if(uri.empty())
+    return false;
+
+  //Check whether file exists already:
+  {
+    // Try to examine the input file.
+    Glib::RefPtr<Gio::File> file = Gio::File::create_for_uri(uri);
+
+    try
+    {
+      return file->query_exists();
+    }
+    catch(const Gio::Error& /* ex */)
+    {
+      return false; //Something went wrong. It does not exist.
+    }
+  }
+}
+
+void AppWindow_WithDoc::on_menu_file_saveas()
+{
+  //Display File Save dialog and respond to choice:
+
+  //Bring document window to front, to make it clear which document is being saved:
+  //This doesn't work: TODO.
+  ui_bring_to_front();
+
+  //Show the save dialog:
+  const Glib::ustring& file_uriOld = m_pDocument->get_file_uri();
+
+  Glib::ustring file_uri = ui_file_select_save(file_uriOld); //Also asks for overwrite confirmation.
+  if(!file_uri.empty())
+  {
+    //Enforce the file extension:
+    file_uri = m_pDocument->get_file_uri_with_extension(file_uri);
+
+    bool bUseThisFileUri = true;
+
+    //Save to this filepath:
+    if(bUseThisFileUri)
+    {
+      m_pDocument->set_file_uri(file_uri, true); //true = enforce file extension
+      const bool bTest = m_pDocument->save();
+
+      if(!bTest)
+      {
+        ui_warning(_("Save failed."), _("There was an error while saving the file. Your changes have not been saved."));
+      }
+      else
+      {
+        //Disable Save and SaveAs menu items:
+        after_successful_save();
+      }
+
+      update_window_title();
+
+
+      //Close if this save was a result of a File|Close or File|Exit:.
+      //if(bTest && m_bCloseAfterSave) //Don't close if the save failed.
+      //{
+      //  on_menu_file_close(); //This could be the second time, but now there are no unsaved changes.
+      //}
+    }
+    else
+    {
+      //Let the user choose a different file path,
+      //because he decided not to overwrite the 1st one.
+      offer_saveas(); //recursive.
+    }
+  }
+  else
+  {
+    cancel_close_or_exit();
+  }
+}
+
+void AppWindow_WithDoc::on_menu_file_save()
+{
+  if(m_pDocument)
+  {
+    //If there is already a filepath, then save to that location:
+    if(!(m_pDocument->get_file_uri().empty()))
+    {
+      bool bTest = m_pDocument->save();
+
+      if(bTest)
+      {
+        //Disable Save and SaveAs menu items:
+        after_successful_save();
+
+        //Close the document if this save was in response to a 'Do you want to save before closing?':
+        //if(m_bCloseAfterSave) // || m_bExiting
+        //  close_mark_or_destroy();
+      }
+      else
+      {
+        //The save failed. Tell the user and don't do anything else:
+        ui_warning(_("Save failed."), _("There was an error while saving the file. Your changes have not been saved."));
+
+        cancel_close_or_exit();
+      }
+    }
+    else
+    {
+      //If there is no filepath, then ask for one and save to that location:
+      offer_saveas();
+    }
+  }
+
+  if(!m_bCloseAfterSave) //Don't try to do anything after closing - this instance would not exist anymore.
+  {
+    update_window_title();
+  }
+
+}
+
+void AppWindow_WithDoc::init()
+{
+  init_create_document();
+
+  //Call base method:
+  AppWindow::init();
+
+  on_document_load(); //Show default empty document in the View.
+
+  set_document_modified(false); //Disable Save menu item.
+}
+
+void AppWindow_WithDoc::init_create_document()
+{
+  //Overrides should call this base method at the end.
+
+  if(!m_pDocument)
+  {
+    m_pDocument = new Document();
+  }
+
+  m_pDocument->set_is_new(true); //Can't be modified if it's just been created.
+
+  m_pDocument->signal_modified().connect(sigc::mem_fun(*this, &AppWindow_WithDoc::on_document_modified));
+
+  update_window_title();
+}
+
+Document* AppWindow_WithDoc::get_document()
+{
+  return m_pDocument;
+}
+
+const Document* AppWindow_WithDoc::get_document() const
+{
+  return m_pDocument;
+}
+
+void AppWindow_WithDoc::offer_to_save_changes()
+{
+  if(m_pDocument)
+  {
+    if(m_pDocument->get_modified())
+    {
+      set_operation_cancelled(false); //Initialize it again. It might be set later in this method by cancel_close_or_exit().
+
+      //The document has unsaved changes,
+      //so ask the user whether the document should be saved:
+      enumSaveChanges buttonClicked = ui_offer_to_save_changes();
+
+      //Respond to button that was clicked:
+      switch(buttonClicked)
+      {
+        case(SAVECHANGES_Save):
+        {
+          on_menu_file_save(); //If File|Exit is in progress, this could cancel it.
+          break;
+        }
+
+        case(SAVECHANGES_Discard):
+        {
+          //Close if this save offer was a result of a FileClose (It probably always is):
+          //close_mark_or_destroy();
+          //Do nothing - the caller will probably hide() the window to have it deleted.
+          break;
+        }
+
+        case(SAVECHANGES_Cancel): //Cancel.
+        {
+          cancel_close_or_exit();
+          break;
+        }
+
+        default:
+        {
+          break;
+        }
+      }
+    }
+  }
+}
+
+void AppWindow_WithDoc::cancel_close_or_exit()
+{
+  set_operation_cancelled();
+  m_bCloseAfterSave = false;
+
+  //exit_destroy_marked_instances(); //Clean up after an exit.
+}
+
+bool AppWindow_WithDoc::on_document_load()
+{
+  //Show document contents:
+  if(m_pDocument)
+  {
+    GlomBakery::ViewBase* pView = m_pDocument->get_view();
+    if(pView)
+      pView->load_from_document();
+
+    //Set document as unmodified (this could have been wrongly set during the load):
+    set_document_modified(false);
+
+    return true;
+  }
+  else
+    return false; //I can't think of any reason why this would happen.
+
+  //If you are not using Views, then override this to fill your various windows with stuff according to the contents of the document.
+}
+
+void AppWindow_WithDoc::on_document_close()
+{
+}
+
+void AppWindow_WithDoc::update_window_title()
+{
+
+}
+
+void AppWindow_WithDoc::on_document_modified(bool /* modified */)
+{
+  //Change the displayed 'modified' status.
+  //This method could be overridden to e.g. enable a Save icon or enable the Save menu item.
+  //TODO: enable/disable the Save menu item.
+
+  ui_show_modification_status();
+
+  //Change title bar:
+  update_window_title();
+}
+
+void AppWindow_WithDoc::set_document_modified(bool bModified /* = true */)
+{
+  m_pDocument->set_modified(bModified);
+
+  //Enable/Disable Save menu item and toolbar item:
+  ui_show_modification_status();
+}
+
+void AppWindow_WithDoc::on_menu_edit_copy()
+{
+  GlomBakery::ViewBase* pView = m_pDocument->get_view();
+  if(pView)
+    pView->clipboard_copy();
+}
+
+void AppWindow_WithDoc::on_menu_edit_paste()
+{
+  GlomBakery::ViewBase* pView = m_pDocument->get_view();
+  if(pView)
+    pView->clipboard_paste();
+}
+
+void AppWindow_WithDoc::on_menu_edit_clear()
+{
+  GlomBakery::ViewBase* pView = m_pDocument->get_view();
+  if(pView)
+    pView->clipboard_clear();
+}
+
+void AppWindow_WithDoc::after_successful_save()
+{
+  set_document_modified(false); //enables/disables menu and toolbar widgets.
+
+  //Update document history list:
+  document_history_add(m_pDocument->get_file_uri());
+}
+
+Glib::ustring AppWindow_WithDoc::get_conf_fullkey(const Glib::ustring& key)
+{
+  return "/apps/" + m_strAppName + '/' + key;
+}
+
+
+void AppWindow_WithDoc::document_history_add(const Glib::ustring& /* file_uri */)
+{
+  //Override this.
+}
+
+void AppWindow_WithDoc::document_history_remove(const Glib::ustring& /* file_uri */)
+{
+  //Override this.
+}
+
+void AppWindow_WithDoc::ui_warning_load_failed(int)
+{
+  ui_warning(_("Open Failed."), _("The document could not be opened."));
+}
+
+} //namespace
diff --git a/glom/bakery/appwindow_withdoc.h b/glom/bakery/appwindow_withdoc.h
new file mode 100644
index 0000000..e45d244
--- /dev/null
+++ b/glom/bakery/appwindow_withdoc.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2000 Murray Cumming
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GLOM_BAKERY_APP_WITHDOC_H
+#define GLOM_BAKERY_APP_WITHDOC_H
+
+#include <glom/bakery/appwindow.h>
+#include <libglom/document/bakery/document.h>
+
+namespace GlomBakery
+{
+
+/** Main Window which supports documents.
+ *
+ * This is an abstract class. You must use a class such as AppWindow_WithDoc_Gtk, which implements
+ * the ui_* methods for a particular GUI toolkit.
+ 
+ * Features:
+ * - 1 document per application instance. Uses Document-derived class polymorphically.
+ * - Override init_create_document() to create new blank document.
+ * - Appropriate Default handling of document open, save, save as.
+ * - Appropriate checking of document 'modified' status - asks user about unsaved changes.
+ * - Asks user about overwriting existing documents.
+ * - Override methods to add/change menus/toolbars/statusbar.
+ *   - Default is basic File, Edit, Help menus and toolbar icons.
+ * - Shows document name (or 'untitled') in window title.
+ * - Shows * in title bar for unsaved docs. Overridable to e.g. shade a Save icon.
+ * - Enforces a file extension.
+ * - Recent Documents menu item - if you use add_mime_type().
+ *
+ *
+ * TODO:
+ * - Printing -?
+ *  - Print Setup
+ *  - Print Preview
+ *  - Multiple document-types:
+ *  - File/New sub menu
+ *  - Some way to associate a view with a document type: class factory.
+ */
+class AppWindow_WithDoc : public AppWindow
+{
+public: 
+  ///Don't forget to call init() too.
+  AppWindow_WithDoc(const Glib::ustring& appname = ""); //TODO: appname when using get_derived_widget()
+
+  virtual ~AppWindow_WithDoc();
+
+  virtual void init(); //overridden to create document.
+
+  enum enumSaveChanges
+  {
+    SAVECHANGES_Save,
+    SAVECHANGES_Cancel,
+    SAVECHANGES_Discard
+  };
+
+  static bool file_exists(const Glib::ustring& uri);
+
+protected:
+  virtual void init_create_document(); //override this to new() the specific document type.
+
+  /** Add a MIME-type that this application can support.
+   * You should also register the MIME-type when the application is installed:
+   * See http://freedesktop.org/Standards/AddingMIMETutor
+   */
+  static void add_mime_type(const Glib::ustring& mime_type);
+
+  ///static_cast<> or dynamic_cast<> this pointer to the correct type.
+  virtual Document* get_document();
+
+  ///static_cast<> or dynamic_cast<> this pointer to the correct type.
+  virtual const Document* get_document() const ;
+
+  virtual void set_document_modified(bool bModified = true);
+
+  /** Open the document from a file at a URI.
+   * This will check whether the document is already open.
+   * @result true indicates success.
+   */
+  virtual bool open_document(const Glib::ustring& file_uri);
+
+  //This cannot be virtual, because that would break our ABI.
+  //Hopefully that is not necessary.
+  /** Open the document using the supplied document contents.
+   * Unlike open_document(), this has no way to know whether the document is already open.
+   * @param data A pointer to the bytes of the document contents.
+   * @param length The number of bytes in the data.
+   * @result true indicates success.
+   */
+  bool open_document_from_data(const guchar* data, std::size_t length);
+
+  virtual void document_history_add(const Glib::ustring& file_uri);
+  virtual void document_history_remove(const Glib::ustring& file_uri);
+
+public:
+  // We can not take function pointers of these methods in 
+  // a derived class if they are protected - for instance, with sigc::mem_fun()
+  //Signal handlers:
+
+  //Menu items:
+  virtual void on_menu_file_open();
+  virtual void on_menu_file_saveas(); //signal handler.
+  virtual void offer_saveas(); //For direct use.
+  virtual void on_menu_file_save(); //signal handler.
+  virtual void on_menu_file_close();
+
+  virtual void on_menu_edit_copy();
+  virtual void on_menu_edit_paste();
+  virtual void on_menu_edit_clear();
+
+protected:
+  //Document:
+
+  ///Update visual status.
+  virtual void on_document_modified(bool modified);
+
+  ///override this to show document contents.
+  virtual bool on_document_load();
+  
+  ///override this to do extra cleanup.
+  virtual void on_document_close();
+
+  virtual void offer_to_save_changes();
+
+  ///Stop the File|Close or the File|Exit.
+  virtual void cancel_close_or_exit();
+
+  virtual void update_window_title();
+
+  virtual void after_successful_save(); //e.g. disable File|Save.
+
+  virtual void ui_warning(const Glib::ustring& text, const Glib::ustring& secondary_text) = 0;
+
+  /** Warn the user about a failure while loading a document.
+   * Override this to show a specific message in response to your application's 
+   * custom @a failure_code.
+   */
+  virtual void ui_warning_load_failed(int failure_code = 0);
+
+  virtual Glib::ustring ui_file_select_open(const Glib::ustring& ui_file_select_open = Glib::ustring()) = 0;
+
+  /** Present a user interface that allows the user to select a location to save the file.
+   * @param old_file_uri The existing URI of the file, if any.
+   * @result The URI of the file chosen by the user.
+   */
+  virtual Glib::ustring ui_file_select_save(const Glib::ustring& old_file_uri) = 0;
+
+  virtual void ui_show_modification_status() = 0;
+
+  virtual enumSaveChanges ui_offer_to_save_changes() = 0;
+
+  static Glib::ustring get_conf_fullkey(const Glib::ustring& key);
+
+  //Document:
+  Document* m_pDocument; //An instance of a derived type.
+  bool m_bCloseAfterSave;
+
+  //Mime types which this application can load and save:
+  typedef std::list<Glib::ustring> type_list_strings;
+  static type_list_strings m_mime_types;
+};
+
+} //namespace
+
+#endif //BAKERY_APP_WITHDOC_H
diff --git a/glom/bakery/appwindow_withdoc_gtk.cc b/glom/bakery/appwindow_withdoc_gtk.cc
new file mode 100644
index 0000000..9f4b7c1
--- /dev/null
+++ b/glom/bakery/appwindow_withdoc_gtk.cc
@@ -0,0 +1,661 @@
+/*
+ * Copyright 2000 Murray Cumming
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <glom/bakery/appwindow_withdoc_gtk.h>
+#include <glom/bakery/dialog_offersave.h>
+//#include <libgnomevfsmm/utils.h> //For escape_path_string()
+//#include <libgnomevfsmm/mime-handlers.h> //For type_is_known(). 
+#include <gtkmm/toolbutton.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/recentchoosermenu.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/filechooserdialog.h>
+#include <gtkmm/editable.h>
+#include <gtkmm/textview.h>
+#include <giomm/file.h>
+#include <algorithm>
+#include <iostream>
+
+#include "config.h"
+
+#include <glibmm/i18n-lib.h>
+
+namespace GlomBakery
+{
+
+
+//Initialize static member data:
+
+AppWindow_WithDoc_Gtk::AppWindow_WithDoc_Gtk(const Glib::ustring& appname)
+: AppWindow_WithDoc(appname),
+  m_pVBox(0),
+  m_VBox_PlaceHolder(Gtk::ORIENTATION_VERTICAL)
+{
+  init_app_name(appname);
+}
+
+/// This constructor can be used with Gtk::Builder::get_derived_widget().
+AppWindow_WithDoc_Gtk::AppWindow_WithDoc_Gtk(BaseObjectType* cobject, const Glib::ustring& appname)
+: AppWindow_WithDoc(appname),
+  ParentWindow(cobject), 
+  m_pVBox(0),
+  m_VBox_PlaceHolder(Gtk::ORIENTATION_VERTICAL)
+{
+  init_app_name(appname);
+}
+
+  
+AppWindow_WithDoc_Gtk::~AppWindow_WithDoc_Gtk()
+{
+  delete m_pVBox;
+  m_pVBox = 0;
+}
+
+
+void AppWindow_WithDoc_Gtk::on_hide()
+{
+  ui_signal_hide().emit();
+}
+
+void AppWindow_WithDoc_Gtk::ui_hide()
+{
+  hide();  
+}
+
+void AppWindow_WithDoc_Gtk::ui_bring_to_front()
+{
+  get_window()->raise();
+}
+
+
+void AppWindow_WithDoc_Gtk::init_layout()
+{
+  set_resizable(); //resizable
+  set_default_size(640, 400); //A sensible default.
+
+  //This might have been instantiated by Gtk::Builder::get_widget() instead.
+  //If not, then we create a default one and add it to the window.
+  if(!m_pVBox)
+  {
+    m_pVBox = new Gtk::VBox(Gtk::ORIENTATION_VERTICAL);
+    Gtk::Window::add(*m_pVBox);
+  }
+
+  //Add menu bar at the top:
+  //These were defined in init_uimanager().
+  Gtk::MenuBar* pMenuBar = static_cast<Gtk::MenuBar*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
+  m_pVBox->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
+
+  add_accel_group(m_refUIManager->get_accel_group());
+
+
+  //Add placeholder, to be used by add():
+  m_pVBox->pack_start(m_VBox_PlaceHolder);
+  m_VBox_PlaceHolder.show();
+  
+  m_pVBox->show(); //Show it last so the child widgets all show at once.
+}
+
+void AppWindow_WithDoc_Gtk::add_ui_from_string(const Glib::ustring& ui_description)
+{
+  try
+  {
+    m_refUIManager->add_ui_from_string(ui_description);
+  }
+  catch(const Glib::Error& ex)
+  {
+    std::cerr << "building menus failed: " <<  ex.what();
+  }
+}
+
+void AppWindow_WithDoc_Gtk::init()
+{  
+  AppWindow_WithDoc::init(); //Create document and ask to show it in the UI.
+  init_layout(); // start setting up layout after we've gotten all widgets from UIManager
+  show();
+}
+
+
+void AppWindow_WithDoc_Gtk::init_ui_manager()
+{
+  using namespace Gtk;
+
+  m_refUIManager = UIManager::create();
+
+  //This is just a skeleton structure.
+  //The placeholders allow us to merge the menus and toolbars in later,
+  //by adding a us string with one of the placeholders, but with menu items underneath it.
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_File' />"
+    "    <placeholder name='Bakery_MenuPH_Edit' />"
+    "    <placeholder name='Bakery_MenuPH_Others' />" //Note that extra menus should be inserted before the Help menu, which should always be at the end.
+    "    <placeholder name='Bakery_MenuPH_Help' />"
+    "  </menubar>"
+    "  <toolbar name='Bakery_ToolBar'>"
+    "    <placeholder name='Bakery_ToolBarItemsPH' />"
+    "  </toolbar>"
+    "</ui>";
+  
+  add_ui_from_string(ui_description);
+}
+
+void AppWindow_WithDoc_Gtk::init_toolbars()
+{
+  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <toolbar name='Bakery_ToolBar'>"
+    "    <placeholder name='Bakery_ToolBarItemsPH'>"
+    "      <toolitem action='BakeryAction_File_New' />"
+    "      <toolitem action='BakeryAction_File_Open' />"
+    "      <toolitem action='BakeryAction_File_Save' />"
+    "    </placeholder>"
+    "  </toolbar>"
+    "</ui>";
+
+  add_ui_from_string(ui_description);
+}
+
+void AppWindow_WithDoc_Gtk::init_menus()
+{
+  //Override this to add more menus
+  init_menus_file();
+  init_menus_edit();
+}
+
+void AppWindow_WithDoc_Gtk::init_menus_file_recentfiles(const Glib::ustring& path)
+{
+  if(!m_mime_types.empty()) //"Recent-files" is useless unless it knows what documents (which MIME-types) to show.
+  {
+    //Add recent-files submenu:
+    Gtk::MenuItem* pMenuItem = dynamic_cast<Gtk::MenuItem*>(m_refUIManager->get_widget(path));
+    if(pMenuItem)
+    {
+      Glib::RefPtr<Gtk::RecentFilter> filter = Gtk::RecentFilter::create();
+
+      //Add the mime-types, so that it only shows those documents:
+      for(type_list_strings::iterator iter = m_mime_types.begin(); iter != m_mime_types.end(); ++iter)
+      {
+        const Glib::ustring mime_type = *iter;
+
+        //TODO: Find a gio equivalent for gnome_vfs_mime_type_is_known(). murrayc.
+        filter->add_mime_type(mime_type);
+      }
+
+      Gtk::RecentChooserMenu* menu = Gtk::manage(new Gtk::RecentChooserMenu);
+      menu->set_filter(filter);
+      menu->set_show_numbers(false);
+      menu->set_sort_type(Gtk::RECENT_SORT_MRU);
+      menu->signal_item_activated().connect(sigc::bind(sigc::mem_fun(*this, static_cast<void(AppWindow_WithDoc_Gtk::*)(Gtk::RecentChooser&)>(&AppWindow_WithDoc_Gtk::on_recent_files_activate)), sigc::ref(*menu)));
+
+      pMenuItem->set_submenu(*menu);
+    }
+    else
+    {
+      std::cout << "debug: recent files menu not found" << std::endl;
+    }
+  }
+  else
+  {
+    //std::cout << "debug: " << G_STRFUNC << ": No recent files sub-menu added, because no MIME types are specified." << std::endl;
+  }
+}
+
+void AppWindow_WithDoc_Gtk::init_menus_file()
+{
+  // File menu
+
+  //Build actions:
+  m_refFileActionGroup = Gtk::ActionGroup::create("BakeryFileActions");
+
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File", _("_File")));
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File_RecentFiles", _("_Recent Files")));
+
+  //File actions
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_New", Gtk::Stock::NEW),
+                        sigc::mem_fun((AppWindow&)*this, &AppWindow::on_menu_file_new));
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Open", Gtk::Stock::OPEN),
+                        sigc::mem_fun((AppWindow_WithDoc&)*this, &AppWindow_WithDoc::on_menu_file_open));
+
+  //Remember thes ones for later, so we can disable Save menu and toolbar items:
+  m_action_save = Gtk::Action::create("BakeryAction_File_Save", Gtk::Stock::SAVE);
+  m_refFileActionGroup->add(m_action_save,
+                        sigc::mem_fun((AppWindow_WithDoc&)*this, &AppWindow_WithDoc::on_menu_file_save));
+
+  m_action_saveas = Gtk::Action::create("BakeryAction_File_SaveAs", Gtk::Stock::SAVE_AS);                   
+  m_refFileActionGroup->add(m_action_saveas,
+                        sigc::mem_fun((AppWindow_WithDoc&)*this, &AppWindow_WithDoc::on_menu_file_saveas));
+                        
+  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Close", Gtk::Stock::CLOSE),
+                        sigc::mem_fun((AppWindow_WithDoc&)*this, &AppWindow_WithDoc::on_menu_file_close));
+                        
+  m_refUIManager->insert_action_group(m_refFileActionGroup);
+
+  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_File'>"
+    "      <menu action='BakeryAction_Menu_File'>"
+    "        <menuitem action='BakeryAction_File_New' />"
+    "        <menuitem action='BakeryAction_File_Open' />"
+    "        <menu action='BakeryAction_Menu_File_RecentFiles'>"
+    "        </menu>"
+    "        <menuitem action='BakeryAction_File_Save' />"
+    "        <menuitem action='BakeryAction_File_SaveAs' />"
+    "        <separator/>"
+    "        <menuitem action='BakeryAction_File_Close' />"
+    "      </menu>"
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+  
+  //Add menu:
+  add_ui_from_string(ui_description);
+ 
+  //Add recent-files submenu:
+  init_menus_file_recentfiles("/Bakery_MainMenu/Bakery_MenuPH_File/BakeryAction_Menu_File/BakeryAction_Menu_File_RecentFiles");
+}
+
+void AppWindow_WithDoc_Gtk::init_menus_edit()
+{
+  using namespace Gtk;
+  //Edit menu
+  
+  //Build actions:
+  m_refEditActionGroup = ActionGroup::create("BakeryEditActions");
+  m_refEditActionGroup->add(Action::create("BakeryAction_Menu_Edit", _("_Edit")));
+  
+  m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Cut", Gtk::Stock::CUT),
+                        sigc::mem_fun((AppWindow_WithDoc_Gtk&)*this, &AppWindow_WithDoc_Gtk::on_menu_edit_cut_activate));
+  m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Copy", Gtk::Stock::COPY),
+                        sigc::mem_fun((AppWindow_WithDoc_Gtk&)*this, &AppWindow_WithDoc_Gtk::on_menu_edit_copy_activate));
+  m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Paste", Gtk::Stock::PASTE),
+                        sigc::mem_fun((AppWindow_WithDoc_Gtk&)*this, &AppWindow_WithDoc_Gtk::on_menu_edit_paste_activate));
+  m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Clear", Gtk::Stock::CLEAR));
+
+  m_refUIManager->insert_action_group(m_refEditActionGroup);
+  
+  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
+  static const Glib::ustring ui_description =
+    "<ui>"
+    "  <menubar name='Bakery_MainMenu'>"
+    "    <placeholder name='Bakery_MenuPH_Edit'>"
+    "      <menu action='BakeryAction_Menu_Edit'>"
+    "        <menuitem action='BakeryAction_Edit_Cut' />"
+    "        <menuitem action='BakeryAction_Edit_Copy' />"
+    "        <menuitem action='BakeryAction_Edit_Paste' />"
+    "        <menuitem action='BakeryAction_Edit_Clear' />"
+    "      </menu>"
+    "    </placeholder>"
+    "  </menubar>"
+    "</ui>";
+
+  //Add menu:
+  add_ui_from_string(ui_description);
+}
+
+void AppWindow_WithDoc_Gtk::add(Gtk::Widget& child)
+{
+  m_VBox_PlaceHolder.pack_start(child);
+}
+
+bool AppWindow_WithDoc_Gtk::on_delete_event(GdkEventAny* /* event */)
+{
+  //Clicking on the [x] in the title bar should be like choosing File|Close
+  on_menu_file_close();
+
+  return true; // true = don't hide, don't destroy
+}
+
+
+void AppWindow_WithDoc_Gtk::update_window_title()
+{
+  //Set application's main window title:
+
+  Document* pDoc = get_document();
+  if(!pDoc)
+    return;
+
+  Glib::ustring strTitle = m_strAppName;
+  strTitle += " - " + pDoc->get_name();
+
+  //Indicate unsaved changes:
+  if(pDoc->get_modified())
+    strTitle += " *";
+
+  //Indicate read-only files:
+  if(pDoc->get_read_only())
+    strTitle += _(" (read-only)");
+
+  set_title(strTitle);
+}
+
+void AppWindow_WithDoc_Gtk::ui_warning(const Glib::ustring& text, const Glib::ustring& secondary_text)
+{
+  Gtk::Window* pWindow = this;
+
+  Gtk::MessageDialog dialog(AppWindow_WithDoc_Gtk::util_bold_message(text), true /* use markup */, Gtk::MESSAGE_WARNING);
+  dialog.set_secondary_text(secondary_text);
+
+  dialog.set_title(""); //The HIG says that alert dialogs should not have titles. The default comes from the message type.
+
+  if(pWindow)
+    dialog.set_transient_for(*pWindow);
+
+  dialog.run();
+}
+
+
+Glib::ustring AppWindow_WithDoc_Gtk::util_bold_message(const Glib::ustring& message)
+{
+  return "<b>" + message + "</b>";
+}
+
+Glib::ustring AppWindow_WithDoc_Gtk::ui_file_select_open(const Glib::ustring& starting_folder_uri)
+{
+  Gtk::Window* pWindow = this;
+
+  Gtk::FileChooserDialog fileChooser_Open(_("Open Document"), Gtk::FILE_CHOOSER_ACTION_OPEN);
+  fileChooser_Open.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  fileChooser_Open.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
+  fileChooser_Open.set_default_response(Gtk::RESPONSE_OK);
+
+  if(pWindow)
+    fileChooser_Open.set_transient_for(*pWindow);
+
+  if(!starting_folder_uri.empty())
+    fileChooser_Open.set_current_folder_uri(starting_folder_uri);
+
+  const int response_id = fileChooser_Open.run();
+  fileChooser_Open.hide();
+  if(response_id != Gtk::RESPONSE_CANCEL)
+  {
+    return fileChooser_Open.get_uri();
+  }
+  else
+    return Glib::ustring();
+}
+
+static bool uri_is_writable(const Glib::RefPtr<const Gio::File>& uri)
+{
+  if(!uri)
+    return false;
+
+  Glib::RefPtr<const Gio::FileInfo> file_info;
+
+  try
+  {
+    file_info = uri->query_info(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+  }
+  catch(const Glib::Error& /* ex */)
+  {
+    return false;
+  }
+
+  if(file_info)
+  {
+    return file_info->get_attribute_boolean(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
+  }
+  else
+    return true; //Not every URI protocol supports access rights, so assume that it's writable and complain later.
+}
+
+Glib::ustring AppWindow_WithDoc_Gtk::ui_file_select_save(const Glib::ustring& old_file_uri)
+{
+ Gtk::Window* pWindow = this;
+
+  Gtk::FileChooserDialog fileChooser_Save(_("Save Document"), Gtk::FILE_CHOOSER_ACTION_SAVE);
+  fileChooser_Save.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+  fileChooser_Save.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+  fileChooser_Save.set_default_response(Gtk::RESPONSE_OK);
+
+  if(pWindow)
+    fileChooser_Save.set_transient_for(*pWindow);
+
+  fileChooser_Save.set_do_overwrite_confirmation(); //Ask the user if the file already exists.
+
+  //Make the save dialog show the existing filename, if any:
+  if(!old_file_uri.empty())
+  {
+    //Just start with the parent folder,
+    //instead of the whole name, to avoid overwriting:
+    Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(old_file_uri);
+    if(gio_file)
+    {
+      Glib::RefPtr<Gio::File> parent = gio_file->get_parent();
+      if(parent)
+      {
+        const Glib::ustring uri_parent = parent->get_uri();
+        fileChooser_Save.set_uri(uri_parent);
+      }
+    }
+  }
+
+  bool try_again = true;
+  while(try_again)
+  {
+    try_again = false;
+
+    const int response_id = fileChooser_Save.run();
+    fileChooser_Save.hide();
+    if(response_id != Gtk::RESPONSE_CANCEL)
+    {
+      const Glib::ustring uri = fileChooser_Save.get_uri();
+
+      Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(uri);
+
+      //If the file exists (the FileChooser offers a "replace?" dialog, so this is possible.):
+      if(AppWindow_WithDoc::file_exists(uri))
+      {
+        //Check whether we have rights to the file to change it:
+        //Really, GtkFileChooser should do this for us.
+        if(!uri_is_writable(gio_file))
+        {
+           //Warn the user:
+           ui_warning(_("Read-only File."), _("You may not overwrite the existing file, because you do not have sufficient access rights."));
+           try_again = true; //Try again.
+           continue;
+        }
+      }
+
+      //Check whether we have rights to the directory, to create a new file in it:
+      //Really, GtkFileChooser should do this for us.
+      Glib::RefPtr<const Gio::File> gio_file_parent = gio_file->get_parent();
+      if(gio_file_parent)
+      {
+        if(!uri_is_writable(gio_file_parent))
+        {
+          //Warn the user:
+           ui_warning(_("Read-only Directory."), _("You may not create a file in this directory, because you do not have sufficient access rights."));
+           try_again = true; //Try again.
+           continue;
+        }
+      }
+
+      if(!try_again)
+        return uri;
+    }
+    else
+      return Glib::ustring(); //The user cancelled.
+  }
+
+  return Glib::ustring();
+}
+
+void AppWindow_WithDoc_Gtk::ui_show_modification_status()
+{
+  const bool modified = m_pDocument->get_modified();
+
+  //Enable Save and SaveAs menu items:
+  if(m_action_save)
+    m_action_save->set_sensitive(modified);
+
+  if(m_action_saveas)
+    m_action_saveas->set_sensitive(modified);
+}
+
+AppWindow_WithDoc_Gtk::enumSaveChanges AppWindow_WithDoc_Gtk::ui_offer_to_save_changes()
+{
+  AppWindow_WithDoc::enumSaveChanges result = AppWindow_WithDoc::SAVECHANGES_Cancel;
+
+  if(!m_pDocument)
+    return result;
+
+  GlomBakery::Dialog_OfferSave* pDialogQuestion 
+    = new GlomBakery::Dialog_OfferSave( m_pDocument->get_file_uri() );
+
+  Gtk::Window* pWindow = this;
+  if(pWindow)
+    pDialogQuestion->set_transient_for(*pWindow);
+
+  GlomBakery::Dialog_OfferSave::enumButtons buttonClicked = (GlomBakery::Dialog_OfferSave::enumButtons)pDialogQuestion->run();
+  delete pDialogQuestion;
+  pDialogQuestion = 0;
+
+  if(buttonClicked == GlomBakery::Dialog_OfferSave::BUTTON_Save)
+     result = AppWindow_WithDoc::SAVECHANGES_Save;
+  else if(buttonClicked == GlomBakery::Dialog_OfferSave::BUTTON_Discard)
+     result = AppWindow_WithDoc::SAVECHANGES_Discard;
+  else
+     result = AppWindow_WithDoc::SAVECHANGES_Cancel;
+
+  return result;
+}
+
+void AppWindow_WithDoc_Gtk::document_history_add(const Glib::ustring& file_uri)
+{
+  if(file_uri.empty())
+    return;
+
+  //This can sometimes be called for a file that does not yet exist on disk.
+  //Avoid warning in RecentManager if that is the case.
+  //For instance, Glom does this when the user chooses a new filename, 
+  //but before Glom has enough information to save a useful file.
+  if(!file_exists(file_uri))
+    return;
+
+  {
+    //TODO: Wrap gnome_vfs_escape_path_string() in gnome-vfsmm.
+    //Glib::ustring filename_e = Gnome::Vfs::escape_path_string(file_uri);
+    const Glib::ustring uri = file_uri; // "file://" + filename_e;
+
+    Gtk::RecentManager::get_default()->add_item(uri);
+  }
+}
+
+void AppWindow_WithDoc_Gtk::document_history_remove(const Glib::ustring& file_uri)
+{
+  if(!file_uri.empty())
+  {
+    //Glib::ustring filename_e = Gnome::Vfs::escape_path_string(file_uri.c_str());
+    const Glib::ustring uri = file_uri; //"file://" + filename_e;
+
+    Gtk::RecentManager::get_default()->remove_item(uri);
+  }
+}
+
+void AppWindow_WithDoc_Gtk::on_menu_edit_copy_activate()
+{
+  Gtk::Widget* widget = get_focus();
+  Gtk::Editable* editable = dynamic_cast<Gtk::Editable*>(widget);
+
+  if(editable)
+  {
+    editable->copy_clipboard();
+    return;
+  }
+
+  //GtkTextView does not implement GtkTextView.
+  //See GTK+ bug: https://bugzilla.gnome.org/show_bug.cgi?id=667008
+  Gtk::TextView* textview = dynamic_cast<Gtk::TextView*>(widget);
+  if(textview)
+  {
+    Glib::RefPtr<Gtk::TextBuffer> buffer = textview->get_buffer();
+    if(buffer)
+    {
+      Glib::RefPtr<Gtk::Clipboard> clipboard = 
+        Gtk::Clipboard::get_for_display(get_display());
+      buffer->copy_clipboard(clipboard);
+    }
+  }
+}
+
+void AppWindow_WithDoc_Gtk::on_menu_edit_cut_activate()
+{
+  Gtk::Widget* widget = get_focus();
+  Gtk::Editable* editable = dynamic_cast<Gtk::Editable*>(widget);
+
+  if(editable)
+  {
+    editable->cut_clipboard();
+    return;
+  }
+
+  //GtkTextView does not implement GtkTextView.
+  //See GTK+ bug: https://bugzilla.gnome.org/show_bug.cgi?id=667008
+  Gtk::TextView* textview = dynamic_cast<Gtk::TextView*>(widget);
+  if(textview)
+  {
+    Glib::RefPtr<Gtk::TextBuffer> buffer = textview->get_buffer();
+    if(buffer)
+    {
+      Glib::RefPtr<Gtk::Clipboard> clipboard = 
+        Gtk::Clipboard::get_for_display(get_display());
+      buffer->cut_clipboard(clipboard, textview->get_editable());
+    }
+  }
+}
+
+void AppWindow_WithDoc_Gtk::on_menu_edit_paste_activate()
+{
+  Gtk::Widget* widget = get_focus();
+  Gtk::Editable* editable = dynamic_cast<Gtk::Editable*>(widget);
+
+  if(editable)
+  {
+    editable->paste_clipboard();
+    return;
+  }
+
+  //GtkTextView does not implement GtkTextView.
+  //See GTK+ bug: https://bugzilla.gnome.org/show_bug.cgi?id=667008
+  Gtk::TextView* textview = dynamic_cast<Gtk::TextView*>(widget);
+  if(textview)
+  {
+    Glib::RefPtr<Gtk::TextBuffer> buffer = textview->get_buffer();
+    if(buffer)
+    {
+      Glib::RefPtr<Gtk::Clipboard> clipboard = 
+        Gtk::Clipboard::get_for_display(get_display());
+      buffer->paste_clipboard(clipboard);
+    }
+  }
+}
+
+void AppWindow_WithDoc_Gtk::on_recent_files_activate(Gtk::RecentChooser& chooser)
+{
+  const Glib::ustring uri = chooser.get_current_uri();
+  const bool bTest = open_document(uri);
+  if(!bTest)
+    document_history_remove(uri);
+}
+
+} //namespace
diff --git a/glom/bakery/appwindow_withdoc_gtk.h b/glom/bakery/appwindow_withdoc_gtk.h
new file mode 100644
index 0000000..09f7ba5
--- /dev/null
+++ b/glom/bakery/appwindow_withdoc_gtk.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2000 Murray Cumming
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GLOM_BAKERY_APP_WITHDOC_GTK_H
+#define GLOM_BAKERY_APP_WITHDOC_GTK_H
+
+#include <glom/bakery/appwindow_withdoc.h>
+#include <glom/bakery/appwindow.h>
+
+#include <gtkmm/dialog.h>
+#include <gtkmm/menubar.h>
+#include <gtkmm/menu.h>
+#include <gtkmm/toolbar.h>
+#include <gtkmm/handlebox.h>
+#include <gtkmm/uimanager.h>
+#include <gtkmm/builder.h>
+
+
+#include <libglom/document/bakery/document.h>
+#include <gtkmm/toolbutton.h>
+#include <gtkmm/recentmanager.h>
+#include <gtkmm/recentchooser.h>
+
+namespace GlomBakery
+{
+
+/** This class implements GlomBakery::AppWindow_WithDoc using gtkmm.
+ *
+ * Your application's installation should register your document's MIME-type in GNOME's (freedesktop's) MIME-type system,
+ * and register your application as capable of opening documents of that MIME-type.
+ *
+ *
+ */
+class AppWindow_WithDoc_Gtk
+  : public AppWindow_WithDoc,
+    public Gtk::Window //inherit virtually to share sigc::trackable.
+{
+public:
+  typedef Gtk::Window ParentWindow;
+
+  ///Don't forget to call init() too.
+  AppWindow_WithDoc_Gtk(const Glib::ustring& appname);
+
+  /// This constructor can be used to implement derived classes for use with Gtk::Builder::get_derived_widget().
+  AppWindow_WithDoc_Gtk(BaseObjectType* cobject, const Glib::ustring& appname);
+
+  virtual ~AppWindow_WithDoc_Gtk();
+
+  virtual void init(); //Unique final overrider.
+
+  /// Overidden to add a widget in the middle, under the menu, instead of replacing the whole contents.
+  virtual void add(Gtk::Widget& child);
+
+  /// For instance, to create bold primary text for a dialog box, without marking the markup for translation.
+  static Glib::ustring util_bold_message(const Glib::ustring& message);
+
+protected:
+  virtual void init_layout(); //Arranges the menu, toolbar, etc.
+  void init_menus_file_recentfiles(const Glib::ustring& path); // call this in init_menus_file()
+  virtual void init_ui_manager(); //Override this to add more UI placeholders
+  virtual void init_menus(); //Override this to add more or different menus.
+  virtual void init_menus_file(); //Call this from init_menus() to add the standard file menu.
+  virtual void init_menus_edit(); //Call this from init_menus() to add the standard edit menu
+  virtual void init_toolbars();
+
+  void add_ui_from_string(const Glib::ustring& ui_description); //Convenience function
+
+  virtual void on_hide(); //override.
+
+  //Overrides from AppWindow_WithDoc:
+  virtual void document_history_add(const Glib::ustring& file_uri); //overridden.
+  virtual void document_history_remove(const Glib::ustring& file_uri); //overridden.
+  virtual void update_window_title();
+  virtual void ui_warning(const Glib::ustring& text, const Glib::ustring& secondary_text);
+  virtual Glib::ustring ui_file_select_open(const Glib::ustring& starting_folder_uri = Glib::ustring());
+  virtual Glib::ustring ui_file_select_save(const Glib::ustring& old_file_uri);
+  virtual void ui_show_modification_status();
+  virtual enumSaveChanges ui_offer_to_save_changes();
+
+
+  //Signal handlers:
+
+  //Menus:
+
+
+  virtual void ui_hide();
+  virtual void ui_bring_to_front();
+
+  virtual bool on_delete_event(GdkEventAny* event); //override
+
+  void on_menu_edit_copy_activate();
+  void on_menu_edit_cut_activate();
+  void on_menu_edit_paste_activate();
+  void on_recent_files_activate(Gtk::RecentChooser& recent_chooser);
+
+  //UIManager and Actions
+  Glib::RefPtr<Gtk::UIManager> m_refUIManager;
+  Glib::RefPtr<Gtk::ActionGroup> m_refFileActionGroup;
+  Glib::RefPtr<Gtk::ActionGroup> m_refEditActionGroup;
+
+  //Member widgets:
+  Gtk::Box* m_pVBox;
+  Gtk::Box m_VBox_PlaceHolder;
+
+  //Menu stuff:
+  Glib::RefPtr<Gtk::Action> m_action_save, m_action_saveas;
+};
+
+} //namespace
+
+#endif //BAKERY_APP_WITHDOC_GTK_H



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