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

« back to all changes in this revision

Viewing changes to glom/frame_glom.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:
18
18
 * Boston, MA 02111-1307, USA.
19
19
 */
20
20
 
21
 
#include "config.h"
22
 
 
23
 
#include "frame_glom.h"
24
 
#include "application.h"
25
 
#include "dialog_import_csv.h"
26
 
#include "dialog_import_csv_progress.h"
27
 
#include <glom/libglom/appstate.h>
 
21
#include "config.h" //For GLOM_ENABLE_SQLITE
 
22
#include <libglom/libglom_config.h> // For GLOM_ENABLE_CLIENT_ONLY
 
23
 
 
24
#include <glom/frame_glom.h>
 
25
#include <glom/application.h>
 
26
#include <glom/import_csv/dialog_import_csv.h>
 
27
#include <glom/import_csv/dialog_import_csv_progress.h>
 
28
#include <libglom/appstate.h>
 
29
 
 
30
#include <libglom/connectionpool.h>
 
31
 
 
32
#ifdef GLOM_ENABLE_POSTGRESQL
 
33
#include <libglom/connectionpool_backends/postgres_central.h>
 
34
#include <libglom/connectionpool_backends/postgres_self.h>
 
35
#endif
 
36
 
 
37
#ifdef GLOM_ENABLE_SQLITE
 
38
# include <libglom/connectionpool_backends/sqlite.h>
 
39
#endif
28
40
 
29
41
#ifndef GLOM_ENABLE_CLIENT_ONLY
30
 
#include "mode_design/users/dialog_groups_list.h"
31
 
#include "dialog_database_preferences.h"
32
 
#include "reports/dialog_layout_report.h"
 
42
#include <glom/mode_design/users/dialog_groups_list.h>
 
43
#include <glom/mode_design/dialog_database_preferences.h>
 
44
#include <glom/mode_design/report_layout/dialog_layout_report.h>
33
45
#include <glom/mode_design/print_layouts/window_print_layout_edit.h>
34
46
#include <glom/mode_design/dialog_add_related_table.h>
35
47
#include <glom/mode_design/script_library/dialog_script_library.h>
36
 
#include <glom/dialog_new_self_hosted_connection.h>
37
 
#include "relationships_overview/dialog_relationships_overview.h"
 
48
#include <glom/mode_design/dialog_initial_password.h>
 
49
#include <glom/mode_design/relationships_overview/dialog_relationships_overview.h>
38
50
#endif // !GLOM_ENABLE_CLIENT_ONLY
39
51
 
40
 
#include <glom/libglom/utils.h>
41
 
#include <glom/libglom/glade_utils.h>
42
 
#include <glom/libglom/data_structure/glomconversions.h>
43
 
#include <glom/libglom/data_structure/layout/report_parts/layoutitem_summary.h>
44
 
#include <glom/libglom/data_structure/layout/report_parts/layoutitem_fieldsummary.h>
 
52
#include <glom/utils_ui.h>
 
53
#include <glom/glade_utils.h>
 
54
#include <libglom/data_structure/glomconversions.h>
 
55
#include <libglom/data_structure/layout/report_parts/layoutitem_summary.h>
 
56
#include <libglom/data_structure/layout/report_parts/layoutitem_fieldsummary.h>
45
57
 
46
 
#include <glom/reports/report_builder.h>
 
58
#include <glom/report_builder.h>
47
59
#ifndef GLOM_ENABLE_CLIENT_ONLY
48
60
#include <glom/mode_design/dialog_add_related_table.h>
49
61
#include <glom/mode_design/script_library/dialog_script_library.h>
52
64
 
53
65
#ifdef GLOM_ENABLE_MAEMO
54
66
#include <hildonmm/note.h>
 
67
#include <hildonmm/entry.h>
 
68
#include <hildonmm/text-view.h>
 
69
#include <hildonmm/button.h>
55
70
#endif
56
71
 
57
 
#include "filechooser_export.h"
 
72
#include <glom/filechooser_export.h>
58
73
#include <glom/glom_privs.h>
59
74
#include <sstream> //For stringstream.
60
75
#include <fstream>
63
78
namespace Glom
64
79
{
65
80
 
66
 
Frame_Glom::Frame_Glom(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade)
67
 
: PlaceHolder(cobject, refGlade),
68
 
  m_pLabel_Name(0),
 
81
Frame_Glom::Frame_Glom(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
 
82
: PlaceHolder(cobject, builder),
69
83
  m_pLabel_Table(0),
 
84
  m_box_header(0),
70
85
  m_box_footer(0),
71
86
  m_pLabel_Mode(0),
72
87
  m_pLabel_userlevel(0),
73
 
  m_pBox_QuickFind(0),
74
 
  m_pEntry_QuickFind(0),
75
 
  m_pButton_QuickFind(0),
76
88
  m_pBox_RecordsCount(0),
77
89
  m_pLabel_RecordsCount(0),
78
90
  m_pLabel_FoundCount(0),
79
91
  m_pButton_FindAll(0),
80
92
  m_pBox_Mode(0),
 
93
#ifndef GLOM_ENABLE_MAEMO
81
94
  m_pBox_Tables(0),
82
95
  m_pDialog_Tables(0),
 
96
#endif //GLOM_ENABLE_MAEMO
 
97
  m_pBox_QuickFind(0),
 
98
  m_pEntry_QuickFind(0),
 
99
  m_pButton_QuickFind(0),
83
100
#ifndef GLOM_ENABLE_CLIENT_ONLY
84
101
  m_pDialog_Reports(0),
85
102
  m_pDialogLayoutReport(0),
91
108
  m_pDialog_Relationships(0),
92
109
  m_dialog_addrelatedtable(0),
93
110
  m_dialog_relationships_overview(0),
 
111
  m_dialog_progess_connection_initialize(0),
94
112
#endif // !GLOM_ENABLE_CLIENT_ONLY
 
113
  m_dialog_progess_connection_startup(0),
 
114
  m_dialog_progess_connection_cleanup(0),
95
115
  m_pDialogConnection(0)
96
116
{
97
117
  //Load widgets from glade file:
98
 
  refGlade->get_widget("label_name", m_pLabel_Name);
99
 
  refGlade->get_widget("label_table_name", m_pLabel_Table);
 
118
  builder->get_widget("label_table_name", m_pLabel_Table);
100
119
 
101
 
  refGlade->get_widget("hbox_footer", m_box_footer);
102
 
  refGlade->get_widget("label_mode", m_pLabel_Mode);
103
 
  refGlade->get_widget("label_user_level", m_pLabel_userlevel);
 
120
  builder->get_widget("hbox_header", m_box_header);
 
121
  builder->get_widget("hbox_footer", m_box_footer);
 
122
  builder->get_widget("label_mode", m_pLabel_Mode);
 
123
  builder->get_widget("label_user_level", m_pLabel_userlevel);
104
124
  
105
 
  //Hide the footer on maemo (It takes too much space),
 
125
  //Hide unnecessary widgets on maemo that take too much space,
106
126
  //and reduce the border width:
107
127
  #ifdef GLOM_ENABLE_MAEMO
 
128
  m_box_header->hide();
108
129
  m_box_footer->hide();
109
130
  set_border_width(Glom::Utils::DEFAULT_SPACING_LARGE);
110
131
  #endif
111
132
 
112
 
  refGlade->get_widget("hbox_quickfind", m_pBox_QuickFind);
113
 
  m_pBox_QuickFind->hide();
114
 
 
115
 
  refGlade->get_widget("entry_quickfind", m_pEntry_QuickFind);
 
133
  //QuickFind widgets:
 
134
  //We don't use Glade for these, so it easier to modify them for the Maemo port.
 
135
  m_pBox_QuickFind = Gtk::manage(new Gtk::HBox(false, Utils::DEFAULT_SPACING_SMALL));
 
136
  Gtk::Label* label = Gtk::manage(new Gtk::Label(_("Quick Find")));
 
137
  m_pBox_QuickFind->pack_start(*label, Gtk::PACK_SHRINK);
 
138
  
 
139
  #ifndef GLOM_ENABLE_MAEMO
 
140
  m_pEntry_QuickFind = Gtk::manage(new Gtk::Entry());
 
141
  #else
 
142
  m_pEntry_QuickFind = Gtk::manage(new Hildon::Entry(Gtk::Hildon::SIZE_AUTO));
 
143
  #endif
116
144
  m_pEntry_QuickFind->signal_activate().connect(
117
145
   sigc::mem_fun(*this, &Frame_Glom::on_button_quickfind) ); //Pressing Enter here is like pressing Find.
118
146
 
119
 
  refGlade->get_widget("button_quickfind", m_pButton_QuickFind);
 
147
  m_pBox_QuickFind->pack_start(*m_pEntry_QuickFind, Gtk::PACK_EXPAND_WIDGET);
 
148
  #ifndef GLOM_ENABLE_MAEMO
 
149
  m_pButton_QuickFind = Gtk::manage(new Gtk::Button(_("_Find"), true));
 
150
  #else
 
151
  m_pButton_QuickFind = Gtk::manage(new Hildon::Button(Gtk::Hildon::SIZE_AUTO, 
 
152
    Hildon::BUTTON_ARRANGEMENT_VERTICAL, _("Find"), _("Search for records")));
 
153
  #endif
120
154
  m_pButton_QuickFind->signal_clicked().connect(
121
155
    sigc::mem_fun(*this, &Frame_Glom::on_button_quickfind) );
122
 
 
123
 
  refGlade->get_widget("hbox_records_count", m_pBox_RecordsCount);
124
 
  refGlade->get_widget("label_records_count", m_pLabel_RecordsCount);
125
 
  refGlade->get_widget("label_records_found_count", m_pLabel_FoundCount);
126
 
  refGlade->get_widget("button_find_all", m_pButton_FindAll);
 
156
  m_pBox_QuickFind->pack_start(*m_pButton_QuickFind, Gtk::PACK_SHRINK);
 
157
  
 
158
  m_pBox_QuickFind->show_all_children();
 
159
  m_pBox_QuickFind->hide();
 
160
 
 
161
  #ifndef GLOM_ENABLE_MAEMO
 
162
  PlaceHolder* placeholder_quickfind = 0;
 
163
  builder->get_widget_derived("vbox_quickfind", placeholder_quickfind);
 
164
  placeholder_quickfind->add(*m_pBox_QuickFind);
 
165
  #endif //GLOM_ENABLE_MAEMO
 
166
 
 
167
 
 
168
  builder->get_widget("hbox_records_count", m_pBox_RecordsCount);
 
169
  builder->get_widget("label_records_count", m_pLabel_RecordsCount);
 
170
  builder->get_widget("label_records_found_count", m_pLabel_FoundCount);
 
171
  builder->get_widget("button_find_all", m_pButton_FindAll);
127
172
  m_pButton_FindAll->signal_clicked().connect(
128
173
    sigc::mem_fun(*this, &Frame_Glom::on_button_find_all) );
129
174
 
130
 
  refGlade->get_widget_derived("vbox_mode", m_pBox_Mode);
131
 
 
132
 
  //m_pLabel_Mode->set_text(_("No database selected.\n Use the Navigation menu, or open a previous Glom document."));
 
175
  builder->get_widget_derived("vbox_mode", m_pBox_Mode);
133
176
 
134
177
  m_Mode = MODE_None;
135
178
  m_Mode_Previous = MODE_None;
137
180
 
138
181
  m_Notebook_Find.signal_find_criteria.connect(sigc::mem_fun(*this, &Frame_Glom::on_notebook_find_criteria));
139
182
  m_Notebook_Find.show();
140
 
 
141
183
  m_Notebook_Data.signal_record_details_requested().connect(sigc::mem_fun(*this, &Frame_Glom::on_notebook_data_record_details_requested));
142
184
  m_Notebook_Data.signal_switch_page().connect(sigc::mem_fun(*this, &Frame_Glom::on_notebook_data_switch_page));
143
185
  m_Notebook_Data.show();
148
190
  add_view(&m_Notebook_Find); //Also a composite view.
149
191
 
150
192
  on_userlevel_changed(AppState::USERLEVEL_OPERATOR); //A default to show before a document is created or loaded.
151
 
 
152
 
#ifdef GLOM_ENABLE_MAEMO
153
 
  // Don't show the document name on maemo. The label is on top of the window,
154
 
  // and the document name is already shown in the window title (note that
155
 
  // there is not even a menu between them as in the non-maemo version). This
156
 
  // looks a bit strange and takes vertical screen space.
157
 
  m_pLabel_Name->hide();
158
 
#endif
 
193
  
 
194
  #ifdef GLOM_ENABLE_MAEMO
 
195
  m_maemo_window_find.set_title(_("Glom: Find"));
 
196
  
 
197
  Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox(false, Utils::DEFAULT_SPACING_SMALL));
 
198
  vbox->pack_start(*m_pBox_QuickFind, Gtk::PACK_SHRINK);
 
199
  vbox->pack_start(m_Notebook_Find, Gtk::PACK_EXPAND_WIDGET);
 
200
  m_maemo_window_find.add(*vbox);
 
201
  m_maemo_window_find.show_all_children();
 
202
  #endif //GLOM_ENABLE_MAEMO
159
203
}
160
204
 
161
205
Frame_Glom::~Frame_Glom()
162
206
{
 
207
#ifndef GLOM_ENABLE_MAEMO
163
208
  if(m_pBox_Tables)
164
209
    remove_view(m_pBox_Tables);
165
210
 
 
211
  if(m_pDialog_Tables)
 
212
  {
 
213
    delete m_pDialog_Tables;
 
214
    m_pDialog_Tables = 0;
 
215
  }
 
216
#endif //GLOM_ENABLE_MAEMO
 
217
 
166
218
  remove_view(&m_Notebook_Data); //Also a composite view.
167
219
  remove_view(&m_Notebook_Find); //Also a composite view.
168
220
 
169
 
  if(m_pDialog_Tables)
170
 
  {
171
 
    delete m_pDialog_Tables;
172
 
    m_pDialog_Tables = 0;
173
 
  }
174
221
 
175
222
  if(m_pDialogConnection)
176
223
  {
178
225
    delete m_pDialogConnection;
179
226
    m_pDialogConnection = 0;
180
227
  }
 
228
  
 
229
  
 
230
  if(m_dialog_progess_connection_startup)
 
231
  {
 
232
    delete m_dialog_progess_connection_startup;
 
233
    m_dialog_progess_connection_startup = 0;
 
234
  }
 
235
  
 
236
  if(m_dialog_progess_connection_cleanup)
 
237
  {
 
238
    delete m_dialog_progess_connection_cleanup;
 
239
    m_dialog_progess_connection_cleanup = 0;
 
240
  }
181
241
 
182
242
#ifndef GLOM_ENABLE_CLIENT_ONLY
 
243
  if(m_dialog_progess_connection_initialize)
 
244
  {
 
245
    delete m_dialog_progess_connection_initialize;
 
246
    m_dialog_progess_connection_initialize = 0;
 
247
  }
 
248
 
183
249
  if(m_pBox_Reports)
184
250
    remove_view(m_pBox_Reports);
185
251
 
236
302
 
237
303
  get_document()->set_connection_database(strName);
238
304
 
239
 
  show_system_name();
 
305
  //show_system_name();
240
306
 
241
307
  do_menu_Navigate_Table(true /* open default */);
242
308
}
243
309
 
244
310
void Frame_Glom::on_box_tables_selected(const Glib::ustring& strName)
245
311
{
 
312
#ifndef GLOM_ENABLE_MAEMO
246
313
  if(m_pDialog_Tables)
247
314
    m_pDialog_Tables->hide();
 
315
#endif //GLOM_ENABLE_MAEMO
248
316
 
249
317
  show_table(strName);
250
318
}
251
319
 
252
320
void Frame_Glom::set_mode_widget(Gtk::Widget& widget)
253
321
{
 
322
  #ifdef GLOM_ENABLE_MAEMO
 
323
  //On Maemo, the find UI is always shown in a separate window instead.
 
324
  if(&widget == &m_Notebook_Find)
 
325
    return;
 
326
  else
 
327
  {
 
328
    //Make sure that this is hidden:
 
329
    m_maemo_window_find.hide();
 
330
  }
 
331
  #endif
 
332
 
254
333
  //Remove current contents.
255
334
  //I wish that there was a better way to do this:
256
335
  //Trying to remove all of them leads to warnings,
269
348
 
270
349
    m_pBox_Mode->add(widget);
271
350
    widget.show();
272
 
 
273
 
    //Show help text:
274
 
    //Notebook_Glom* pNotebook = dynamic_cast<Notebook_Glom*>(&widget);
275
 
    //if(pNotebook)
276
 
   // {
277
 
   //   pNotebook->show_hint();
278
 
   // }
279
351
  }
280
352
}
281
353
 
336
408
  show_table(m_table_name);
337
409
}
338
410
 
339
 
void Frame_Glom::show_table(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value_for_details)
 
411
void Frame_Glom::show_table_allow_empty(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value_for_details)
340
412
{
341
413
  App_Glom* pApp = dynamic_cast<App_Glom*>(get_app_window());
342
414
 
343
415
  //This can take quite a long time, so we show the busy cursor while it's working:
344
 
  Bakery::BusyCursor busy_cursor(pApp);
345
 
 
346
 
  //Check that there is a table to show:
347
 
  if(table_name.empty())
348
 
  {
349
 
    alert_no_table();
350
 
  }
351
 
  else
352
 
  {
353
 
    //Choose a default mode, if necessary:
354
 
    if(m_Mode == MODE_None)
355
 
      set_mode(m_Mode);
356
 
 
357
 
    //Show the table:
358
 
    m_table_name = table_name;
359
 
    Glib::ustring strMode;
 
416
  BusyCursor busy_cursor(pApp);
 
417
 
 
418
  //Choose a default mode, if necessary:
 
419
  if(m_Mode == MODE_None)
 
420
    set_mode(m_Mode);
 
421
 
 
422
  //Show the table:
 
423
  m_table_name = table_name;
 
424
  Glib::ustring strMode;
360
425
#ifndef GLOM_ENABLE_CLIENT_ONLY
361
 
    //Update the document with any new information in the database if necessary (though the database _should never have changed information)
362
 
    update_table_in_document_from_database();
 
426
  //Update the document with any new information in the database if necessary (though the database _should never have changed information)
 
427
  update_table_in_document_from_database();
363
428
#endif // !GLOM_ENABLE_CLIENT_ONLY
364
429
 
365
 
    //Update user-level dependent UI:
366
 
    if(pApp)
367
 
      on_userlevel_changed(pApp->get_userlevel());
 
430
  //Update user-level dependent UI:
 
431
  if(pApp)
 
432
    on_userlevel_changed(pApp->get_userlevel());
368
433
 
369
 
    switch(m_Mode)
 
434
  switch(m_Mode)
 
435
  {
 
436
    case(MODE_Data):
370
437
    {
371
 
      case(MODE_Data):
372
 
      {
373
 
        strMode = _("Data");
374
 
        FoundSet found_set;
375
 
 
376
 
        //Start with the last-used found set (sort order and where clause)
377
 
        //for this layout:
378
 
        //(This would be ignored anyway if a details primary key is specified.)
379
 
        Document_Glom* document = get_document(); 
380
 
        if(document)
381
 
          found_set = document->get_criteria_current(m_table_name);
382
 
 
383
 
        //Make sure that this is set:
384
 
        found_set.m_table_name = m_table_name;
385
 
 
386
 
        //If there is no saved sort clause, 
387
 
        //then sort by the ID, just so we sort by something, so that the order is predictable:
388
 
        if(found_set.m_sort_clause.empty())
389
 
        {
390
 
          sharedptr<Field> field_primary_key = get_field_primary_key_for_table(m_table_name);
391
 
          if(field_primary_key)
392
 
          {
393
 
            sharedptr<LayoutItem_Field> layout_item_sort = sharedptr<LayoutItem_Field>::create();
394
 
            layout_item_sort->set_full_field_details(field_primary_key);
395
 
 
396
 
            found_set.m_sort_clause.clear();
397
 
 
398
 
            //Avoid the sort clause if the found set will include too many records, 
399
 
            //because that would be too slow.
400
 
            //The user can explicitly request a sort later, by clicking on a column header.
401
 
            //TODO_Performance: This causes an almost-duplicate COUNT query (we do it in the treemodel too), but it's not that slow. 
402
 
            sharedptr<LayoutItem_Field> layout_item_temp = sharedptr<LayoutItem_Field>::create();
403
 
            layout_item_temp->set_full_field_details(field_primary_key);
404
 
            type_vecLayoutFields layout_fields;
405
 
            layout_fields.push_back(layout_item_temp);
406
 
            const Glib::ustring sql_query_without_sort = Utils::build_sql_select_with_where_clause(found_set.m_table_name, layout_fields, found_set.m_where_clause, found_set.m_extra_join, type_sort_clause(), found_set.m_extra_group_by);
407
 
            const int count = Base_DB::count_rows_returned_by(sql_query_without_sort);
408
 
            if(count < 10000) //Arbitrary large number.
409
 
              found_set.m_sort_clause.push_back( type_pair_sort_field(layout_item_sort, true /* ascending */) );
410
 
          }
 
438
      strMode = _("Data");
 
439
      FoundSet found_set;
 
440
 
 
441
      //Start with the last-used found set (sort order and where clause)
 
442
      //for this layout:
 
443
      //(This would be ignored anyway if a details primary key is specified.)
 
444
      Document* document = get_document(); 
 
445
      if(document)
 
446
        found_set = document->get_criteria_current(m_table_name);
 
447
 
 
448
      //Make sure that this is set:
 
449
      found_set.m_table_name = m_table_name;
 
450
 
 
451
      //If there is no saved sort clause, 
 
452
      //then sort by the ID, just so we sort by something, so that the order is predictable:
 
453
      if(found_set.m_sort_clause.empty())
 
454
      {
 
455
        sharedptr<Field> field_primary_key = get_field_primary_key_for_table(m_table_name);
 
456
        if(field_primary_key)
 
457
      {
 
458
          sharedptr<LayoutItem_Field> layout_item_sort = sharedptr<LayoutItem_Field>::create();
 
459
          layout_item_sort->set_full_field_details(field_primary_key);
 
460
 
 
461
          found_set.m_sort_clause.clear();
 
462
 
 
463
          //Avoid the sort clause if the found set will include too many records, 
 
464
          //because that would be too slow.
 
465
          //The user can explicitly request a sort later, by clicking on a column header.
 
466
          //TODO_Performance: This causes an almost-duplicate COUNT query (we do it in the treemodel too), but it's not that slow. 
 
467
          sharedptr<LayoutItem_Field> layout_item_temp = sharedptr<LayoutItem_Field>::create();
 
468
          layout_item_temp->set_full_field_details(field_primary_key);
 
469
          type_vecLayoutFields layout_fields;
 
470
          layout_fields.push_back(layout_item_temp);
 
471
          const Glib::ustring sql_query_without_sort = Utils::build_sql_select_with_where_clause(found_set.m_table_name, layout_fields, found_set.m_where_clause, found_set.m_extra_join, type_sort_clause(), found_set.m_extra_group_by);
 
472
          const int count = Base_DB::count_rows_returned_by(sql_query_without_sort);
 
473
          if(count < 10000) //Arbitrary large number.
 
474
            found_set.m_sort_clause.push_back( type_pair_sort_field(layout_item_sort, true /* ascending */) );
411
475
        }
412
 
 
413
 
        //Show the wanted records in the notebook, showing details for a particular record if wanted:
414
 
        m_Notebook_Data.init_db_details(found_set, primary_key_value_for_details);
415
 
        set_mode_widget(m_Notebook_Data);
416
 
 
417
 
        //Show how many records were found:
418
 
        update_records_count();
419
 
 
420
 
        break;
421
 
      }
422
 
      case(MODE_Find):
423
 
      {
424
 
        strMode = _("Find");
425
 
        m_Notebook_Find.init_db_details(m_table_name);
426
 
        set_mode_widget(m_Notebook_Find);
427
 
        break;
428
 
      }
429
 
      default:
430
 
      {
431
 
        std::cout << "Frame_Glom::on_box_tables_selected(): Unexpected mode" << std::endl;
432
 
        strMode = _("Unknown");
433
 
        break;
434
 
      }
435
 
    }
436
 
 
437
 
    m_pLabel_Mode->set_text(strMode);
 
476
      }
 
477
 
 
478
      //Show the wanted records in the notebook, showing details for a particular record if wanted:
 
479
      m_Notebook_Data.init_db_details(found_set, primary_key_value_for_details);
 
480
      set_mode_widget(m_Notebook_Data);
 
481
 
 
482
      //Show how many records were found:
 
483
      update_records_count();
 
484
 
 
485
      break;
 
486
    }
 
487
    case(MODE_Find):
 
488
    {
 
489
      strMode = _("Find");
 
490
      m_Notebook_Find.init_db_details(m_table_name, get_active_layout_platform(get_document()));
 
491
      set_mode_widget(m_Notebook_Find);
 
492
      break;
 
493
    }
 
494
    default:
 
495
    {
 
496
      std::cout << "Frame_Glom::on_box_tables_selected(): Unexpected mode" << std::endl;
 
497
      strMode = _("Unknown");
 
498
      break;
 
499
    }
438
500
  }
439
501
 
 
502
  m_pLabel_Mode->set_text(strMode);
 
503
 
440
504
  show_table_title();
441
505
 
442
506
  //List the reports and print layouts in the menus:
446
510
  //show_all();
447
511
}
448
512
 
 
513
void Frame_Glom::show_table(const Glib::ustring& table_name, const Gnome::Gda::Value& primary_key_value_for_details)
 
514
{
 
515
  //Check that there is a table to show:
 
516
  if(table_name.empty())
 
517
  {
 
518
    alert_no_table();
 
519
  }
 
520
  else
 
521
  {
 
522
    show_table_allow_empty(table_name, primary_key_value_for_details);
 
523
  }
 
524
}
 
525
 
 
526
void Frame_Glom::show_no_table()
 
527
{
 
528
  show_table_allow_empty(Glib::ustring());
 
529
}
 
530
 
449
531
#ifndef GLOM_ENABLE_CLIENT_ONLY
450
532
void Frame_Glom::on_menu_userlevel_Developer(const Glib::RefPtr<Gtk::RadioAction>& action, const Glib::RefPtr<Gtk::RadioAction>& operator_action)
451
533
{
452
534
  if(action && action->get_active())
453
535
  {
454
 
    Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
 
536
    Document* document = dynamic_cast<Document*>(get_document());
455
537
    if(document)
456
538
    {
457
539
      //Check whether the current user has developer privileges:
458
 
      const ConnectionPool* connection_pool = ConnectionPool::get_instance();
459
 
      bool test = Privs::get_user_is_in_group(connection_pool->get_user(), GLOM_STANDARD_GROUP_NAME_DEVELOPER);
 
540
      ConnectionPool* connection_pool = ConnectionPool::get_instance();
 
541
      sharedptr<SharedConnection> sharedconnection = connection_pool->connect();
 
542
 
 
543
      // Default to true; if we don't support users, we always have
 
544
      // priviliges to change things in developer mode.
 
545
      bool test = true;
 
546
 
 
547
      if(sharedconnection && sharedconnection->get_gda_connection()->supports_feature(Gnome::Gda::CONNECTION_FEATURE_USERS))
 
548
        Privs::get_user_is_in_group(connection_pool->get_user(), GLOM_STANDARD_GROUP_NAME_DEVELOPER);
 
549
 
460
550
      if(test)
461
551
      {
462
552
        std::cout << "DEBUG: User=" << connection_pool->get_user() << " _is_ in the developer group on the server." << std::endl;
477
567
        if(document->get_opened_from_browse())
478
568
        {
479
569
          //TODO: Obviously this could be possible but it would require a network protocol and some work:
480
 
          Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Developer Mode Not Available.")), true, Gtk::MESSAGE_WARNING);
 
570
          Gtk::MessageDialog dialog(Utils::bold_message(_("Developer Mode Not Available.")), true, Gtk::MESSAGE_WARNING);
481
571
          dialog.set_secondary_text(_("Developer mode is not available because the file was opened over the network from a running Glom. Only the original file may be edited."));
482
572
          dialog.set_transient_for(*get_app_window());
483
573
          dialog.run();
484
574
        }
485
575
        else
486
576
        {
487
 
          Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Developer Mode Not Available")), true, Gtk::MESSAGE_WARNING);
 
577
          Gtk::MessageDialog dialog(Utils::bold_message(_("Developer Mode Not Available")), true, Gtk::MESSAGE_WARNING);
488
578
          dialog.set_secondary_text(_("Developer mode is not available. Check that you have sufficient database access rights and that the glom file is not read-only."));
489
579
          dialog.set_transient_for(*get_app_window());
490
580
          dialog.run();
491
581
        }
492
582
      }
493
 
      else if(document->get_document_format_version() < Document_Glom::get_latest_known_document_format_version())
 
583
      else if(document->get_document_format_version() < Document::get_latest_known_document_format_version())
494
584
      {
495
 
        Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("Saving in New Document Format")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
 
585
        Gtk::MessageDialog dialog(Utils::bold_message(_("Saving in New Document Format")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
496
586
        dialog.set_secondary_text(_("The document was created by an earlier version of the application. Making changes to the document will mean that the document cannot be opened by some earlier versions of the application."));
497
587
        dialog.set_transient_for(*get_app_window());
498
588
        dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
518
608
{
519
609
  if(action &&  action->get_active())
520
610
  {
521
 
    Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
 
611
    Document* document = dynamic_cast<Document*>(get_document());
522
612
    if(document)
523
613
    {
524
614
      //Avoid double signals:
532
622
{
533
623
  //Start with a sequence based on the Details view:
534
624
  //The user can changed this by clicking the button in the FileChooser:
535
 
  Document_Glom::type_list_layout_groups mapGroupSequence =  get_document()->get_data_layout_groups_plus_new_fields("details", m_table_name);
 
625
  Document* document = get_document();
 
626
  if(!document)
 
627
    return;
 
628
 
 
629
  Document::type_list_layout_groups mapGroupSequence = document->get_data_layout_groups_plus_new_fields("details", m_table_name, get_active_layout_platform(document));
536
630
 
537
631
  Gtk::Window* pWindowApp = get_app_window();
538
632
  g_assert(pWindowApp);
547
641
 
548
642
  //Ask the user for the new file location, and to optionally modify the format:
549
643
  FileChooser_Export dialog;
 
644
  dialog.set_transient_for(*get_app_window());
 
645
  dialog.set_do_overwrite_confirmation();
550
646
  dialog.set_export_layout(mapGroupSequence, m_table_name, get_document());
551
647
  const int response = dialog.run();
552
648
  dialog.hide();
554
650
  if((response == Gtk::RESPONSE_CANCEL) || (response == Gtk::RESPONSE_DELETE_EVENT))
555
651
    return;
556
652
 
557
 
  const std::string filepath = dialog.get_filename();
 
653
  std::string filepath = dialog.get_filename();
558
654
  if(filepath.empty())
559
655
    return;
560
656
 
 
657
  filepath = Utils::get_filepath_with_extension(filepath, "csv");
 
658
 
561
659
  dialog.get_layout_groups(mapGroupSequence);
 
660
  //std::cout << "DEBUG 0: mapGroupSequence.size()=" << mapGroupSequence.size() << std::endl;
562
661
 
563
662
  //const int index_primary_key = fieldsSequence.size() - 1;
564
663
 
574
673
  export_data_to_stream(the_stream, found_set, mapGroupSequence);
575
674
}
576
675
 
577
 
void Frame_Glom::export_data_to_string(Glib::ustring& the_string, const FoundSet& found_set, const Document_Glom::type_list_layout_groups& sequence)
578
 
{
579
 
  type_vecLayoutFields fieldsSequence = get_table_fields_to_show_for_sequence(found_set.m_table_name, sequence);
580
 
 
581
 
  if(fieldsSequence.empty())
582
 
    return;
583
 
 
584
 
  const Glib::ustring query = Utils::build_sql_select_with_where_clause(found_set.m_table_name, fieldsSequence, found_set.m_where_clause, found_set.m_extra_join, found_set.m_sort_clause, found_set.m_extra_group_by);
585
 
 
586
 
  //TODO: Lock the database (prevent changes) during export.
587
 
  Glib::RefPtr<Gnome::Gda::DataModel> result = query_execute(query, get_app_window());
 
676
//TODO: Reduce copy/pasting in these export_data_to_*() methods:
 
677
void Frame_Glom::export_data_to_vector(Document::type_example_rows& the_vector, const FoundSet& found_set, const Document::type_list_layout_groups& sequence)
 
678
{
 
679
  type_vecLayoutFields fieldsSequence = get_table_fields_to_show_for_sequence(found_set.m_table_name, sequence);
 
680
 
 
681
  if(fieldsSequence.empty())
 
682
  {
 
683
    std::cerr << "Glom: Frame_Glom::export_data_to_string(): No fields in sequence." << std::endl;
 
684
    return;
 
685
  }
 
686
 
 
687
  const Glib::ustring query = Utils::build_sql_select_with_where_clause(found_set.m_table_name, fieldsSequence, found_set.m_where_clause, found_set.m_extra_join, found_set.m_sort_clause, found_set.m_extra_group_by);
 
688
 
 
689
  //TODO: Lock the database (prevent changes) during export.
 
690
  Glib::RefPtr<Gnome::Gda::DataModel> result = query_execute_select(query);
 
691
 
 
692
  guint rows_count = 0;
 
693
  if(result)
 
694
    rows_count = result->get_n_rows();
 
695
 
 
696
  if(rows_count)
 
697
  {
 
698
    const guint columns_count = result->get_n_columns();
 
699
 
 
700
    for(guint row_index = 0; row_index < rows_count; ++row_index)
 
701
    {
 
702
        Document::type_row_data row_data;
 
703
 
 
704
        for(guint col_index = 0; col_index < columns_count; ++col_index)
 
705
        {
 
706
          const Gnome::Gda::Value value = result->get_value_at(col_index, row_index);
 
707
 
 
708
          sharedptr<LayoutItem_Field> layout_item = fieldsSequence[col_index];
 
709
          //if(layout_item->m_field.get_glom_type() != Field::TYPE_IMAGE) //This is too much data.
 
710
          //{
 
711
 
 
712
            //Output data in canonical SQL format, ignoring the user's locale, and ignoring the layout formatting:
 
713
            row_data.push_back(value);  //TODO_Performance: reserve the size.
 
714
 
 
715
            //if(layout_item->m_field.get_glom_type() == Field::TYPE_IMAGE) //This is too much data.
 
716
            //{
 
717
             //std::cout << "  field name=" << layout_item->get_name() << ", value=" << layout_item->m_field.sql(value) << std::endl;
 
718
            //}
 
719
        }
 
720
 
 
721
        //std::cout << " row_string=" << row_string << std::endl;
 
722
        the_vector.push_back(row_data); //TODO_Performance: Reserve the size.
 
723
    }
 
724
  }
 
725
}
 
726
 
 
727
void Frame_Glom::export_data_to_string(Glib::ustring& the_string, const FoundSet& found_set, const Document::type_list_layout_groups& sequence)
 
728
{
 
729
  type_vecLayoutFields fieldsSequence = get_table_fields_to_show_for_sequence(found_set.m_table_name, sequence);
 
730
 
 
731
  if(fieldsSequence.empty())
 
732
  {
 
733
    std::cerr << "Glom: Frame_Glom::export_data_to_string(): No fields in sequence." << std::endl;
 
734
    return;
 
735
  }
 
736
 
 
737
  const Glib::ustring query = Utils::build_sql_select_with_where_clause(found_set.m_table_name, fieldsSequence, found_set.m_where_clause, found_set.m_extra_join, found_set.m_sort_clause, found_set.m_extra_group_by);
 
738
 
 
739
  //TODO: Lock the database (prevent changes) during export.
 
740
  Glib::RefPtr<Gnome::Gda::DataModel> result = query_execute_select(query);
588
741
 
589
742
  guint rows_count = 0;
590
743
  if(result)
609
762
              row_string += ",";
610
763
 
611
764
            //Output data in canonical SQL format, ignoring the user's locale, and ignoring the layout formatting:
612
 
            row_string += layout_item->get_full_field_details()->sql(value);
 
765
            row_string += layout_item->get_full_field_details()->to_file_format(value);
613
766
 
614
767
            //if(layout_item->m_field.get_glom_type() == Field::TYPE_IMAGE) //This is too much data.
615
768
            //{
623
776
  }
624
777
}
625
778
 
626
 
void Frame_Glom::export_data_to_stream(std::ostream& the_stream, const FoundSet& found_set, const Document_Glom::type_list_layout_groups& sequence)
 
779
void Frame_Glom::export_data_to_stream(std::ostream& the_stream, const FoundSet& found_set, const Document::type_list_layout_groups& sequence)
627
780
{
628
781
  type_vecLayoutFields fieldsSequence = get_table_fields_to_show_for_sequence(found_set.m_table_name, sequence);
629
782
 
630
783
  if(fieldsSequence.empty())
 
784
  {
 
785
    std::cerr << "Glom: Frame_Glom::export_data_to_stream(): No fields in sequence." << std::endl;
631
786
    return;
 
787
  }
632
788
 
633
789
  const Glib::ustring query = Utils::build_sql_select_with_where_clause(found_set.m_table_name, fieldsSequence, found_set.m_where_clause, found_set.m_extra_join, found_set.m_sort_clause, found_set.m_extra_group_by);
634
 
 
 
790
 
635
791
  //TODO: Lock the database (prevent changes) during export.
636
 
  Glib::RefPtr<Gnome::Gda::DataModel> result = query_execute(query, get_app_window());
 
792
  Glib::RefPtr<Gnome::Gda::DataModel> result = query_execute_select(query);
637
793
 
638
794
  guint rows_count = 0;
639
795
  if(result)
658
814
              row_string += ",";
659
815
 
660
816
            //Output data in canonical SQL format, ignoring the user's locale, and ignoring the layout formatting:
661
 
            row_string += layout_item->get_full_field_details()->sql(value);
 
817
            sharedptr<const Field> field = layout_item->get_full_field_details();
 
818
            if(!field)
 
819
            {
 
820
              std::cerr << "Glom: Frame_Glom::export_data_to_stream(): A field was null." << std::endl;
 
821
              return;
 
822
            }
 
823
 
 
824
            const Glib::ustring field_text = field->to_file_format(value);
662
825
 
663
826
            if(layout_item->get_glom_type() == Field::TYPE_IMAGE) //This is too much data.
664
827
            {
665
 
              if(!Conversions::value_is_empty(value))
666
 
                std::cout << "  field name=" << layout_item->get_name() << ", image value not empty=" << std::endl;
667
 
            }
 
828
              // Some extra debug checks, 
 
829
              // though we believe that all these problems are now fixed in File::to_file_format():
 
830
 
 
831
              const char* newline_to_find = "\r\n";
 
832
              size_t pos = field_text.find_first_of(newline_to_find);
 
833
              if(pos != std::string::npos)
 
834
              {
 
835
                std::cerr << "export: binary data field text contains an unexpected newline: " << field_text << std::endl;
 
836
                continue;
 
837
              }
 
838
 
 
839
              const char* quote_to_find = "\"";
 
840
              pos = field_text.find_first_of(quote_to_find);
 
841
              if(pos != std::string::npos)
 
842
              {
 
843
                std::cerr << "export: binary data field text contains an unexpected quote: " << field_text << std::endl;
 
844
                continue;
 
845
              }
 
846
            }
 
847
            
 
848
            if(layout_item->get_glom_type() == Field::TYPE_TEXT)
 
849
            {
 
850
              //The CSV RFC says text may be quoted and should be if it has newlines:
 
851
              row_string += ("\"" + field_text + "\""); 
 
852
            }
 
853
            else
 
854
              row_string += field_text;
 
855
 
 
856
           
668
857
            //std::cout << "  field name=" << layout_item->get_name() << ", value=" << layout_item->m_field.sql(value) << std::endl;
669
858
          //}
670
859
        }
679
868
{
680
869
  if(m_table_name.empty())
681
870
  {
682
 
    Gtk::MessageDialog dialog(*get_app_window(), "There is no table to import data into", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
683
 
    dialog.run();
 
871
    Utils::show_ok_dialog(_("No Table"), _("There is no table in to which data could be imported."), *get_app_window(), Gtk::MESSAGE_ERROR);
684
872
  }
685
873
  else
686
874
  {
687
875
    Gtk::FileChooserDialog file_chooser(*get_app_window(), _("Choose a CSV file to open"), Gtk::FILE_CHOOSER_ACTION_OPEN);
688
876
    file_chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
689
877
    file_chooser.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
 
878
    Gtk::FileFilter filter_csv;
 
879
    filter_csv.set_name(_("CSV files"));
 
880
    filter_csv.add_mime_type("text/csv");
 
881
    file_chooser.add_filter(filter_csv);
 
882
    Gtk::FileFilter filter_any;
 
883
    filter_any.set_name(_("All files"));
 
884
    filter_any.add_pattern("*");
 
885
    file_chooser.add_filter(filter_any);
690
886
 
691
887
    if(file_chooser.run() == Gtk::RESPONSE_ACCEPT)
692
888
    {
 
889
      file_chooser.hide();
 
890
 
 
891
      //GtkBuilder can't find top-level objects (GtkAdjustments in this case),
 
892
      //that one top-level object references.
 
893
      //See http://bugzilla.gnome.org/show_bug.cgi?id=575714
 
894
      //so we need to this silliness. murrayc.
 
895
      std::list<Glib::ustring> builder_ids;
 
896
      builder_ids.push_back("dialog_import_csv");
 
897
      builder_ids.push_back("adjustment1");
 
898
 
693
899
      Dialog_Import_CSV* dialog = 0;
694
 
      Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom.glade"), "dialog_import_csv");
 
900
      Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom.glade"), builder_ids);
695
901
      refXml->get_widget_derived("dialog_import_csv", dialog);
696
902
      add_view(dialog);
697
903
 
698
 
      file_chooser.hide();
699
 
 
700
904
      dialog->import(file_chooser.get_uri(), m_table_name);
701
905
      while(Glom::Utils::dialog_run_with_help(dialog, "dialog_import_csv") == Gtk::RESPONSE_ACCEPT)
702
906
      {
703
907
        dialog->hide();
 
908
 
704
909
        Dialog_Import_CSV_Progress* progress_dialog = 0;
705
 
      Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom.glade"), "dialog_import_csv_progress");
 
910
 
 
911
        //GtkBuilder can't find top-level objects (GtkTextBuffer in this case),
 
912
        //that one top-level object references.
 
913
        //See http://bugzilla.gnome.org/show_bug.cgi?id=575714
 
914
        //so we need to this silliness. murrayc.
 
915
        std::list<Glib::ustring> builder_ids;
 
916
        builder_ids.push_back("dialog_import_csv_progress");
 
917
        builder_ids.push_back("textbuffer1");
 
918
 
 
919
        Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom.glade"), builder_ids);
706
920
        refXml->get_widget_derived("dialog_import_csv_progress", progress_dialog);
707
921
        add_view(progress_dialog);
708
922
 
712
926
 
713
927
        remove_view(progress_dialog);
714
928
        delete progress_dialog;
 
929
        progress_dialog = 0;
715
930
 
716
931
        // Force update from database so the newly added entries are shown
717
932
        show_table_refresh();
727
942
    }
728
943
  }
729
944
}
 
945
 
 
946
void Frame_Glom::on_menu_file_toggle_share(const Glib::RefPtr<Gtk::ToggleAction>& action)
 
947
{
 
948
  if(!action)
 
949
  {
 
950
    std::cerr << "Frame_Glom::on_menu_file_toggle_share(): action was null." << std::endl;
 
951
  }
 
952
 
 
953
  //Prevent this change if not in developer mode,
 
954
  //though the menu item should be disabled then anyway.
 
955
  Document* document = dynamic_cast<Document*>(get_document());
 
956
  if(!document || document->get_userlevel() != AppState::USERLEVEL_DEVELOPER)
 
957
    return;
 
958
 
 
959
  bool shared = action->get_active(); //Whether it should be shared.
 
960
  if(shared == document->get_network_shared())
 
961
  {
 
962
    //Do nothing, because things are already as requested.
 
963
    //This is probably just an extra signal emitted when we set the toggle in the UI.
 
964
    //So we avoid the endless loop:
 
965
    return;
 
966
  }
 
967
 
 
968
  bool change = true;
 
969
 
 
970
  //Ask user for confirmation:
 
971
  //TODO: Warn that this will be saved as the default if doing this in developer mode?
 
972
  if(shared)
 
973
  {
 
974
    Gtk::MessageDialog dialog(Utils::bold_message(_("Share On Network")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
 
975
    dialog.set_secondary_text(_("Are you sure that you wish to allow other users on the network to use this database?"));
 
976
    dialog.set_transient_for(*get_app_window());
 
977
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
 
978
    dialog.add_button(_("_Share"), Gtk::RESPONSE_OK);
 
979
 
 
980
    const int response = dialog.run();
 
981
    dialog.hide();
 
982
    if(response == Gtk::RESPONSE_OK)
 
983
    {
 
984
      shared = true;
 
985
 
 
986
      //Ask for a user/password if none is set:
 
987
      const bool real_user_exists = Privs::get_developer_user_exists_with_password();
 
988
      if(!real_user_exists)
 
989
      {
 
990
        //Ask for an initial user:
 
991
        Glib::ustring user, password;
 
992
        const bool initial_password_provided = connection_request_initial_password(user, password);
 
993
        bool added = false;
 
994
        if(initial_password_provided)
 
995
          added = add_user(user, password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
 
996
        
 
997
        if(initial_password_provided && added)
 
998
        {
 
999
          //Use the new user/password from now on:
 
1000
          ConnectionPool* connectionpool = ConnectionPool::get_instance();      
 
1001
          connectionpool->set_user(user);
 
1002
          connectionpool->set_password(password);
 
1003
        }
 
1004
        else
 
1005
        {
 
1006
          shared = false;
 
1007
          change = false;
 
1008
        }
 
1009
      }
 
1010
      else
 
1011
      {
 
1012
        //Ask for the password of a developer user, to 
 
1013
        //a) Check that the user knows it, so he won't lose access.
 
1014
        //b) Reconnect as that user so we can remove the default user.
 
1015
        //TODO: Check that this user is a developer.
 
1016
        bool database_not_found = false; //Ignored;
 
1017
        const bool dev_password_known = connection_request_password_and_attempt(database_not_found, "" ,"", true /* alternative text */);
 
1018
        if(!dev_password_known)
 
1019
        {
 
1020
          shared = false;
 
1021
          change = false;
 
1022
        }
 
1023
      }
 
1024
 
 
1025
      if(change) //If nothing has gone wrong so far.
 
1026
      {
 
1027
        //Remove the default no-password user, because that would be a security hole:
 
1028
        //We do this after adding/using the non-default user, because we can't 
 
1029
        //remove a currently-used user.
 
1030
        const bool default_user_exists = Privs::get_default_developer_user_exists();
 
1031
        if(default_user_exists)
 
1032
        {
 
1033
          //Force a reconnection with the new password:
 
1034
          //ConnectionPool* connectionpool = ConnectionPool::get_instance();      
 
1035
 
 
1036
          //Remove it, after stopping it from being the database owner: 
 
1037
          bool disabled = true;
 
1038
          Glib::ustring default_password;
 
1039
          const Glib::ustring default_user = Privs::get_default_developer_user_name(default_password);
 
1040
 
 
1041
          ConnectionPool* connectionpool = ConnectionPool::get_instance();
 
1042
          const bool reowned = set_database_owner_user(connectionpool->get_user());
 
1043
          bool removed = false;
 
1044
          if(reowned)
 
1045
            removed = remove_user(default_user);
 
1046
 
 
1047
          if(!removed)
 
1048
          {
 
1049
            //This is a workaround.
 
1050
            //Try to revoke it instead.
 
1051
            //TODO: Discover how to make remove_user() succeed.
 
1052
            disabled = disable_user(default_user);
 
1053
          }
 
1054
 
 
1055
          if(!reowned || !(removed || disabled))
 
1056
          {
 
1057
            std::cerr << "Frame_Glom::on_menu_file_toggle_share(): Failed to reown and remove/revoke default user." << std::endl;
 
1058
            shared = false;
 
1059
            change = false;
 
1060
          }
 
1061
        }
 
1062
      }
 
1063
    }
 
1064
    else
 
1065
    {
 
1066
      shared = false;
 
1067
      change = false;
 
1068
    }
 
1069
  }
 
1070
  else //not shared:
 
1071
  {
 
1072
    //TODO: Warn about connected users if possible.
 
1073
    Gtk::MessageDialog dialog(Utils::bold_message(_("Stop Sharing On Network")), true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE);
 
1074
    dialog.set_secondary_text(_("Are you sure that you wish to prevent other users on the network from using this database?"));
 
1075
    dialog.set_transient_for(*get_app_window());
 
1076
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
 
1077
    dialog.add_button(_("_Stop Sharing"), Gtk::RESPONSE_OK);
 
1078
 
 
1079
    const int response = dialog.run();
 
1080
    dialog.hide();
 
1081
    if(response == Gtk::RESPONSE_OK)
 
1082
    {
 
1083
      shared = false;
 
1084
 
 
1085
      //Make sure the default no-password user exists:
 
1086
      const bool default_user_exists = Privs::get_default_developer_user_exists();
 
1087
      if(!default_user_exists)
 
1088
      {
 
1089
        //Add it:
 
1090
        Glib::ustring default_password;
 
1091
        const Glib::ustring default_user = Privs::get_default_developer_user_name(default_password);
 
1092
 
 
1093
        const bool added = add_user(default_user, default_password, GLOM_STANDARD_GROUP_NAME_DEVELOPER);
 
1094
        if(!added)
 
1095
        {
 
1096
           shared = true;
 
1097
           change = false;
 
1098
        }
 
1099
      }
 
1100
    }
 
1101
    else
 
1102
    {
 
1103
      shared = true;
 
1104
      change = false;
 
1105
    }
 
1106
  }
 
1107
 
 
1108
  if(document)
 
1109
    document->set_network_shared(shared);
 
1110
 
 
1111
 
 
1112
  //Stop the self-hosted database server,
 
1113
  //change its configuration,
 
1114
  //and start it again:
 
1115
  if(change)
 
1116
  {
 
1117
    ConnectionPool* connectionpool = ConnectionPool::get_instance();
 
1118
    sharedptr<SharedConnection> sharedconnection = connectionpool->connect();
 
1119
    if(sharedconnection)
 
1120
    {
 
1121
      sharedconnection->close();
 
1122
      sharedconnection.clear();
 
1123
    }   
 
1124
 
 
1125
    connectionpool->cleanup( sigc::mem_fun(*this, &Frame_Glom::on_connection_cleanup_progress) );
 
1126
 
 
1127
    if(m_dialog_progess_connection_cleanup)
 
1128
    {
 
1129
      delete m_dialog_progess_connection_cleanup;
 
1130
      m_dialog_progess_connection_cleanup = 0;
 
1131
    }
 
1132
 
 
1133
    connectionpool->set_network_shared(sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress), shared);
 
1134
    connectionpool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) );
 
1135
    connectionpool->set_ready_to_connect();
 
1136
 
 
1137
    if(m_dialog_progess_connection_startup)
 
1138
    {
 
1139
      delete m_dialog_progess_connection_startup;
 
1140
      m_dialog_progess_connection_startup = 0;
 
1141
    }
 
1142
  }
 
1143
 
 
1144
  //Update the UI:
 
1145
  App_Glom* pApp = dynamic_cast<App_Glom*>(get_app_window());
 
1146
  if(pApp)
 
1147
  {
 
1148
    pApp->update_network_shared_ui();
 
1149
  }
 
1150
}
730
1151
#endif // !GLOM_ENABLE_CLIENT_ONLY
731
1152
 
732
1153
void Frame_Glom::on_menu_file_print()
759
1180
{
760
1181
  //This can take quite a long time, flicking between 1 or 2 intermediate screens. 
761
1182
  //It shouldn't, but until we fix that, let's show the busy cursor while it's working:
762
 
  Bakery::BusyCursor busy_cursor(get_app_window());
 
1183
  BusyCursor busy_cursor(get_app_window());
763
1184
 
764
1185
  const bool previously_in_data_mode = (m_Mode == MODE_Data);
765
1186
 
771
1192
  if(previously_in_data_mode && (list_or_details == Notebook_Data::DATA_VIEW_Details))
772
1193
    m_Notebook_Data.set_current_view(Notebook_Data::DATA_VIEW_List);
773
1194
 
774
 
  if(set_mode(MODE_Find))
 
1195
  if(!set_mode(MODE_Find))
 
1196
    return;
 
1197
  
 
1198
  show_table(m_table_name);
 
1199
 
 
1200
  if(previously_in_data_mode)
775
1201
  {
776
 
    show_table(m_table_name);
777
 
 
778
 
    if(previously_in_data_mode)
779
 
    {
780
 
      //Show the same layout in Find mode as was just being viewed in Data mode:
781
 
      m_Notebook_Find.set_current_view(list_or_details);
782
 
    }
 
1202
    //Show the same layout in Find mode as was just being viewed in Data mode:
 
1203
    m_Notebook_Find.set_current_view(list_or_details);
783
1204
  }
 
1205
  
 
1206
  #ifdef GLOM_ENABLE_CLIENT_ONLY
 
1207
  Gtk::Window* parent = get_app_window();
 
1208
  g_assert(parent);
 
1209
  if(parent)
 
1210
    m_maemo_window_find.set_transient_for(*parent);
 
1211
    
 
1212
  m_maemo_window_find.show(); //TODO: Switch back to data on hide?
 
1213
  #endif
784
1214
}
785
1215
 
786
1216
#ifndef GLOM_ENABLE_CLIENT_ONLY
812
1242
 
813
1243
  try
814
1244
  {
815
 
    Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "dialog_add_related_table");
 
1245
    Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_add_related_table");
816
1246
 
817
1247
    refXml->get_widget_derived("dialog_add_related_table", m_dialog_addrelatedtable);
818
1248
  }
819
 
  catch(const Gnome::Glade::XmlError& ex)
 
1249
  catch(const Gtk::BuilderError& ex)
820
1250
  {
821
1251
    std::cerr << ex.what() << std::endl;
822
1252
  }
915
1345
      relationship->set_allow_edit(true);
916
1346
      relationship->set_auto_create(true);
917
1347
 
918
 
      Document_Glom* document = get_document();
 
1348
      Document* document = get_document();
919
1349
      if(!document)
920
1350
        return;
921
1351
 
950
1380
  if(open_default)
951
1381
    default_table_name = get_document()->get_default_table();
952
1382
  
 
1383
#ifndef GLOM_ENABLE_MAEMO
953
1384
  //Create the dialog, if it has not already been created:
954
1385
  if(!m_pBox_Tables)
955
1386
  {
956
1387
    Utils::get_glade_widget_derived_with_warning("box_navigation_tables", m_pBox_Tables);
957
 
    m_pDialog_Tables = new Dialog_Glom(m_pBox_Tables);
 
1388
    m_pDialog_Tables = new Window_BoxHolder(m_pBox_Tables, _("Tables"));
958
1389
    m_pDialog_Tables->signal_hide().connect(sigc::mem_fun(*this, &Frame_Glom::on_dialog_tables_hide));
959
1390
 
960
1391
    Gtk::Window* pWindow = get_app_window();
961
1392
    if(pWindow)
962
1393
      m_pDialog_Tables->set_transient_for(*pWindow);
963
1394
 
964
 
    m_pDialog_Tables->get_vbox()->pack_start(*m_pBox_Tables);
965
1395
    m_pDialog_Tables->set_default_size(300, 400);
966
1396
    m_pBox_Tables->show_all();
967
1397
    add_view(m_pBox_Tables);
971
1401
  }
972
1402
 
973
1403
  {
974
 
    Bakery::BusyCursor busy_cursor(get_app_window());
 
1404
    BusyCursor busy_cursor(get_app_window());
975
1405
    m_pBox_Tables->init_db_details();
976
1406
  }
 
1407
  #endif // !GLOM_ENABLE_CLIENT_ONLY
977
1408
 
978
1409
  //Let the user choose a table:
979
1410
  //m_pDialog_Tables->set_policy(false, true, false); //TODO_port
985
1416
  }
986
1417
  else
987
1418
  {
 
1419
    #ifndef GLOM_ENABLE_CLIENT_ONLY
988
1420
    m_pDialog_Tables->show();
 
1421
    #else
 
1422
    //For Maemo: TODO
 
1423
    #endif // !GLOM_ENABLE_CLIENT_ONLY
989
1424
  }
 
1425
 
990
1426
}
991
1427
 
992
1428
const Gtk::Window* Frame_Glom::get_app_window() const
1033
1469
    Hildon::Note note(Hildon::NOTE_TYPE_INFORMATION, *get_app_window(), message);
1034
1470
    note.run();
1035
1471
#else
1036
 
    Gtk::MessageDialog dialog(Bakery::App_Gtk::util_bold_message(_("No Find Criteria")), true, Gtk::MESSAGE_WARNING );
 
1472
    Gtk::MessageDialog dialog(Utils::bold_message(_("No Find Criteria")), true, Gtk::MESSAGE_WARNING );
1037
1473
    dialog.set_secondary_text(message);
1038
1474
    dialog.set_transient_for(*get_app_window());
1039
1475
    dialog.run();
1058
1494
    bool records_found = false;
1059
1495
 
1060
1496
    { //Extra scope, to control the lifetime of the busy cursor. 
1061
 
      Bakery::BusyCursor busy_cursor(pApp);
 
1497
      BusyCursor busy_cursor(pApp);
1062
1498
 
1063
1499
      pApp->set_mode_data();
1064
1500
 
1105
1541
 
1106
1542
void Frame_Glom::show_table_title()
1107
1543
{
1108
 
  if(get_document())
 
1544
  Document* document = dynamic_cast<Document*>(get_document());
 
1545
  if(!document)
 
1546
    return;
 
1547
 
 
1548
  //Show the table title:
 
1549
  Glib::ustring table_label = document->get_table_title(m_table_name);
 
1550
  if(!table_label.empty())
1109
1551
  {
1110
 
    //Show the table title:
1111
 
    Glib::ustring table_label = get_document()->get_table_title(m_table_name);
1112
 
    if(!table_label.empty())
1113
 
    {
1114
 
      Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
1115
 
      if(document)
1116
 
      {
1117
 
        if(document->get_userlevel() == AppState::USERLEVEL_DEVELOPER)
1118
 
          table_label += " (" + m_table_name + ")"; //Show the table name as well, if in developer mode.
1119
 
      }
1120
 
    }
1121
 
    else //Use the table name if there is no table title.
1122
 
      table_label = m_table_name;
 
1552
    if(document->get_userlevel() == AppState::USERLEVEL_DEVELOPER)
 
1553
      table_label += " (" + m_table_name + ")"; //Show the table name as well, if in developer mode.
 
1554
  }
 
1555
  else //Use the table name if there is no table title.
 
1556
    table_label = m_table_name;
1123
1557
 
1124
1558
#ifdef GLOM_ENABLE_MAEMO
1125
 
    // xx-large is too large on maemo, taking away too much (vertical)
1126
 
    // screen estate
1127
 
    m_pLabel_Table->set_markup("<b>" + table_label + "</b>");
 
1559
  //Show the system's human-readable title and the table title in 
 
1560
  //the window's title bar:
 
1561
  Gtk::Window* app = get_app_window();
 
1562
  if(app)
 
1563
    app->set_title(document->get_name() + ": " + table_label);
 
1564
 
 
1565
  // xx-large is too large on maemo, using far too much (vertical) space.
 
1566
  // We hide this anyway: m_pLabel_Table->set_markup("<b>" + table_label + "</b>");
1128
1567
#else
1129
 
    m_pLabel_Table->set_markup("<b><span size=\"xx-large\">" + table_label + "</span></b>"); //Show the table title in large text, because it's very important to the user.
 
1568
  m_pLabel_Table->set_markup("<b><span size=\"xx-large\">" + table_label + "</span></b>"); //Show the table title in large text, because it's very important to the user.
1130
1569
#endif
1131
 
  }
1132
1570
}
1133
1571
 
1134
1572
#ifndef GLOM_ENABLE_CLIENT_ONLY
1140
1578
  //TODO_performance: There are a lot of temporary Field and Column instances here, with a lot of string copying.
1141
1579
 
1142
1580
  //For instance, changed field details, or new fields, or removed fields.
1143
 
  typedef Box_DB_Table::type_vecFields type_vecFields;
 
1581
  typedef Box_DB_Table::type_vec_fields type_vec_fields;
1144
1582
 
1145
1583
  //Get the fields information from the database:
1146
 
  Base_DB::type_vecFields fieldsDatabase = Base_DB::get_fields_for_table_from_database(m_table_name);
 
1584
  Base_DB::type_vec_fields fieldsDatabase = Base_DB::get_fields_for_table_from_database(m_table_name);
1147
1585
 
1148
 
  Document_Glom* pDoc = dynamic_cast<const Document_Glom*>(get_document());
 
1586
  Document* pDoc = dynamic_cast<const Document*>(get_document());
1149
1587
  if(pDoc)
1150
1588
  {
1151
1589
    bool document_must_be_updated = false;
1152
1590
 
1153
1591
    //Get the fields information from the document.
1154
1592
    //and add to, or update Document's list of fields:
1155
 
    type_vecFields fieldsDocument = pDoc->get_table_fields(m_table_name);
 
1593
    type_vec_fields fieldsDocument = pDoc->get_table_fields(m_table_name);
1156
1594
 
1157
 
    for(Base_DB::type_vecFields::const_iterator iter = fieldsDatabase.begin(); iter != fieldsDatabase.end(); ++iter)
 
1595
    for(Base_DB::type_vec_fields::const_iterator iter = fieldsDatabase.begin(); iter != fieldsDatabase.end(); ++iter)
1158
1596
    {
1159
1597
      sharedptr<Field> field_database = *iter;
1160
1598
      if(field_database)
1161
1599
      {
1162
1600
        //Is the field already in the document?
1163
 
        type_vecFields::iterator iterFindDoc = std::find_if( fieldsDocument.begin(), fieldsDocument.end(), predicate_FieldHasName<Field>( field_database->get_name() ) );
 
1601
        type_vec_fields::iterator iterFindDoc = std::find_if( fieldsDocument.begin(), fieldsDocument.end(), predicate_FieldHasName<Field>( field_database->get_name() ) );
1164
1602
        if(iterFindDoc == fieldsDocument.end()) //If it was not found:
1165
1603
        {
1166
1604
          //Add it
1199
1637
 
1200
1638
    //Remove fields that are no longer in the database:
1201
1639
    //TODO_performance: This is incredibly inefficient - but it's difficut to erase() items while iterating over them.
1202
 
    type_vecFields fieldsActual;
1203
 
    for(type_vecFields::const_iterator iter = fieldsDocument.begin(); iter != fieldsDocument.end(); ++iter)
 
1640
    type_vec_fields fieldsActual;
 
1641
    for(type_vec_fields::const_iterator iter = fieldsDocument.begin(); iter != fieldsDocument.end(); ++iter)
1204
1642
    {
1205
1643
      sharedptr<Field> field = *iter;
1206
1644
 
1207
1645
      //Check whether it's in the database:
1208
 
      type_vecFields::iterator iterFindDatabase = std::find_if( fieldsDatabase.begin(), fieldsDatabase.end(), predicate_FieldHasName<Field>( field->get_name() ) );
 
1646
      type_vec_fields::iterator iterFindDatabase = std::find_if( fieldsDatabase.begin(), fieldsDatabase.end(), predicate_FieldHasName<Field>( field->get_name() ) );
1209
1647
      if(iterFindDatabase != fieldsDatabase.end()) //If it was found
1210
1648
      {
1211
1649
        fieldsActual.push_back(field);
1222
1660
}
1223
1661
#endif // !GLOM_ENABLE_CLIENT_ONLY
1224
1662
 
1225
 
void Frame_Glom::set_document(Document_Glom* pDocument)
 
1663
void Frame_Glom::set_document(Document* pDocument)
1226
1664
{
1227
1665
  View_Composite_Glom::set_document(pDocument);
1228
1666
 
1229
 
  Document_Glom* document = get_document();
 
1667
  Document* document = get_document();
1230
1668
  if(document)
1231
1669
  {
1232
1670
    //Connect to a signal that is only on the derived document class:
1237
1675
  }
1238
1676
}
1239
1677
 
1240
 
void Frame_Glom::show_system_name()
1241
 
{
1242
 
  // Don't show the document name on maemo. The label is on top of the window,
1243
 
  // and the document name is already shown in the window title (note that
1244
 
  // there is not even a menu between them as in the non-maemo version). This
1245
 
  // looks a bit strange and takes unnecessarily vertical screen space.
1246
 
#ifndef GLOM_ENABLE_MAEMO
1247
 
  const SystemPrefs prefs = get_database_preferences();
1248
 
  const Glib::ustring org = prefs.m_org_name;
1249
 
  const Glib::ustring name = prefs.m_name;
1250
 
 
1251
 
  Glib::ustring system_name = org;
1252
 
  if(!system_name.empty() && !name.empty())
1253
 
    system_name += ": ";
1254
 
 
1255
 
  system_name += name;
1256
 
 
1257
 
  m_pLabel_Name->set_text ( Bakery::App_Gtk::util_bold_message(system_name) );
1258
 
  m_pLabel_Name->set_use_markup();
1259
 
  m_pLabel_Name->show();
1260
 
#endif // !GLOM_ENABLE_MAEMO
1261
 
}
1262
 
 
1263
1678
void Frame_Glom::load_from_document()
1264
1679
{
1265
 
  Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
 
1680
  Document* document = dynamic_cast<Document*>(get_document());
1266
1681
  if(document)
1267
1682
  {
1268
1683
    //Call base class:
1276
1691
  Dialog_Database_Preferences* dialog = 0;
1277
1692
  try
1278
1693
  {
1279
 
    Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "dialog_database_preferences");
 
1694
    Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_database_preferences");
1280
1695
    refXml->get_widget_derived("dialog_database_preferences", dialog);
1281
1696
    if(dialog)
1282
1697
    {
1289
1704
      remove_view(dialog);
1290
1705
      delete dialog;
1291
1706
 
1292
 
      show_system_name(); //In case it has changed.
 
1707
      //show_system_name(); //In case it has changed.
1293
1708
    }
1294
1709
  }
1295
1710
 
1296
 
  catch(const Gnome::Glade::XmlError& ex)
 
1711
  catch(const Gtk::BuilderError& ex)
1297
1712
  {
1298
1713
    std::cerr << ex.what() << std::endl;
1299
1714
  }
1313
1728
  {
1314
1729
    try
1315
1730
    {
1316
 
      Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "window_design");
 
1731
      Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "window_design");
1317
1732
 
1318
1733
      refXml->get_widget_derived("window_design", m_pDialog_Fields);
1319
1734
      m_pDialog_Fields->signal_hide().connect( sigc::mem_fun(*this, &Frame_Glom::on_developer_dialog_hide));
1320
1735
    }
1321
 
    catch(const Gnome::Glade::XmlError& ex)
 
1736
    catch(const Gtk::BuilderError& ex)
1322
1737
    {
1323
1738
      std::cerr << ex.what() << std::endl;
1324
1739
    }
1326
1741
    add_view(m_pDialog_Fields);
1327
1742
  }
1328
1743
 
 
1744
  // Some database backends (SQLite) require the table to change no longer
 
1745
  // being in use when changing the records, so clear the database usage
 
1746
  // here. We reshow everything in on_developer_dialog_hide anyway.
 
1747
  show_no_table();
 
1748
 
 
1749
  // Remember the old table name so that we re-show the previous table as
 
1750
  // soon as the dialog has been closed.
 
1751
  m_table_name = table_name;
 
1752
 
1329
1753
  m_pDialog_Fields->set_transient_for(parent);
1330
1754
  m_pDialog_Fields->init_db_details(table_name);
1331
1755
  m_pDialog_Fields->show();
1400
1824
  Dialog_GroupsList* dialog = 0;
1401
1825
  try
1402
1826
  {
1403
 
    Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "window_groups");
 
1827
    Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "window_groups");
1404
1828
 
1405
1829
    refXml->get_widget_derived("window_groups", dialog);
1406
1830
  }
1407
 
  catch(const Gnome::Glade::XmlError& ex)
 
1831
  catch(const Gtk::BuilderError& ex)
1408
1832
  {
1409
1833
    std::cerr << ex.what() << std::endl;
1410
1834
  }
1452
1876
  if(!m_pBox_Reports)
1453
1877
  {
1454
1878
    Utils::get_glade_developer_widget_derived_with_warning("box_reports", m_pBox_Reports);
1455
 
    m_pDialog_Reports = new Dialog_Glom(m_pBox_Reports);
 
1879
    m_pDialog_Reports = new Window_BoxHolder(m_pBox_Reports);
1456
1880
    m_pDialog_Reports->set_transient_for(*(get_app_window()));
1457
1881
 
1458
1882
    Utils::get_glade_developer_widget_derived_with_warning("window_report_layout", m_pDialogLayoutReport);
1460
1884
    m_pDialogLayoutReport->set_transient_for(*(get_app_window()));
1461
1885
    m_pDialogLayoutReport->signal_hide().connect( sigc::mem_fun(*this, &Frame_Glom::on_dialog_layout_report_hide) );
1462
1886
 
1463
 
    m_pDialog_Reports->get_vbox()->pack_start(*m_pBox_Reports);
1464
1887
    m_pDialog_Reports->set_default_size(300, 400);
1465
1888
    m_pBox_Reports->show_all();
1466
1889
 
1485
1908
  if(!m_pBox_PrintLayouts)
1486
1909
  {
1487
1910
    Utils::get_glade_developer_widget_derived_with_warning("box_print_layouts", m_pBox_PrintLayouts);
1488
 
    m_pDialog_PrintLayouts = new Dialog_Glom(m_pBox_PrintLayouts);
 
1911
    m_pDialog_PrintLayouts = new Window_BoxHolder(m_pBox_PrintLayouts);
1489
1912
 
1490
 
    m_pDialog_PrintLayouts->get_vbox()->pack_start(*m_pBox_PrintLayouts);
1491
1913
    m_pDialog_PrintLayouts->set_default_size(300, 400);
1492
1914
    m_pBox_PrintLayouts->show_all();
1493
1915
    add_view(m_pBox_PrintLayouts);
1502
1924
void Frame_Glom::on_menu_developer_script_library()
1503
1925
{
1504
1926
  Dialog_ScriptLibrary* dialog = 0;
1505
 
  Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "dialog_script_library");
 
1927
  Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_script_library");
1506
1928
  refXml->get_widget_derived("dialog_script_library", dialog);
1507
1929
  dialog->set_transient_for(*(get_app_window()));
1508
1930
  add_view(dialog); //Give it access to the document.
1564
1986
}
1565
1987
#endif // !GLOM_ENABLE_CLIENT_ONLY
1566
1988
 
 
1989
namespace 
 
1990
{
 
1991
  void setup_connection_pool_from_document(Document* document)
 
1992
  {
 
1993
    ConnectionPool* connection_pool = ConnectionPool::get_instance();
 
1994
    switch(document->get_hosting_mode())
 
1995
    {
 
1996
#ifdef GLOM_ENABLE_POSTGRESQL
 
1997
 
 
1998
#ifndef GLOM_ENABLE_CLIENT_ONLY
 
1999
    case Document::HOSTING_MODE_POSTGRES_SELF:
 
2000
      {
 
2001
        ConnectionPoolBackends::PostgresSelfHosted* backend = new ConnectionPoolBackends::PostgresSelfHosted;
 
2002
        backend->set_self_hosting_data_uri(document->get_connection_self_hosted_directory_uri());
 
2003
        connection_pool->set_backend(std::auto_ptr<ConnectionPool::Backend>(backend));
 
2004
      }
 
2005
      break;
 
2006
#endif //GLOM_ENABLE_CLIENT_ONLY
 
2007
 
 
2008
    case Document::HOSTING_MODE_POSTGRES_CENTRAL:
 
2009
      {
 
2010
        ConnectionPoolBackends::PostgresCentralHosted* backend = new ConnectionPoolBackends::PostgresCentralHosted;
 
2011
        backend->set_host(document->get_connection_server());
 
2012
        backend->set_port(document->get_connection_port());
 
2013
        backend->set_try_other_ports(document->get_connection_try_other_ports());
 
2014
        connection_pool->set_backend(std::auto_ptr<ConnectionPool::Backend>(backend));
 
2015
      }
 
2016
      break;
 
2017
#endif //GLOM_ENABLE_POSTGRESQL
 
2018
 
 
2019
#ifdef GLOM_ENABLE_SQLITE
 
2020
    case Document::HOSTING_MODE_SQLITE:
 
2021
      {
 
2022
        ConnectionPoolBackends::Sqlite* backend = new ConnectionPoolBackends::Sqlite;
 
2023
        backend->set_database_directory_uri(document->get_connection_self_hosted_directory_uri());
 
2024
        connection_pool->set_backend(std::auto_ptr<ConnectionPool::Backend>(backend));
 
2025
      }
 
2026
      break;
 
2027
#endif // GLOM_ENABLE_SQLITE
 
2028
 
 
2029
    default:
 
2030
      //on_document_load() should have checked for this already, informing the user.
 
2031
      std::cerr << "Glom: setup_connection_pool_from_document(): Unhandled hosting mode: " << document->get_hosting_mode() << std::endl;
 
2032
      g_assert_not_reached();
 
2033
      break;
 
2034
    }
 
2035
 
 
2036
    // Might be overwritten later when actually attempting a connection:
 
2037
    connection_pool->set_user(document->get_connection_user());
 
2038
    connection_pool->set_database(document->get_connection_database());
 
2039
 
 
2040
    connection_pool->set_ready_to_connect();
 
2041
  }
 
2042
}
 
2043
 
 
2044
#ifndef GLOM_ENABLE_CLIENT_ONLY
 
2045
void Frame_Glom::on_connection_initialize_progress()
 
2046
{
 
2047
  if(!m_dialog_progess_connection_initialize)
 
2048
    m_dialog_progess_connection_initialize = Utils::get_and_show_pulse_dialog(_("Initializing Database Data"), get_app_window());
 
2049
        
 
2050
  m_dialog_progess_connection_initialize->pulse();
 
2051
}
 
2052
#endif //GLOM_ENABLE_CLIENT_ONLY
 
2053
 
 
2054
void Frame_Glom::on_connection_startup_progress()
 
2055
{
 
2056
  if(!m_dialog_progess_connection_startup)
 
2057
    m_dialog_progess_connection_startup = Utils::get_and_show_pulse_dialog(_("Starting Database Server"), get_app_window());
 
2058
        
 
2059
  m_dialog_progess_connection_startup->pulse();
 
2060
}
 
2061
 
 
2062
void Frame_Glom::on_connection_cleanup_progress()
 
2063
{
 
2064
  if(!m_dialog_progess_connection_cleanup)
 
2065
    m_dialog_progess_connection_cleanup = Utils::get_and_show_pulse_dialog(_("Stopping Database Server"), get_app_window());
 
2066
        
 
2067
  m_dialog_progess_connection_cleanup->pulse();
 
2068
}
 
2069
 
 
2070
bool Frame_Glom::handle_connection_initialize_errors(ConnectionPool::InitErrors error)
 
2071
{
 
2072
  Glib::ustring title;
 
2073
  Glib::ustring message;
 
2074
  
 
2075
  if(error == ConnectionPool::Backend::INITERROR_NONE)
 
2076
    return true;
 
2077
  else if(error == ConnectionPool::Backend::INITERROR_DIRECTORY_ALREADY_EXISTS)
 
2078
  {
 
2079
    title = _("Directory Already Exists");
 
2080
    message = _("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.");
 
2081
  }
 
2082
  else if (error == ConnectionPool::Backend::INITERROR_COULD_NOT_CREATE_DIRECTORY)
 
2083
  {
 
2084
    title = _("Could Not Create Directory");
 
2085
    message = _("There was an error when attempting to create the directory for the new database files.");
 
2086
  }
 
2087
  else if(error == ConnectionPool::Backend::INITERROR_COULD_NOT_START_SERVER)
 
2088
  {
 
2089
    title = _("Could Not Start Database Server");
 
2090
    message = _("There was an error when attempting to start the database server.");
 
2091
  }
 
2092
  
 
2093
  Utils::show_ok_dialog(title, message, *get_app_window(), Gtk::MESSAGE_ERROR);
 
2094
  
 
2095
  return false;
 
2096
}
 
2097
 
 
2098
#ifndef GLOM_ENABLE_CLIENT_ONLY
 
2099
bool Frame_Glom::connection_request_initial_password(Glib::ustring& user, Glib::ustring& password)
 
2100
{
 
2101
  //Intialze output parameters:
 
2102
  user = Glib::ustring();
 
2103
  password = Glib::ustring();
 
2104
 
 
2105
  Document* document = dynamic_cast<Document*>(get_document());
 
2106
  if(!document)
 
2107
    return false;
 
2108
 
 
2109
  //This is only useful for self-hosted postgres:
 
2110
  if(document->get_hosting_mode() != Document::HOSTING_MODE_POSTGRES_SELF)
 
2111
    return false;
 
2112
 
 
2113
  //Ask for a new username and password to specify when creating a new self-hosted database.
 
2114
  Dialog_InitialPassword* dialog = 0;
 
2115
  Glib::RefPtr<Gtk::Builder> refXml;
 
2116
 
 
2117
#ifdef GLIBMM_EXCEPTIONS_ENABLED
 
2118
  try
 
2119
  {
 
2120
    refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_initial_password");
 
2121
  }
 
2122
  catch(const Gtk::BuilderError& ex)
 
2123
  {
 
2124
    std::cerr << ex.what() << std::endl;
 
2125
    return false;
 
2126
  }
 
2127
#else
 
2128
  std::auto_ptr<Gtk::BuilderError> error;
 
2129
  refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_initial_password", error);
 
2130
  if(error.get())
 
2131
  {
 
2132
    std::cerr << error->what() << std::endl;
 
2133
    return false;
 
2134
  }
 
2135
#endif //GLIBMM_EXCEPTIONS_ENABLED
 
2136
 
 
2137
  refXml->get_widget_derived("dialog_initial_password", dialog);
 
2138
  if(!dialog)
 
2139
    return false;
 
2140
 
 
2141
  add_view(dialog);
 
2142
 
 
2143
 
 
2144
  int response = Gtk::RESPONSE_OK;
 
2145
  bool keep_trying = true;
 
2146
  while(keep_trying)
 
2147
  {
 
2148
    response = Utils::dialog_run_with_help(dialog, "dialog_new_self_hosted_connection");
 
2149
 
 
2150
    //Check the password is acceptable:
 
2151
    if(response == Gtk::RESPONSE_OK)
 
2152
    {
 
2153
      const bool password_ok = dialog->check_password();
 
2154
      if(password_ok)
 
2155
      {
 
2156
        user = dialog->get_user();
 
2157
        password = dialog->get_password();
 
2158
 
 
2159
        keep_trying = false; //Everything is OK.
 
2160
      }
 
2161
    }
 
2162
    else
 
2163
    {
 
2164
      keep_trying = false; //The user cancelled.
 
2165
    }
 
2166
 
 
2167
    dialog->hide();
 
2168
  }
 
2169
 
 
2170
  remove_view(dialog);
 
2171
  delete dialog;
 
2172
  dialog = 0;
 
2173
 
 
2174
  return (response == Gtk::RESPONSE_OK);
 
2175
}
 
2176
 
1567
2177
bool Frame_Glom::connection_request_password_and_choose_new_database_name()
1568
2178
{
1569
 
  Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
 
2179
  Document* document = dynamic_cast<Document*>(get_document());
1570
2180
  if(!document)
1571
2181
    return false;
1572
2182
 
1573
 
#ifndef GLOM_ENABLE_CLIENT_ONLY
1574
 
  //Give the ConnectionPool the self-hosted file path, 
1575
 
  //so that create_self_hosted() can succeed:
1576
2183
  ConnectionPool* connection_pool = ConnectionPool::get_instance();
1577
 
 
1578
 
  // Client only mode does not support self hosting, so there is nothing to do.
1579
 
  if(connection_pool && document && document->get_connection_is_self_hosted())
1580
 
  {
1581
 
    // TODO: sleep, to give postgres time to start?
1582
 
    connection_pool->set_self_hosted(document->get_connection_self_hosted_directory_uri());
1583
 
  }
1584
 
  else
1585
 
  {
1586
 
    connection_pool->set_self_hosted(std::string());
1587
 
  }
1588
 
#endif // !GLOM_ENABLE_CLIENT_ONLY
 
2184
  setup_connection_pool_from_document(document);
1589
2185
 
1590
2186
  if(!m_pDialogConnection)
1591
2187
  {
1593
2189
    add_view(m_pDialogConnection); //Also a composite view.
1594
2190
  }
1595
2191
 
1596
 
  //Ask either for the existing username and password to use an existing database server,
1597
 
  //or ask for a new username and password to specify when creating a new self-hosted database.
1598
 
  int response = 0;
1599
 
#ifndef GLOM_ENABLE_CLIENT_ONLY
1600
 
  if(document->get_connection_is_self_hosted())
 
2192
  switch(document->get_hosting_mode())
1601
2193
  {
1602
 
    Dialog_NewSelfHostedConnection* dialog = 0;
1603
 
    Glib::RefPtr<Gnome::Glade::Xml> refXml;
1604
 
 
1605
 
#ifdef GLIBMM_EXCEPTIONS_ENABLED
1606
 
    try
1607
 
    {
1608
 
      refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "dialog_new_self_hosted_connection");
1609
 
    }
1610
 
    catch(const Gnome::Glade::XmlError& ex)
1611
 
    {
1612
 
      std::cerr << ex.what() << std::endl;
1613
 
      return false;
1614
 
    }
1615
 
#else
1616
 
    std::auto_ptr<Gnome::Glade::XmlError> error;
1617
 
    refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "dialog_new_self_hosted_connection", error);
1618
 
    if(error.get())
1619
 
    {
1620
 
      std::cerr << error->what() << std::endl;
1621
 
      return false;
1622
 
    }
1623
 
#endif
1624
 
 
1625
 
    refXml->get_widget_derived("dialog_new_self_hosted_connection", dialog);
1626
 
    if(!dialog) return false;
1627
 
 
1628
 
    add_view(dialog);
1629
 
 
1630
 
 
1631
 
    response = Gtk::RESPONSE_OK;
1632
 
    bool keep_trying = true;
1633
 
    while(keep_trying)
1634
 
    {
1635
 
      response = Glom::Utils::dialog_run_with_help(dialog, "dialog_new_self_hosted_connection");
1636
 
 
1637
 
      //Check the password is acceptable:
1638
 
      if(response == Gtk::RESPONSE_OK)
1639
 
      {
1640
 
        const bool password_ok = dialog->check_password();
1641
 
        if(password_ok)
1642
 
        {
1643
 
          keep_trying = false; //Everything is OK.
1644
 
        }
1645
 
      }
1646
 
      else
1647
 
        keep_trying = false; //The user cancelled.
1648
 
 
1649
 
    }
1650
 
 
1651
 
    dialog->hide();
1652
 
 
1653
 
    // Create the requested self-hosting database:
1654
 
    bool created = false;
1655
 
    if(response == Gtk::RESPONSE_OK)
1656
 
    {
1657
 
      created = dialog->create_self_hosted();
1658
 
      if(created)
1659
 
      {
1660
 
        const bool test = connection_pool->start_self_hosting(get_app_window());
 
2194
    case Document::HOSTING_MODE_POSTGRES_SELF:
 
2195
    {
 
2196
      Glib::ustring user, password;
 
2197
 
 
2198
      if(document->get_network_shared()) //Usually not the case when creating new documents.
 
2199
      {
 
2200
        const bool test = connection_request_initial_password(user, password);
1661
2201
        if(!test)
1662
2202
          return false;
1663
 
 
1664
 
        // Store in document, so these values are actually used when connecting
1665
 
        Document_Glom* document = get_document();
1666
 
        if(document)
1667
 
        {
1668
 
          document->set_connection_port(connection_pool->get_port());
1669
 
          document->set_connection_try_other_ports(connection_pool->get_try_other_ports());
1670
 
        }
1671
2203
      }
1672
2204
      else
 
2205
      {
 
2206
        //Use the default user because we are not network shared:
 
2207
        user = Privs::get_default_developer_user_name(password);
 
2208
      }
 
2209
 
 
2210
      // Create the requested self-hosting database:
 
2211
      
 
2212
      //Set the connection details in the ConnectionPool singleton.
 
2213
      //The ConnectionPool will now use these every time it tries to connect.
 
2214
      connection_pool->set_user(user);
 
2215
      connection_pool->set_password(password);
 
2216
      
 
2217
      const bool initialized = handle_connection_initialize_errors( connection_pool->initialize(
 
2218
        sigc::mem_fun(*this, &Frame_Glom::on_connection_initialize_progress) ) );
 
2219
 
 
2220
      if(m_dialog_progess_connection_initialize)
 
2221
      {
 
2222
        delete m_dialog_progess_connection_initialize;
 
2223
        m_dialog_progess_connection_initialize = 0;
 
2224
      }
 
2225
 
 
2226
      if(!initialized)
1673
2227
        return false;
1674
2228
 
1675
 
      //dialog->create_self_hosted() has already set enough information in the ConnectionPool to allow a connection so we can create the database in the new database cluster:
1676
 
     
1677
2229
      //Put the details into m_pDialogConnection too, because that's what we use to connect.
1678
2230
      //This is a bit of a hack:
1679
2231
      m_pDialogConnection->set_self_hosted_user_and_password(connection_pool->get_user(), connection_pool->get_password());
 
2232
 
 
2233
      //std::cout << "DEBUG: after connection_pool->initialize(). The database cluster should now exist." << std::endl;
1680
2234
    }
1681
2235
 
1682
 
    remove_view(dialog);
1683
 
 
1684
 
    //std::cout << "DEBUG: after dialog->create_self_hosted(). The database cluster should now exist." << std::endl;
1685
 
 
1686
 
    if(!created)
1687
 
      return false;
1688
 
  }
1689
 
  else
1690
 
#endif // !GLOM_ENABLE_CLIENT_ONLY
1691
 
  {
1692
 
    //Ask for connection details:
1693
 
    m_pDialogConnection->load_from_document(); //Get good defaults.
1694
 
    m_pDialogConnection->set_transient_for(*get_app_window());
1695
 
    response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
1696
 
    m_pDialogConnection->hide();
1697
 
  }
1698
 
 
1699
 
  if(response == Gtk::RESPONSE_OK)
1700
 
  {
1701
 
    const Glib::ustring database_name = document->get_connection_database();
1702
 
 
1703
 
    //std::cout << "debug: database_name to create=" << database_name << std::endl;
1704
 
 
1705
 
 
1706
 
    bool keep_trying = true;
1707
 
    size_t extra_num = 0;
1708
 
    while(keep_trying)
 
2236
    break;
 
2237
 
 
2238
#ifdef GLOM_ENABLE_POSTGRESQL
 
2239
    case Document::HOSTING_MODE_POSTGRES_CENTRAL:
1709
2240
    {
1710
 
      Glib::ustring database_name_possible;
1711
 
      if(extra_num == 0)
1712
 
        database_name_possible = database_name; //Try the original name first.
 
2241
      //Ask for connection details:
 
2242
      m_pDialogConnection->load_from_document(); //Get good defaults.
 
2243
      m_pDialogConnection->set_transient_for(*get_app_window());
 
2244
 
 
2245
      const int response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
 
2246
      m_pDialogConnection->hide();
 
2247
 
 
2248
      if(response == Gtk::RESPONSE_OK)
 
2249
      {
 
2250
        // We are not self-hosting, but we also call initialize() for
 
2251
        // consistency (the backend will ignore it anyway).
 
2252
        ConnectionPool::SlotProgress slot_ignored;
 
2253
        if(!handle_connection_initialize_errors( connection_pool->initialize(slot_ignored)) )
 
2254
          return false;
 
2255
 
 
2256
        // Remember the user name in the document, to be able to open the
 
2257
        // document again later:
 
2258
        Glib::ustring username, password;
 
2259
        m_pDialogConnection->get_username_and_password(username, password);
 
2260
        document->set_connection_user(username);
 
2261
      }
1713
2262
      else
1714
2263
      {
1715
 
        //Create a new database name by appending a number to the original name:
1716
 
        char pchExtraNum[10];
1717
 
        sprintf(pchExtraNum, "%d", extra_num);
1718
 
        database_name_possible = (database_name + pchExtraNum);
 
2264
        // The user cancelled
 
2265
        return false;
1719
2266
      }
1720
 
      ++extra_num;
1721
 
 
1722
 
      m_pDialogConnection->set_database_name(database_name_possible);
1723
 
      //std::cout << "debug: possible name=" << database_name_possible << std::endl;
 
2267
    }
 
2268
 
 
2269
    break;
 
2270
#endif //GLOM_ENABLE_POSTGRESQL
 
2271
#ifdef GLOM_ENABLE_SQLITE
 
2272
  case Document::HOSTING_MODE_SQLITE:
 
2273
    {
 
2274
      // sqlite
 
2275
      ConnectionPool::SlotProgress slot_ignored;
 
2276
      if(!handle_connection_initialize_errors( connection_pool->initialize(slot_ignored)) )
 
2277
        return false;
 
2278
 
 
2279
      m_pDialogConnection->load_from_document(); //Get good defaults.
 
2280
      // No authentication required
 
2281
    }
 
2282
 
 
2283
    break;
 
2284
#endif //GLOM_ENABLE_SQLITE
 
2285
  default:
 
2286
    g_assert_not_reached();
 
2287
    break;
 
2288
  }
 
2289
 
 
2290
  // Do startup, such as starting the self-hosting database server
 
2291
  if(!connection_pool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) ))
 
2292
    return false;
 
2293
    
 
2294
  if(m_dialog_progess_connection_startup)
 
2295
  {
 
2296
    delete m_dialog_progess_connection_startup;
 
2297
    m_dialog_progess_connection_startup = 0;
 
2298
  }
 
2299
 
 
2300
  const Glib::ustring database_name = document->get_connection_database();
 
2301
 
 
2302
  //std::cout << "debug: database_name to create=" << database_name << std::endl;
 
2303
 
 
2304
 
 
2305
  bool keep_trying = true;
 
2306
  size_t extra_num = 0;
 
2307
  while(keep_trying)
 
2308
  {
 
2309
    Glib::ustring database_name_possible;
 
2310
    if(extra_num == 0)
 
2311
      database_name_possible = database_name; //Try the original name first.
 
2312
    else
 
2313
    {
 
2314
      //Create a new database name by appending a number to the original name:
 
2315
      Glib::ustring pchExtraNum = Glib::ustring::compose("%1", extra_num);
 
2316
      database_name_possible = (database_name + pchExtraNum);
 
2317
    }
 
2318
    ++extra_num;
 
2319
 
 
2320
    m_pDialogConnection->set_database_name(database_name_possible);
 
2321
    //std::cout << "debug: possible name=" << database_name_possible << std::endl;
1724
2322
 
1725
2323
#ifdef GLIBMM_EXCEPTIONS_ENABLED
1726
 
      try
 
2324
    try
 
2325
    {
 
2326
      g_assert(m_pDialogConnection);
 
2327
      sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings();
 
2328
      //If no exception was thrown then the database exists.
 
2329
      //But we are looking for an unused database name, so we will try again.
 
2330
    }
 
2331
    catch(const ExceptionConnection& ex)
 
2332
    {
 
2333
#else //GLIBMM_EXCEPTIONS_ENABLED
 
2334
    std::auto_ptr<ExceptionConnection> error;
 
2335
    g_assert(m_pDialogConnection);
 
2336
    sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings(error);
 
2337
    if(error.get())
 
2338
    {
 
2339
      const ExceptionConnection& ex = *error;
 
2340
#endif //GLIBMM_EXCEPTIONS_ENABLED
 
2341
      //g_warning("Frame_Glom::connection_request_password_and_choose_new_database_name(): caught exception.");
 
2342
 
 
2343
      if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
1727
2344
      {
1728
 
        sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings();
1729
 
        //If no exception was thrown then the database exists.
1730
 
        //But we are looking for an unused database name, so we will try again.
 
2345
        //Warn the user, and let him try again:
 
2346
        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."), *(get_app_window()), Gtk::MESSAGE_ERROR); //TODO: Add help button.
 
2347
        keep_trying = false;
1731
2348
      }
1732
 
      catch(const ExceptionConnection& ex)
1733
 
      {
1734
 
#else
1735
 
      std::auto_ptr<ExceptionConnection> error;
1736
 
      sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings(error);
1737
 
      if(error.get())
1738
 
      {
1739
 
        const ExceptionConnection& ex = *error;
1740
 
#endif
1741
 
        //g_warning("Frame_Glom::connection_request_password_and_choose_new_database_name(): caught exception.");
1742
 
 
1743
 
        if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
1744
 
        {
1745
 
          //Warn the user, and let him try again:
1746
 
          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."), *(get_app_window()), Gtk::MESSAGE_ERROR); //TODO: Add help button.
1747
 
          return false;
1748
 
        }
1749
 
        else
1750
 
        {
1751
 
          std::cout << "Frame_Glom::connection_request_password_and_choose_new_database_name(): unused database name successfully found: " << database_name_possible << std::endl; 
1752
 
          //The connection to the server is OK, but the specified database does not exist.
1753
 
          //That's good - we were looking for an unused database name.
1754
 
          Document_Glom* document = get_document();
1755
 
          if(document)
1756
 
          {
1757
 
            std::cout << "debug: unused database name found: " << database_name_possible << std::endl;
1758
 
            document->set_connection_database(database_name_possible);
1759
 
 
1760
 
            ConnectionPool* connection_pool = ConnectionPool::get_instance();
1761
 
            if(connection_pool)
1762
 
              document->set_connection_server(connection_pool->get_host());
1763
 
          }
1764
 
 
1765
 
          return true;
1766
 
        }
 
2349
      else
 
2350
      {
 
2351
        std::cout << "Frame_Glom::connection_request_password_and_choose_new_database_name(): unused database name successfully found: " << database_name_possible << std::endl; 
 
2352
        //The connection to the server is OK, but the specified database does not exist.
 
2353
        //That's good - we were looking for an unused database name.
 
2354
 
 
2355
        std::cout << "debug: unused database name found: " << database_name_possible << std::endl;
 
2356
        document->set_connection_database(database_name_possible);
 
2357
 
 
2358
        // Remember host and port if the document is not self hosted
 
2359
        #ifdef GLOM_ENABLE_POSTGRESQL
 
2360
        if(document->get_hosting_mode() == Document::HOSTING_MODE_POSTGRES_CENTRAL)
 
2361
        {
 
2362
          ConnectionPool::Backend* backend = connection_pool->get_backend();
 
2363
          ConnectionPoolBackends::PostgresCentralHosted* central = dynamic_cast<ConnectionPoolBackends::PostgresCentralHosted*>(backend);
 
2364
          g_assert(central != NULL);
 
2365
 
 
2366
          document->set_connection_server(central->get_host());
 
2367
          document->set_connection_port(central->get_port());
 
2368
          document->set_connection_try_other_ports(false);
 
2369
        }
 
2370
 
 
2371
        // Remember port if the document is self-hosted, so that remote
 
2372
        // connections to the database (usinc browse network) know what port to use.
 
2373
        // TODO: There is already similar code in
 
2374
        // connect_to_server_with_connection_settings, which is just not
 
2375
        // executed because it failed with no database present. We should
 
2376
        // somehow avoid this code duplication.
 
2377
        else if(document->get_hosting_mode() == Document::HOSTING_MODE_POSTGRES_SELF)
 
2378
        {
 
2379
          ConnectionPool::Backend* backend = connection_pool->get_backend();
 
2380
          ConnectionPoolBackends::PostgresSelfHosted* self = dynamic_cast<ConnectionPoolBackends::PostgresSelfHosted*>(backend);
 
2381
          g_assert(self != NULL);
 
2382
 
 
2383
          document->set_connection_port(self->get_port());
 
2384
          document->set_connection_try_other_ports(false);
 
2385
        }
 
2386
 
 
2387
        #endif //GLOM_ENABLE_POSTGRESQL
 
2388
 
 
2389
        return true;
1767
2390
      }
1768
2391
    }
1769
2392
  }
1770
 
  else
1771
 
    return false; //The user cancelled.
1772
2393
 
 
2394
  cleanup_connection();
 
2395
  
1773
2396
  return false;
1774
2397
}
1775
 
 
1776
 
#ifdef GLIBMM_EXCEPTIONS_ENABLED
1777
 
bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring known_username, const Glib::ustring& known_password)
1778
 
#else
1779
 
bool Frame_Glom::connection_request_password_and_attempt(const Glib::ustring known_username, const Glib::ustring& known_password, std::auto_ptr<ExceptionConnection>& error)
1780
 
#endif
1781
 
{
1782
 
  if(!m_pDialogConnection)
1783
 
  {
 
2398
#endif //GLOM_ENABLE_CLIENT_ONLY
 
2399
 
 
2400
void Frame_Glom::cleanup_connection()
 
2401
{
 
2402
  ConnectionPool* connection_pool = ConnectionPool::get_instance(); 
 
2403
  connection_pool->cleanup( sigc::mem_fun(*this, &Frame_Glom::on_connection_cleanup_progress) );
 
2404
 
 
2405
  if(m_dialog_progess_connection_cleanup)
 
2406
  {
 
2407
    delete m_dialog_progess_connection_cleanup;
 
2408
    m_dialog_progess_connection_cleanup = 0;
 
2409
  }
 
2410
}
 
2411
 
 
2412
bool Frame_Glom::handle_request_password_connection_error(bool asked_for_password, const ExceptionConnection& ex, bool& database_not_found)
 
2413
{
 
2414
  g_warning("Frame_Glom::connection_request_password_and_attempt(): caught exception.");
 
2415
 
 
2416
  //Initialize input parameter:
 
2417
  database_not_found = false;
 
2418
 
 
2419
  if(asked_for_password && ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
 
2420
  {
 
2421
    //Warn the user, and let him try again:
 
2422
    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."), *(get_app_window()), Gtk::MESSAGE_ERROR); //TODO: Add help button.
 
2423
    return true;
 
2424
  }
 
2425
  else if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_DATABASE)
 
2426
  {
 
2427
    cleanup_connection();
 
2428
 
 
2429
    //The connection to the server might be OK, but the specified database does not exist:
 
2430
    //Or the connection failed when trying without a password.
 
2431
    database_not_found = true; //Tell the caller about this error.
 
2432
    return false;
 
2433
  }
 
2434
  else
 
2435
  {
 
2436
    std::cerr << "Frame_Glom::connection_request_password_and_attempt(): Unexpected exception: " << ex.what() << std::endl;
 
2437
    cleanup_connection();
 
2438
    return false;
 
2439
  }
 
2440
}
 
2441
 
 
2442
bool Frame_Glom::connection_request_password_and_attempt(bool& database_not_found, const Glib::ustring known_username, const Glib::ustring& known_password, bool confirm_known_user)
 
2443
{
 
2444
  //Initialize output parameter:
 
2445
  database_not_found = false;
 
2446
 
 
2447
  Document* document = dynamic_cast<Document*>(get_document());
 
2448
  if(!document)
 
2449
    return false;
 
2450
 
 
2451
 
 
2452
  //Start a self-hosted server if necessary:
 
2453
  ConnectionPool* connection_pool = ConnectionPool::get_instance();
 
2454
  setup_connection_pool_from_document(document);
 
2455
  if(!connection_pool->startup( sigc::mem_fun(*this, &Frame_Glom::on_connection_startup_progress) ))
 
2456
    return false;
 
2457
    
 
2458
  if(m_dialog_progess_connection_startup)
 
2459
  {
 
2460
    delete m_dialog_progess_connection_startup;
 
2461
    m_dialog_progess_connection_startup = 0;
 
2462
  }
 
2463
 
 
2464
  //Only ask for the password if we are shared on the network, or we are using a centrally hosted server.
 
2465
  //Otherwise, no password question is necessary, due to how our self-hosted database server is configured.
 
2466
  if(document->get_network_shared()
 
2467
    || document->get_hosting_mode() == Document::HOSTING_MODE_POSTGRES_CENTRAL)
 
2468
  {
 
2469
    //We recreate the dialog each time to make sure it is clean of any changes:
 
2470
    if(m_pDialogConnection)
 
2471
    {
 
2472
      delete m_pDialogConnection;
 
2473
      m_pDialogConnection = 0;
 
2474
    }
 
2475
 
1784
2476
    Utils::get_glade_widget_derived_with_warning("dialog_connection", m_pDialogConnection);
1785
2477
    add_view(m_pDialogConnection); //Also a composite view.
1786
 
  }
1787
 
 
1788
 
  while(true) //Loop until a return
1789
 
  {
1790
 
    //Ask for connection details:
 
2478
  
1791
2479
    m_pDialogConnection->load_from_document(); //Get good defaults.
1792
2480
    m_pDialogConnection->set_transient_for(*get_app_window());
1793
2481
 
 
2482
    //Show alternative text if necessary:
 
2483
    if(confirm_known_user)
 
2484
      m_pDialogConnection->set_confirm_existing_user_note();
 
2485
 
1794
2486
    if(!known_username.empty())
1795
2487
      m_pDialogConnection->set_username(known_username);
1796
2488
 
1797
2489
    if(!known_password.empty())
1798
2490
      m_pDialogConnection->set_password(known_password);
1799
 
 
 
2491
  }
 
2492
  else if(m_pDialogConnection)
 
2493
  {
 
2494
    //Later, if m_pDialogConnection is null then we assume we should use the known user/password:
 
2495
    delete m_pDialogConnection;
 
2496
    m_pDialogConnection = 0;
 
2497
  }
 
2498
 
 
2499
 
 
2500
  //Ask for connection details: 
 
2501
  while(true) //Loop until a return
 
2502
  {
1800
2503
    //Only show the dialog if we don't know the correct username/password yet:
1801
2504
    int response = Gtk::RESPONSE_OK;
1802
 
    if(known_username.empty() && known_password.empty())
 
2505
 
 
2506
    if(m_pDialogConnection)
1803
2507
    {
1804
 
       response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
1805
 
       m_pDialogConnection->hide();
 
2508
      response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
 
2509
      m_pDialogConnection->hide();
1806
2510
    }
1807
2511
 
 
2512
    //Try to use the entered username/password:
1808
2513
    if(response == Gtk::RESPONSE_OK)
1809
2514
    {
1810
 
#ifdef GLIBMM_EXCEPTIONS_ENABLED
1811
 
      try
 
2515
      sharedptr<SharedConnection> sharedconnection;
 
2516
 
 
2517
      //Ask for the user/password if necessary:
 
2518
      //TODO: Remove any previous database setting?
 
2519
      if(m_pDialogConnection)
1812
2520
      {
1813
 
        //TODO: Remove any previous database setting?
1814
 
        sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings();
1815
 
        return true; //Succeeeded, because no exception was thrown.
 
2521
        #ifdef GLIBMM_EXCEPTIONS_ENABLED
 
2522
        try
 
2523
        {
 
2524
          sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings();
 
2525
          // TODO: Save username in document?
 
2526
          return true; //Succeeded, because no exception was thrown.
 
2527
        }
 
2528
        catch(const ExceptionConnection& ex)
 
2529
        {
 
2530
          if(!handle_request_password_connection_error(true, ex, database_not_found))
 
2531
            return false;
 
2532
        }
 
2533
        #else //GLIBMM_EXCEPTIONS_ENABLED
 
2534
        std::auto_ptr<ExceptionConnection> local_error;
 
2535
        sharedconnection = 
 
2536
          m_pDialogConnection->connect_to_server_with_connection_settings(local_error);
 
2537
        if(!local_error.get())
 
2538
          return true;
 
2539
        else if(!handle_request_password_connection_error(true, *local_error, database_not_found))
 
2540
          return false;
 
2541
        #endif //GLIBMM_EXCEPTIONS_ENABLED
1816
2542
      }
1817
 
      catch(const ExceptionConnection& ex)
1818
 
      {
1819
 
#else
1820
 
      std::auto_ptr<ExceptionConnection> local_error;
1821
 
      sharedptr<SharedConnection> sharedconnection = m_pDialogConnection->connect_to_server_with_connection_settings(local_error);
1822
 
      if(!local_error.get())
1823
 
        return true;
1824
2543
      else
1825
2544
      {
1826
 
        const ExceptionConnection& ex = *local_error;
1827
 
#endif
1828
 
        g_warning("Frame_Glom::connection_request_password_and_attempt(): caught exception.");
1829
 
 
1830
 
        if(ex.get_failure_type() == ExceptionConnection::FAILURE_NO_SERVER)
1831
 
        {
1832
 
          //Warn the user, and let him try again:
1833
 
          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."), *(get_app_window()), Gtk::MESSAGE_ERROR); //TODO: Add help button.
1834
 
 
1835
 
          response = Glom::Utils::dialog_run_with_help(m_pDialogConnection, "dialog_connection");
1836
 
          m_pDialogConnection->hide();
1837
 
          if(response != Gtk::RESPONSE_OK)
1838
 
            return false; //The user cancelled.
 
2545
        //Use the known password:
 
2546
        ConnectionPool* connectionpool = ConnectionPool::get_instance();
 
2547
        connectionpool->set_user(known_username);
 
2548
        connectionpool->set_password(known_password);
 
2549
    
 
2550
        #ifdef GLIBMM_EXCEPTIONS_ENABLED
 
2551
        try
 
2552
        {
 
2553
          Base_DB::connect_to_server(get_app_window());
 
2554
          return true; //Succeeded, because no exception was thrown.
 
2555
        }
 
2556
        catch(const ExceptionConnection& ex)
 
2557
        {
 
2558
          if(!handle_request_password_connection_error(false, ex, database_not_found))
 
2559
            return false;
 
2560
        }
 
2561
        #else
 
2562
        std::auto_ptr<ExceptionConnection> error;
 
2563
        const bool connected = Base_DB::connect_to_server(get_app_window(), error);
 
2564
        if(!connected || error.get())
 
2565
        {
 
2566
          if(!handle_request_password_connection_error(false, *error, database_not_found))
 
2567
            return false;
1839
2568
        }
1840
2569
        else
1841
 
        {
1842
 
          g_warning("Frame_Glom::connection_request_password_and_attempt(): rethrowing exception.");
1843
 
 
1844
 
          //The connection to the server is OK, but the specified database does not exist:
1845
 
#ifdef GLIBMM_EXCEPTIONS_ENABLED
1846
 
          throw ex; //Pass it on for the caller to handle.
1847
 
#else
1848
 
          error = local_error; //Pass it on for the caller to handle.
1849
 
#endif
1850
 
          return false;
1851
 
        }
 
2570
          return true;
 
2571
        #endif
1852
2572
      }
1853
2573
 
1854
2574
      //Try again.
1855
2575
    }
1856
2576
    else
 
2577
    {
 
2578
      cleanup_connection();
1857
2579
      return false; //The user cancelled.
 
2580
    }
1858
2581
  }
1859
2582
}
1860
2583
 
1872
2595
  Gtk::Window* pWindowApp = get_app_window();
1873
2596
  g_assert(pWindowApp);
1874
2597
 
1875
 
  Bakery::BusyCursor busycursor(*pWindowApp);
 
2598
  BusyCursor busycursor(*pWindowApp);
1876
2599
 
1877
2600
  try
1878
2601
  {
1880
2603
  }
1881
2604
  catch(const Glib::Exception& ex) // libgda does not set error domain
1882
2605
  {
1883
 
    //I think a failure here might be caused by installing unstable libgda, which seems to affect stable libgda-1.2.
1884
 
    //Doing a "make install" in libgda-1.2 seems to fix this:
1885
 
    //TODO: Is this still relevant in libgda-3.0?
1886
2606
    std::cerr << "Frame_Glom::create_database():  Gnome::Gda::Connection::create_database(" << database_name << ") failed: " << ex.what() << std::endl;
1887
2607
 
1888
2608
    //Tell the user:
1890
2610
    try
1891
2611
    {
1892
2612
       // TODO: Tell the user what has gone wrong (ex.what())
1893
 
      Glib::RefPtr<Gnome::Glade::Xml> refXml = Gnome::Glade::Xml::create(Utils::get_glade_file_path("glom_developer.glade"), "dialog_error_create_database");
 
2613
      Glib::RefPtr<Gtk::Builder> refXml = Gtk::Builder::create_from_file(Utils::get_glade_file_path("glom_developer.glade"), "dialog_error_create_database");
1894
2614
      refXml->get_widget("dialog_error_create_database", dialog);
1895
2615
      dialog->set_transient_for(*pWindowApp);
1896
2616
      Glom::Utils::dialog_run_with_help(dialog, "dialog_error_create_database");
1897
2617
      delete dialog;
1898
2618
    }
1899
 
    catch(const Gnome::Glade::XmlError& ex)
 
2619
    catch(const Gtk::BuilderError& ex)
1900
2620
    {
1901
2621
      std::cerr << ex.what() << std::endl;
1902
2622
    }
1972
2692
    return;
1973
2693
  }
1974
2694
 
1975
 
  Document_Glom* document = get_document();
 
2695
  Document* document = get_document();
1976
2696
  sharedptr<Report> report = document->get_report(m_table_name, report_name);
1977
2697
  if(!report)
1978
2698
    return;
1996
2716
    return;
1997
2717
  }
1998
2718
 
1999
 
  Document_Glom* document = get_document();
 
2719
  Document* document = get_document();
2000
2720
  sharedptr<PrintLayout> print_layout = document->get_print_layout(m_table_name, print_layout_name);
2001
2721
  if(!print_layout)
2002
2722
    return;
2011
2731
  print->set_canvas(&canvas);
2012
2732
 
2013
2733
  print->set_track_print_status();
2014
 
  print->set_default_page_setup(print_layout->get_page_setup());
 
2734
 
 
2735
  //TODO: Put this in a utility function.
 
2736
  Glib::RefPtr<Gtk::PageSetup> page_setup;
 
2737
  const Glib::ustring key_file_text = print_layout->get_page_setup();
 
2738
  if(!key_file_text.empty())
 
2739
  {
 
2740
    Glib::KeyFile key_file;
 
2741
    key_file.load_from_data(key_file_text);
 
2742
    //TODO: Use this when gtkmm and GTK+ have been fixed: page_setup = Gtk::PageSetup::create(key_file);
 
2743
    page_setup = Glib::wrap(gtk_page_setup_new_from_key_file(key_file.gobj(), NULL, NULL));
 
2744
  }
 
2745
  
 
2746
  print->set_default_page_setup(page_setup);
 
2747
  
2015
2748
  //print->set_print_settings(m_refSettings);
2016
2749
 
2017
2750
  //print->signal_done().connect(sigc::bind(sigc::mem_fun(*this,
2040
2773
#ifndef GLOM_ENABLE_CLIENT_ONLY
2041
2774
void Frame_Glom::on_dialog_layout_report_hide()
2042
2775
{
2043
 
  Document_Glom* document = get_document();
 
2776
  Document* document = get_document();
2044
2777
 
2045
2778
  if(document && true) //m_pDialogLayoutReport->get_modified())
2046
2779
  {
2060
2793
 
2061
2794
void Frame_Glom::on_dialog_layout_print_hide()
2062
2795
{
2063
 
  Document_Glom* document = get_document();
 
2796
  Document* document = get_document();
2064
2797
 
2065
2798
  if(document && true) //m_pDialogLayoutReport->get_modified())
2066
2799
  {
2098
2831
void Frame_Glom::on_dialog_tables_hide()
2099
2832
{
2100
2833
  //If tables could have been added or removed, update the tables menu:
2101
 
  Document_Glom* document = dynamic_cast<Document_Glom*>(get_document());
 
2834
  Document* document = dynamic_cast<Document*>(get_document());
2102
2835
  if(document)
2103
2836
  {
2104
2837
    // This is never true in client only mode, so we can as well save the
2189
2922
  return (m_Notebook_Data.get_current_view() == Notebook_Data::DATA_VIEW_Details);
2190
2923
}
2191
2924
 
 
2925
Glib::ustring Frame_Glom::get_shown_table_name() const
 
2926
{
 
2927
  return m_table_name;
 
2928
}
 
2929
 
2192
2930
} //namespace Glom
2193
2931