~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to frontend/linux/workbench/sqlide_form.cpp

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "editor_base.h"
 
2
#include "gtk/lf_mforms.h"
 
3
#include "sqlide_form.h"
 
4
#include "linux_utilities/gtk_helpers.h"
 
5
#include "base/util_functions.h"
 
6
#include "base/wb_iterators.h"
 
7
#include "base/log.h"
 
8
#include "linux_utilities/toolbar_manager.h"
 
9
#include "mforms/../gtk/lf_view.h"
 
10
#include "mforms/../gtk/lf_menubar.h"
 
11
#include "mforms/../gtk/lf_toolbar.h"
 
12
#include "sqlide/query_side_palette.h"
 
13
#include <glib.h>
 
14
#include "grt/common.h"
 
15
#include "widget_saver.h"
 
16
#include "plugin_editor_base.h"
 
17
 
 
18
DEFAULT_LOG_DOMAIN("UI")
 
19
using base::strfmt;
 
20
 
 
21
static const std::vector<bec::NodeId> selected_nodeids(GridView& g)
 
22
{
 
23
  GridView::SelectedNodes nodes;
 
24
  g.get_selected_nodes(&nodes);
 
25
 
 
26
  std::vector<bec::NodeId>  entries;
 
27
  entries.reserve(nodes.size());
 
28
 
 
29
  for (base::const_range<GridView::SelectedNodes> it(nodes); it; ++it)
 
30
    entries.push_back(it->second);
 
31
 
 
32
  return entries;
 
33
}
 
34
 
 
35
//==============================================================================
 
36
//
 
37
//==============================================================================
 
38
struct SigcBlocker
 
39
{
 
40
  SigcBlocker(sigc::connection& c) : _c(c) {_c.block();}
 
41
  ~SigcBlocker() {_c.unblock();}
 
42
 
 
43
  sigc::connection& _c;
 
44
};
 
45
 
 
46
void drop_eol(const int column, Glib::ValueBase* vbase)
 
47
{
 
48
  if (column == 7)
 
49
  {
 
50
    GValue *vb = vbase->gobj();
 
51
    char* str = g_value_dup_string(vb);
 
52
    char* tstr = str;
 
53
    while (*tstr++)
 
54
    {
 
55
      if (*tstr == '\n')
 
56
        *tstr = ' ';
 
57
    }
 
58
    g_value_take_string(vb, str);
 
59
  }
 
60
}
 
61
 
 
62
//==============================================================================
 
63
//
 
64
//==============================================================================
 
65
QueryOutputView::QueryOutputView(const SqlEditorForm::Ref& be, DbSqlEditorView *db_sql_editor_view)
 
66
          : _be(be)
 
67
          , _action_output(be->log(), true)
 
68
          , _entries_grid(be->history()->entries_model())
 
69
          , _details_grid(be->history()->details_model())
 
70
          , _db_sql_editor_view(db_sql_editor_view)
 
71
{
 
72
  const char* const sections[] = {"Action Output", "Text Output", "History"};
 
73
  _action_output.show();
 
74
  _action_output.row_numbers_visible(false);
 
75
  _action_output.set_fixed_height_mode(true);
 
76
  _action_output.refresh(true);
 
77
  _action_output.view_model()->before_render = sigc::ptr_fun(drop_eol);
 
78
  _action_output.set_has_tooltip(true);
 
79
  _action_output.signal_query_tooltip().connect(sigc::mem_fun(this, &QueryOutputView::on_query_tooltip));
 
80
 
 
81
  _action_swnd.add(_action_output);
 
82
  _action_swnd.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
 
83
  
 
84
  {
 
85
    Gtk::TreeViewColumn *col;
 
86
    col = _action_output.get_column(0); // icon
 
87
    if (col)
 
88
      col->set_resizable(false);
 
89
    col = _action_output.get_column(1); // index
 
90
    if (col)
 
91
      col->set_fixed_width(40);
 
92
    col = _action_output.get_column(2); // time
 
93
    if (col)
 
94
      col->set_fixed_width(80);
 
95
    col = _action_output.get_column(3); // action
 
96
    if (col)
 
97
    {
 
98
      col->set_fixed_width(400);
 
99
    }
 
100
    col = _action_output.get_column(4); // message
 
101
    if (col)
 
102
    {
 
103
      col->set_fixed_width(350);
 
104
    }
 
105
    col = _action_output.get_column(5); // duration
 
106
    if (col)
 
107
    {
 
108
      col->set_fixed_width(150);
 
109
      col->set_resizable(false);
 
110
    }
 
111
  }
 
112
 
 
113
  mforms::Menu* context_menu = be->log()->get_context_menu();
 
114
  context_menu->set_handler(sigc::mem_fun(this, &QueryOutputView::handle_action_output_context_menu));
 
115
  _action_output.set_context_menu(context_menu);
 
116
 
 
117
  _entries_grid.set_context_menu_responder(sigc::mem_fun(this, &QueryOutputView::history_context_menu_responder));
 
118
 
 
119
  context_menu = be->history()->details_model()->get_context_menu();
 
120
  context_menu->set_handler(sigc::mem_fun(this, &QueryOutputView::handle_history_context_menu));
 
121
  _details_grid.set_context_menu(context_menu);
 
122
 
 
123
  _be->history()->entries_model()->refresh_ui_cb = sigc::mem_fun(this, &QueryOutputView::on_history_entries_refresh);
 
124
  _be->history()->details_model()->refresh_ui_cb = sigc::mem_fun(this, &QueryOutputView::on_history_details_refresh);
 
125
 
 
126
  _on_history_entries_selection_changed_conn = _entries_grid.get_selection()->signal_changed().connect(
 
127
                                                    sigc::mem_fun(this, &QueryOutputView::on_history_entries_selection_changed));
 
128
 
 
129
  _entries_grid.refresh(true);
 
130
  _details_grid.refresh(true);
 
131
 
 
132
 
 
133
  for (size_t i = 0; i < (sizeof(sections) / sizeof(const char* const)); ++i)
 
134
    _mode.append_text(sections[i]);
 
135
 
 
136
  _text_swnd.add(_text_output);
 
137
  _text_swnd.show_all();
 
138
 
 
139
  _entries_swnd.add(_entries_grid);
 
140
  _details_swnd.add(_details_grid);
 
141
 
 
142
  _entries_swnd.show_all();
 
143
  _details_swnd.show_all();
 
144
 
 
145
  _history_box.pack1(_entries_swnd, Gtk::FILL);
 
146
  _entries_swnd.set_size_request(100, -1);
 
147
  _history_box.pack2(_details_swnd, Gtk::EXPAND);
 
148
  _history_box.show_all();
 
149
 
 
150
  _note.append_page(_action_swnd,   sections[0]);
 
151
  _note.append_page(_text_swnd, sections[1]);
 
152
  _note.append_page(_history_box, sections[2]);
 
153
  _note.show_all();
 
154
 
 
155
  _note.set_show_tabs(false);
 
156
 
 
157
  Gtk::HBox  *mode_box = Gtk::manage(new Gtk::HBox());
 
158
  Gtk::Label *spacer   = Gtk::manage(new Gtk::Label());
 
159
  mode_box->pack_start(_mode, false, true);
 
160
  mode_box->pack_start(*spacer, true, true);
 
161
  _mode.property_has_frame() = false;
 
162
 
 
163
  _top_box.pack_start(*mode_box, false, true);
 
164
  _top_box.pack_start(_note, true, true);
 
165
  _top_box.show_all();
 
166
 
 
167
  _be->log()->refresh_ui_cb = sigc::bind<bool>(sigc::mem_fun(_action_output, &GridView::refresh), false);
 
168
 
 
169
  _mode.signal_changed().connect(sigc::mem_fun(this, &QueryOutputView::mode_change_requested));
 
170
  _mode.set_active(0);
 
171
}
 
172
 
 
173
 
 
174
//------------------------------------------------------------------------------
 
175
bool QueryOutputView::on_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip)
 
176
{
 
177
  Gtk::TreePath path;
 
178
  if (_action_output.get_path_at_pos(x, y, path))
 
179
  {
 
180
    std::string action, response, duration;
 
181
    _be->log()->get_field(path[0], 3, action);
 
182
    _be->log()->get_field(path[0], 4, response);
 
183
    _be->log()->get_field(path[0], 5, duration);
 
184
    if (duration.empty())
 
185
      tooltip->set_markup(base::strfmt("<b>Action:</b> %s\n<b>Response:</b> %s",
 
186
              action.c_str(), response.c_str()));
 
187
    else
 
188
      tooltip->set_markup(base::strfmt("<b>Action:</b> %s\n<b>Response:</b> %s\n<b>Duration:</b> %s", 
 
189
              action.c_str(), response.c_str(), duration.c_str()));
 
190
    return true;
 
191
  }
 
192
  return false;
 
193
}
 
194
 
 
195
//------------------------------------------------------------------------------
 
196
void QueryOutputView::mode_change_requested()
 
197
{
 
198
  const int mode = _mode.get_active_row_number();
 
199
  if (mode >= 0)
 
200
    _note.set_current_page(mode);
 
201
}
 
202
 
 
203
//------------------------------------------------------------------------------
 
204
void QueryOutputView::refresh()
 
205
{
 
206
  const int mode = _mode.get_active_row_number();
 
207
  switch (mode)
 
208
  {
 
209
    case 0: // Action Output
 
210
    {
 
211
      _action_output.refresh(false);
 
212
 
 
213
      const int log_row_count = _action_output.row_count();
 
214
      if (log_row_count > 0)
 
215
      {
 
216
        Gtk::TreePath path;
 
217
        path.push_back(log_row_count-1);
 
218
        _action_output.scroll_to_row(path);
 
219
        _action_output.set_cursor(path);
 
220
      }
 
221
      break;
 
222
    }
 
223
    case 2: // History output
 
224
    {
 
225
      const Glib::RefPtr<Gtk::TreeModel> entry_model = _entries_grid.get_model();
 
226
      const Gtk::TreeModel::Children children = entry_model->children();
 
227
      const int size = children.size();
 
228
      if (size > 0)
 
229
      {
 
230
        const Gtk::TreeIter iter = (--children.end());
 
231
        const Gtk::TreePath path = entry_model->get_path(iter);
 
232
        _entries_grid.set_cursor(path);
 
233
      }
 
234
 
 
235
      _details_grid.scroll_to(1);
 
236
      break;
 
237
    }
 
238
  }
 
239
}
 
240
 
 
241
//------------------------------------------------------------------------------
 
242
int QueryOutputView::on_history_entries_refresh()
 
243
{
 
244
  SigcBlocker signal_block(_on_history_entries_selection_changed_conn);
 
245
 
 
246
  _entries_grid.refresh(false);
 
247
  _entries_grid.scroll_to(1);
 
248
 
 
249
  return 0;
 
250
}
 
251
 
 
252
//------------------------------------------------------------------------------
 
253
int QueryOutputView::on_history_details_refresh()
 
254
{
 
255
  SigcBlocker signal_block(_on_history_entries_selection_changed_conn);
 
256
 
 
257
  _details_grid.refresh(false);
 
258
   _details_grid.scroll_to(1);
 
259
 
 
260
  return 0;
 
261
}
 
262
 
 
263
//------------------------------------------------------------------------------
 
264
void QueryOutputView::on_history_entries_selection_changed()
 
265
{
 
266
  const int row = _entries_grid.current_row();
 
267
  if (-1 < row)
 
268
  {
 
269
    _be->history()->current_entry(row);
 
270
    _details_grid.refresh(false);
 
271
  }
 
272
}
 
273
 
 
274
//------------------------------------------------------------------------------
 
275
void QueryOutputView::handle_action_output_context_menu(const std::string& action)
 
276
{
 
277
  if (action == "clear")
 
278
  {
 
279
    _be->log()->reset();
 
280
    _action_output.refresh(false);
 
281
  }
 
282
  else
 
283
  {
 
284
    GridView::SelectedNodes nodes;
 
285
    _action_output.get_selected_nodes(&nodes);
 
286
 
 
287
    std::list<int>  sel_indices;
 
288
 
 
289
    for (base::const_range<GridView::SelectedNodes> it(nodes); it; ++it)
 
290
      sel_indices.push_back(it->first);
 
291
 
 
292
 
 
293
    if (nodes.size() > 0)
 
294
    {
 
295
      const bool         copy_rows  = (action == "copy_row");
 
296
      const std::string  sql        = _db_sql_editor_view->be()->get_text_for_actions(sel_indices, !copy_rows);
 
297
 
 
298
      if (action == "append_selected_items" || action == "replace_sql_script")
 
299
      {
 
300
        QueryView* qv = _db_sql_editor_view->active_view();
 
301
        if (qv)
 
302
          qv->set_sql((action == "replace_sql_script") ? sql : qv->get_sql() + sql);
 
303
      }
 
304
      else if (copy_rows)
 
305
      {
 
306
        Glib::RefPtr<Gtk::Clipboard> clip = Gtk::Clipboard::get();
 
307
        if (clip)
 
308
          clip->set_text(sql);
 
309
      }
 
310
    }
 
311
  }
 
312
}
 
313
 
 
314
//------------------------------------------------------------------------------
 
315
void QueryOutputView::handle_history_context_menu(const std::string& action)
 
316
{
 
317
  DbSqlEditorHistory::EntriesModel::Ref entries_model = _be->history()->entries_model();
 
318
 
 
319
  const std::vector<bec::NodeId> entries = selected_nodeids(_entries_grid);
 
320
 
 
321
  if (action == "delete_selection")
 
322
  {
 
323
    entries_model->activate_popup_item_for_nodes(action, entries);
 
324
    _entries_grid.refresh(false);
 
325
  }
 
326
  else if (action == "delete_all")
 
327
  {
 
328
    entries_model->activate_popup_item_for_nodes(action, entries);
 
329
    _entries_grid.refresh(false);
 
330
  }
 
331
  else
 
332
  {
 
333
    const int selected_entry = (entries.size() > 0) ? (*entries.begin())[0] : -1;
 
334
 
 
335
    if (selected_entry >= 0)
 
336
    {
 
337
      if (action == "clear")
 
338
      {
 
339
        {
 
340
          std::vector<int> e(1, selected_entry);
 
341
          entries_model->delete_entries(e);
 
342
          _entries_grid.refresh(false);
 
343
        }
 
344
      }
 
345
      else
 
346
      {
 
347
        GridView::SelectedNodes nodes;
 
348
        _details_grid.get_selected_nodes(&nodes);
 
349
 
 
350
        std::list<int>  details;
 
351
 
 
352
        for (base::const_range<GridView::SelectedNodes> it(nodes); it; ++it)
 
353
          details.push_back(it->first);
 
354
 
 
355
        const std::string sql = _db_sql_editor_view->be()->restore_sql_from_history(selected_entry, details);
 
356
 
 
357
        if (action == "append_selected_items" || action == "replace_sql_script")
 
358
        {
 
359
          QueryView* qv = _db_sql_editor_view->active_view();
 
360
          if (qv)
 
361
            qv->set_sql((action == "replace_sql_script") ? sql : qv->get_sql() + sql);
 
362
        }
 
363
        else if (action == "copy_row")
 
364
        {
 
365
          Glib::RefPtr<Gtk::Clipboard> clip = Gtk::Clipboard::get();
 
366
          if (clip)
 
367
            clip->set_text(sql);
 
368
        }
 
369
      }
 
370
    }
 
371
  }
 
372
}
 
373
 
 
374
//------------------------------------------------------------------------------
 
375
void QueryOutputView::history_context_menu_responder()
 
376
{
 
377
  const std::vector<bec::NodeId> entries = selected_nodeids(_entries_grid);
 
378
  const bec::MenuItemList menuitems = _be->history()->entries_model()->get_popup_items_for_nodes(entries);
 
379
 
 
380
  run_popup_menu(menuitems, gtk_get_current_event_time(), sigc::mem_fun(this, &QueryOutputView::handle_history_context_menu), &_context_menu);
 
381
}
 
382
 
 
383
//------------------------------------------------------------------------------
 
384
void QueryOutputView::output_text(const std::string& text, const bool bring_to_front)
 
385
{
 
386
  Glib::RefPtr<Gtk::TextBuffer> buf = _text_output.get_buffer();
 
387
  buf->insert(buf->end(), text);
 
388
 
 
389
  if (bring_to_front)
 
390
    _mode.set_active(1); // 1 - Text output tab
 
391
}
 
392
 
 
393
//==============================================================================
 
394
//
 
395
//==============================================================================
 
396
QueryView::QueryView(const int current_index, DbSqlEditorView* owner)
 
397
          : _owner(owner)
 
398
          , _editor_box(false)
 
399
          , _label(0)
 
400
          , _query_toolbar(owner->be()->sql_editor_toolbar(current_index))
 
401
          , _apply_btn(_("Apply"))
 
402
          , _cancel_btn(_("Revert"))
 
403
          , _query_collapsed(false)
 
404
          , _updating_results(false)
 
405
{
 
406
  Gtk::Widget&        editor_widget = _editor.container();
 
407
  SqlEditorForm::Ref             be = owner->be();
 
408
  Sql_editor::Ref            editor = be->sql_editor(current_index);
 
409
 
 
410
  _editor.be(editor);
 
411
 
 
412
  editor_widget.set_size_request(-1, 1);
 
413
  _editor.set_font(be->wbsql()->get_wbui()->get_wb()->get_wb_options().get_string("workbench.general.Editor:Font"));
 
414
  _editor.set_text(editor->sql());
 
415
 
 
416
  _editor.background_action_cb(sigc::bind(sigc::mem_fun(&_editor, &SqlEditorFE::check_sql), false));
 
417
  _editor.signal_selection_changed().connect(sigc::mem_fun(this, &QueryView::on_sql_editor_selection_change));
 
418
 
 
419
  _rs_tabs.set_tab_pos(Gtk::POS_BOTTOM);
 
420
 
 
421
  boost::shared_ptr<mforms::ToolBar> toolbar(be->sql_editor_toolbar(be->sql_editor_index(editor)));
 
422
  Gtk::Widget* w = mforms::gtk::widget_for_toolbar(toolbar.get());
 
423
  if (w)
 
424
  {
 
425
    w->show();
 
426
    _editor_box.pack_start(*w, false, true);
 
427
    //_rs_box.pack_start(*w, false, true);
 
428
  }
 
429
  else
 
430
    g_warning("No toolbar for editor");
 
431
 
 
432
  _rs_box.pack_start(_rs_tabs, true, true);
 
433
  _rs_box.show_all();
 
434
 
 
435
  _editor_box.pack_start(editor_widget, true, true);
 
436
  _top_pane.pack1(_editor_box, true, true);
 
437
  _top_pane.pack2(_rs_box, false, true);
 
438
 
 
439
  _top_pane.set_name("sqled.query_view.top_pane");
 
440
  _top_pane.show_all();
 
441
  _top_pane.property_position().signal_changed().connect(sigc::mem_fun(this, &QueryView::top_pane_changed));
 
442
 
 
443
  _rs_tabs.signal_switch_page().connect(sigc::mem_fun(this, &QueryView::rs_page_switched));
 
444
  _rs_tabs.signal_page_reordered().connect(sigc::mem_fun(this, &QueryView::tab_reordered));
 
445
 
 
446
  // map is not reliable, it can be mapped but still not visible on screen. so monitor idle 
 
447
 // _polish_conn = _top_pane.signal_map().connect(sigc::mem_fun(this, &QueryView::polish));
 
448
 
 
449
  _btn_box.pack_start(_apply_btn, true, true);
 
450
  _btn_box.pack_start(_cancel_btn, true, true);
 
451
  _btn_box.pack_start(_editability_label, true, true);
 
452
  _btn_box.pack_start(_editability_icon, false, true);
 
453
 
 
454
  _editability_icon.set(Gdk::Pixbuf::create_from_file(_owner->grt_manager()->get_data_file_path("images/mini_notice.png")));
 
455
 
 
456
  _rs_tabs.set_action_widget(&_btn_box, Gtk::PACK_END);
 
457
  _btn_box.show_all();
 
458
  _editability_label.hide();
 
459
  _editability_icon.hide();
 
460
  _apply_btn.signal_clicked().connect(sigc::mem_fun(this, &QueryView::apply_recordset_changes));
 
461
  _cancel_btn.signal_clicked().connect(sigc::mem_fun(this, &QueryView::discard_recordset_changes));
 
462
  utils::gtk::load_settings(_owner->grt_manager(), &_top_pane);
 
463
  _polish_conn = Glib::signal_idle().connect(sigc::bind_return(sigc::mem_fun(this, &QueryView::polish), false));
 
464
 
 
465
}
 
466
 
 
467
//------------------------------------------------------------------------------
 
468
QueryView::~QueryView()
 
469
{
 
470
}
 
471
 
 
472
 
 
473
//------------------------------------------------------------------------------
 
474
void QueryView::top_pane_changed()
 
475
{
 
476
  if (!_polish_conn.connected())
 
477
  {
 
478
    // user has dragged the splitter (or maybe it was changed by us, but shouldn't be the case)
 
479
    _query_collapsed = false;
 
480
    utils::gtk::save_settings(_owner->grt_manager(), &_top_pane, false);
 
481
  }
 
482
}
 
483
 
 
484
//------------------------------------------------------------------------------
 
485
void QueryView::polish()
 
486
{
 
487
  if (_owner->be()->sql_editor_start_collapsed(_owner->be()->sql_editor_index(_editor.be())))
 
488
  {
 
489
    _query_collapsed = true;
 
490
    _top_pane.set_position(0);
 
491
  }
 
492
  else
 
493
    gtk_paned_set_pos_ratio(&_top_pane, 1);
 
494
  _polish_conn.disconnect();
 
495
}
 
496
 
 
497
//------------------------------------------------------------------------------
 
498
void QueryView::on_sql_editor_selection_change()
 
499
{
 
500
  _owner->be()->wbsql()->get_wbui()->get_command_ui()->signal_validate_edit_menu_items();
 
501
}
 
502
 
 
503
//------------------------------------------------------------------------------
 
504
void QueryView::update_exec_sql_progress(float progress, const std::string &message)
 
505
{}
 
506
 
 
507
//------------------------------------------------------------------------------
 
508
void QueryView::update_label(const std::string& label)
 
509
{
 
510
  if (_label)
 
511
    _label->set_text(label);
 
512
}
 
513
 
 
514
 
 
515
//------------------------------------------------------------------------------
 
516
 
 
517
void QueryView::update_recordset_caption()
 
518
{
 
519
  RecordsetView* rs = active_recordset();
 
520
  if (rs)
 
521
  {
 
522
    ActiveLabel *label = dynamic_cast<ActiveLabel*>(_rs_tabs.get_tab_label(*rs));
 
523
    if (label)
 
524
      label->set_text(rs->model()->caption());
 
525
 
 
526
    const bool enable_buttons = rs->model()->has_pending_changes();
 
527
    _btn_box.set_sensitive(enable_buttons);
 
528
    if (_owner->be()->get_menubar())
 
529
      _owner->be()->get_menubar()->validate();
 
530
  }
 
531
}
 
532
 
 
533
//------------------------------------------------------------------------------
 
534
 
 
535
static void hide_rs_pane(Gtk::Paned *pane)
 
536
{
 
537
  void *p = pane->get_data("allow_save");
 
538
  pane->set_data("allow_save", 0);
 
539
  gtk_paned_set_pos_ratio(pane, 1);
 
540
  pane->set_data("allow_save", p);
 
541
}
 
542
 
 
543
 
 
544
void QueryView::update_resultsets()
 
545
{
 
546
  const int editor_index = index();
 
547
 
 
548
  log_debug("updating rsets\n");
 
549
  SqlEditorForm::Ref             be                  = _owner->be();
 
550
  const int                      n_recordsets_in_be  = be->recordset_count(editor_index);
 
551
  std::vector<Recordset::Ref>    recordsets_from_be;
 
552
  std::map<Recordset::Ref, int>  recordset_indexes;
 
553
 
 
554
  _updating_results = true;
 
555
 
 
556
  recordsets_from_be.reserve(n_recordsets_in_be);
 
557
 
 
558
  // prepare list of recordsets in BE, which later be matched against exisiting ones in UI(tabs)
 
559
  if (n_recordsets_in_be > 0)
 
560
  {
 
561
    for (int i = n_recordsets_in_be - 1; i >= 0; --i)
 
562
    {
 
563
      recordsets_from_be.push_back(be->recordset(editor_index, i));
 
564
      recordset_indexes[recordsets_from_be.back()] = i;
 
565
    }
 
566
  }
 
567
 
 
568
  Recordset::Ref              rs;
 
569
  const int                   ntabs = _rs_tabs.get_n_pages();
 
570
  std::vector<Gtk::Widget*>   orphan_views; // list of views which Recordset::ref is not in BE
 
571
 
 
572
  // Walk notebook pages removing existing Recordset objects from the @recordsets_from_be list
 
573
  // Also add view to @orphan_views if Recordset is not in BE, for later remove_page
 
574
  for (int i = 0; i < ntabs; ++i)
 
575
  {
 
576
    RecordsetView* view = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
 
577
    if (view)
 
578
    {
 
579
      rs = view->model();
 
580
      const size_t before_remove = recordsets_from_be.size();
 
581
      base::vector_remove(recordsets_from_be, rs);
 
582
 
 
583
      if (recordsets_from_be.size() == before_remove)
 
584
        orphan_views.push_back(view);
 
585
    }
 
586
  }
 
587
 
 
588
  // Remove orphan pages
 
589
  for (size_t i = 0; i < orphan_views.size(); ++i)
 
590
    _rs_tabs.remove_page(*orphan_views[i]);
 
591
 
 
592
  // Create new pages
 
593
  const int size = recordsets_from_be.size();
 
594
  for (int i = 0; i < size; ++i)
 
595
  {
 
596
    const Recordset::Ref&        rs = recordsets_from_be[i];
 
597
 
 
598
    std::auto_ptr<ActiveLabel>   lbl(new ActiveLabel(rs->caption(), sigc::bind(sigc::mem_fun(this, &QueryView::close_recordset), rs->key(), true)));
 
599
    std::auto_ptr<RecordsetView> view(RecordsetView::create(rs, 0));
 
600
 
 
601
 
 
602
    if (lbl.get() && view.get())
 
603
    {
 
604
      lbl->show();
 
605
      view->show();
 
606
 
 
607
      RecordsetView* rs_view   = view.get();
 
608
 
 
609
      mforms::Menu* menu = lbl.get()->get_menu();
 
610
      init_tab_menu(menu);
 
611
      menu->set_handler(sigc::bind(sigc::mem_fun(this, &QueryView::tab_menu_handler), lbl.get(), rs_view));
 
612
      rs_view->set_data("active_label", lbl.get());
 
613
 
 
614
      const int pos_added = _rs_tabs.append_page(*Gtk::manage(view.release()), *(Gtk::manage(lbl.release())));
 
615
      _rs_tabs.set_tab_reorderable(*rs_view, true);
 
616
 
 
617
      rs_view->refresh();
 
618
      rs_view->show();
 
619
      rs->task->msg_cb(sigc::bind(sigc::mem_fun(this, &QueryView::process_task_msg), rs_view));
 
620
 
 
621
      _rs_tabs.set_current_page(pos_added);
 
622
    }
 
623
  }
 
624
 
 
625
  if (!_query_collapsed)
 
626
  {
 
627
    const int max_position = _top_pane.get_height() - 200;
 
628
    _polish_conn.disconnect();
 
629
    if (ntabs == 0 && _rs_tabs.get_n_pages() > 0) // first tab was added
 
630
      utils::gtk::load_settings(_owner->grt_manager(), &_top_pane, sigc::bind(sigc::ptr_fun(gtk_paned_set_pos_ratio), &_top_pane, 0.4), false, -max_position);
 
631
    else if (_rs_tabs.get_n_pages() == 0) // tabs are gone
 
632
      Glib::signal_idle().connect_once(sigc::bind(sigc::ptr_fun(hide_rs_pane), &_top_pane));
 
633
    else if (_rs_tabs.get_n_pages() > 0) // more tabs was added or replaced
 
634
    { // if the current size is too small, make it a bit bigger
 
635
      if (_top_pane.get_position() > max_position)
 
636
        Glib::signal_idle().connect_once(sigc::bind(sigc::mem_fun(&_top_pane, &Gtk::Paned::set_position), max_position));
 
637
    }
 
638
  }
 
639
  _updating_results = false;
 
640
 
 
641
  rs_page_switched(0, _rs_tabs.get_current_page());
 
642
 
 
643
  reenable_items_in_tab_menus();
 
644
  if (_label)
 
645
    _label->stop_busy();
 
646
}
 
647
 
 
648
//------------------------------------------------------------------------------
 
649
void QueryView::recalc_rstab_indices()
 
650
{
 
651
  const int size = _rs_tabs.get_n_pages();
 
652
  for (int i = 0; i < size; ++i)
 
653
  {
 
654
    RecordsetView* view = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
 
655
    if (view)
 
656
      _owner->be()->recordset_reorder(index(), view->model(), i);
 
657
  }
 
658
}
 
659
 
 
660
//------------------------------------------------------------------------------
 
661
void QueryView::close()
 
662
{
 
663
  _owner->close_editor_tab(this);
 
664
}
 
665
 
 
666
//------------------------------------------------------------------------------
 
667
RecordsetView* QueryView::active_recordset()
 
668
{
 
669
  RecordsetView* rs(0);
 
670
  const int page = _rs_tabs.get_current_page();
 
671
  if (page >= 0)
 
672
    rs = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(page));
 
673
 
 
674
  return rs;
 
675
}
 
676
 
 
677
//------------------------------------------------------------------------------
 
678
void QueryView::close_recordset(long long key, bool confirm)
 
679
{
 
680
  const int ntabs = _rs_tabs.get_n_pages();
 
681
 
 
682
  for (int i = 0; i < ntabs; ++i)
 
683
  {
 
684
    RecordsetView* view  = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
 
685
    if (view)
 
686
    {
 
687
      const Recordset::Ref model = view->model();
 
688
      if (model && model->key() == key)
 
689
      {
 
690
        close_recordset_by_ptr(view, confirm);
 
691
        break;
 
692
      }
 
693
    }
 
694
  }
 
695
}
 
696
 
 
697
//------------------------------------------------------------------------------
 
698
void QueryView::close_recordset_by_ptr(RecordsetView* view, const bool confirm)
 
699
{
 
700
  if (view)
 
701
  {
 
702
    if (confirm && view->model()->can_close(true))
 
703
    {
 
704
      view->model()->close();
 
705
     // _rs_tabs.remove_page(*view);
 
706
    }
 
707
  }
 
708
 
 
709
  if (_rs_tabs.get_n_pages() == 0) // tabs are gone
 
710
    gtk_paned_set_pos_ratio(&_top_pane, 1);
 
711
 
 
712
  reenable_items_in_tab_menus();
 
713
}
 
714
 
 
715
//------------------------------------------------------------------------------
 
716
void QueryView::rs_page_switched(GtkNotebookPage *page, guint page_index)
 
717
{
 
718
  bool show_action_area = false;
 
719
  bool action_area_enabled = false;
 
720
 
 
721
  if (_updating_results)
 
722
    return;
 
723
 
 
724
  RecordsetView* rs = active_recordset();
 
725
  if (rs)
 
726
  {
 
727
    Recordset::Ref rsm = rs->model();
 
728
    _owner->be()->active_recordset(index(), rsm);
 
729
    show_action_area    = !rsm->is_readonly();
 
730
    action_area_enabled = rsm->has_pending_changes();
 
731
    _editability_label.set_tooltip_text(rsm->readonly_reason());
 
732
    _editability_icon.set_tooltip_text(rsm->readonly_reason());
 
733
    log_debug("show %i, enabled %i\n", (int)show_action_area, (int)action_area_enabled);
 
734
  }
 
735
  else
 
736
  {
 
737
    _owner->be()->active_recordset(index(), Recordset::Ref());
 
738
  }
 
739
 
 
740
  if (show_action_area)
 
741
  {
 
742
    _apply_btn.show();
 
743
    _cancel_btn.show();
 
744
    _editability_label.hide();
 
745
    _editability_icon.hide();
 
746
    _btn_box.set_sensitive(action_area_enabled);
 
747
  }
 
748
  else
 
749
  {
 
750
    _apply_btn.hide();
 
751
    _cancel_btn.hide();
 
752
    _editability_label.show();
 
753
    _editability_label.set_markup("<small>ReadOnly</small>");
 
754
    _editability_icon.show();
 
755
  }
 
756
}
 
757
 
 
758
// Label associated with the view in gtk::notebook
 
759
//------------------------------------------------------------------------------
 
760
void QueryView::set_linked_label(ActiveLabel* lbl)
 
761
{
 
762
  _label = lbl;
 
763
}
 
764
 
 
765
//------------------------------------------------------------------------------
 
766
void QueryView::execute_sql(bool current_statement_only)
 
767
{
 
768
  std::string sql;
 
769
  if (current_statement_only)
 
770
  {
 
771
    _editor.check_sql(true);
 
772
    sql = _editor.current_sql_statement();
 
773
  }
 
774
  else
 
775
  {
 
776
    sql = _editor.get_selected_text();
 
777
    if (sql.empty())
 
778
      sql = _editor.get_text();
 
779
  }
 
780
 
 
781
  if (sql.empty())
 
782
    return;
 
783
 
 
784
  //_tab_pages->set_current_page(_tab_pages->page_num(*_log_page));
 
785
 
 
786
  if (_label)
 
787
    _label->start_busy();
 
788
  _owner->be()->exec_sql(sql, _editor.be(), false, current_statement_only);
 
789
}
 
790
 
 
791
//------------------------------------------------------------------------------
 
792
int QueryView::index()
 
793
{
 
794
  return _owner->be()->sql_editor_index(_editor.be());
 
795
}
 
796
 
 
797
//------------------------------------------------------------------------------
 
798
void QueryView::save()
 
799
{
 
800
  const int view_index = index();
 
801
 
 
802
  if (view_index >= 0)
 
803
    _owner->be()->save_sql_script_file(_owner->be()->sql_editor_path(view_index), view_index);
 
804
}
 
805
 
 
806
//------------------------------------------------------------------------------
 
807
std::string QueryView::editor_path()
 
808
{
 
809
  std::string  path;
 
810
  const int    view_index = index();
 
811
 
 
812
  if (view_index >= 0)
 
813
    path = _owner->be()->sql_editor_path(view_index);
 
814
 
 
815
  return path;
 
816
}
 
817
 
 
818
//------------------------------------------------------------------------------
 
819
void QueryView::apply_recordset_changes()
 
820
{
 
821
  RecordsetView* rs = active_recordset();
 
822
  if (rs)
 
823
  {
 
824
    Recordset::Ref rsm = rs->model();
 
825
    if (rsm->has_pending_changes())
 
826
      rsm->apply_changes();
 
827
  }
 
828
}
 
829
 
 
830
//------------------------------------------------------------------------------
 
831
void QueryView::discard_recordset_changes()
 
832
{
 
833
  RecordsetView* rs = active_recordset();
 
834
  if (rs)
 
835
  {
 
836
    Recordset::Ref rsm = rs->model();
 
837
    if (rsm->has_pending_changes())
 
838
      rsm->rollback();
 
839
  }
 
840
}
 
841
 
 
842
//------------------------------------------------------------------------------
 
843
int QueryView::process_task_msg(int msgType, const std::string &message, const std::string &detail, RecordsetView *rsv)
 
844
{
 
845
  _owner->output_text(message, true);
 
846
  return 0;
 
847
}
 
848
 
 
849
//------------------------------------------------------------------------------
 
850
void QueryView::init_tab_menu(mforms::Menu* menu)
 
851
{
 
852
  menu->add_item("Close Tab", "close tab");
 
853
  menu->add_item("Close Other Tabs", "close other tabs");
 
854
}
 
855
 
 
856
//------------------------------------------------------------------------------
 
857
void QueryView::tab_menu_handler(const std::string& action, ActiveLabel* sender, RecordsetView* rsview)
 
858
{
 
859
  if (action == "close tab")
 
860
  {
 
861
    close_recordset_by_ptr(rsview, true);
 
862
  }
 
863
  else if (action == "close other tabs")
 
864
  {
 
865
    int size = _rs_tabs.get_n_pages();
 
866
    std::vector<RecordsetView*> to_delete;
 
867
    to_delete.reserve(size - 1);
 
868
 
 
869
    for (int i = 0; i < size; ++i)
 
870
    {
 
871
      RecordsetView* view  = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
 
872
      if (view && rsview != view)
 
873
        to_delete.push_back(view);
 
874
    }
 
875
 
 
876
    size = to_delete.size();
 
877
    for (int i = 0; i < size; ++i)
 
878
      close_recordset_by_ptr(to_delete[i], true);
 
879
  }
 
880
}
 
881
 
 
882
//------------------------------------------------------------------------------
 
883
void QueryView::reenable_items_in_tab_menus()
 
884
{
 
885
  const int size = _rs_tabs.get_n_pages();
 
886
 
 
887
  for (int i = 0; i < size; ++i)
 
888
  {
 
889
    ActiveLabel* const al = (ActiveLabel*)_rs_tabs.get_nth_page(i)->get_data("active_label");
 
890
    if (al)
 
891
    {
 
892
      mforms::Menu* const menu = al->get_menu();
 
893
      const int index = menu->get_item_index("close other tabs");
 
894
      if (index >= 0)
 
895
        menu->set_item_enabled(index, size > 1);
 
896
    }
 
897
  }
 
898
}
 
899
 
 
900
//------------------------------------------------------------------------------
 
901
void QueryView::stop_busy()
 
902
{
 
903
  if (_label)
 
904
    _label->stop_busy();
 
905
}
 
906
 
 
907
//------------------------------------------------------------------------------
 
908
void QueryView::focus()
 
909
{
 
910
  _editor.widget().grab_focus();
 
911
}
 
912
 
 
913
//------------------------------------------------------------------------------
 
914
//------------------------------------------------------------------------------
 
915
DbSqlEditorView *DbSqlEditorView::create(SqlEditorForm::Ref editor_be)
 
916
{
 
917
  return Gtk::manage(new DbSqlEditorView(editor_be));
 
918
}
 
919
 
 
920
//------------------------------------------------------------------------------
 
921
DbSqlEditorView::DbSqlEditorView(SqlEditorForm::Ref editor_be)
 
922
                : _be(editor_be)
 
923
                , _output(_be, this)
 
924
                , _side_palette(mforms::gtk::ViewImpl::get_widget_for_view(_be->get_side_palette()))
 
925
                , _grtm(editor_be->grt_manager())
 
926
                , _right_aligned(editor_be->wbsql()->get_wbui()->get_wb()->get_wb_options().get_int("Sidebar:RightAligned", 0))
 
927
{
 
928
  _top_pane.set_name("sqled.top_pane" + std::string(_right_aligned ? ".right_aligned" : ".left_aligned"));
 
929
  _main_pane.set_name("sqled.main_pane" + std::string(_right_aligned ? ".right_aligned" : ".left_aligned"));
 
930
  _top_right_pane.set_name("sqled.top_right_pane" + std::string(_right_aligned ? ".right_aligned" : ".left_aligned"));
 
931
 
 
932
  Gtk::Widget *v = this;
 
933
  _be = editor_be;
 
934
  _be->set_frontend_data(dynamic_cast<FormViewBase*>(this));
 
935
 
 
936
  mforms::View *sbview  = _be->get_sidebar();
 
937
  Gtk::Widget  *sidebar = mforms::gtk::ViewImpl::get_widget_for_view(sbview);
 
938
 
 
939
  _editor_note = Gtk::manage(new Gtk::Notebook()); // init FormViewBase::_editor_note
 
940
  _editor_note->show();
 
941
  _editor_note->set_scrollable(true);
 
942
  _editor_note->set_show_border(false);
 
943
 
 
944
  _main_pane.pack1(_top_right_pane, true, false);
 
945
  _main_pane.pack2(_output.get_outer(), false, true);
 
946
 
 
947
  sidebar->set_no_show_all(true);
 
948
  if (_right_aligned)
 
949
  {
 
950
    _top_pane.add1(_main_pane);
 
951
    _top_pane.add2(*sidebar);
 
952
 
 
953
    _top_right_pane.pack2(*_editor_note, true, false);
 
954
    if (_side_palette)
 
955
      _top_right_pane.pack1(*_side_palette, false, true);
 
956
  }
 
957
  else
 
958
  {
 
959
    _top_pane.add1(*sidebar);
 
960
    _top_pane.add2(_main_pane);
 
961
 
 
962
    _top_right_pane.pack1(*_editor_note, true, false);
 
963
    if (_side_palette)
 
964
      _top_right_pane.pack2(*_side_palette, false, true);
 
965
  }
 
966
 
 
967
  _top_pane.show_all();
 
968
  _top_right_pane.show_all();
 
969
  _main_pane.show_all();
 
970
 
 
971
  // Add main menu
 
972
  Gtk::Widget *w = mforms::gtk::widget_for_menubar(_be->get_menubar());
 
973
  if (w)
 
974
    pack_start(*w, false, true);
 
975
 
 
976
  // Add main toolbar
 
977
  w = mforms::gtk::widget_for_toolbar(_be->get_toolbar());
 
978
  if (w)
 
979
  {
 
980
    w->show();
 
981
    pack_start(*w, false, true);
 
982
  }
 
983
 
 
984
  pack_start(_top_pane, true, true);
 
985
  show_all();
 
986
 
 
987
  // Connect signals
 
988
  _be->sql_editor_new_ui.connect(sigc::mem_fun(this, &DbSqlEditorView::add_editor_tab));
 
989
  _be->set_partial_refresh_ui_slot(sigc::mem_fun(this, &DbSqlEditorView::partial_refresh_ui));
 
990
 
 
991
  _be->exec_sql_task->progress_cb(sigc::mem_fun(this, &DbSqlEditorView::on_exec_sql_progress));
 
992
  _be->exec_sql_task->finish_cb(sigc::mem_fun(this, &DbSqlEditorView::on_exec_sql_done));
 
993
  _be->recordset_list_changed.connect(sigc::mem_fun(this, &DbSqlEditorView::recordset_list_changed));
 
994
  _be->output_text_slot= sigc::mem_fun(this, &DbSqlEditorView::output_text);
 
995
 
 
996
  _be->sql_editor_text_insert_signal.connect(sigc::mem_fun(this, &DbSqlEditorView::on_sql_editor_text_insert));
 
997
 
 
998
  _update_resultset_slots_lock = g_mutex_new();
 
999
 
 
1000
  _dispatch_rset_update_conn = _dispatch_rset_update.connect(sigc::mem_fun(this, &DbSqlEditorView::update_resultsets_from_main));
 
1001
  _editor_note->signal_switch_page().connect(sigc::mem_fun(this, &DbSqlEditorView::editor_page_switched));
 
1002
  _editor_note->signal_page_reordered().connect(sigc::mem_fun(this, &DbSqlEditorView::editor_page_reordered));
 
1003
 
 
1004
 
 
1005
  // realize pre-existing editors
 
1006
  for (int i = 0; i < _be->sql_editor_count(); i++)
 
1007
  {
 
1008
    _be->active_sql_editor_index(i);
 
1009
    add_editor_tab(i);
 
1010
  }
 
1011
 
 
1012
  _polish_conn = signal_map().connect(sigc::mem_fun(this, &DbSqlEditorView::polish));
 
1013
 // ->add_builtin_command("query.explain",
 
1014
 //       sigc::mem_fun(this, &DbSqlEditorView::explain_sql),
 
1015
 //       sigc::mem_fun(this, &DbSqlEditorView::validate_explain_sql));
 
1016
 
 
1017
  // restore state of toolbar
 
1018
  {
 
1019
    mforms::ToolBar *toolbar = _be->get_toolbar();
 
1020
    bool flag;
 
1021
 
 
1022
    toolbar->set_item_checked("wb.toggleSchemataBar", flag = !_grtm->get_app_option_int("DbSqlEditor:SchemaSidebarHidden", 0));
 
1023
    if (flag) _top_pane.get_child1()->show(); else _top_pane.get_child1()->hide();
 
1024
 
 
1025
    toolbar->set_item_checked("wb.toggleOutputArea", flag = !_grtm->get_app_option_int("DbSqlEditor:OutputAreaHidden", 0));
 
1026
    if (flag) _main_pane.get_child2()->show(); else _main_pane.get_child2()->hide();
 
1027
 
 
1028
    toolbar->set_item_checked("wb.toggleSideBar", flag = !_grtm->get_app_option_int("DbSqlEditor:SidebarHidden", 0));
 
1029
    if (flag) _top_right_pane.get_child2()->show(); else _top_right_pane.get_child2()->hide();
 
1030
  }
 
1031
 
 
1032
//  utils::gtk::load_toolbar_state(_grtm, _be->get_toolbar());
 
1033
 
 
1034
  utils::gtk::load_settings(_grtm, &_top_pane, sigc::bind(sigc::ptr_fun(gtk_paned_set_pos_ratio), &_top_pane, _right_aligned ? 0.8 : 0.2), false);
 
1035
  utils::gtk::load_settings(_grtm, &_main_pane, sigc::bind(sigc::ptr_fun(gtk_paned_set_pos_ratio), &_main_pane, _right_aligned ? 0.2 : 0.8), false);
 
1036
  utils::gtk::load_settings(_grtm, &_top_right_pane, sigc::bind(sigc::ptr_fun(gtk_paned_set_pos_ratio), &_top_right_pane, _right_aligned ? 0.1 : 0.9), true);
 
1037
 
 
1038
  _top_pane.property_position().signal_changed().connect(sigc::bind(sigc::ptr_fun(utils::gtk::save_settings), _grtm, &_top_pane, false));
 
1039
  _main_pane.property_position().signal_changed().connect(sigc::bind(sigc::ptr_fun(utils::gtk::save_settings), _grtm, &_main_pane, false));
 
1040
  _top_right_pane.property_position().signal_changed().connect(sigc::bind(sigc::ptr_fun(utils::gtk::save_settings), _grtm, &_top_right_pane, true));
 
1041
}
 
1042
 
 
1043
//------------------------------------------------------------------------------
 
1044
 
 
1045
bool DbSqlEditorView::perform_command(const std::string &cmd)
 
1046
{
 
1047
  if (cmd == "wb.toggleSchemataBar")
 
1048
  {
 
1049
    Gtk::Widget* w = _top_pane.get_child1();
 
1050
    bool hidden = !be()->get_toolbar()->get_item_checked("wb.toggleSchemataBar");
 
1051
    if (!hidden)
 
1052
      w->show();
 
1053
    else
 
1054
      w->hide();
 
1055
    _grtm->set_app_option("DbSqlEditor:SchemaSidebarHidden", grt::IntegerRef(hidden));
 
1056
  }
 
1057
  else if (cmd == "wb.toggleOutputArea")
 
1058
  {
 
1059
    Gtk::Widget* w = _main_pane.get_child2();
 
1060
    bool hidden = !be()->get_toolbar()->get_item_checked("wb.toggleOutputArea");
 
1061
    if (!hidden)
 
1062
      w->show();
 
1063
    else
 
1064
      w->hide();
 
1065
    _grtm->set_app_option("DbSqlEditor:OutputAreaHidden", grt::IntegerRef(hidden));
 
1066
  } 
 
1067
  else if (cmd == "wb.toggleSideBar")
 
1068
  {
 
1069
    Gtk::Widget* w = _top_right_pane.get_child2();
 
1070
    bool hidden = !be()->get_toolbar()->get_item_checked("wb.toggleSideBar");
 
1071
    if (!hidden)
 
1072
      w->show();
 
1073
    else
 
1074
      w->hide();
 
1075
    _grtm->set_app_option("DbSqlEditor:SidebarHidden", grt::IntegerRef(hidden));
 
1076
  }
 
1077
  else 
 
1078
    return false;
 
1079
  return true;
 
1080
}
 
1081
 
 
1082
//------------------------------------------------------------------------------
 
1083
void DbSqlEditorView::polish()
 
1084
{
 
1085
  gtk_paned_set_pos_ratio(&_main_pane, 0.7);
 
1086
  _polish_conn.disconnect();
 
1087
}
 
1088
 
 
1089
//------------------------------------------------------------------------------
 
1090
DbSqlEditorView::~DbSqlEditorView()
 
1091
{
 
1092
// state is saved when it changes. saving again on quit, will give unexpected results
 
1093
//  utils::gtk::save_settings(_grtm, &_top_pane, false);
 
1094
//  utils::gtk::save_settings(_grtm, &_main_pane, false);
 
1095
//  utils::gtk::save_settings(_grtm, &_top_right_pane, true);
 
1096
 
 
1097
  const std::vector<QueryView*> views = DbSqlEditorView::query_views();
 
1098
  for (size_t i = 0; i < views.size(); ++i)
 
1099
  {
 
1100
    _editor_note->remove_page(*views[i]->get_outer());
 
1101
    delete views[i];
 
1102
  }
 
1103
 
 
1104
  _dispatch_rset_update_conn.disconnect();
 
1105
 
 
1106
  if (_side_palette)
 
1107
    _side_palette->hide();
 
1108
 
 
1109
  dispose();
 
1110
 
 
1111
  while (!g_mutex_trylock(_update_resultset_slots_lock)); // maybe add sleep here
 
1112
 
 
1113
  g_mutex_unlock(_update_resultset_slots_lock);
 
1114
  g_mutex_free(_update_resultset_slots_lock);
 
1115
  _update_resultset_slots_lock = 0;
 
1116
}
 
1117
 
 
1118
//------------------------------------------------------------------------------
 
1119
void DbSqlEditorView::dispose()
 
1120
{
 
1121
  if (_be)
 
1122
  {
 
1123
  //  utils::gtk::save_toolbar_state(_grtm, _be->get_toolbar());
 
1124
    _be->close();
 
1125
    _be.reset();
 
1126
  }
 
1127
}
 
1128
 
 
1129
//------------------------------------------------------------------------------
 
1130
std::vector<QueryView*> DbSqlEditorView::query_views()
 
1131
{
 
1132
  std::vector<QueryView*> list;
 
1133
 
 
1134
  const int size = _editor_note->get_n_pages();
 
1135
  list.reserve(size);
 
1136
 
 
1137
  for (int i = 0; i < size; ++i)
 
1138
  {
 
1139
    Gtk::Widget* const child = _editor_note->get_nth_page(i);
 
1140
    if (child)
 
1141
    {
 
1142
      QueryView* qv = reinterpret_cast<QueryView*>(child->get_data("query_view"));
 
1143
      if (qv)
 
1144
        list.push_back(qv);
 
1145
    }
 
1146
  }
 
1147
 
 
1148
  return list;
 
1149
}
 
1150
 
 
1151
//------------------------------------------------------------------------------
 
1152
void DbSqlEditorView::init()
 
1153
{
 
1154
}
 
1155
 
 
1156
//------------------------------------------------------------------------------
 
1157
bool DbSqlEditorView::validate_explain_sql()
 
1158
{
 
1159
  return true;
 
1160
}
 
1161
 
 
1162
//------------------------------------------------------------------------------
 
1163
void DbSqlEditorView::explain_sql()
 
1164
{
 
1165
  _be->explain_sql();
 
1166
}
 
1167
 
 
1168
//------------------------------------------------------------------------------
 
1169
 
 
1170
bool DbSqlEditorView::show_find(bool replace)
 
1171
{
 
1172
  QueryView *view = active_view();
 
1173
  if (view)
 
1174
  {
 
1175
    view->get_editor_fe()->show_find_panel(true);
 
1176
    view->get_editor_fe()->enable_replace_panel(replace);
 
1177
    return true;
 
1178
  }
 
1179
  return false;
 
1180
}
 
1181
 
 
1182
//------------------------------------------------------------------------------
 
1183
QueryView* DbSqlEditorView::active_view()
 
1184
{
 
1185
  QueryView* ret = 0;
 
1186
 
 
1187
  const int     cur_page_id = _editor_note->get_current_page();
 
1188
  Gtk::Widget*  content     = _editor_note->get_nth_page(cur_page_id);
 
1189
  if (content)
 
1190
    ret = (QueryView*)content->get_data("query_view");
 
1191
 
 
1192
  return ret;
 
1193
}
 
1194
 
 
1195
//------------------------------------------------------------------------------
 
1196
QueryView* DbSqlEditorView::find_view_by_index(const int index)
 
1197
{
 
1198
  QueryView* view = 0;
 
1199
 
 
1200
  const int size = _editor_note->get_n_pages();
 
1201
  for (int i = 0; i < size; ++i)
 
1202
  {
 
1203
    Gtk::Widget* const child = _editor_note->get_nth_page(i);
 
1204
    if (child)
 
1205
    {
 
1206
      QueryView* qv = reinterpret_cast<QueryView*>(child->get_data("query_view"));
 
1207
      if (qv && qv->index() == index)
 
1208
      {
 
1209
        view = qv;
 
1210
        break;
 
1211
      }
 
1212
    }
 
1213
  }
 
1214
 
 
1215
  return view;
 
1216
}
 
1217
 
 
1218
 
 
1219
//------------------------------------------------------------------------------
 
1220
void DbSqlEditorView::recalc_tab_indices()
 
1221
{
 
1222
  int query_views_cnt = 0;
 
1223
 
 
1224
  const int size = _editor_note->get_n_pages();
 
1225
  for (int i = 0; i < size; ++i)
 
1226
  {
 
1227
    Gtk::Widget* const child = _editor_note->get_nth_page(i);
 
1228
    if (child)
 
1229
    {
 
1230
      QueryView* qv = reinterpret_cast<QueryView*>(child->get_data("query_view"));
 
1231
      if (qv) // skip non query view tabs, query view tabs have "query_view" set
 
1232
      {
 
1233
        if (qv->sql_editor_be())
 
1234
          _be->sql_editor_reorder(qv->sql_editor_be(), query_views_cnt++);
 
1235
        else
 
1236
          log_error("QueryView has no backend\n");
 
1237
      }
 
1238
    }
 
1239
  }
 
1240
}
 
1241
 
 
1242
 
 
1243
//------------------------------------------------------------------------------
 
1244
void DbSqlEditorView::init_tab_menu(mforms::Menu* menu)
 
1245
{
 
1246
  menu->add_item("New Tab", "new tab");
 
1247
  menu->add_item("Save Tab", "save tab");
 
1248
  menu->add_separator();
 
1249
  menu->add_item("Close Tab", "close tab");
 
1250
  menu->add_item("Close Other Tabs", "close other tabs");
 
1251
  menu->add_separator();
 
1252
  menu->add_item("Copy Path to Clipboard", "copy path");
 
1253
  menu->set_item_enabled(4, false);
 
1254
}
 
1255
 
 
1256
//------------------------------------------------------------------------------
 
1257
void DbSqlEditorView::tab_menu_handler(const std::string& action, ActiveLabel* sender, QueryView* qv)
 
1258
{
 
1259
  if (qv)
 
1260
  {
 
1261
    if (action == "new tab")
 
1262
      _be->new_sql_script_file();
 
1263
    else if (action == "save tab")
 
1264
      qv->save();
 
1265
    else if (action == "copy path")
 
1266
    {
 
1267
      const std::string path = qv->editor_path();
 
1268
      Glib::RefPtr<Gtk::Clipboard> clip = Gtk::Clipboard::get();
 
1269
      if (clip && !path.empty())
 
1270
        clip->set_text(path);
 
1271
    }
 
1272
    else if (action == "close tab")
 
1273
    {
 
1274
      close_editor_tab(qv);
 
1275
    }
 
1276
    else if (action == "close other tabs")
 
1277
    {
 
1278
      int size = _editor_note->get_n_pages();
 
1279
      std::vector<QueryView*> to_close_list;
 
1280
      to_close_list.reserve(size);
 
1281
 
 
1282
      for (int i = 0; i < size; ++i)
 
1283
      {
 
1284
        QueryView *cqv = find_view_by_index(i);
 
1285
        if (cqv != NULL && cqv != qv)
 
1286
          to_close_list.push_back(cqv);
 
1287
      }
 
1288
 
 
1289
      size = to_close_list.size();
 
1290
      for (int i = 0; i < size; ++i)
 
1291
        close_editor_tab(to_close_list[i]);
 
1292
    }
 
1293
  }
 
1294
}
 
1295
 
 
1296
//------------------------------------------------------------------------------
 
1297
void DbSqlEditorView::reenable_items_in_tab_menus()
 
1298
{
 
1299
  const Gtk::Notebook::PageList pages = _editor_note->pages();
 
1300
  const int size = pages.size();
 
1301
 
 
1302
  for (int i = 0; i < size; ++i)
 
1303
  {
 
1304
    Gtk::Notebook_Helpers::Page page = pages[i];
 
1305
    ActiveLabel* const al = dynamic_cast<ActiveLabel*>(page.get_tab_label());
 
1306
    if (al)
 
1307
    {
 
1308
      mforms::Menu* const menu = al->get_menu();
 
1309
      const int index = menu->get_item_index("close other tabs");
 
1310
      if (index >= 0)
 
1311
        menu->set_item_enabled(index, size > 1);
 
1312
    }
 
1313
 
 
1314
    QueryView* qv = reinterpret_cast<QueryView*>(page.get_child()->get_data("query_view"));
 
1315
    if (qv)
 
1316
      qv->reenable_items_in_tab_menus();
 
1317
  }
 
1318
 
 
1319
}
 
1320
 
 
1321
//------------------------------------------------------------------------------
 
1322
void DbSqlEditorView::partial_refresh_ui(const int what)
 
1323
{
 
1324
  switch (what)
 
1325
  {
 
1326
    case SqlEditorForm::RefreshEditor:
 
1327
    {
 
1328
      QueryView* qv = active_view();
 
1329
      if (qv)
 
1330
        qv->set_sql(_be->active_sql_editor()->sql());
 
1331
      break;
 
1332
    }
 
1333
    case SqlEditorForm::RefreshEditorBackend:
 
1334
    {
 
1335
      QueryView* qv = active_view();
 
1336
      if (qv)
 
1337
        _be->active_sql_editor()->sql(qv->get_sql());
 
1338
      break;
 
1339
    }
 
1340
    case SqlEditorForm::RefreshEditorTitle:
 
1341
    {
 
1342
      QueryView* qv = active_view();
 
1343
      if (qv)
 
1344
        qv->update_label(_be->sql_editor_caption());
 
1345
      break;
 
1346
    }
 
1347
    case SqlEditorForm::RefreshRecordsetTitle:
 
1348
    {
 
1349
      QueryView* qv = active_view();
 
1350
      if (qv)
 
1351
        qv->update_recordset_caption();
 
1352
      break;
 
1353
    }
 
1354
    case SqlEditorForm::RunCurrentScript:
 
1355
    {
 
1356
      QueryView* qv = active_view();
 
1357
      if (qv)
 
1358
        qv->execute_sql(false);
 
1359
      break;
 
1360
    }
 
1361
    case SqlEditorForm::RunCurrentStatement:
 
1362
    {
 
1363
      QueryView* qv = active_view();
 
1364
      if (qv)
 
1365
        qv->execute_sql(true);
 
1366
      break;
 
1367
    }
 
1368
    case SqlEditorForm::ShowFindPanel:
 
1369
      show_find(false);
 
1370
      break;
 
1371
    case SqlEditorForm::ShowSpecialCharacters:
 
1372
    {
 
1373
      QueryView* qv = active_view();
 
1374
      if (qv)
 
1375
        qv->get_editor_fe()->show_special_chars(true);
 
1376
      break;
 
1377
    }
 
1378
    case SqlEditorForm::HideSpecialCharacters:
 
1379
    {
 
1380
      QueryView* qv = active_view();
 
1381
      if (qv)
 
1382
        qv->get_editor_fe()->show_special_chars(false);
 
1383
      break;
 
1384
    }
 
1385
  }
 
1386
}
 
1387
 
 
1388
//------------------------------------------------------------------------------
 
1389
void DbSqlEditorView::plugin_tab_added(PluginEditorBase *plugin)
 
1390
{
 
1391
  const int page_num = _editor_note->page_num(*(static_cast<Gtk::Widget*>(plugin)));
 
1392
  if (page_num >= 0)
 
1393
    _editor_note->set_current_page(page_num);
 
1394
}
 
1395
 
 
1396
//------------------------------------------------------------------------------
 
1397
int DbSqlEditorView::add_editor_tab(int active_sql_editor_index)
 
1398
{
 
1399
  QueryView    *qv    = new QueryView(active_sql_editor_index, this);
 
1400
  ActiveLabel  *label = Gtk::manage(new ActiveLabel(_be->sql_editor_caption(active_sql_editor_index)
 
1401
                                       ,sigc::mem_fun(qv, &QueryView::close)
 
1402
                                       ));
 
1403
 
 
1404
  qv->set_linked_label(label);
 
1405
  mforms::Menu* menu = label->get_menu();
 
1406
  init_tab_menu(menu);
 
1407
  menu->set_handler(sigc::bind(sigc::mem_fun(this, &DbSqlEditorView::tab_menu_handler), label, qv));
 
1408
 
 
1409
  qv->get_outer()->set_data("query_view", (void*)qv); // Adding backlink
 
1410
 
 
1411
  const int page_index =_editor_note->append_page(*qv->get_outer(), *label);
 
1412
  _editor_note->set_tab_reorderable(*qv->get_outer(), true);
 
1413
  qv->get_outer()->show();
 
1414
  label->show();
 
1415
 
 
1416
  _editor_note->set_current_page(page_index);
 
1417
  reenable_items_in_tab_menus();
 
1418
 
 
1419
  qv->focus();
 
1420
//  utils::gtk::load_toolbar_state(_grtm, _be->get_toolbar());
 
1421
 
 
1422
  return 0;
 
1423
}
 
1424
 
 
1425
//------------------------------------------------------------------------------
 
1426
void DbSqlEditorView::close_editor_tab(QueryView* qv)
 
1427
{
 
1428
  const int idx = qv->index();
 
1429
 
 
1430
  if (!_be->sql_editor_will_close(idx))
 
1431
    return;
 
1432
 
 
1433
  _be->remove_sql_editor(idx);
 
1434
  _editor_note->remove_page(*qv->get_outer());
 
1435
 
 
1436
  delete qv;
 
1437
 
 
1438
  recalc_tab_indices();
 
1439
 
 
1440
  int size = 0;
 
1441
  for (int i = _editor_note->get_n_pages() - 1; i >= 0; --i)
 
1442
  {
 
1443
    Gtk::Widget* const child = _editor_note->get_nth_page(i);
 
1444
    if (child && child->get_data("query_view"))
 
1445
        ++size;
 
1446
  }
 
1447
 
 
1448
  log_debug("got %i pages in editor\n", size);
 
1449
  if (size == 0)
 
1450
  {
 
1451
    _be->new_sql_script_file();
 
1452
    //const int new_editor_index = _be->add_sql_editor();
 
1453
    //add_editor_tab(new_editor_index);
 
1454
  }
 
1455
 
 
1456
  reenable_items_in_tab_menus();
 
1457
}
 
1458
 
 
1459
//------------------------------------------------------------------------------
 
1460
 
 
1461
bool DbSqlEditorView::close_focused_tab()
 
1462
{
 
1463
  QueryView *qv = active_view();
 
1464
  if (qv)
 
1465
  {
 
1466
    close_editor_tab(qv);
 
1467
    return true;
 
1468
  }
 
1469
  else
 
1470
  {
 
1471
    Gtk::Widget* content = _editor_note->get_nth_page(_editor_note->get_current_page());
 
1472
    Gtk::Widget* label   = _editor_note->get_tab_label(*content);
 
1473
 
 
1474
    ActiveLabel* const al = dynamic_cast<ActiveLabel*>(label);
 
1475
    if (al)
 
1476
    {
 
1477
      al->call_close();
 
1478
      _editor_note->remove_page(*content);
 
1479
 
 
1480
      recalc_tab_indices();
 
1481
      reenable_items_in_tab_menus();
 
1482
    }
 
1483
  }
 
1484
 
 
1485
  return false;
 
1486
}
 
1487
 
 
1488
//------------------------------------------------------------------------------
 
1489
void DbSqlEditorView::editor_page_switched(GtkNotebookPage *page, guint index)
 
1490
{
 
1491
  if (_be)
 
1492
  {
 
1493
    Gtk::Widget*  content = _editor_note->get_nth_page(index);
 
1494
    if (content)
 
1495
    {
 
1496
      QueryView* qv = (QueryView*)content->get_data("query_view");
 
1497
      if (qv)
 
1498
        _be->active_sql_editor_index(qv->index());
 
1499
      else
 
1500
        _be->active_sql_editor_index(-1);
 
1501
    }
 
1502
  }
 
1503
}
 
1504
 
 
1505
//------------------------------------------------------------------------------
 
1506
bool DbSqlEditorView::on_close()
 
1507
{
 
1508
  if (_be->can_close())
 
1509
  {
 
1510
    //_closing = true;
 
1511
    return true;
 
1512
  }
 
1513
 
 
1514
  return false;
 
1515
}
 
1516
 
 
1517
//------------------------------------------------------------------------------
 
1518
int DbSqlEditorView::on_exec_sql_progress(float progress, const std::string &message)
 
1519
{
 
1520
  _output.refresh();
 
1521
 
 
1522
  QueryView* qv = active_view();
 
1523
  if (qv)
 
1524
    qv->update_exec_sql_progress(progress, message);
 
1525
  return 0;
 
1526
}
 
1527
 
 
1528
//------------------------------------------------------------------------------
 
1529
int DbSqlEditorView::on_exec_sql_done()
 
1530
{
 
1531
  QueryView* qv = active_view();
 
1532
  if (qv)
 
1533
  {
 
1534
    qv->stop_busy();
 
1535
 
 
1536
    qv->update_resultsets();
 
1537
  }
 
1538
  return 0;
 
1539
}
 
1540
 
 
1541
//------------------------------------------------------------------------------
 
1542
void DbSqlEditorView::update_resultsets_from_main()
 
1543
{
 
1544
  log_debug2("update_resultsets_from_main\n");
 
1545
 
 
1546
  std::deque<sigc::slot<void> >  slots;
 
1547
 
 
1548
  {
 
1549
    bec::GMutexLock lock(_update_resultset_slots_lock);
 
1550
    slots = _update_resultset_slots;
 
1551
    _update_resultset_slots.clear();
 
1552
  }
 
1553
 
 
1554
  const size_t size = slots.size();
 
1555
  for (size_t i = 0; i < size; ++i)
 
1556
    slots[i]();
 
1557
}
 
1558
 
 
1559
//------------------------------------------------------------------------------
 
1560
void DbSqlEditorView::update_resultsets(int editor_index, Recordset::Ref rset, bool added)
 
1561
{
 
1562
  log_debug2("update_resultsets: editor_index = %i, added = %i\n", editor_index, added);
 
1563
  QueryView* qv = find_view_by_index(editor_index);
 
1564
  if (qv)
 
1565
    qv->update_resultsets();
 
1566
  else
 
1567
    log_error("editor index %i is unknown\n", editor_index);
 
1568
}
 
1569
 
 
1570
//------------------------------------------------------------------------------
 
1571
void DbSqlEditorView::recordset_list_changed(int editor_index, Recordset::Ref rset, bool added)
 
1572
{
 
1573
  log_debug2("recordset_list_changed: editor_index = %i, added = %i\n", editor_index, added);
 
1574
  if (_grtm->in_main_thread())
 
1575
    update_resultsets(editor_index, rset, added);
 
1576
  else
 
1577
  {
 
1578
    sigc::slot<void> update_resultset_slot = sigc::bind(sigc::mem_fun(this, &DbSqlEditorView::update_resultsets), editor_index, rset, added);
 
1579
 
 
1580
    {
 
1581
      bec::GMutexLock lock(_update_resultset_slots_lock);
 
1582
      _update_resultset_slots.push_back(update_resultset_slot);
 
1583
    }
 
1584
 
 
1585
    _dispatch_rset_update.emit();
 
1586
  }
 
1587
}
 
1588
 
 
1589
//------------------------------------------------------------------------------
 
1590
void DbSqlEditorView::output_text(const std::string &text, bool bring_to_front)
 
1591
{
 
1592
  _output.output_text(text, bring_to_front);
 
1593
}
 
1594
 
 
1595
//------------------------------------------------------------------------------
 
1596
int DbSqlEditorView::on_sql_editor_text_insert(const std::string &text)
 
1597
{
 
1598
  QueryView* qv = active_view();
 
1599
  if (qv)
 
1600
    qv->get_editor_fe()->insert_text(text);
 
1601
  return 0;
 
1602
}
 
1603