~ubuntu-branches/ubuntu/precise/glom/precise-updates

« back to all changes in this revision

Viewing changes to glom/bakery/app_withdoc_gtk.cc

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2009-10-09 16:50:36 UTC
  • mfrom: (1.1.42 upstream)
  • Revision ID: james.westby@ubuntu.com-20091009165036-orinvwmohk838xxl
Tags: 1.12.2-0ubuntu1
* New upstream version:
  - FFE LP: #391664
* debian/control:
  - Bump python-gnome2-extras-dev build-dep to >= 2.25.3.
  - Bump libgdamm3.0-dev build-dep to libgdamm4.0-dev >= 3.99.14.
  - Change libgda3-dev build-dep to libgda-4.0-dev.
  - Change libgda3-postgres dependency to libgda-4.0-postgres.
  - Bump libgtkmm-2.4-dev build-dep to >= 2.14.
  - Add build-dep on libgconfmm-2.6-dev.
  - Bump libgoocanvasmm-dev build-dep to >= 0.14.0.
  - Remove build-dep on libbakery-2.6-dev.
  - Bump postgresql-8.3 dependency to postgresql-8.4.
  - Change scrollkeeper build-dep to rarian-compat.
  - Rename libglom{0,-dev} -> libglom-1.12-{0,dev}. Upstream include
    APIVER in the library name now.
* debian/rules:
  - Update --with-postgres-utils configure flag to point to the new
    path.
  - Drop deprecated --disable-scrollkeeper configure flag.
  - Update DEB_SHLIBDEPS_INCLUDE with new libglom-1.12-0 package name.
  - Don't include /usr/share/cdbs/1/rules/simple-patchsys.mk - there
    are currently no patches.
* debian/libglom-1.12-0.install:
  - Updated for new version.
* debian/libglom-1.12-dev.install:
  - Install pc and header files.
* debian/glom-doc.install:
  - Updated for new version.
* debian/glom.install:
  - Updated for new version.
* Fix debian/watch.
* Dropped obsolete 10-distro-install-postgres-change.patch.
* Built against latest libgoocanvasmm (LP: #428445).
* Also closes LP: #230007, LP: #393229, LP: #393231, LP: #394507,
  LP: #394887, LP: #394894, LP: #397409, LP: #381563.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2000 Murray Cumming
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
17
 */
 
18
 
 
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>
 
29
#include <giomm.h>
 
30
#include <algorithm>
 
31
 
 
32
#include <config.h>
 
33
 
 
34
#ifdef GLOM_ENABLE_MAEMO
 
35
#include <hildon-fmmm/file-chooser-dialog.h>
 
36
#include <hildon/hildon-window.h>
 
37
#endif // GLOM_ENABLE_MAEMO
 
38
 
 
39
#include <glibmm/i18n-lib.h>
 
40
 
 
41
//#include <gtk/gtkfilesel.h>
 
42
 
 
43
namespace GlomBakery
 
44
{
 
45
 
 
46
 
 
47
//Initialize static member data:
 
48
 
 
49
App_WithDoc_Gtk::App_WithDoc_Gtk(const Glib::ustring& appname)
 
50
: App_WithDoc(appname),
 
51
  m_pVBox(0),
 
52
  m_pAbout(0)
 
53
{
 
54
  init_app_name(appname);
 
55
 
 
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));
 
59
#endif
 
60
}
 
61
 
 
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), 
 
66
  m_pVBox(0),
 
67
  m_pAbout(0)
 
68
{
 
69
  init_app_name(appname);
 
70
 
 
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
 
75
 
 
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));
 
79
#endif
 
80
}
 
81
 
 
82
  
 
83
App_WithDoc_Gtk::~App_WithDoc_Gtk()
 
84
{
 
85
  if(m_pVBox)
 
86
  {
 
87
    delete m_pVBox;
 
88
    m_pVBox = 0;
 
89
  }
 
90
  
 
91
  if(m_pAbout)
 
92
  {
 
93
    delete m_pAbout;
 
94
    m_pAbout = 0;
 
95
  }
 
96
}
 
97
 
 
98
 
 
99
void App_WithDoc_Gtk::on_hide()
 
100
{
 
101
  ui_signal_hide().emit();
 
102
}
 
103
 
 
104
void App_WithDoc_Gtk::ui_hide()
 
105
{
 
106
  hide();  
 
107
}
 
108
 
 
109
void App_WithDoc_Gtk::ui_bring_to_front()
 
110
{
 
111
  get_window()->raise();
 
112
}
 
113
 
 
114
 
 
115
void App_WithDoc_Gtk::init_layout()
 
116
{
 
117
  set_resizable(); //resizable
 
118
  set_default_size(640, 400); //A sensible default.
 
119
 
 
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.
 
122
  if(!m_pVBox)
 
123
  {
 
124
    m_pVBox = new Gtk::VBox();
 
125
    Gtk::Window::add(*m_pVBox);
 
126
  }
 
127
 
 
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"));
 
133
  //set_menu(*pMenu);
 
134
#else
 
135
  Gtk::MenuBar* pMenuBar = static_cast<Gtk::MenuBar*>(m_refUIManager->get_widget("/Bakery_MainMenu"));
 
136
  m_pVBox->pack_start(*pMenuBar, Gtk::PACK_SHRINK);
 
137
#endif
 
138
 
 
139
  add_accel_group(m_refUIManager->get_accel_group());
 
140
 
 
141
 
 
142
  //Add placeholder, to be used by add():
 
143
  m_pVBox->pack_start(m_VBox_PlaceHolder);
 
144
  m_VBox_PlaceHolder.show();
 
145
  
 
146
  m_pVBox->show(); //Show it last so the child widgets all show at once.
 
147
}
 
148
 
 
149
void App_WithDoc_Gtk::add_ui_from_string(const Glib::ustring& ui_description)
 
150
{
 
151
#ifdef GLIBMM_EXCEPTIONS_ENABLED
 
152
  try
 
153
  {
 
154
    m_refUIManager->add_ui_from_string(ui_description);
 
155
  }
 
156
  catch(const Glib::Error& ex)
 
157
  {
 
158
    std::cerr << "building menus failed: " <<  ex.what();
 
159
  }
 
160
#else
 
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();
 
164
#endif
 
165
}
 
166
 
 
167
void App_WithDoc_Gtk::init()
 
168
{  
 
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
 
171
  show();
 
172
}
 
173
 
 
174
 
 
175
void App_WithDoc_Gtk::init_ui_manager()
 
176
{
 
177
  using namespace Gtk;
 
178
 
 
179
  m_refUIManager = UIManager::create();
 
180
 
 
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 =
 
185
    "<ui>"
 
186
#ifdef GLOM_ENABLE_MAEMO
 
187
    "  <popup name='Bakery_MainMenu'>"
 
188
#else
 
189
    "  <menubar name='Bakery_MainMenu'>"
 
190
#endif
 
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
 
196
    "  </popup>"
 
197
#else
 
198
    "  </menubar>"
 
199
#endif
 
200
    "  <toolbar name='Bakery_ToolBar'>"
 
201
    "    <placeholder name='Bakery_ToolBarItemsPH' />"
 
202
    "  </toolbar>"
 
203
    "</ui>";
 
204
  
 
205
  add_ui_from_string(ui_description);
 
206
}
 
207
 
 
208
void App_WithDoc_Gtk::init_toolbars()
 
209
{
 
210
  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
 
211
  static const Glib::ustring ui_description =
 
212
    "<ui>"
 
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' />"
 
218
    "    </placeholder>"
 
219
    "  </toolbar>"
 
220
    "</ui>";
 
221
 
 
222
  add_ui_from_string(ui_description);
 
223
}
 
224
 
 
225
void App_WithDoc_Gtk::init_menus()
 
226
{
 
227
  //Override this to add more menus
 
228
  init_menus_file();
 
229
  init_menus_edit();
 
230
  init_menus_help();
 
231
 
 
232
}
 
233
void App_WithDoc_Gtk::init_menus_file_recentfiles(const Glib::ustring& path)
 
234
{
 
235
  if(!m_mime_types.empty()) //"Recent-files" is useless unless it knows what documents (which MIME-types) to show.
 
236
  {
 
237
    //Add recent-files submenu:
 
238
    Gtk::MenuItem* pMenuItem = dynamic_cast<Gtk::MenuItem*>(m_refUIManager->get_widget(path));
 
239
    if(pMenuItem)
 
240
    {
 
241
      Gtk::RecentFilter filter;
 
242
 
 
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)
 
245
      {
 
246
        const Glib::ustring mime_type = *iter;
 
247
 
 
248
        //TODO: Find a gio equivalent for gnome_vfs_mime_type_is_known(). murrayc.
 
249
        filter.add_mime_type(mime_type);
 
250
      }
 
251
 
 
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)));
 
258
 
 
259
      pMenuItem->set_submenu(*menu);
 
260
    }
 
261
    else
 
262
    {
 
263
      std::cout << "debug: recent files menu not found" << std::endl;
 
264
    }
 
265
  }
 
266
  else
 
267
  {
 
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;
 
269
  }
 
270
}
 
271
 
 
272
void App_WithDoc_Gtk::init_menus_file()
 
273
{
 
274
  // File menu
 
275
 
 
276
  //Build actions:
 
277
  m_refFileActionGroup = Gtk::ActionGroup::create("BakeryFileActions");
 
278
 
 
279
  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File", _("_File")));
 
280
  m_refFileActionGroup->add(Gtk::Action::create("BakeryAction_Menu_File_RecentFiles", _("_Recent Files")));
 
281
 
 
282
  //File actions
 
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));
 
287
 
 
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));
 
292
 
 
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));
 
296
                        
 
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));
 
299
                        
 
300
  m_refUIManager->insert_action_group(m_refFileActionGroup);
 
301
 
 
302
  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
 
303
  static const Glib::ustring ui_description =
 
304
    "<ui>"
 
305
#ifdef GLOM_ENABLE_MAEMO
 
306
    "  <popup name='Bakery_MainMenu'>"
 
307
#else
 
308
    "  <menubar name='Bakery_MainMenu'>"
 
309
#endif
 
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'>"
 
315
    "        </menu>"
 
316
    "        <menuitem action='BakeryAction_File_Save' />"
 
317
    "        <menuitem action='BakeryAction_File_SaveAs' />"
 
318
    "        <separator/>"
 
319
    "        <menuitem action='BakeryAction_File_Close' />"
 
320
    "      </menu>"
 
321
    "    </placeholder>"
 
322
#ifdef GLOM_ENABLE_MAEMO
 
323
    "  </popup>"
 
324
#else
 
325
    "  </menubar>"
 
326
#endif
 
327
    "</ui>";
 
328
  
 
329
  //Add menu:
 
330
  add_ui_from_string(ui_description);
 
331
 
 
332
  //Add recent-files submenu:
 
333
  init_menus_file_recentfiles("/Bakery_MainMenu/Bakery_MenuPH_File/BakeryAction_Menu_File/BakeryAction_Menu_File_RecentFiles");
 
334
}
 
335
 
 
336
void App_WithDoc_Gtk::init_menus_edit()
 
337
{
 
338
  using namespace Gtk;
 
339
  //Edit menu
 
340
  
 
341
  //Build actions:
 
342
  m_refEditActionGroup = ActionGroup::create("BakeryEditActions");
 
343
  m_refEditActionGroup->add(Action::create("BakeryAction_Menu_Edit", _("_Edit")));
 
344
  
 
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));
 
349
 
 
350
  m_refUIManager->insert_action_group(m_refEditActionGroup);
 
351
  
 
352
  //Build part of the menu structure, to be merged in by using the "PH" placeholders:
 
353
  static const Glib::ustring ui_description =
 
354
    "<ui>"
 
355
#ifdef GLOM_ENABLE_MAEMO
 
356
    "  <popup name='Bakery_MainMenu'>"
 
357
#else
 
358
    "  <menubar name='Bakery_MainMenu'>"
 
359
#endif
 
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' />"
 
366
    "      </menu>"
 
367
    "    </placeholder>"
 
368
#ifdef GLOM_ENABLE_MAEMO
 
369
    "  </popup>"
 
370
#else
 
371
    "  </menubar>"
 
372
#endif
 
373
    "</ui>";
 
374
 
 
375
  //Add menu:
 
376
  add_ui_from_string(ui_description);
 
377
}
 
378
 
 
379
void App_WithDoc_Gtk::init_menus_help()
 
380
{
 
381
  using namespace Gtk;
 
382
  //Help menu
 
383
 
 
384
  //Build actions:
 
385
  m_refHelpActionGroup = ActionGroup::create("BakeryHelpActions");
 
386
  m_refHelpActionGroup->add(Action::create("BakeryAction_Menu_Help", _("_Help")));
 
387
 
 
388
  //TODO: Use stock?
 
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));
 
392
 
 
393
  m_refUIManager->insert_action_group(m_refHelpActionGroup);
 
394
 
 
395
  //Build part of the menu structure, to be merged in by using the "PH" plaeholders:
 
396
  static const Glib::ustring ui_description =
 
397
    "<ui>"
 
398
#ifdef GLOM_ENABLE_MAEMO
 
399
    "  <popup name='Bakery_MainMenu'>"
 
400
#else
 
401
    "  <menubar name='Bakery_MainMenu'>"
 
402
#endif
 
403
    "    <placeholder name='Bakery_MenuPH_Help'>"
 
404
    "      <menu action='BakeryAction_Menu_Help'>"
 
405
    "        <menuitem action='BakeryAction_Help_About' />"
 
406
    "      </menu>"
 
407
    "    </placeholder>"
 
408
#ifdef GLOM_ENABLE_MAEMO
 
409
    "  </popup>"
 
410
#else
 
411
    "  </menubar>"
 
412
#endif
 
413
    "</ui>";
 
414
 
 
415
  //Add menu:
 
416
  add_ui_from_string(ui_description);
 
417
}
 
418
 
 
419
 
 
420
void App_WithDoc_Gtk::on_menu_help_about()
 
421
{
 
422
  if(m_pAbout && m_bAboutShown) // "About" box hasn't been closed, so just raise it
 
423
  {
 
424
    m_pAbout->set_transient_for(*this);
 
425
 
 
426
    Glib::RefPtr<Gdk::Window> about_win = m_pAbout->get_window();
 
427
    about_win->show();
 
428
    about_win->raise();
 
429
  }
 
430
  else
 
431
  {
 
432
    //Re-create About box:
 
433
    if(m_pAbout)
 
434
    {
 
435
      delete m_pAbout;
 
436
      m_pAbout = 0;
 
437
    }
 
438
 
 
439
    Gtk::AboutDialog* pDerived = new Gtk::AboutDialog;
 
440
    m_pAbout = pDerived;
 
441
 
 
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);
 
448
 
 
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:
 
452
    m_pAbout->hide();
 
453
    //m_pAbout->show(); //TODO: respond to the OK button.
 
454
  }
 
455
}
 
456
 
 
457
void App_WithDoc_Gtk::on_about_close()
 
458
{
 
459
  m_bAboutShown = false;
 
460
}
 
461
 
 
462
void App_WithDoc_Gtk::add(Gtk::Widget& child)
 
463
{
 
464
  m_VBox_PlaceHolder.pack_start(child);
 
465
}
 
466
 
 
467
bool App_WithDoc_Gtk::on_delete_event(GdkEventAny* /* event */)
 
468
{
 
469
  //Clicking on the [x] in the title bar should be like choosing File|Close
 
470
  on_menu_file_close();
 
471
 
 
472
  return true; // true = don't hide, don't destroy
 
473
}
 
474
 
 
475
 
 
476
void App_WithDoc_Gtk::update_window_title()
 
477
{
 
478
  //Set application's main window title:
 
479
 
 
480
  Document* pDoc = get_document();
 
481
  if(!pDoc)
 
482
    return;
 
483
 
 
484
  Glib::ustring strTitle = m_strAppName;
 
485
  strTitle += " - " + pDoc->get_name();
 
486
 
 
487
  //Indicate unsaved changes:
 
488
  if(pDoc->get_modified())
 
489
    strTitle += " *";
 
490
 
 
491
  //Indicate read-only files:
 
492
  if(pDoc->get_read_only())
 
493
    strTitle += _(" (read-only)");
 
494
 
 
495
  set_title(strTitle);
 
496
}
 
497
 
 
498
void App_WithDoc_Gtk::ui_warning(const Glib::ustring& text, const Glib::ustring& secondary_text)
 
499
{
 
500
  Gtk::Window* pWindow = this;
 
501
 
 
502
#ifdef GLOM_ENABLE_MAEMO
 
503
  Hildon::Note dialog(Hildon::NOTE_TYPE_INFORMATION, *pWindow, text);
 
504
#else
 
505
  Gtk::MessageDialog dialog(App_WithDoc_Gtk::util_bold_message(text), true /* use markup */, Gtk::MESSAGE_WARNING);
 
506
  dialog.set_secondary_text(secondary_text);
 
507
 
 
508
  dialog.set_title(""); //The HIG says that alert dialogs should not have titles. The default comes from the message type.
 
509
#endif
 
510
 
 
511
  if(pWindow)
 
512
    dialog.set_transient_for(*pWindow);
 
513
 
 
514
  dialog.run();
 
515
}
 
516
 
 
517
 
 
518
Glib::ustring App_WithDoc_Gtk::util_bold_message(const Glib::ustring& message)
 
519
{
 
520
  return "<b>" + message + "</b>";
 
521
}
 
522
 
 
523
Glib::ustring App_WithDoc_Gtk::ui_file_select_open(const Glib::ustring& starting_folder_uri)
 
524
{
 
525
  Gtk::Window* pWindow = this;
 
526
 
 
527
#ifdef GLOM_ENABLE_MAEMO
 
528
  Hildon::FileChooserDialog fileChooser_Open(Gtk::FILE_CHOOSER_ACTION_OPEN);
 
529
#else
 
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
 
535
 
 
536
  if(pWindow)
 
537
    fileChooser_Open.set_transient_for(*pWindow);
 
538
 
 
539
  if(!starting_folder_uri.empty())
 
540
    fileChooser_Open.set_current_folder_uri(starting_folder_uri);
 
541
 
 
542
  const int response_id = fileChooser_Open.run();
 
543
  fileChooser_Open.hide();
 
544
  if(response_id != Gtk::RESPONSE_CANCEL)
 
545
  {
 
546
    return fileChooser_Open.get_uri();
 
547
  }
 
548
  else
 
549
    return Glib::ustring();
 
550
}
 
551
 
 
552
static bool uri_is_writable(const Glib::RefPtr<const Gio::File>& uri)
 
553
{
 
554
  if(!uri)
 
555
    return false;
 
556
 
 
557
  Glib::RefPtr<const Gio::FileInfo> file_info;
 
558
 
 
559
#ifdef GLIBMM_EXCEPTIONS_ENABLED
 
560
  try
 
561
  {
 
562
    file_info = uri->query_info(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
 
563
  }
 
564
  catch(const Glib::Error& /* ex */)
 
565
  {
 
566
    return false;
 
567
  }
 
568
#else
 
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);
 
571
  if(error.get())
 
572
    return false;
 
573
#endif
 
574
 
 
575
  if(file_info)
 
576
  {
 
577
    return file_info->get_attribute_boolean(G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
 
578
  }
 
579
  else
 
580
    return true; //Not every URI protocol supports access rights, so assume that it's writable and complain later.
 
581
}
 
582
 
 
583
Glib::ustring App_WithDoc_Gtk::ui_file_select_save(const Glib::ustring& old_file_uri)
 
584
{
 
585
 Gtk::Window* pWindow = this;
 
586
 
 
587
#ifdef GLOM_ENABLE_MAEMO
 
588
  Hildon::FileChooserDialog fileChooser_Save(Gtk::FILE_CHOOSER_ACTION_SAVE);
 
589
#else
 
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
 
595
 
 
596
 if(pWindow)
 
597
    fileChooser_Save.set_transient_for(*pWindow);
 
598
 
 
599
  fileChooser_Save.set_do_overwrite_confirmation(); //Ask the user if the file already exists.
 
600
 
 
601
  //Make the save dialog show the existing filename, if any:
 
602
  if(!old_file_uri.empty())
 
603
  {
 
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);
 
607
    if(gio_file)
 
608
    {
 
609
      Glib::RefPtr<Gio::File> parent = gio_file->get_parent();
 
610
      if(parent)
 
611
      {
 
612
        const Glib::ustring uri_parent = parent->get_uri();
 
613
        fileChooser_Save.set_uri(uri_parent);
 
614
      }
 
615
    }
 
616
  }
 
617
 
 
618
  bool try_again = true;
 
619
  while(try_again)
 
620
  {
 
621
    try_again = false;
 
622
 
 
623
    const int response_id = fileChooser_Save.run();
 
624
    fileChooser_Save.hide();
 
625
    if(response_id != Gtk::RESPONSE_CANCEL)
 
626
    {
 
627
      const Glib::ustring uri = fileChooser_Save.get_uri();
 
628
 
 
629
      Glib::RefPtr<Gio::File> gio_file = Gio::File::create_for_uri(uri);
 
630
 
 
631
      //If the file exists (the FileChooser offers a "replace?" dialog, so this is possible.):
 
632
      if(App_WithDoc::file_exists(uri))
 
633
      {
 
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))
 
637
        {
 
638
           //Warn the user:
 
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.
 
641
           continue;
 
642
        }
 
643
      }
 
644
 
 
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();
 
648
      if(gio_file_parent)
 
649
      {
 
650
        if(!uri_is_writable(gio_file_parent))
 
651
        {
 
652
          //Warn the user:
 
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.
 
655
           continue;
 
656
        }
 
657
      }
 
658
 
 
659
      if(!try_again)
 
660
        return uri;
 
661
    }
 
662
    else
 
663
      return Glib::ustring(); //The user cancelled.
 
664
  }
 
665
 
 
666
  return Glib::ustring();
 
667
}
 
668
 
 
669
void App_WithDoc_Gtk::ui_show_modification_status()
 
670
{
 
671
  bool modified = m_pDocument->get_modified();
 
672
 
 
673
  //Enable Save and SaveAs menu items:
 
674
  if(m_action_save)
 
675
    g_object_set(G_OBJECT(m_action_save->gobj()), "sensitive", modified, NULL); // TODO: Use a set_sensitive(modified)?
 
676
 
 
677
  if(m_action_saveas)
 
678
    g_object_set(G_OBJECT(m_action_saveas->gobj()), "sensitive", modified, NULL); // TODO: Use a set_sensitive(modified)?
 
679
 
 
680
}
 
681
 
 
682
App_WithDoc_Gtk::enumSaveChanges App_WithDoc_Gtk::ui_offer_to_save_changes()
 
683
{
 
684
  App_WithDoc::enumSaveChanges result = App_WithDoc::SAVECHANGES_Cancel;
 
685
 
 
686
  if(!m_pDocument)
 
687
    return result;
 
688
 
 
689
  GlomBakery::Dialog_OfferSave* pDialogQuestion 
 
690
    = new GlomBakery::Dialog_OfferSave( m_pDocument->get_file_uri() );
 
691
 
 
692
  Gtk::Window* pWindow = this;
 
693
  if(pWindow)
 
694
    pDialogQuestion->set_transient_for(*pWindow);
 
695
 
 
696
  GlomBakery::Dialog_OfferSave::enumButtons buttonClicked = (GlomBakery::Dialog_OfferSave::enumButtons)pDialogQuestion->run();
 
697
  delete pDialogQuestion;
 
698
  pDialogQuestion = 0;
 
699
 
 
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;
 
704
  else
 
705
     result = App_WithDoc::SAVECHANGES_Cancel;
 
706
 
 
707
  return result;
 
708
}
 
709
 
 
710
void App_WithDoc_Gtk::document_history_add(const Glib::ustring& file_uri)
 
711
{
 
712
  if(file_uri.empty())
 
713
    return;
 
714
 
 
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))
 
720
    return;
 
721
 
 
722
  {
 
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;
 
726
 
 
727
#ifdef GLIBMM_EXCEPTIONS_ENABLED
 
728
    Gtk::RecentManager::get_default()->add_item(uri);
 
729
#else
 
730
    std::auto_ptr<Glib::Error> error;
 
731
    Gtk::RecentManager::get_default()->add_item(uri, error);
 
732
    // Ignore error
 
733
#endif
 
734
  }
 
735
}
 
736
 
 
737
void App_WithDoc_Gtk::document_history_remove(const Glib::ustring& file_uri)
 
738
{
 
739
  if(!file_uri.empty())
 
740
  {
 
741
    //Glib::ustring filename_e = Gnome::Vfs::escape_path_string(file_uri.c_str());
 
742
    const Glib::ustring uri = file_uri; //"file://" + filename_e;
 
743
 
 
744
#ifdef GLIBMM_EXCEPTIONS_ENABLED
 
745
    Gtk::RecentManager::get_default()->remove_item(uri);
 
746
#else
 
747
    std::auto_ptr<Glib::Error> error;
 
748
    Gtk::RecentManager::get_default()->remove_item(uri, error);
 
749
    // Ignore error
 
750
#endif
 
751
  }
 
752
}
 
753
 
 
754
void App_WithDoc_Gtk::on_recent_files_activate(Gtk::RecentChooser& chooser)
 
755
{
 
756
  const Glib::ustring uri = chooser.get_current_uri();
 
757
  const bool bTest = open_document(uri);
 
758
  if(!bTest)
 
759
    document_history_remove(uri);
 
760
}
 
761
 
 
762
} //namespace