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"
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"
14
#include "grt/common.h"
15
#include "widget_saver.h"
16
#include "plugin_editor_base.h"
18
DEFAULT_LOG_DOMAIN("UI")
21
static const std::vector<bec::NodeId> selected_nodeids(GridView& g)
23
GridView::SelectedNodes nodes;
24
g.get_selected_nodes(&nodes);
26
std::vector<bec::NodeId> entries;
27
entries.reserve(nodes.size());
29
for (base::const_range<GridView::SelectedNodes> it(nodes); it; ++it)
30
entries.push_back(it->second);
35
//==============================================================================
37
//==============================================================================
40
SigcBlocker(sigc::connection& c) : _c(c) {_c.block();}
41
~SigcBlocker() {_c.unblock();}
46
void drop_eol(const int column, Glib::ValueBase* vbase)
50
GValue *vb = vbase->gobj();
51
char* str = g_value_dup_string(vb);
58
g_value_take_string(vb, str);
62
//==============================================================================
64
//==============================================================================
65
QueryOutputView::QueryOutputView(const SqlEditorForm::Ref& be, DbSqlEditorView *db_sql_editor_view)
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)
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));
81
_action_swnd.add(_action_output);
82
_action_swnd.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
85
Gtk::TreeViewColumn *col;
86
col = _action_output.get_column(0); // icon
88
col->set_resizable(false);
89
col = _action_output.get_column(1); // index
91
col->set_fixed_width(40);
92
col = _action_output.get_column(2); // time
94
col->set_fixed_width(80);
95
col = _action_output.get_column(3); // action
98
col->set_fixed_width(400);
100
col = _action_output.get_column(4); // message
103
col->set_fixed_width(350);
105
col = _action_output.get_column(5); // duration
108
col->set_fixed_width(150);
109
col->set_resizable(false);
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);
117
_entries_grid.set_context_menu_responder(sigc::mem_fun(this, &QueryOutputView::history_context_menu_responder));
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);
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);
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));
129
_entries_grid.refresh(true);
130
_details_grid.refresh(true);
133
for (size_t i = 0; i < (sizeof(sections) / sizeof(const char* const)); ++i)
134
_mode.append_text(sections[i]);
136
_text_swnd.add(_text_output);
137
_text_swnd.show_all();
139
_entries_swnd.add(_entries_grid);
140
_details_swnd.add(_details_grid);
142
_entries_swnd.show_all();
143
_details_swnd.show_all();
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();
150
_note.append_page(_action_swnd, sections[0]);
151
_note.append_page(_text_swnd, sections[1]);
152
_note.append_page(_history_box, sections[2]);
155
_note.set_show_tabs(false);
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;
163
_top_box.pack_start(*mode_box, false, true);
164
_top_box.pack_start(_note, true, true);
167
_be->log()->refresh_ui_cb = sigc::bind<bool>(sigc::mem_fun(_action_output, &GridView::refresh), false);
169
_mode.signal_changed().connect(sigc::mem_fun(this, &QueryOutputView::mode_change_requested));
174
//------------------------------------------------------------------------------
175
bool QueryOutputView::on_query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip)
178
if (_action_output.get_path_at_pos(x, y, path))
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()));
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()));
195
//------------------------------------------------------------------------------
196
void QueryOutputView::mode_change_requested()
198
const int mode = _mode.get_active_row_number();
200
_note.set_current_page(mode);
203
//------------------------------------------------------------------------------
204
void QueryOutputView::refresh()
206
const int mode = _mode.get_active_row_number();
209
case 0: // Action Output
211
_action_output.refresh(false);
213
const int log_row_count = _action_output.row_count();
214
if (log_row_count > 0)
217
path.push_back(log_row_count-1);
218
_action_output.scroll_to_row(path);
219
_action_output.set_cursor(path);
223
case 2: // History output
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();
230
const Gtk::TreeIter iter = (--children.end());
231
const Gtk::TreePath path = entry_model->get_path(iter);
232
_entries_grid.set_cursor(path);
235
_details_grid.scroll_to(1);
241
//------------------------------------------------------------------------------
242
int QueryOutputView::on_history_entries_refresh()
244
SigcBlocker signal_block(_on_history_entries_selection_changed_conn);
246
_entries_grid.refresh(false);
247
_entries_grid.scroll_to(1);
252
//------------------------------------------------------------------------------
253
int QueryOutputView::on_history_details_refresh()
255
SigcBlocker signal_block(_on_history_entries_selection_changed_conn);
257
_details_grid.refresh(false);
258
_details_grid.scroll_to(1);
263
//------------------------------------------------------------------------------
264
void QueryOutputView::on_history_entries_selection_changed()
266
const int row = _entries_grid.current_row();
269
_be->history()->current_entry(row);
270
_details_grid.refresh(false);
274
//------------------------------------------------------------------------------
275
void QueryOutputView::handle_action_output_context_menu(const std::string& action)
277
if (action == "clear")
280
_action_output.refresh(false);
284
GridView::SelectedNodes nodes;
285
_action_output.get_selected_nodes(&nodes);
287
std::list<int> sel_indices;
289
for (base::const_range<GridView::SelectedNodes> it(nodes); it; ++it)
290
sel_indices.push_back(it->first);
293
if (nodes.size() > 0)
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);
298
if (action == "append_selected_items" || action == "replace_sql_script")
300
QueryView* qv = _db_sql_editor_view->active_view();
302
qv->set_sql((action == "replace_sql_script") ? sql : qv->get_sql() + sql);
306
Glib::RefPtr<Gtk::Clipboard> clip = Gtk::Clipboard::get();
314
//------------------------------------------------------------------------------
315
void QueryOutputView::handle_history_context_menu(const std::string& action)
317
DbSqlEditorHistory::EntriesModel::Ref entries_model = _be->history()->entries_model();
319
const std::vector<bec::NodeId> entries = selected_nodeids(_entries_grid);
321
if (action == "delete_selection")
323
entries_model->activate_popup_item_for_nodes(action, entries);
324
_entries_grid.refresh(false);
326
else if (action == "delete_all")
328
entries_model->activate_popup_item_for_nodes(action, entries);
329
_entries_grid.refresh(false);
333
const int selected_entry = (entries.size() > 0) ? (*entries.begin())[0] : -1;
335
if (selected_entry >= 0)
337
if (action == "clear")
340
std::vector<int> e(1, selected_entry);
341
entries_model->delete_entries(e);
342
_entries_grid.refresh(false);
347
GridView::SelectedNodes nodes;
348
_details_grid.get_selected_nodes(&nodes);
350
std::list<int> details;
352
for (base::const_range<GridView::SelectedNodes> it(nodes); it; ++it)
353
details.push_back(it->first);
355
const std::string sql = _db_sql_editor_view->be()->restore_sql_from_history(selected_entry, details);
357
if (action == "append_selected_items" || action == "replace_sql_script")
359
QueryView* qv = _db_sql_editor_view->active_view();
361
qv->set_sql((action == "replace_sql_script") ? sql : qv->get_sql() + sql);
363
else if (action == "copy_row")
365
Glib::RefPtr<Gtk::Clipboard> clip = Gtk::Clipboard::get();
374
//------------------------------------------------------------------------------
375
void QueryOutputView::history_context_menu_responder()
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);
380
run_popup_menu(menuitems, gtk_get_current_event_time(), sigc::mem_fun(this, &QueryOutputView::handle_history_context_menu), &_context_menu);
383
//------------------------------------------------------------------------------
384
void QueryOutputView::output_text(const std::string& text, const bool bring_to_front)
386
Glib::RefPtr<Gtk::TextBuffer> buf = _text_output.get_buffer();
387
buf->insert(buf->end(), text);
390
_mode.set_active(1); // 1 - Text output tab
393
//==============================================================================
395
//==============================================================================
396
QueryView::QueryView(const int current_index, DbSqlEditorView* owner)
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)
406
Gtk::Widget& editor_widget = _editor.container();
407
SqlEditorForm::Ref be = owner->be();
408
Sql_editor::Ref editor = be->sql_editor(current_index);
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());
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));
419
_rs_tabs.set_tab_pos(Gtk::POS_BOTTOM);
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());
426
_editor_box.pack_start(*w, false, true);
427
//_rs_box.pack_start(*w, false, true);
430
g_warning("No toolbar for editor");
432
_rs_box.pack_start(_rs_tabs, true, true);
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);
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));
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));
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));
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);
454
_editability_icon.set(Gdk::Pixbuf::create_from_file(_owner->grt_manager()->get_data_file_path("images/mini_notice.png")));
456
_rs_tabs.set_action_widget(&_btn_box, Gtk::PACK_END);
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));
467
//------------------------------------------------------------------------------
468
QueryView::~QueryView()
473
//------------------------------------------------------------------------------
474
void QueryView::top_pane_changed()
476
if (!_polish_conn.connected())
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);
484
//------------------------------------------------------------------------------
485
void QueryView::polish()
487
if (_owner->be()->sql_editor_start_collapsed(_owner->be()->sql_editor_index(_editor.be())))
489
_query_collapsed = true;
490
_top_pane.set_position(0);
493
gtk_paned_set_pos_ratio(&_top_pane, 1);
494
_polish_conn.disconnect();
497
//------------------------------------------------------------------------------
498
void QueryView::on_sql_editor_selection_change()
500
_owner->be()->wbsql()->get_wbui()->get_command_ui()->signal_validate_edit_menu_items();
503
//------------------------------------------------------------------------------
504
void QueryView::update_exec_sql_progress(float progress, const std::string &message)
507
//------------------------------------------------------------------------------
508
void QueryView::update_label(const std::string& label)
511
_label->set_text(label);
515
//------------------------------------------------------------------------------
517
void QueryView::update_recordset_caption()
519
RecordsetView* rs = active_recordset();
522
ActiveLabel *label = dynamic_cast<ActiveLabel*>(_rs_tabs.get_tab_label(*rs));
524
label->set_text(rs->model()->caption());
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();
533
//------------------------------------------------------------------------------
535
static void hide_rs_pane(Gtk::Paned *pane)
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);
544
void QueryView::update_resultsets()
546
const int editor_index = index();
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;
554
_updating_results = true;
556
recordsets_from_be.reserve(n_recordsets_in_be);
558
// prepare list of recordsets in BE, which later be matched against exisiting ones in UI(tabs)
559
if (n_recordsets_in_be > 0)
561
for (int i = n_recordsets_in_be - 1; i >= 0; --i)
563
recordsets_from_be.push_back(be->recordset(editor_index, i));
564
recordset_indexes[recordsets_from_be.back()] = i;
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
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)
576
RecordsetView* view = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
580
const size_t before_remove = recordsets_from_be.size();
581
base::vector_remove(recordsets_from_be, rs);
583
if (recordsets_from_be.size() == before_remove)
584
orphan_views.push_back(view);
588
// Remove orphan pages
589
for (size_t i = 0; i < orphan_views.size(); ++i)
590
_rs_tabs.remove_page(*orphan_views[i]);
593
const int size = recordsets_from_be.size();
594
for (int i = 0; i < size; ++i)
596
const Recordset::Ref& rs = recordsets_from_be[i];
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));
602
if (lbl.get() && view.get())
607
RecordsetView* rs_view = view.get();
609
mforms::Menu* menu = lbl.get()->get_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());
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);
619
rs->task->msg_cb(sigc::bind(sigc::mem_fun(this, &QueryView::process_task_msg), rs_view));
621
_rs_tabs.set_current_page(pos_added);
625
if (!_query_collapsed)
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));
639
_updating_results = false;
641
rs_page_switched(0, _rs_tabs.get_current_page());
643
reenable_items_in_tab_menus();
648
//------------------------------------------------------------------------------
649
void QueryView::recalc_rstab_indices()
651
const int size = _rs_tabs.get_n_pages();
652
for (int i = 0; i < size; ++i)
654
RecordsetView* view = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
656
_owner->be()->recordset_reorder(index(), view->model(), i);
660
//------------------------------------------------------------------------------
661
void QueryView::close()
663
_owner->close_editor_tab(this);
666
//------------------------------------------------------------------------------
667
RecordsetView* QueryView::active_recordset()
669
RecordsetView* rs(0);
670
const int page = _rs_tabs.get_current_page();
672
rs = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(page));
677
//------------------------------------------------------------------------------
678
void QueryView::close_recordset(long long key, bool confirm)
680
const int ntabs = _rs_tabs.get_n_pages();
682
for (int i = 0; i < ntabs; ++i)
684
RecordsetView* view = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
687
const Recordset::Ref model = view->model();
688
if (model && model->key() == key)
690
close_recordset_by_ptr(view, confirm);
697
//------------------------------------------------------------------------------
698
void QueryView::close_recordset_by_ptr(RecordsetView* view, const bool confirm)
702
if (confirm && view->model()->can_close(true))
704
view->model()->close();
705
// _rs_tabs.remove_page(*view);
709
if (_rs_tabs.get_n_pages() == 0) // tabs are gone
710
gtk_paned_set_pos_ratio(&_top_pane, 1);
712
reenable_items_in_tab_menus();
715
//------------------------------------------------------------------------------
716
void QueryView::rs_page_switched(GtkNotebookPage *page, guint page_index)
718
bool show_action_area = false;
719
bool action_area_enabled = false;
721
if (_updating_results)
724
RecordsetView* rs = active_recordset();
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);
737
_owner->be()->active_recordset(index(), Recordset::Ref());
740
if (show_action_area)
744
_editability_label.hide();
745
_editability_icon.hide();
746
_btn_box.set_sensitive(action_area_enabled);
752
_editability_label.show();
753
_editability_label.set_markup("<small>ReadOnly</small>");
754
_editability_icon.show();
758
// Label associated with the view in gtk::notebook
759
//------------------------------------------------------------------------------
760
void QueryView::set_linked_label(ActiveLabel* lbl)
765
//------------------------------------------------------------------------------
766
void QueryView::execute_sql(bool current_statement_only)
769
if (current_statement_only)
771
_editor.check_sql(true);
772
sql = _editor.current_sql_statement();
776
sql = _editor.get_selected_text();
778
sql = _editor.get_text();
784
//_tab_pages->set_current_page(_tab_pages->page_num(*_log_page));
787
_label->start_busy();
788
_owner->be()->exec_sql(sql, _editor.be(), false, current_statement_only);
791
//------------------------------------------------------------------------------
792
int QueryView::index()
794
return _owner->be()->sql_editor_index(_editor.be());
797
//------------------------------------------------------------------------------
798
void QueryView::save()
800
const int view_index = index();
803
_owner->be()->save_sql_script_file(_owner->be()->sql_editor_path(view_index), view_index);
806
//------------------------------------------------------------------------------
807
std::string QueryView::editor_path()
810
const int view_index = index();
813
path = _owner->be()->sql_editor_path(view_index);
818
//------------------------------------------------------------------------------
819
void QueryView::apply_recordset_changes()
821
RecordsetView* rs = active_recordset();
824
Recordset::Ref rsm = rs->model();
825
if (rsm->has_pending_changes())
826
rsm->apply_changes();
830
//------------------------------------------------------------------------------
831
void QueryView::discard_recordset_changes()
833
RecordsetView* rs = active_recordset();
836
Recordset::Ref rsm = rs->model();
837
if (rsm->has_pending_changes())
842
//------------------------------------------------------------------------------
843
int QueryView::process_task_msg(int msgType, const std::string &message, const std::string &detail, RecordsetView *rsv)
845
_owner->output_text(message, true);
849
//------------------------------------------------------------------------------
850
void QueryView::init_tab_menu(mforms::Menu* menu)
852
menu->add_item("Close Tab", "close tab");
853
menu->add_item("Close Other Tabs", "close other tabs");
856
//------------------------------------------------------------------------------
857
void QueryView::tab_menu_handler(const std::string& action, ActiveLabel* sender, RecordsetView* rsview)
859
if (action == "close tab")
861
close_recordset_by_ptr(rsview, true);
863
else if (action == "close other tabs")
865
int size = _rs_tabs.get_n_pages();
866
std::vector<RecordsetView*> to_delete;
867
to_delete.reserve(size - 1);
869
for (int i = 0; i < size; ++i)
871
RecordsetView* view = dynamic_cast<RecordsetView*>(_rs_tabs.get_nth_page(i));
872
if (view && rsview != view)
873
to_delete.push_back(view);
876
size = to_delete.size();
877
for (int i = 0; i < size; ++i)
878
close_recordset_by_ptr(to_delete[i], true);
882
//------------------------------------------------------------------------------
883
void QueryView::reenable_items_in_tab_menus()
885
const int size = _rs_tabs.get_n_pages();
887
for (int i = 0; i < size; ++i)
889
ActiveLabel* const al = (ActiveLabel*)_rs_tabs.get_nth_page(i)->get_data("active_label");
892
mforms::Menu* const menu = al->get_menu();
893
const int index = menu->get_item_index("close other tabs");
895
menu->set_item_enabled(index, size > 1);
900
//------------------------------------------------------------------------------
901
void QueryView::stop_busy()
907
//------------------------------------------------------------------------------
908
void QueryView::focus()
910
_editor.widget().grab_focus();
913
//------------------------------------------------------------------------------
914
//------------------------------------------------------------------------------
915
DbSqlEditorView *DbSqlEditorView::create(SqlEditorForm::Ref editor_be)
917
return Gtk::manage(new DbSqlEditorView(editor_be));
920
//------------------------------------------------------------------------------
921
DbSqlEditorView::DbSqlEditorView(SqlEditorForm::Ref editor_be)
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))
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"));
932
Gtk::Widget *v = this;
934
_be->set_frontend_data(dynamic_cast<FormViewBase*>(this));
936
mforms::View *sbview = _be->get_sidebar();
937
Gtk::Widget *sidebar = mforms::gtk::ViewImpl::get_widget_for_view(sbview);
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);
944
_main_pane.pack1(_top_right_pane, true, false);
945
_main_pane.pack2(_output.get_outer(), false, true);
947
sidebar->set_no_show_all(true);
950
_top_pane.add1(_main_pane);
951
_top_pane.add2(*sidebar);
953
_top_right_pane.pack2(*_editor_note, true, false);
955
_top_right_pane.pack1(*_side_palette, false, true);
959
_top_pane.add1(*sidebar);
960
_top_pane.add2(_main_pane);
962
_top_right_pane.pack1(*_editor_note, true, false);
964
_top_right_pane.pack2(*_side_palette, false, true);
967
_top_pane.show_all();
968
_top_right_pane.show_all();
969
_main_pane.show_all();
972
Gtk::Widget *w = mforms::gtk::widget_for_menubar(_be->get_menubar());
974
pack_start(*w, false, true);
977
w = mforms::gtk::widget_for_toolbar(_be->get_toolbar());
981
pack_start(*w, false, true);
984
pack_start(_top_pane, true, true);
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));
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);
996
_be->sql_editor_text_insert_signal.connect(sigc::mem_fun(this, &DbSqlEditorView::on_sql_editor_text_insert));
998
_update_resultset_slots_lock = g_mutex_new();
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));
1005
// realize pre-existing editors
1006
for (int i = 0; i < _be->sql_editor_count(); i++)
1008
_be->active_sql_editor_index(i);
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));
1017
// restore state of toolbar
1019
mforms::ToolBar *toolbar = _be->get_toolbar();
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();
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();
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();
1032
// utils::gtk::load_toolbar_state(_grtm, _be->get_toolbar());
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);
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));
1043
//------------------------------------------------------------------------------
1045
bool DbSqlEditorView::perform_command(const std::string &cmd)
1047
if (cmd == "wb.toggleSchemataBar")
1049
Gtk::Widget* w = _top_pane.get_child1();
1050
bool hidden = !be()->get_toolbar()->get_item_checked("wb.toggleSchemataBar");
1055
_grtm->set_app_option("DbSqlEditor:SchemaSidebarHidden", grt::IntegerRef(hidden));
1057
else if (cmd == "wb.toggleOutputArea")
1059
Gtk::Widget* w = _main_pane.get_child2();
1060
bool hidden = !be()->get_toolbar()->get_item_checked("wb.toggleOutputArea");
1065
_grtm->set_app_option("DbSqlEditor:OutputAreaHidden", grt::IntegerRef(hidden));
1067
else if (cmd == "wb.toggleSideBar")
1069
Gtk::Widget* w = _top_right_pane.get_child2();
1070
bool hidden = !be()->get_toolbar()->get_item_checked("wb.toggleSideBar");
1075
_grtm->set_app_option("DbSqlEditor:SidebarHidden", grt::IntegerRef(hidden));
1082
//------------------------------------------------------------------------------
1083
void DbSqlEditorView::polish()
1085
gtk_paned_set_pos_ratio(&_main_pane, 0.7);
1086
_polish_conn.disconnect();
1089
//------------------------------------------------------------------------------
1090
DbSqlEditorView::~DbSqlEditorView()
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);
1097
const std::vector<QueryView*> views = DbSqlEditorView::query_views();
1098
for (size_t i = 0; i < views.size(); ++i)
1100
_editor_note->remove_page(*views[i]->get_outer());
1104
_dispatch_rset_update_conn.disconnect();
1107
_side_palette->hide();
1111
while (!g_mutex_trylock(_update_resultset_slots_lock)); // maybe add sleep here
1113
g_mutex_unlock(_update_resultset_slots_lock);
1114
g_mutex_free(_update_resultset_slots_lock);
1115
_update_resultset_slots_lock = 0;
1118
//------------------------------------------------------------------------------
1119
void DbSqlEditorView::dispose()
1123
// utils::gtk::save_toolbar_state(_grtm, _be->get_toolbar());
1129
//------------------------------------------------------------------------------
1130
std::vector<QueryView*> DbSqlEditorView::query_views()
1132
std::vector<QueryView*> list;
1134
const int size = _editor_note->get_n_pages();
1137
for (int i = 0; i < size; ++i)
1139
Gtk::Widget* const child = _editor_note->get_nth_page(i);
1142
QueryView* qv = reinterpret_cast<QueryView*>(child->get_data("query_view"));
1151
//------------------------------------------------------------------------------
1152
void DbSqlEditorView::init()
1156
//------------------------------------------------------------------------------
1157
bool DbSqlEditorView::validate_explain_sql()
1162
//------------------------------------------------------------------------------
1163
void DbSqlEditorView::explain_sql()
1168
//------------------------------------------------------------------------------
1170
bool DbSqlEditorView::show_find(bool replace)
1172
QueryView *view = active_view();
1175
view->get_editor_fe()->show_find_panel(true);
1176
view->get_editor_fe()->enable_replace_panel(replace);
1182
//------------------------------------------------------------------------------
1183
QueryView* DbSqlEditorView::active_view()
1187
const int cur_page_id = _editor_note->get_current_page();
1188
Gtk::Widget* content = _editor_note->get_nth_page(cur_page_id);
1190
ret = (QueryView*)content->get_data("query_view");
1195
//------------------------------------------------------------------------------
1196
QueryView* DbSqlEditorView::find_view_by_index(const int index)
1198
QueryView* view = 0;
1200
const int size = _editor_note->get_n_pages();
1201
for (int i = 0; i < size; ++i)
1203
Gtk::Widget* const child = _editor_note->get_nth_page(i);
1206
QueryView* qv = reinterpret_cast<QueryView*>(child->get_data("query_view"));
1207
if (qv && qv->index() == index)
1219
//------------------------------------------------------------------------------
1220
void DbSqlEditorView::recalc_tab_indices()
1222
int query_views_cnt = 0;
1224
const int size = _editor_note->get_n_pages();
1225
for (int i = 0; i < size; ++i)
1227
Gtk::Widget* const child = _editor_note->get_nth_page(i);
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
1233
if (qv->sql_editor_be())
1234
_be->sql_editor_reorder(qv->sql_editor_be(), query_views_cnt++);
1236
log_error("QueryView has no backend\n");
1243
//------------------------------------------------------------------------------
1244
void DbSqlEditorView::init_tab_menu(mforms::Menu* menu)
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);
1256
//------------------------------------------------------------------------------
1257
void DbSqlEditorView::tab_menu_handler(const std::string& action, ActiveLabel* sender, QueryView* qv)
1261
if (action == "new tab")
1262
_be->new_sql_script_file();
1263
else if (action == "save tab")
1265
else if (action == "copy path")
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);
1272
else if (action == "close tab")
1274
close_editor_tab(qv);
1276
else if (action == "close other tabs")
1278
int size = _editor_note->get_n_pages();
1279
std::vector<QueryView*> to_close_list;
1280
to_close_list.reserve(size);
1282
for (int i = 0; i < size; ++i)
1284
QueryView *cqv = find_view_by_index(i);
1285
if (cqv != NULL && cqv != qv)
1286
to_close_list.push_back(cqv);
1289
size = to_close_list.size();
1290
for (int i = 0; i < size; ++i)
1291
close_editor_tab(to_close_list[i]);
1296
//------------------------------------------------------------------------------
1297
void DbSqlEditorView::reenable_items_in_tab_menus()
1299
const Gtk::Notebook::PageList pages = _editor_note->pages();
1300
const int size = pages.size();
1302
for (int i = 0; i < size; ++i)
1304
Gtk::Notebook_Helpers::Page page = pages[i];
1305
ActiveLabel* const al = dynamic_cast<ActiveLabel*>(page.get_tab_label());
1308
mforms::Menu* const menu = al->get_menu();
1309
const int index = menu->get_item_index("close other tabs");
1311
menu->set_item_enabled(index, size > 1);
1314
QueryView* qv = reinterpret_cast<QueryView*>(page.get_child()->get_data("query_view"));
1316
qv->reenable_items_in_tab_menus();
1321
//------------------------------------------------------------------------------
1322
void DbSqlEditorView::partial_refresh_ui(const int what)
1326
case SqlEditorForm::RefreshEditor:
1328
QueryView* qv = active_view();
1330
qv->set_sql(_be->active_sql_editor()->sql());
1333
case SqlEditorForm::RefreshEditorBackend:
1335
QueryView* qv = active_view();
1337
_be->active_sql_editor()->sql(qv->get_sql());
1340
case SqlEditorForm::RefreshEditorTitle:
1342
QueryView* qv = active_view();
1344
qv->update_label(_be->sql_editor_caption());
1347
case SqlEditorForm::RefreshRecordsetTitle:
1349
QueryView* qv = active_view();
1351
qv->update_recordset_caption();
1354
case SqlEditorForm::RunCurrentScript:
1356
QueryView* qv = active_view();
1358
qv->execute_sql(false);
1361
case SqlEditorForm::RunCurrentStatement:
1363
QueryView* qv = active_view();
1365
qv->execute_sql(true);
1368
case SqlEditorForm::ShowFindPanel:
1371
case SqlEditorForm::ShowSpecialCharacters:
1373
QueryView* qv = active_view();
1375
qv->get_editor_fe()->show_special_chars(true);
1378
case SqlEditorForm::HideSpecialCharacters:
1380
QueryView* qv = active_view();
1382
qv->get_editor_fe()->show_special_chars(false);
1388
//------------------------------------------------------------------------------
1389
void DbSqlEditorView::plugin_tab_added(PluginEditorBase *plugin)
1391
const int page_num = _editor_note->page_num(*(static_cast<Gtk::Widget*>(plugin)));
1393
_editor_note->set_current_page(page_num);
1396
//------------------------------------------------------------------------------
1397
int DbSqlEditorView::add_editor_tab(int active_sql_editor_index)
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)
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));
1409
qv->get_outer()->set_data("query_view", (void*)qv); // Adding backlink
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();
1416
_editor_note->set_current_page(page_index);
1417
reenable_items_in_tab_menus();
1420
// utils::gtk::load_toolbar_state(_grtm, _be->get_toolbar());
1425
//------------------------------------------------------------------------------
1426
void DbSqlEditorView::close_editor_tab(QueryView* qv)
1428
const int idx = qv->index();
1430
if (!_be->sql_editor_will_close(idx))
1433
_be->remove_sql_editor(idx);
1434
_editor_note->remove_page(*qv->get_outer());
1438
recalc_tab_indices();
1441
for (int i = _editor_note->get_n_pages() - 1; i >= 0; --i)
1443
Gtk::Widget* const child = _editor_note->get_nth_page(i);
1444
if (child && child->get_data("query_view"))
1448
log_debug("got %i pages in editor\n", size);
1451
_be->new_sql_script_file();
1452
//const int new_editor_index = _be->add_sql_editor();
1453
//add_editor_tab(new_editor_index);
1456
reenable_items_in_tab_menus();
1459
//------------------------------------------------------------------------------
1461
bool DbSqlEditorView::close_focused_tab()
1463
QueryView *qv = active_view();
1466
close_editor_tab(qv);
1471
Gtk::Widget* content = _editor_note->get_nth_page(_editor_note->get_current_page());
1472
Gtk::Widget* label = _editor_note->get_tab_label(*content);
1474
ActiveLabel* const al = dynamic_cast<ActiveLabel*>(label);
1478
_editor_note->remove_page(*content);
1480
recalc_tab_indices();
1481
reenable_items_in_tab_menus();
1488
//------------------------------------------------------------------------------
1489
void DbSqlEditorView::editor_page_switched(GtkNotebookPage *page, guint index)
1493
Gtk::Widget* content = _editor_note->get_nth_page(index);
1496
QueryView* qv = (QueryView*)content->get_data("query_view");
1498
_be->active_sql_editor_index(qv->index());
1500
_be->active_sql_editor_index(-1);
1505
//------------------------------------------------------------------------------
1506
bool DbSqlEditorView::on_close()
1508
if (_be->can_close())
1517
//------------------------------------------------------------------------------
1518
int DbSqlEditorView::on_exec_sql_progress(float progress, const std::string &message)
1522
QueryView* qv = active_view();
1524
qv->update_exec_sql_progress(progress, message);
1528
//------------------------------------------------------------------------------
1529
int DbSqlEditorView::on_exec_sql_done()
1531
QueryView* qv = active_view();
1536
qv->update_resultsets();
1541
//------------------------------------------------------------------------------
1542
void DbSqlEditorView::update_resultsets_from_main()
1544
log_debug2("update_resultsets_from_main\n");
1546
std::deque<sigc::slot<void> > slots;
1549
bec::GMutexLock lock(_update_resultset_slots_lock);
1550
slots = _update_resultset_slots;
1551
_update_resultset_slots.clear();
1554
const size_t size = slots.size();
1555
for (size_t i = 0; i < size; ++i)
1559
//------------------------------------------------------------------------------
1560
void DbSqlEditorView::update_resultsets(int editor_index, Recordset::Ref rset, bool added)
1562
log_debug2("update_resultsets: editor_index = %i, added = %i\n", editor_index, added);
1563
QueryView* qv = find_view_by_index(editor_index);
1565
qv->update_resultsets();
1567
log_error("editor index %i is unknown\n", editor_index);
1570
//------------------------------------------------------------------------------
1571
void DbSqlEditorView::recordset_list_changed(int editor_index, Recordset::Ref rset, bool added)
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);
1578
sigc::slot<void> update_resultset_slot = sigc::bind(sigc::mem_fun(this, &DbSqlEditorView::update_resultsets), editor_index, rset, added);
1581
bec::GMutexLock lock(_update_resultset_slots_lock);
1582
_update_resultset_slots.push_back(update_resultset_slot);
1585
_dispatch_rset_update.emit();
1589
//------------------------------------------------------------------------------
1590
void DbSqlEditorView::output_text(const std::string &text, bool bring_to_front)
1592
_output.output_text(text, bring_to_front);
1595
//------------------------------------------------------------------------------
1596
int DbSqlEditorView::on_sql_editor_text_insert(const std::string &text)
1598
QueryView* qv = active_view();
1600
qv->get_editor_fe()->insert_text(text);