db_adddel.cc 72 KB
Newer Older
1 2
/* Glom
 *
3
 * Copyright (C) 2001-2010 Murray Cumming
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
17 18
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
19 20 21
 */

#include "db_adddel.h"
22
#include <glibmm/i18n.h>
23
#include <glom/utility_widgets/cellrendererlist.h>
24 25 26
#include <glom/mode_data/datawidget/cellrenderer_buttonimage.h>
#include <glom/mode_data/datawidget/cellrenderer_buttontext.h>
#include <glom/mode_data/datawidget/cellrenderer_dblist.h>
27
#include "db_treeviewcolumn_glom.h"
28
#include <libglom/data_structure/glomconversions.h>
Murray Cumming's avatar
Murray Cumming committed
29
#include <libglom/utils.h>
30
#include <libglom/string_utils.h>
31
#include <glom/dialog_invalid_data.h>
32
#include <glom/appwindow.h>
33
#include <glom/utils_ui.h> //For UiUtils::image_scale_keeping_ratio().
34
#include <glom/mode_data/datawidget/cellcreation.h>
35
#include <glibmm/main.h>
36
#include <giomm/menu.h>
Murray Cumming's avatar
Murray Cumming committed
37
#include <libglom/db_utils.h>
38

39 40
#include <iostream> //For debug output.

41

42 43 44
namespace Glom
{

45
DbAddDel::DbAddDel()
46 47
: Gtk::Box(Gtk::ORIENTATION_VERTICAL),
  m_column_is_sorted(false),
48 49
  m_column_sorted_direction(false),
  m_column_sorted(0),
50 51 52
  m_allow_user_actions(true),
  m_prevent_user_signals(false),
  m_ignore_tree_view_signals(false),
53
  m_allow_add(true),
Murray Cumming's avatar
Murray Cumming committed
54
  m_allow_delete(true),
55 56
  m_find_mode(false),
  m_allow_only_one_related_record(false),
57
  m_validation_retry(false),
Murray Cumming's avatar
Murray Cumming committed
58
  m_allow_view(true),
59
  m_allow_view_details(false),
Murray Cumming's avatar
Murray Cumming committed
60
  m_treeviewcolumn_button(nullptr),
61 62 63
  m_fixed_cell_height(0),
  m_rows_count_min(0),
  m_rows_count_max(0)
64 65
{
  set_prevent_user_signals();
66
  set_ignore_treeview_signals(true);
67

68
  set_spacing(Utils::to_utype(UiUtils::DefaultSpacings::SMALL));
69 70 71 72

  //Start with a useful default TreeModel:
  //set_columns_count(1);
  //construct_specified_columns();
73

74 75 76
  // Give the TreeView an accessible name, to access it in LDTP
  // TODO: Maybe this should be a constructor parameter, so that multiple
  // DbAddDels in a single Window can be addressed separately.
77
#ifdef GTKMM_ATKMM_ENABLED
78
  m_tree_view.get_accessible()->set_name(_("Table Content"));
79
#endif
80

81 82 83 84 85
  m_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
  m_scrolled_window.set_shadow_type(Gtk::SHADOW_IN);
  m_tree_view.set_fixed_height_mode(); //This allows some optimizations.
  m_scrolled_window.add(m_tree_view);
  pack_start(m_scrolled_window);
86

87
  m_tree_view.show();
88 89

  //Make sure that the TreeView doesn't start out only big enough for zero items.
90
  set_height_rows(6, 6);
91

92 93 94
  m_tree_view.add_events(Gdk::BUTTON_PRESS_MASK); //Allow us to catch button_press_event and button_release_event
  m_tree_view.signal_button_press_event().connect_notify( sigc::mem_fun(*this, &DbAddDel::on_treeview_button_press_event) );
  m_tree_view.signal_columns_changed().connect( sigc::mem_fun(*this, &DbAddDel::on_treeview_columns_changed) );
95
  //signal_button_press_event().connect(sigc::mem_fun(*this, &DbAddDel::on_button_press_event_Popup));
96 97
  //add_blank();

98
  setup_menu(this);
99

100 101 102 103 104

  set_prevent_user_signals(false);
  set_ignore_treeview_signals(false);

  remove_all_columns(); //set up the default columns.
105

106
  show_all_children();
107

108
  auto refSelection = m_tree_view.get_selection();
109 110 111 112 113
  if(refSelection)
  {
    refSelection->signal_changed().connect(
      sigc::mem_fun(*this, &DbAddDel::on_treeview_selection_changed));
  }
114 115 116 117
}

DbAddDel::~DbAddDel()
{
118
#ifndef GLOM_ENABLE_CLIENT_ONLY
Murray Cumming's avatar
Murray Cumming committed
119
  auto pApp = get_appwindow();
120 121
  if(pApp)
  {
122
    pApp->remove_developer_action(m_context_layout);
123
  }
124
#endif // !GLOM_ENABLE_CLIENT_ONLY
125 126
}

127 128 129 130 131 132 133 134 135
void DbAddDel::set_height_rows(gulong rows_count_min, gulong rows_count_max)
{
  m_rows_count_min = rows_count_min;
  m_rows_count_max = rows_count_max;

  set_height_rows_actual(m_rows_count_min);
}

void DbAddDel::set_height_rows_actual(gulong rows_count)
136 137
{
  //TODO: File a bug about API for this in GtkTreeView.
138
  const guint height_for_rows = rows_count * get_fixed_cell_height();
139 140
  //std::cout << "debug: height_for_rows = " << height_for_rows << std::endl;
  const guint extra_for_treeview = 50; //TODO: Find some way to guess this.
141
  m_scrolled_window.set_min_content_height(height_for_rows + extra_for_treeview);
142 143
}

144
void DbAddDel::do_user_requested_edit()
145
{
Murray Cumming's avatar
Murray Cumming committed
146
  auto iter = get_item_selected();
147
  if(iter)
148
  {
149 150 151
    signal_user_requested_edit()(iter);
  }
  else
152
    std::cerr << G_STRFUNC << ": No item was selected.\n";
153 154
}

155 156 157 158 159
void DbAddDel::on_idle_row_edit()
{
  on_MenuPopup_activate_Edit();
}

160 161
void DbAddDel::on_cell_button_clicked(const Gtk::TreeModel::Path& path)
{
162
  if(!m_list_store)
163 164
    return;

165
  auto iter = m_list_store->get_iter(path);
166 167 168
  if(iter)
  {
    select_item(iter, false /* start_editing */);
169
  }
170

171
  //This delayed action avoids a warning about a NULL GtkAdjustment.
Murray Cumming's avatar
Murray Cumming committed
172
  //It's fairly understandable that GtkTreeView doesn't like to be destroyed
173
  //as a side-effect of a click on one of its GtkCellRenderers.
Murray Cumming's avatar
Murray Cumming committed
174
  //That's unlikely to be fixed properly until GtkTreeView supports a real
175 176 177
  //button cell-renderer.
  Glib::signal_idle().connect_once(
    sigc::mem_fun(*this, &DbAddDel::on_idle_row_edit));
178 179
}

180
void DbAddDel::on_MenuPopup_activate_Edit()
181
{
182
  do_user_requested_edit();
183 184
}

185 186
void DbAddDel::on_MenuPopup_activate_Add()
{
187 188
  //Create a new record in the database:
  start_new_record();
189 190
}

191 192 193 194
void DbAddDel::on_MenuPopup_activate_Delete()
{
  finish_editing();

195
  auto refSelection = m_tree_view.get_selection();
196 197
  if(refSelection)
  {
Murray Cumming's avatar
Murray Cumming committed
198
    auto iter = refSelection->get_selected();
199
    if(iter && !get_is_placeholder_row(iter))
200 201
    {
      //TODO: We can't handle multiple-selections yet.
202
      user_requested_delete(iter, iter);
203 204 205 206
    }
  }
}

207 208 209 210 211 212
void DbAddDel::on_MenuPopup_activate_layout()
{
  finish_editing();
  signal_user_requested_layout().emit();
}

213
void DbAddDel::setup_menu(Gtk::Widget* /* widget */)
214
{
215
  m_action_group = Gio::SimpleActionGroup::create();
216

217 218
  const Glib::ustring edit_title =
    (m_open_button_title.empty() ? _("_Edit") : m_open_button_title); //TODO: Use this?
219

220
  m_context_edit = m_action_group->add_action("edit",
221 222
    sigc::mem_fun(*this, &DbAddDel::on_MenuPopup_activate_Edit) );

223
  m_context_delete = m_action_group->add_action("delete",
224 225
    sigc::mem_fun(*this, &DbAddDel::on_MenuPopup_activate_Delete) );

226
  m_context_add = m_action_group->add_action("add",
227
    sigc::mem_fun(*this, &DbAddDel::on_MenuPopup_activate_Add) );
228
  m_context_add->set_enabled(m_allow_add);
229

230
#ifndef GLOM_ENABLE_CLIENT_ONLY
231 232
  // Don't add ContextLayout in client only mode because it would never
  // be sensitive anyway
233
  m_context_layout =  m_action_group->add_action("layout",
234
    sigc::mem_fun(*this, &DbAddDel::on_MenuPopup_activate_layout) );
235

236
  //TODO: This does not work until this widget is in a container in the window:
Murray Cumming's avatar
Murray Cumming committed
237
  auto pApp = get_appwindow();
238
  if(pApp)
239
  {
240
    pApp->add_developer_action(m_context_layout); //So that it can be disabled when not in developer mode.
241
    pApp->update_userlevel_ui(); //Update our action's sensitivity.
242
  }
243
#endif // !GLOM_ENABLE_CLIENT_ONLY
244

245
  insert_action_group("context", m_action_group);
246 247 248

  //TODO: add_accel_group(builder->get_accel_group());

249
  auto menu = Gio::Menu::create();
250 251 252
  menu->append(_("_Edit"), "context.edit");
  menu->append(_("_Add"), "context.add");
  menu->append(_("_Delete"), "context.delete");
253
#ifndef GLOM_ENABLE_CLIENT_ONLY
254
  menu->append(_("_Layout"), "context.layout");
255
#endif
256

257 258
  m_menu_popup = std::make_unique<Gtk::Menu>(menu);
  m_menu_popup->attach_to_widget(*this);
259 260 261

  if(get_allow_user_actions())
  {
262 263
    m_context_edit->set_enabled();
    m_context_delete->set_enabled();
264 265 266
  }
  else
  {
267 268
    m_context_edit->set_enabled(false);
    m_context_delete->set_enabled(false);
269
  }
270

271
#ifndef GLOM_ENABLE_CLIENT_ONLY
272
  if(pApp)
273
    m_context_layout->set_enabled(pApp->get_userlevel() == AppState::userlevels::DEVELOPER);
274
#endif // !GLOM_ENABLE_CLIENT_ONLY
275 276
}

277
bool DbAddDel::on_button_press_event_Popup(GdkEventButton *button_event)
278
{
279
#ifndef GLOM_ENABLE_CLIENT_ONLY
280
  //Enable/Disable items.
281
  //We did this earlier, but get_appwindow is more likely to work now:
Murray Cumming's avatar
Murray Cumming committed
282
  auto pApp = get_appwindow();
283 284
  if(pApp)
  {
285
    pApp->add_developer_action(m_context_layout); //So that it can be disabled when not in developer mode.
286
    pApp->update_userlevel_ui(); //Update our action's sensitivity.
287
  }
288
#endif
289

290
  GdkModifierType mods;
Murray Cumming's avatar
Murray Cumming committed
291
  gdk_window_get_device_position( gtk_widget_get_window(Gtk::Widget::gobj()), button_event->device, nullptr, nullptr, &mods );
292 293 294
  if(mods & GDK_BUTTON3_MASK)
  {
    //Give user choices of actions on this item:
295
    m_menu_popup->popup_at_pointer((GdkEvent*)button_event);
296
    return true; //handled.
297 298 299
  }
  else
  {
300
    if(button_event->type == GDK_2BUTTON_PRESS)
301 302
    {
      //Double-click means edit.
303
      //Don't do this usually, because users sometimes double-click by accident when they just want to edit a cell.
304

305
      //TODO: If the cell is not editable, handle the double-click as an edit/selection.
306 307
      //on_MenuPopup_activate_Edit();
      return false; //Not handled.
308 309 310
    }
  }

311
  return  false; //Not handled. TODO: Call base class?
312 313 314 315 316
}

Gtk::TreeModel::iterator DbAddDel::get_item_placeholder()
{
  //Get the existing placeholder row, or add one if necessary:
317 318
  if(m_list_store)
    return m_list_store->get_placeholder_row();
319 320
  else
   return Gtk::TreeModel::iterator();
321 322
}

323
Gnome::Gda::Value DbAddDel::get_value(const Gtk::TreeModel::iterator& iter, const LayoutItem_Field& layout_item) const
324
{
325
  Gnome::Gda::Value value;
326

327
  if(m_list_store)
328 329 330 331 332
  {
    Gtk::TreeModel::Row treerow = *iter;

    if(treerow)
    {
Murray Cumming's avatar
Murray Cumming committed
333
      const auto list_indexes = get_data_model_column_index(layout_item);
334
      if(!list_indexes.empty())
335
      {
336
        const auto iter_begin = list_indexes.begin(); //Just get the first displayed instance of this field->
337

Murray Cumming's avatar
Murray Cumming committed
338
        const guint col_real = *iter_begin + get_count_hidden_system_columns();
339 340
        treerow.get_value(col_real, value);
      }
341 342 343 344 345 346
    }
  }

  return value;
}

347
Gnome::Gda::Value DbAddDel::get_value_key_selected() const
348
{
Murray Cumming's avatar
Murray Cumming committed
349
  auto iter = get_item_selected();
350 351
  if(iter)
  {
352
    return get_value_key(iter);
353 354 355
  }
  else
    return Gnome::Gda::Value();
356 357
}

358
Gnome::Gda::Value DbAddDel::get_value_selected(const LayoutItem_Field& layout_item) const
359
{
360
  return get_value(get_item_selected(), layout_item);
361 362 363 364
}

Gtk::TreeModel::iterator DbAddDel::get_item_selected()
{
365
  auto refTreeSelection = m_tree_view.get_selection();
366 367 368 369 370
  if(refTreeSelection)
  {
     return refTreeSelection->get_selected();
  }

371 372
  if(m_list_store)
    return m_list_store->children().end();
373 374
  else
    return Gtk::TreeModel::iterator();
375 376
}

377 378
Gtk::TreeModel::iterator DbAddDel::get_item_selected() const
{
379
  Glib::RefPtr<const Gtk::TreeSelection> refTreeSelection = m_tree_view.get_selection();
380 381
  if(refTreeSelection)
  {
382
     auto unconst = Glib::RefPtr<Gtk::TreeSelection>::cast_const(refTreeSelection);
383 384 385
     return unconst->get_selected();
  }

386 387
  if(m_list_store)
    return m_list_store->children().end();
388 389 390 391
  else
    return Gtk::TreeModel::iterator();
}

392 393

Gtk::TreeModel::iterator DbAddDel::get_row(const Gnome::Gda::Value& key)
394
{
395
  if(!m_list_store)
396 397
    return Gtk::TreeModel::iterator();

398
  for(auto iter = m_list_store->children().begin(); iter != m_list_store->children().end(); ++iter)
399
  {
400
    //Gtk::TreeModel::Row row = *iter;
Murray Cumming's avatar
Murray Cumming committed
401
    const auto valTemp = get_value_key(iter);
402
    if(valTemp == key)
403 404 405 406 407
    {
      return iter;
    }
  }

408
  return  m_list_store->children().end();
409 410
}

411
bool DbAddDel::select_item(const Gtk::TreeModel::iterator& iter, bool start_editing)
412
{
413
  //Find the first column with a layout_item:
414
  std::shared_ptr<const LayoutItem> layout_item;
415
  for(const auto& item : m_column_items)
416
  {
417
    layout_item = item;
418
    if(layout_item)
419 420 421
      break;
  }

422
  if (!layout_item) {
Murray Cumming's avatar
Murray Cumming committed
423
    return false;
424 425
  }

426
  return select_item(iter, *layout_item, start_editing);
427 428
}

429
bool DbAddDel::select_item(const Gtk::TreeModel::iterator& iter, const LayoutItem& layout_item, bool start_editing)
430
{
431
  if(!m_list_store)
432
    return false;
433

434 435 436 437 438 439
  InnerIgnore innerIgnore(this); //see comments for InnerIgnore class

  bool bResult = false;

  if(iter)
  {
440 441
    //Get the model column:
    guint treemodel_col = 0;
Murray Cumming's avatar
Murray Cumming committed
442
    const auto list_indexes = get_column_index(layout_item);
443 444 445 446 447 448 449
    if(list_indexes.empty())
      return false;
    else
      treemodel_col = *(list_indexes.begin());

    treemodel_col += get_count_hidden_system_columns();

450
    auto refTreeSelection = m_tree_view.get_selection();
Murray Cumming's avatar
Murray Cumming committed
451
    g_assert(refTreeSelection);
452
    refTreeSelection->select(iter);
453

454
    Gtk::TreeModel::Path path = m_list_store->get_path(iter);
455

456
    guint view_column_index = 0;
Murray Cumming's avatar
Murray Cumming committed
457
    const auto test = get_view_column_index(treemodel_col, view_column_index);
458 459
    if(test)
    {
460
      auto pColumn = m_tree_view.get_column(view_column_index);
461
      if(pColumn)
462
      {
463
        if(pColumn != m_treeviewcolumn_button) //This would activate the button. Let's avoid this, though it should never happen.
464
        {
465
          m_tree_view.set_cursor(path, *pColumn, start_editing);
466
        }
467 468
      }
      else
469
       g_warning("DbAddDel::select_item:TreeViewColumn not found.");
470
    }
471 472
    else
       g_warning("DbAddDel::select_item:TreeViewColumn index not found. column=%d", treemodel_col);
473 474 475 476 477 478 479 480 481

    bResult = true;
  }

  return bResult;
}

guint DbAddDel::get_count() const
{
482
  if(!m_list_store)
483 484
    return 0;

485
  guint iCount = m_list_store->children().size();
486 487 488 489

  //Take account of the extra blank for new entries:
  if(get_allow_user_actions()) //If it has the extra row.
  {
490
    --iCount;
491 492 493 494 495 496 497
  }

  return iCount;
}

guint DbAddDel::get_columns_count() const
{
498
  return m_tree_view.get_columns().size();
499 500
}

Murray Cumming's avatar
Murray Cumming committed
501
guint DbAddDel::get_fixed_cell_height()
502
{
503
  if(m_fixed_cell_height <= 0)
504
  {
505 506
    // Discover a suitable height, and cache it,
    // by looking at the heights of all columns:
507
    // Note that this is usually calculated during construct_specified_columns(),
508
    // when all columns are known.
509

510
    //Get a default:
511
    auto refLayoutDefault = m_tree_view.create_pango_layout("ExampleEg");
Murray Cumming's avatar
Murray Cumming committed
512 513 514 515
    int width_default = 0;
    int height_default = 0;
    refLayoutDefault->get_pixel_size(width_default, height_default);
    m_fixed_cell_height = height_default;
516 517

    //Look at each column:
518
    for(const auto& item : m_column_items)
519 520 521
    {
      Glib::ustring font_name;

522
      const auto item_withformatting = std::dynamic_pointer_cast<const LayoutItem_WithFormatting>(item);
523
      if(item_withformatting)
524
      {
525
         const auto& formatting = item_withformatting->get_formatting_used();
526 527 528 529 530 531 532
         font_name = formatting.get_text_format_font();
      }

      if(font_name.empty())
        continue;

      // Translators: This is just some example text used to discover an appropriate height for user-entered text in the UI. This text itself is never shown to the user.
533
      auto refLayout = m_tree_view.create_pango_layout(_("ExampleEg"));
534 535 536 537 538 539
      const Pango::FontDescription font(font_name);
      refLayout->set_font_description(font);
      int width = 0;
      int height = 0;
      refLayout->get_pixel_size(width, height);

Murray Cumming's avatar
Murray Cumming committed
540 541
      if(height > (int)m_fixed_cell_height)
        m_fixed_cell_height = (guint)height;
542
    }
543
  }
Murray Cumming's avatar
Murray Cumming committed
544

545
  //We add extra spacing, because otherwise the bottom of letters such as "g" get cut off.
Murray Cumming's avatar
Murray Cumming committed
546
  //We get this style property, which might be causing it. murrayc
547 548
  //TODO: Find out if this is reallyt the right way to calculate the correct height:
  int extra_height = 0;
Murray Cumming's avatar
Murray Cumming committed
549
  gtk_widget_style_get(GTK_WIDGET(m_tree_view.gobj()), "vertical-separator", &extra_height, (void*)nullptr);
Murray Cumming's avatar
Murray Cumming committed
550
  //std::cout << "debug: extra_height=" << extra_height << std::endl;
551 552

  return m_fixed_cell_height + extra_height;
553 554
}

555

556
Gtk::CellRenderer* DbAddDel::construct_specified_columns_cellrenderer(const std::shared_ptr<LayoutItem>& layout_item, int model_column_index, int data_model_column_index)
557
{
558 559
  InnerIgnore innerIgnore(this); //see comments for InnerIgnore class

Murray Cumming's avatar
Murray Cumming committed
560
  auto pCellRenderer = create_cell(layout_item, m_table_name, get_document(), get_fixed_cell_height());
561

562
  auto item_field = std::dynamic_pointer_cast<const LayoutItem_Field>(layout_item);
563

564 565
  //Set extra cellrenderer attributes, depending on the type used,
  //to support editing:
566

567
  if(auto pCellRendererText = dynamic_cast<Gtk::CellRendererText*>(pCellRenderer))
568 569 570 571
  {
    //Connect to edited signal:
    if(item_field) //Only fields can be edited:
    {
572 573
      pCellRendererText->signal_editing_started().connect(sigc::mem_fun(*this, &DbAddDel::on_treeview_cell_editing_started));

574
      //Make it editable:
575
      pCellRendererText->property_editable() = true;
576 577 578

      //Connect to its signal:
      pCellRendererText->signal_edited().connect(
579
        sigc::bind( sigc::mem_fun(*this, &DbAddDel::on_treeview_cell_edited), model_column_index, data_model_column_index) );
580 581 582 583
    }
  }
  else
  {
584
    if(auto pCellRendererToggle = dynamic_cast<Gtk::CellRendererToggle*>(pCellRenderer))
585
    {
586
      pCellRendererToggle->property_activatable() = true;
587

588 589 590 591
      if(item_field) //Only fields can be edited:
      {
        //Connect to its signal:
        pCellRendererToggle->signal_toggled().connect(
592
          sigc::bind( sigc::mem_fun(*this, &DbAddDel::on_treeview_cell_edited_bool), model_column_index, data_model_column_index ) );
593
      }
594
    }
595 596
    /*
    else if(auto pCellRendererPixbuf = dynamic_cast<Gtk::CellRendererPixbuf*>(pCellRenderer))
597 598 599 600
      {
        //TODO: Do something when it's clicked, such as show the big image in a window or tooltip?
      }
    }
601
    */
602 603
  }

Murray Cumming's avatar
Murray Cumming committed
604
  auto pCellButton = Gtk::manage( new GlomCellRenderer_ButtonText() );
605 606
  if(pCellButton)
  {
607
    auto item_button = std::dynamic_pointer_cast<const LayoutItem_Button>(layout_item);
608 609 610 611 612 613
    if(item_button)
    {
      pCellButton->signal_clicked().connect(
        sigc::bind( sigc::mem_fun(*this, &DbAddDel::on_cell_layout_button_clicked), model_column_index) );
    }
  }
614

615
  return pCellRenderer;
616 617
}

618 619 620 621 622 623 624 625 626 627 628 629
void DbAddDel::construct_specified_columns()
{
  InnerIgnore innerIgnore(this);

  //TODO_optimisation: This is called many times, just to simplify the API.

  //Delay actual use of set_column_*() stuff until this method is called.

  if(m_column_items.empty())
  {
    //std::cout << "debug: " << G_STRFUNC << ": showing hint model: m_find_mode=" << m_find_mode << std::endl;

630
    m_list_store.reset();
631 632
    if(m_table_name.empty())
    {
633
      m_tree_view.set_model(m_list_store); // clear old model from treeview
634
    }
635 636 637
    else
      show_hint_model();
    return;
638
  }
639

640
  m_list_store = DbTreeModel::create(m_found_set, m_column_items, m_allow_view, m_find_mode, m_FieldsShown);
641 642
  //m_FieldsShown is needed by Base_DB_Table_Data::record_new().

643
  m_tree_view.set_model(m_list_store); // clear old model from treeview
644

645
  //Remove all View columns:
646
  treeview_delete_all_columns();
647

648 649

  //Add new View Colums:
650
  int model_column_index = 0; //Not including the hidden internal columns.
651
  int view_column_index = 0;
652

Murray Cumming's avatar
Murray Cumming committed
653
  {
Murray Cumming's avatar
Murray Cumming committed
654
    auto pCellButton = Gtk::manage(new GlomCellRenderer_ButtonImage());
655

656
    pCellButton->signal_clicked().connect(sigc::mem_fun(*this, &DbAddDel::on_cell_button_clicked));
657

658
    m_treeviewcolumn_button = Gtk::manage(new Gtk::TreeViewColumn());
659 660
    m_treeviewcolumn_button->pack_start(*pCellButton);

661 662

    Gtk::Requisition requistion_min, requistion_natural; //TODO: Really support natural size.
663
    pCellButton->get_preferred_size(m_tree_view, requistion_min, requistion_natural);
664

665
    m_treeviewcolumn_button->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED); //Needed by fixed-height mode.
666 667 668 669

    // TODO: I am not sure whether this is always correct. Perhaps, we also
    // have to take into account the xpad property of the cell renderer and
    // the spacing property of the treeviewcolumn.
670
    int horizontal_separator = 0;
671
    m_tree_view.get_style_property("horizontal-separator", horizontal_separator);
672
    const int button_width = requistion_min.width + horizontal_separator*2;
673 674
    if(button_width > 0) //Otherwise an assertion fails.
      m_treeviewcolumn_button->set_fixed_width(button_width);
675

676
    m_treeviewcolumn_button->set_visible(m_allow_view_details);
677

678
    m_tree_view.append_column(*m_treeviewcolumn_button);
679

Murray Cumming's avatar
Murray Cumming committed
680 681
    ++view_column_index;
  }
682

683
  bool no_columns_used = true;
Murray Cumming's avatar
Murray Cumming committed
684
  int data_model_column_index = 0; //-1 means undefined index.
685

686
  guint column_to_expand = 0;
Murray Cumming's avatar
Murray Cumming committed
687
  const auto has_expandable_column = get_column_to_expand(column_to_expand);
688
  //std::cout << "DEBUG: column_to_expand=" << column_to_expand  << ", has=" << has_expandable_column << std::endl;
689

690
  for(const auto& layout_item : m_column_items)
691
  {
692
    if(layout_item) //column_info.m_visible)
693
    {
694 695
      no_columns_used = false;

Murray Cumming's avatar
Murray Cumming committed
696 697
      const auto column_name = item_get_title_or_name(layout_item);
      const auto column_id = layout_item->get_name();
698

699
      // Whenever we are dealing with real database fields,
700 701
      // we need to know the index of the field in the query:
      int item_data_model_column_index = -1;
702
      auto item_field = std::dynamic_pointer_cast<const LayoutItem_Field>(layout_item);
703 704 705 706 707 708
      if(item_field)
      {
        item_data_model_column_index = data_model_column_index;
        ++data_model_column_index;
      }

709
      //Add the ViewColumn
Murray Cumming's avatar
Murray Cumming committed
710
      auto pCellRenderer = construct_specified_columns_cellrenderer(layout_item, model_column_index, item_data_model_column_index);
711
      if(pCellRenderer)
712
      {
713
        //Get the index of the field in the query, if it is a field:
714
        //std::cout << "debug: model_column_index=" << model_column_index << ", item_data_model_column_index=" << item_data_model_column_index << std::endl;
Murray Cumming's avatar
Murray Cumming committed
715
        const bool expand = has_expandable_column && ((int)column_to_expand == model_column_index);
716 717 718
        treeview_append_column(column_name,
          *pCellRenderer,
          model_column_index, item_data_model_column_index,
719
          expand);
720

721
        /* TODO:
722
        if(column_info.m_editable)
723
        {
724

725
        }