2
* Copyright 2000 Murray Cumming
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the Free
16
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
#include <glom/bakery/app_withdoc_gtk.h>
20
#include <glom/bakery/dialog_offersave.h>
21
//#include <libgnomevfsmm/utils.h> //For escape_path_string()
22
//#include <libgnomevfsmm/mime-handlers.h> //For type_is_known().
23
#include <gtkmm/toolbutton.h>
24
#include <gtkmm/stock.h>
25
#include <gtkmm/recentchoosermenu.h>
26
#include <gtkmm/messagedialog.h>
27
#include <gtkmm/filechooserdialog.h>
28
#include <gtkmm/aboutdialog.h>
34
#ifdef GLOM_ENABLE_MAEMO
35
#include <hildon-fmmm/file-chooser-dialog.h>
36
#include <hildon/hildon-window.h>
37
#endif // GLOM_ENABLE_MAEMO
39
#include <glibmm/i18n-lib.h>
41
//#include <gtk/gtkfilesel.h>
47
//Initialize static member data:
49
App_WithDoc_Gtk::App_WithDoc_Gtk(const Glib::ustring& appname)
50
: App_WithDoc(appname),
54
init_app_name(appname);
56
#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
57
signal_hide().connect(sigc::mem_fun(*this, &App_WithDoc_Gtk::on_hide));
58
signal_delete_event().connect(sigc::mem_fun(*this, &App_WithDoc_Gtk::on_delete_event));
62
/// This constructor can be used with Gtk::Builder::get_derived_widget().
63
App_WithDoc_Gtk::App_WithDoc_Gtk(BaseObjectType* cobject, const Glib::ustring& appname)
64
: App_WithDoc(appname),
65
ParentWindow(cobject),
69
init_app_name(appname);
71
#ifdef GLOM_ENABLE_MAEMO
72
//The .glade file needs to specify HildonWindow or features such as HildonAppMenu won't work.
73
g_assert(HILDON_IS_WINDOW(gobj()));
74
#endif //GLOM_ENABLE_MAEMO
76
#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
77
signal_hide().connect(sigc::mem_fun(*this, &App_WithDoc_Gtk::on_hide));
78
signal_delete_event().connect(sigc::mem_fun(*this, &App_WithDoc_Gtk::on_delete_event));
83
App_WithDoc_Gtk::~App_WithDoc_Gtk()
99
void App_WithDoc_Gtk::on_hide()
101
ui_signal_hide().emit();
104
void App_WithDoc_Gtk::ui_hide()
109
void App_WithDoc_Gtk::ui_bring_to_front()
111
get_window()->raise();
115
void App_WithDoc_Gtk::init_layout()
117
set_resizable(); //resizable
118
set_default_size(640, 400); //A sensible default.
120
//This might have been instantiated by Gtk::Builder::get_widget() instead.
121
//If not, then we create a default one and add it to the window.
124
m_pVBox = new Gtk::VBox();
125
Gtk::Window::add(*m_pVBox);
128
//Add menu bar at the top:
129
//These were defined in init_uimanager().
130
#ifdef GLOM_ENABLE_MAEMO
131
//TODO: Use Hildon::AppMenu
132
//Gtk::Menu* pMenu = static_cast<Gtk::Menu*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
135
Gtk::MenuBar* pMenuBar = static_cast<Gtk::MenuBar*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
136
m_pVBox->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
139
add_accel_group(m_refUIManager->get_accel_group());
142
//Add placeholder, to be used by add():
143
m_pVBox->pack_start(m_VBox_PlaceHolder);
144
m_VBox_PlaceHolder.show();
146
m_pVBox->show(); //Show it last so the child widgets all show at once.
149
void App_WithDoc_Gtk::add_ui_from_string(const Glib::ustring& ui_description)
151
#ifdef GLIBMM_EXCEPTIONS_ENABLED
154
m_refUIManager->add_ui_from_string(ui_description);
156
catch(const Glib::Error& ex)
158
std::cerr << "building menus failed: " << ex.what();
161
std::auto_ptr<Glib::Error> error;
162
m_refUIManager->add_ui_from_string(ui_description, error);
163
if(error.get() != NULL) std::cerr << "building menus failed: " << error->what();
167
void App_WithDoc_Gtk::init()
169
App_WithDoc::init(); //Create document and ask to show it in the UI.
170
init_layout(); // start setting up layout after we've gotten all widgets from UIManager
175
void App_WithDoc_Gtk::init_ui_manager()
179
m_refUIManager = UIManager::create();
181
//This is just a skeleton structure.
182
//The placeholders allow us to merge the menus and toolbars in later,
183
//by adding a us string with one of the placeholders, but with menu items underneath it.
184
static const Glib::ustring ui_description =
186
#ifdef GLOM_ENABLE_MAEMO
187
" <popup name='Bakery_MainMenu'>"
189
" <menubar name='Bakery_MainMenu'>"
191
" <placeholder name='Bakery_MenuPH_File' />"
192
" <placeholder name='Bakery_MenuPH_Edit' />"
193
" <placeholder name='Bakery_MenuPH_Others' />" //Note that extra menus should be inserted before the Help menu, which should always be at the end.
194
" <placeholder name='Bakery_MenuPH_Help' />"
195
#ifdef GLOM_ENABLE_MAEMO
200
" <toolbar name='Bakery_ToolBar'>"
201
" <placeholder name='Bakery_ToolBarItemsPH' />"
205
add_ui_from_string(ui_description);
208
void App_WithDoc_Gtk::init_toolbars()
210
//Build part of the menu structure, to be merged in by using the "PH" placeholders:
211
static const Glib::ustring ui_description =
213
" <toolbar name='Bakery_ToolBar'>"
214
" <placeholder name='Bakery_ToolBarItemsPH'>"
215
" <toolitem action='BakeryAction_File_New' />"
216
" <toolitem action='BakeryAction_File_Open' />"
217
" <toolitem action='BakeryAction_File_Save' />"
222
add_ui_from_string(ui_description);
225
void App_WithDoc_Gtk::init_menus()
227
//Override this to add more menus
233
void App_WithDoc_Gtk::init_menus_file_recentfiles(const Glib::ustring& path)
235
if(!m_mime_types.empty()) //"Recent-files" is useless unless it knows what documents (which MIME-types) to show.
237
//Add recent-files submenu:
238
Gtk::MenuItem* pMenuItem = dynamic_cast<Gtk::MenuItem*>(m_refUIManager->get_widget(path));
241
Gtk::RecentFilter filter;
243
//Add the mime-types, so that it only shows those documents:
244
for(type_list_strings::iterator iter = m_mime_types.begin(); iter != m_mime_types.end(); ++iter)
246
const Glib::ustring mime_type = *iter;
248
//TODO: Find a gio equivalent for gnome_vfs_mime_type_is_known(). murrayc.
249
filter.add_mime_type(mime_type);
252
Gtk::RecentChooserMenu* menu = Gtk::manage(new Gtk::RecentChooserMenu);
253
menu->set_filter(filter);
254
menu->set_limit(10 /* this should be a global GNOME preference, I think. */);
255
menu->set_show_numbers(false);
256
menu->set_sort_type(Gtk::RECENT_SORT_MRU);
257
menu->signal_item_activated().connect(sigc::bind(sigc::mem_fun(*this, static_cast<void(App_WithDoc_Gtk::*)(Gtk::RecentChooser&)>(&App_WithDoc_Gtk::on_recent_files_activate)), sigc::ref(*menu)));
259
pMenuItem->set_submenu(*menu);
263
std::cout << "debug: recent files menu not found" << std::endl;
268
//std::cout << "debug: GlomBakery::App_WithDoc_Gtk::init_menus_file_recentfiles(): No recent files sub-menu added, because no MIME types are specified." << std::endl;
272
void App_WithDoc_Gtk::init_menus_file()
277
m_refFileActionGroup = Gtk::ActionGroup::create("BakeryFileActions");
279
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File", _("_File")));
280
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File_RecentFiles", _("_Recent Files")));
283
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_New", Gtk::Stock::NEW),
284
sigc::mem_fun((App&)*this, &App::on_menu_file_new));
285
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Open", Gtk::Stock::OPEN),
286
sigc::mem_fun((App_WithDoc&)*this, &App_WithDoc::on_menu_file_open));
288
//Remember thes ones for later, so we can disable Save menu and toolbar items:
289
m_action_save = Gtk::Action::create("BakeryAction_File_Save", Gtk::Stock::SAVE);
290
m_refFileActionGroup->add(m_action_save,
291
sigc::mem_fun((App_WithDoc&)*this, &App_WithDoc::on_menu_file_save));
293
m_action_saveas = Gtk::Action::create("BakeryAction_File_SaveAs", Gtk::Stock::SAVE_AS);
294
m_refFileActionGroup->add(m_action_saveas,
295
sigc::mem_fun((App_WithDoc&)*this, &App_WithDoc::on_menu_file_saveas));
297
m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_File_Close", Gtk::Stock::CLOSE),
298
sigc::mem_fun((App_WithDoc&)*this, &App_WithDoc::on_menu_file_close));
300
m_refUIManager->insert_action_group(m_refFileActionGroup);
302
//Build part of the menu structure, to be merged in by using the "PH" placeholders:
303
static const Glib::ustring ui_description =
305
#ifdef GLOM_ENABLE_MAEMO
306
" <popup name='Bakery_MainMenu'>"
308
" <menubar name='Bakery_MainMenu'>"
310
" <placeholder name='Bakery_MenuPH_File'>"
311
" <menu action='BakeryAction_Menu_File'>"
312
" <menuitem action='BakeryAction_File_New' />"
313
" <menuitem action='BakeryAction_File_Open' />"
314
" <menu action='BakeryAction_Menu_File_RecentFiles'>"
316
" <menuitem action='BakeryAction_File_Save' />"
317
" <menuitem action='BakeryAction_File_SaveAs' />"
319
" <menuitem action='BakeryAction_File_Close' />"
322
#ifdef GLOM_ENABLE_MAEMO
330
add_ui_from_string(ui_description);
332
//Add recent-files submenu:
333
init_menus_file_recentfiles("/Bakery_MainMenu/Bakery_MenuPH_File/BakeryAction_Menu_File/BakeryAction_Menu_File_RecentFiles");
336
void App_WithDoc_Gtk::init_menus_edit()
342
m_refEditActionGroup = ActionGroup::create("BakeryEditActions");
343
m_refEditActionGroup->add(Action::create("BakeryAction_Menu_Edit", _("_Edit")));
345
m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Cut", Gtk::Stock::CUT));
346
m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Copy", Gtk::Stock::COPY));
347
m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Paste", Gtk::Stock::PASTE));
348
m_refEditActionGroup->add(Action::create("BakeryAction_Edit_Clear", Gtk::Stock::CLEAR));
350
m_refUIManager->insert_action_group(m_refEditActionGroup);
352
//Build part of the menu structure, to be merged in by using the "PH" placeholders:
353
static const Glib::ustring ui_description =
355
#ifdef GLOM_ENABLE_MAEMO
356
" <popup name='Bakery_MainMenu'>"
358
" <menubar name='Bakery_MainMenu'>"
360
" <placeholder name='Bakery_MenuPH_Edit'>"
361
" <menu action='BakeryAction_Menu_Edit'>"
362
" <menuitem action='BakeryAction_Edit_Cut' />"
363
" <menuitem action='BakeryAction_Edit_Copy' />"
364
" <menuitem action='BakeryAction_Edit_Paste' />"
365
" <menuitem action='BakeryAction_Edit_Clear' />"
368
#ifdef GLOM_ENABLE_MAEMO
376
add_ui_from_string(ui_description);
379
void App_WithDoc_Gtk::init_menus_help()
385
m_refHelpActionGroup = ActionGroup::create("BakeryHelpActions");
386
m_refHelpActionGroup->add(Action::create("BakeryAction_Menu_Help", _("_Help")));
389
m_refHelpActionGroup->add(Action::create("BakeryAction_Help_About",
390
_("_About"), _("About the application")),
391
sigc::mem_fun((GlomBakery::App&)*this, &App::on_menu_help_about));
393
m_refUIManager->insert_action_group(m_refHelpActionGroup);
395
//Build part of the menu structure, to be merged in by using the "PH" plaeholders:
396
static const Glib::ustring ui_description =
398
#ifdef GLOM_ENABLE_MAEMO
399
" <popup name='Bakery_MainMenu'>"
401
" <menubar name='Bakery_MainMenu'>"
403
" <placeholder name='Bakery_MenuPH_Help'>"
404
" <menu action='BakeryAction_Menu_Help'>"
405
" <menuitem action='BakeryAction_Help_About' />"
408
#ifdef GLOM_ENABLE_MAEMO
416
add_ui_from_string(ui_description);
420
void App_WithDoc_Gtk::on_menu_help_about()
422
if(m_pAbout && m_bAboutShown) // "About" box hasn't been closed, so just raise it
424
m_pAbout->set_transient_for(*this);
426
Glib::RefPtr<Gdk::Window> about_win = m_pAbout->get_window();
432
//Re-create About box:
439
Gtk::AboutDialog* pDerived = new Gtk::AboutDialog;
442
pDerived->set_name(m_strAppName);
443
pDerived->set_version(m_HelpInfo.m_strVersion);
444
pDerived->set_copyright(m_HelpInfo.m_strCopyright);
445
pDerived->set_authors(m_HelpInfo.m_vecAuthors);
446
pDerived->set_documenters(m_HelpInfo.m_vecDocumenters);
447
pDerived->set_translator_credits(m_HelpInfo.m_strTranslatorCredits);
449
m_pAbout->signal_hide().connect(sigc::mem_fun((App&)*this, &App::on_about_close));
450
m_bAboutShown = true;
451
static_cast<Gtk::Dialog*>(m_pAbout)->run(); //show() would be better. see below:
453
//m_pAbout->show(); //TODO: respond to the OK button.
457
void App_WithDoc_Gtk::on_about_close()
459
m_bAboutShown = false;
462
void App_WithDoc_Gtk::add(Gtk::Widget& child)
464
m_VBox_PlaceHolder.pack_start(child);
467
bool App_WithDoc_Gtk::on_delete_event(GdkEventAny* /* event */)
469
//Clicking on the [x] in the title bar should be like choosing File|Close
470
on_menu_file_close();
472
return true; // true = don't hide, don't destroy
476
void App_WithDoc_Gtk::update_window_title()
478
//Set application's main window title:
480
Document* pDoc = get_document();
484
Glib::ustring strTitle = m_strAppName;
485
strTitle += " - " + pDoc->get_name();
487
//Indicate unsaved changes:
488
if(pDoc->get_modified())
491
//Indicate read-only files:
492
if(pDoc->get_read_only())
493
strTitle += _(" (read-only)");
498
void App_WithDoc_Gtk::ui_warning(const Glib::ustring& text, const Glib::ustring& secondary_text)
500
Gtk::Window* pWindow = this;
502
#ifdef GLOM_ENABLE_MAEMO
503
Hildon::Note dialog(Hildon::NOTE_TYPE_INFORMATION, *pWindow, text);
505
Gtk::MessageDialog dialog(App_WithDoc_Gtk::util_bold_message(text), true /* use markup */, Gtk::MESSAGE_WARNING);
506
dialog.set_secondary_text(secondary_text);
508
dialog.set_title(""); //The HIG says that alert dialogs should not have titles. The default comes from the message type.
512
dialog.set_transient_for(*pWindow);
518
Glib::ustring App_WithDoc_Gtk::util_bold_message(const Glib::ustring& message)
520
return "<b>" + message + "</b>";
523
Glib::ustring App_WithDoc_Gtk::ui_file_select_open(const Glib::ustring& starting_folder_uri)
525
Gtk::Window* pWindow = this;
527
#ifdef GLOM_ENABLE_MAEMO
528
Hildon::FileChooserDialog fileChooser_Open(Gtk::FILE_CHOOSER_ACTION_OPEN);
530
Gtk::FileChooserDialog fileChooser_Open(_("Open Document"), Gtk::FILE_CHOOSER_ACTION_OPEN);
531
fileChooser_Open.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
532
fileChooser_Open.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);
533
fileChooser_Open.set_default_response(Gtk::RESPONSE_OK);
534
#endif // GLOM_ENABLE_MAEMO
537
fileChooser_Open.set_transient_for(*pWindow);
539
if(!starting_folder_uri.empty())
540
fileChooser_Open.set_current_folder_uri(starting_folder_uri);
542
const int response_id = fileChooser_Open.run();
543
fileChooser_Open.hide();
544
if(response_id != Gtk::RESPONSE_CANCEL)
546
return fileChooser_Open.get_uri();
549
return Glib::ustring();
552
static bool uri_is_writable(const Glib::RefPtr<const Gio::File>& uri)
557
Glib::RefPtr<const Gio::FileInfo> file_info;
559
#ifdef GLIBMM_EXCEPTIONS_ENABLED
562
file_info = uri->query_info(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
564
catch(const Glib::Error& /* ex */)
569
std::auto_ptr<Glib::Error> error;
570
file_info = uri->query_info(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, Gio::FILE_QUERY_INFO_NONE, error);
577
return file_info->get_attribute_boolean(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
580
return true; //Not every URI protocol supports access rights, so assume that it's writable and complain later.
583
Glib::ustring App_WithDoc_Gtk::ui_file_select_save(const Glib::ustring& old_file_uri)
585
Gtk::Window* pWindow = this;
587
#ifdef GLOM_ENABLE_MAEMO
588
Hildon::FileChooserDialog fileChooser_Save(Gtk::FILE_CHOOSER_ACTION_SAVE);
590
Gtk::FileChooserDialog fileChooser_Save(_("Save Document"), Gtk::FILE_CHOOSER_ACTION_SAVE);
591
fileChooser_Save.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
592
fileChooser_Save.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
593
fileChooser_Save.set_default_response(Gtk::RESPONSE_OK);
594
#endif // GLOM_ENABLE_MAEMO
597
fileChooser_Save.set_transient_for(*pWindow);
599
fileChooser_Save.set_do_overwrite_confirmation(); //Ask the user if the file already exists.
601
//Make the save dialog show the existing filename, if any:
602
if(!old_file_uri.empty())
604
//Just start with the parent folder,
605
//instead of the whole name, to avoid overwriting:
606
Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(old_file_uri);
609
Glib::RefPtr<Gio::File> parent = gio_file->get_parent();
612
const Glib::ustring uri_parent = parent->get_uri();
613
fileChooser_Save.set_uri(uri_parent);
618
bool try_again = true;
623
const int response_id = fileChooser_Save.run();
624
fileChooser_Save.hide();
625
if(response_id != Gtk::RESPONSE_CANCEL)
627
const Glib::ustring uri = fileChooser_Save.get_uri();
629
Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(uri);
631
//If the file exists (the FileChooser offers a "replace?" dialog, so this is possible.):
632
if(App_WithDoc::file_exists(uri))
634
//Check whether we have rights to the file to change it:
635
//Really, GtkFileChooser should do this for us.
636
if(!uri_is_writable(gio_file))
639
ui_warning(_("Read-only File."), _("You may not overwrite the existing file, because you do not have sufficient access rights."));
640
try_again = true; //Try again.
645
//Check whether we have rights to the directory, to create a new file in it:
646
//Really, GtkFileChooser should do this for us.
647
Glib::RefPtr<const Gio::File> gio_file_parent = gio_file->get_parent();
650
if(!uri_is_writable(gio_file_parent))
653
ui_warning(_("Read-only Directory."), _("You may not create a file in this directory, because you do not have sufficient access rights."));
654
try_again = true; //Try again.
663
return Glib::ustring(); //The user cancelled.
666
return Glib::ustring();
669
void App_WithDoc_Gtk::ui_show_modification_status()
671
bool modified = m_pDocument->get_modified();
673
//Enable Save and SaveAs menu items:
675
g_object_set(G_OBJECT(m_action_save->gobj()), "sensitive", modified, NULL); // TODO: Use a set_sensitive(modified)?
678
g_object_set(G_OBJECT(m_action_saveas->gobj()), "sensitive", modified, NULL); // TODO: Use a set_sensitive(modified)?
682
App_WithDoc_Gtk::enumSaveChanges App_WithDoc_Gtk::ui_offer_to_save_changes()
684
App_WithDoc::enumSaveChanges result = App_WithDoc::SAVECHANGES_Cancel;
689
GlomBakery::Dialog_OfferSave* pDialogQuestion
690
= new GlomBakery::Dialog_OfferSave( m_pDocument->get_file_uri() );
692
Gtk::Window* pWindow = this;
694
pDialogQuestion->set_transient_for(*pWindow);
696
GlomBakery::Dialog_OfferSave::enumButtons buttonClicked = (GlomBakery::Dialog_OfferSave::enumButtons)pDialogQuestion->run();
697
delete pDialogQuestion;
700
if(buttonClicked == GlomBakery::Dialog_OfferSave::BUTTON_Save)
701
result = App_WithDoc::SAVECHANGES_Save;
702
else if(buttonClicked == GlomBakery::Dialog_OfferSave::BUTTON_Discard)
703
result = App_WithDoc::SAVECHANGES_Discard;
705
result = App_WithDoc::SAVECHANGES_Cancel;
710
void App_WithDoc_Gtk::document_history_add(const Glib::ustring& file_uri)
715
//This can sometimes be called for a file that does not yet exist on disk.
716
//Avoid warning in RecentManager if that is the case.
717
//For instance, Glom does this when the user chooses a new filename,
718
//but before Glom has enough information to save a useful file.
719
if(!file_exists(file_uri))
723
//TODO: Wrap gnome_vfs_escape_path_string() in gnome-vfsmm.
724
//Glib::ustring filename_e = Gnome::Vfs::escape_path_string(file_uri);
725
const Glib::ustring uri = file_uri; // "file://" + filename_e;
727
#ifdef GLIBMM_EXCEPTIONS_ENABLED
728
Gtk::RecentManager::get_default()->add_item(uri);
730
std::auto_ptr<Glib::Error> error;
731
Gtk::RecentManager::get_default()->add_item(uri, error);
737
void App_WithDoc_Gtk::document_history_remove(const Glib::ustring& file_uri)
739
if(!file_uri.empty())
741
//Glib::ustring filename_e = Gnome::Vfs::escape_path_string(file_uri.c_str());
742
const Glib::ustring uri = file_uri; //"file://" + filename_e;
744
#ifdef GLIBMM_EXCEPTIONS_ENABLED
745
Gtk::RecentManager::get_default()->remove_item(uri);
747
std::auto_ptr<Glib::Error> error;
748
Gtk::RecentManager::get_default()->remove_item(uri, error);
754
void App_WithDoc_Gtk::on_recent_files_activate(Gtk::RecentChooser& chooser)
756
const Glib::ustring uri = chooser.get_current_uri();
757
const bool bTest = open_document(uri);
759
document_history_remove(uri);