frontend/linux/workbench/main_form.cpp (1,360 lines of code) (raw):

/* * Copyright (c) 2008, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2.0, * as published by the Free Software Foundation. * * This program is designed to work with certain software (including * but not limited to OpenSSL) that is licensed under separate terms, as * designated in a particular file or component or in included license * documentation. The authors of MySQL hereby grant you an additional * permission to link the program and your derivative works with the * separately licensed software that they have either included with * the program or referenced in the documentation. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License, version 2.0, for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gtk/lf_mforms.h" #include "grid_view.h" #include <gtkmm/statusbar.h> #include <gtkmm/eventbox.h> #include <gtkmm/fixed.h> #include <gtkmm/main.h> #include <atkmm.h> #include "main_form.h" #include "active_label.h" // the rest, backend, etc ... #include "workbench/wb_context_ui.h" #include "workbench/wb_context.h" #include "model/wb_model_diagram_form.h" #include "workbench/wb_overview.h" #include "model/wb_context_model.h" #include "model/wb_overview_physical.h" #include "plugin_editor_base.h" #include "form_view_base.h" #include "diagram_size_form.h" #include "mdc.h" #include "gtk_helpers.h" #include "model_panel.h" #include "model_diagram_panel.h" #include "overview_panel.h" #include "mforms/find_panel.h" #include "gtk_helpers.h" #include "image_cache.h" #include "base/string_utilities.h" #include "base/geometry.h" #include "base/drawing.h" #include "mforms/../gtk/lf_menubar.h" #include "mforms/../gtk/lf_toolbar.h" #include "mforms/../gtk/lf_form.h" #include <base/log.h> #include "main_app.h" using base::strfmt; static void set_window_icons(Gtk::Window *window) { std::vector<Glib::RefPtr<Gdk::Pixbuf> > icons; icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-16.png", false)); icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-32.png", false)); icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-48.png", false)); icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbench-128.png", false)); window->set_default_icon_list(icons); } static Gdk::Color _sys_selection_color; const double defaultDpi = 96; //------------------------------------------------------------------------------ // @ctx is passed from Program class, see Program::Program MainForm::MainForm() : _exiting(false) { setup_mforms_app(); _ui = Gtk::Builder::create_from_file(bec::GRTManager::get()->get_data_file_path("wb.glade")); set_name("Main Tab Bar"); get_mainwindow()->signal_delete_event().connect(sigc::mem_fun(this, &MainForm::close_window)); get_mainwindow()->signal_set_focus().connect(sigc::mem_fun(this, &MainForm::on_focus_widget)); get_mainwindow()->signal_configure_event().connect_notify(sigc::mem_fun(this, &MainForm::on_configure_window)); get_mainwindow()->signal_window_state_event().connect_notify(sigc::mem_fun(this, &MainForm::on_window_state)); get_mainwindow()->property_is_active().signal_changed().connect(sigc::mem_fun(this, &MainForm::is_active_changed)); get_mainwindow()->set_events(Gdk::FOCUS_CHANGE_MASK); get_mainwindow()->signal_focus_in_event().connect( sigc::bind_return(sigc::hide(sigc::mem_fun(mforms::Form::main_form(), &mforms::Form::activated)), false)); get_mainwindow()->signal_focus_out_event().connect( sigc::bind_return(sigc::hide(sigc::mem_fun(mforms::Form::main_form(), &mforms::Form::deactivated)), false)); get_mainwindow()->set_title("MySQL Workbench"); _model_panel = nullptr; get_upper_note()->signal_switch_page().connect(sigc::mem_fun(*this, &MainForm::switch_page)); Gtk::Statusbar *status = 0; _ui->get_widget("statusbar1", status); if (status) { status->pack_end(_progress_bar, false, false); _progress_bar.show(); } _gui_locked = false; Gdk::Screen::get_default()->property_resolution().signal_changed().connect([this]{ base::NotificationCenter::get()->send("GNBackingScaleChanged", nullptr); }); get_mainwindow()->property_scale_factor().signal_changed().connect([this]{ base::NotificationCenter::get()->send("GNBackingScaleChanged", nullptr); }); // switch_page(0, 0); mforms::gtk::FormImpl::init_main_form(this->get_mainwindow()); base::NotificationCenter::get()->add_observer(this, "GNFormTitleDidChange"); base::NotificationCenter::get()->add_observer(this, "GNFocusChanged"); } //------------------------------------------------------------------------------ MainForm::~MainForm() { _sig_flush_idle.disconnect(); _sig_set_current_page.disconnect(); _sig_close_tab.disconnect(); _sig_change_status.disconnect(); base::NotificationCenter::get()->remove_observer(this); const size_t n_slots = _slots.size(); for (size_t i = 0; i < n_slots; ++i) _slots[i].disconnect(); _slots.clear(); std::map<mdc::CanvasView *, ModelDiagramPanel *>::iterator it; for (it = _diagram_panel_list.begin(); it != _diagram_panel_list.end(); it++) delete it->second; _diagram_panel_list.clear(); notify_callbacks(); // disconnect all signals delete _model_panel; } //------------------------------------------------------------------------------ void MainForm::register_form_view_factory(const std::string &name, FormViewFactory factory) { _form_view_factories[name] = factory; } //------------------------------------------------------------------------------ static void close_plugin(PluginEditorBase *editor, wb::WBContext *wb) { wb->close_gui_plugin(dynamic_cast<GUIPluginBase *>(editor)); } //------------------------------------------------------------------------------ bool MainForm::close_window(GdkEventAny *ev) { /* if (_wbui_context->request_quit()) { get_mainwindow()->hide(); _wbui_context->perform_quit(); }*/ wb::WBContextUI::get()->get_wb()->_frontendCallbacks->quit_application(); return true; // true means stop processing the event } //------------------------------------------------------------------------------ void MainForm::is_active_changed() { static bool reentrancy_preventer = false; if (!reentrancy_preventer && get_mainwindow()->property_is_active()) { // We sent notification on each focus_in of the main window, // because we have no reliable way to detect when the app itself got focus, // as opposed to focus switch between windows of the same app reentrancy_preventer = true; base::NotificationInfo info; base::NotificationCenter::get()->send("GNApplicationActivated", NULL, info); reentrancy_preventer = false; } } //------------------------------------------------------------------------------ void MainForm::on_focus_widget(Gtk::Widget *w) { if (_ui) { void *data = 0; bec::UIForm *form = 0; while (!data && w) { data = w->get_data("uiform"); if (data) { form = reinterpret_cast<bec::UIForm *>(data); break; } w = w->get_parent(); } if (form) wb::WBContextUI::get()->set_active_form(form); } wb::WBContextUI::get()->get_command_ui()->revalidate_edit_menu_items(); } //------------------------------------------------------------------------------ void MainForm::on_configure_window(GdkEventConfigure *conf) { if (get_mainwindow()->is_visible()) { int x, y; get_mainwindow()->get_position(x, y); std::string geom = base::strfmt("%i %i %i %i", x, y, conf->width, conf->height); wb::WBContextUI::get()->get_wb()->save_state("MainWindow", "geometry", geom); } } //------------------------------------------------------------------------------ void MainForm::on_window_state(GdkEventWindowState *conf) { if (get_mainwindow()->is_visible() && (conf->changed_mask & GDK_WINDOW_STATE_MAXIMIZED)) { if (conf->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) wb::WBContextUI::get()->get_wb()->save_state("MainWindow", "geometry", std::string("maximized")); else { int x, y, width, height; get_mainwindow()->get_position(x, y); get_mainwindow()->get_size(width, height); std::string geom = base::strfmt("%i %i %i %i", x, y, width, height); wb::WBContextUI::get()->get_wb()->save_state("MainWindow", "geometry", geom); } } } //------------------------------------------------------------------------------ void MainForm::setup_ui() { set_window_icons(get_mainwindow()); register_commands(); reset_layout(); switch_page(0, 0); } //------------------------------------------------------------------------------ void MainForm::show() { Gtk::Window *window = get_mainwindow(); // restore saved size/pos std::string geom = wb::WBContextUI::get()->get_wb()->read_state("MainWindow", "geometry", std::string()); if (!geom.empty()) { int x, y, width, height; if (geom == "maximized") window->maximize(); else if (sscanf(geom.c_str(), "%i %i %i %i", &x, &y, &width, &height) == 4) { window->move(x, y); window->resize(width, height); } } window->show(); } //------------------------------------------------------------------------------ Gtk::Window *MainForm::get_mainwindow() const { Gtk::Window *win = 0; _ui->get_widget("wb_main_window", win); return win; } Gtk::Notebook *MainForm::get_upper_note() const { Gtk::Notebook *note = 0; _ui->get_widget("model_tabs", note); return note; } //------------------------------------------------------------------------------ static bool change_status(Gtk::Statusbar *status, const std::string &text) { status->pop(); status->push(text); return false; } void MainForm::show_status_text_becb(const std::string &text) { Gtk::Statusbar *status = 0; _ui->get_widget("statusbar1", status); if (bec::GRTManager::get()->in_main_thread()) change_status(status, text); else // execute when idle in case we're being called from the worker thread _sig_change_status = Glib::signal_idle().connect( sigc::bind<Gtk::Statusbar *, std::string>(sigc::ptr_fun(change_status), status, text)); } //------------------------------------------------------------------------------ bool MainForm::quit_app_becb() { // close the model 1st if (wb::WBContextUI::get()->get_wb()->can_close_document()) wb::WBContextUI::get()->get_wb()->close_document(); else return false; Gtk::Notebook *note = get_upper_note(); for (int i = note->get_n_pages() - 1; i > 0; --i) { // skip diagram tabs from check if (i < note->get_n_pages()) { bool is_diagram = dynamic_cast<ModelDiagramPanel *>( reinterpret_cast<FormViewBase *>(note->get_nth_page(i)->get_data("FormViewBase"))); if (!close_tab(note, note->get_nth_page(i)) && !is_diagram) return false; } } runtime::app::get().quit(); return true; } //------------------------------------------------------------------------------ bool MainForm::show_progress_becb(const std::string &title, const std::string &status, float pct) { if (pct < 0.0) pct = 0.0; _progress_bar.set_fraction(pct); _progress_bar.set_text(status); return true; } //------------------------------------------------------------------------------ NativeHandle MainForm::open_plugin_becb(grt::Module *grtmodule, const std::string &shlib, const std::string &editor_class, grt::BaseListRef args, bec::GUIPluginFlags flags) { GUIPluginCreateFunction create_function = 0; std::string path = grtmodule->path(); std::string full_path = path.substr(0, path.rfind('/') + 1) + shlib; // if not forcing a new window creation, check if there's an editor already open for it if (!(flags & bec::ForceNewWindowFlag) && !(flags & bec::StandaloneWindowFlag)) { std::vector<NativeHandle> handles = bec::GRTManager::get()->get_plugin_manager()->get_similar_open_plugins(grtmodule, editor_class, args); if (!handles.empty()) { GUIPluginBase *guiplugin = reinterpret_cast<GUIPluginBase *>(handles[0]); // try to reuse the plugin PluginEditorBase *editor = dynamic_cast<PluginEditorBase *>(guiplugin); if (flags & bec::StandaloneWindowFlag) { add_plugin_form(editor); } else { // add it to the bottom panel of the current tab bring_plugin_pane(editor); } if (editor && !editor->is_editing_live_object() && editor->can_close() && editor->switch_edited_object(args)) { bec::GRTManager::get()->get_plugin_manager()->forget_gui_plugin_handle(handles[0]); return handles[0]; } } } // lookup for the editor_class symbol (create<class>) in the shlib GModule *module = g_module_open(full_path.c_str(), (GModuleFlags)G_MODULE_BIND_LOCAL); if (!module) { g_warning("Could not open editor shared object '%s'", full_path.c_str()); return 0; } if (!g_module_symbol(module, ("create" + editor_class).c_str(), (void **)&create_function) || !create_function) { g_warning("UI creation function '%s' not found in %s", ("create" + editor_class).c_str(), full_path.c_str()); g_module_close(module); return 0; } // call the initializer with arguments GUIPluginBase *object = (*create_function)(grtmodule, args); if (!object) { g_warning("UI creation function from %s returned 0", full_path.c_str()); g_module_close(module); return 0; } g_module_close(module); PluginEditorBase *editor = dynamic_cast<PluginEditorBase *>(object); if (editor) { if (flags & bec::StandaloneWindowFlag) { add_plugin_form(editor); } else { // add it to the bottom panel add_plugin_pane(editor); } } else { // object->execute(); } // return the pointer return reinterpret_cast<NativeHandle>(object); } //------------------------------------------------------------------------------ void MainForm::show_plugin_becb(NativeHandle handle) { GUIPluginBase *plugin = reinterpret_cast<GUIPluginBase *>(handle); PluginEditorBase *editor = dynamic_cast<PluginEditorBase *>(plugin); if (editor) bring_plugin_pane(editor); else g_message("Can't show plugin"); } //------------------------------------------------------------------------------ void MainForm::hide_plugin_becb(NativeHandle handle) { // reinterpret_cast<GUIPluginBase*>(handle)->hide_plugin(); } static mforms::CodeEditor *get_focused_code_editor(Gtk::Window *w) { Gtk::Widget *focused = w->get_focus(); if (focused) return dynamic_cast<mforms::CodeEditor *>(reinterpret_cast<mforms::View *>(focused->get_data("mforms"))); return NULL; } void MainForm::call_find() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) { // generic handling for Scintilla editors editor->show_find_panel(false); return; } if (wb::WBContextUI::get()->get_active_context(true) != WB_CONTEXT_QUERY) { if (validate_find_replace()) call_find_replace(); } FormViewBase *form = reinterpret_cast<FormViewBase *>( get_upper_note()->get_nth_page(get_upper_note()->get_current_page())->get_data("FormViewBase")); if (form) { mforms::ToolBar *toolbar = form->get_form()->get_toolbar(); if (toolbar) { Gtk::Entry *entry = dynamic_cast<Gtk::Entry *>(mforms::widget_for_toolbar_item_named(toolbar, "find")); if (entry) { entry->grab_focus(); form->find_text(entry->get_text()); } } } } void MainForm::call_find_replace() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) { // generic handling for Scintilla editors editor->show_find_panel(true); return; } // FormViewBase *fview = get_active_pane(); // if (fview && !fview->show_find(true)) // return; } void MainForm::call_undo() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) { editor->undo(); return; } auto wbui = wb::WBContextUI::get(); if (wbui->get_active_main_form() && wbui->get_active_main_form()->can_undo()) wbui->get_active_main_form()->undo(); } void MainForm::call_redo() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) { editor->redo(); return; } auto wbui = wb::WBContextUI::get(); if (wbui->get_active_main_form() && wbui->get_active_main_form()->can_redo()) wbui->get_active_main_form()->redo(); } void MainForm::call_copy() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); Gtk::Widget *focused = get_mainwindow()->get_focus(); if (editor) { editor->copy(); return; } auto wbui = wb::WBContextUI::get(); GridView *gv = NULL; if (dynamic_cast<Gtk::Editable *>(focused)) dynamic_cast<Gtk::Editable *>(focused)->copy_clipboard(); else if (wbui->get_active_form() && wbui->get_active_form()->can_copy()) wbui->get_active_form()->copy(); else if ((gv = dynamic_cast<GridView *>(focused))) gv->copy(); } void MainForm::call_cut() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); Gtk::Widget *focused = get_mainwindow()->get_focus(); if (editor) { editor->cut(); return; } if (dynamic_cast<Gtk::Editable *>(focused)) dynamic_cast<Gtk::Editable *>(focused)->cut_clipboard(); else if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_cut()) wb::WBContextUI::get()->get_active_form()->cut(); } void MainForm::call_paste() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); Gtk::Widget *focused = get_mainwindow()->get_focus(); if (editor) { editor->paste(); return; } if (dynamic_cast<Gtk::Editable *>(focused)) dynamic_cast<Gtk::Editable *>(focused)->paste_clipboard(); else if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_paste()) wb::WBContextUI::get()->get_active_form()->paste(); } void MainForm::call_delete() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); Gtk::Widget *focused = get_mainwindow()->get_focus(); if (editor) { editor->do_delete(); return; } if (dynamic_cast<Gtk::Editable *>(focused)) dynamic_cast<Gtk::Editable *>(focused)->delete_selection(); else if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_delete()) wb::WBContextUI::get()->get_active_form()->delete_selection(); } void MainForm::call_select_all() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); Gtk::Widget *focused = get_mainwindow()->get_focus(); if (editor) { editor->select_all(); } else if (dynamic_cast<Gtk::Editable *>(focused)) dynamic_cast<Gtk::Editable *>(focused)->select_region(0, -1); else if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_select_all()) wb::WBContextUI::get()->get_active_form()->select_all(); } bool MainForm::validate_find() { std::string context = wb::WBContextUI::get()->get_active_context(); if (context == WB_CONTEXT_MODEL || context == WB_CONTEXT_QUERY || context == WB_CONTEXT_PHYSICAL_OVERVIEW) return true; return validate_find_replace(); } bool MainForm::validate_undo() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return editor->can_undo(); bec::UIForm *form = wb::WBContextUI::get()->get_active_main_form(); if (form) return form->can_undo(); return false; } bool MainForm::validate_redo() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return editor->can_redo(); bec::UIForm *form = wb::WBContextUI::get()->get_active_main_form(); if (form) return form->can_redo(); return false; } bool MainForm::validate_copy() { bool ret = false; mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return editor->can_copy(); if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_copy()) ret = true; else { Gtk::Editable *edit = dynamic_cast<Gtk::Editable *>(get_mainwindow()->get_focus()); int s, e; if (edit && edit->get_selection_bounds(s, e)) ret = true; else { GridView *gv = dynamic_cast<GridView *>(get_mainwindow()->get_focus()); if (gv) ret = true; } } return ret; } bool MainForm::validate_cut() { bool ret = false; mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return editor->can_cut(); if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_copy()) ret = true; else { Gtk::Editable *edit = dynamic_cast<Gtk::Editable *>(get_mainwindow()->get_focus()); int s, e; if (edit && edit->get_selection_bounds(s, e) && edit->get_editable()) ret = true; } return ret; } bool MainForm::validate_paste() { bool ret = false; mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return editor->can_paste(); if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_paste()) ret = true; else { Gtk::Editable *edit = dynamic_cast<Gtk::Editable *>(get_mainwindow()->get_focus()); if (edit && edit->get_editable()) ret = true; } return ret; } bool MainForm::validate_delete() { bool ret = false; mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return editor->can_delete(); if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_delete()) ret = true; else { Gtk::Editable *edit = dynamic_cast<Gtk::Editable *>(get_mainwindow()->get_focus()); int s, e; if (edit && edit->get_selection_bounds(s, e) && edit->get_editable()) ret = true; } return ret; } bool MainForm::validate_select_all() { bool ret = false; Gtk::Widget *focused = get_mainwindow()->get_focus(); if (focused) { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return true; } if (wb::WBContextUI::get()->get_active_form() && wb::WBContextUI::get()->get_active_form()->can_select_all()) ret = true; else { Gtk::Editable *edit = dynamic_cast<Gtk::Editable *>(focused); if (edit) ret = true; } return ret; } bool MainForm::validate_find_replace() { mforms::CodeEditor *editor = get_focused_code_editor(get_mainwindow()); if (editor) return true; return false; } void MainForm::register_commands() { std::list<std::string> commands; commands.push_back("diagram_size"); // commands.push_back("view_model_navigator"); // commands.push_back("view_catalog"); // commands.push_back("view_layers"); // commands.push_back("view_user_datatypes"); // commands.push_back("view_object_properties"); // commands.push_back("view_object_description"); // commands.push_back("view_undo_history"); commands.push_back("reset_layout"); commands.push_back("wb.page_setup"); commands.push_back("closetab"); commands.push_back("close_tab"); commands.push_back("close_editor"); commands.push_back("wb.next_tab"); commands.push_back("wb.back_tab"); commands.push_back("wb.next_query_tab"); commands.push_back("wb.back_query_tab"); // commands.push_back("help_index"); // commands.push_back("help_version_check"); commands.push_back("wb.sidebarHide"); // SQLIDE commands.push_back("wb.toggleSidebar"); commands.push_back("wb.toggleSecondarySidebar"); commands.push_back("wb.toggleOutputArea"); auto wbui = wb::WBContextUI::get(); wbui->get_command_ui()->add_frontend_commands(commands); wbui->get_command_ui()->add_builtin_command("find_replace", make_slot(&MainForm::call_find_replace), make_slot(&MainForm::validate_find_replace)); wbui->get_command_ui()->add_builtin_command("find", make_slot(&MainForm::call_find), make_slot(&MainForm::validate_find)); wbui->get_command_ui()->add_builtin_command("undo", make_slot(&MainForm::call_undo), make_slot(&MainForm::validate_undo)); wbui->get_command_ui()->add_builtin_command("redo", make_slot(&MainForm::call_redo), make_slot(&MainForm::validate_redo)); wbui->get_command_ui()->add_builtin_command("copy", make_slot(&MainForm::call_copy), make_slot(&MainForm::validate_copy)); wbui->get_command_ui()->add_builtin_command("cut", make_slot(&MainForm::call_cut), make_slot(&MainForm::validate_cut)); wbui->get_command_ui()->add_builtin_command("paste", make_slot(&MainForm::call_paste), make_slot(&MainForm::validate_paste)); wbui->get_command_ui()->add_builtin_command("delete", make_slot(&MainForm::call_delete), make_slot(&MainForm::validate_delete)); wbui->get_command_ui()->add_builtin_command("selectAll", make_slot(&MainForm::call_select_all), make_slot(&MainForm::validate_select_all)); } //------------------------------------------------------------------------------ void MainForm::perform_command_becb(const std::string &command) { if (command == "reset_layout") reset_layout(); else if (command == "diagram_size") show_diagram_options(); else if (command == "wb.page_setup") show_page_setup(); else if (command == "closetab") close_active_tab(); else if (command == "close_tab") close_main_tab(); else if (command == "close_editor") close_inner_tab(); else if (command == "wb.next_tab") { int i = get_upper_note()->get_current_page(); get_upper_note()->next_page(); if (i == get_upper_note()->get_current_page()) get_upper_note()->set_current_page(0); } else if (command == "wb.back_tab") { int i = get_upper_note()->get_current_page(); get_upper_note()->prev_page(); if (i == get_upper_note()->get_current_page()) get_upper_note()->set_current_page(get_upper_note()->get_n_pages() - 1); } /* // Help else if (command == "help_index") Help.ShowHelp(null, System.IO.Path.Combine(Application.StartupPath, "MySQLWorkbench.chm")); else if (command == "help_version_check") Program.CheckForNewVersion(); */ else if (command == "wb.toggleSidebar" || command == "wb.toggleSecondarySidebar") { FormViewBase *form = get_active_pane(); if (form) form->perform_command(command); } else { FormViewBase *form = get_active_pane(); if (!form || !form->perform_command(command)) g_message("Command '%s' not handled!\n", command.c_str()); } } //------------------------------------------------------------------------------ mdc::CanvasView *MainForm::create_view_becb(const model_DiagramRef &diagram) { ModelDiagramPanel *model_panel = Gtk::manage(ModelDiagramPanel::create()); model_panel->set_close_editor_callback(sigc::bind(sigc::ptr_fun(close_plugin), wb::WBContextUI::get()->get_wb())); _signal_close_editor.connect( sigc::hide_return(sigc::mem_fun(model_panel, &ModelDiagramPanel::close_editors_for_object))); model_panel->init(diagram.id()); TabStateInfo tabState; if (!model_panel->get_diagram_form()->is_closed()) { if (model_ModelRef::cast_from(diagram->owner())->currentDiagram() == diagram) tabState = TabOpenActive; else tabState = TabOpen; } else { tabState = TabClosed; } // even considering this widget is not managed, we need to reference it as later we can end up with crash because gtk3 // will release it // this will be deleted later in destroy_view_becb // TODO: check if this can be removed after refactoring modeling #if GTK_VERSION_GE(3, 14) model_panel->reference(); #endif add_form_pane(model_panel, tabState); model_panel->show_all_children(true); // model_panel->set_data("model_panel", model_panel); mdc::CanvasView *view = model_panel->get_canvas(); _diagram_panel_list.insert(std::pair<mdc::CanvasView *, ModelDiagramPanel *>(view, model_panel)); return view; } //------------------------------------------------------------------------------ void MainForm::destroy_view_becb(mdc::CanvasView *view) { Gtk::Notebook *note = get_upper_note(); if (!bec::GRTManager::get()->in_main_thread()) G_BREAKPOINT(); for (int c = note->get_n_pages(), i = 1; i < c; i++) { FormViewBase *form = reinterpret_cast<FormViewBase *>(note->get_nth_page(i)->get_data("FormViewBase")); ModelDiagramPanel *model_panel = dynamic_cast<ModelDiagramPanel *>(form); // ModelDiagramPanel *model_panel = // reinterpret_cast<ModelDiagramPanel*>(note->get_nth_page(i)->get_data("model_panel")); if (model_panel && model_panel->get_canvas() == view) { note->remove_page(i); break; } } std::map<mdc::CanvasView *, ModelDiagramPanel *>::iterator it; it = _diagram_panel_list.find(view); if (it != _diagram_panel_list.end()) { delete it->second; _diagram_panel_list.erase(it); } } //------------------------------------------------------------------------------ void MainForm::switched_view_becb(mdc::CanvasView *view) { Gtk::Notebook *note = get_upper_note(); bec::UIForm *view_form = wb::WBContextUI::get()->get_wb()->get_model_context()->get_diagram_form(view); for (int c = note->get_n_pages(), i = 1; i < c; i++) { if (note->get_nth_page(i)->get_data("uiform") == view_form) { note->get_nth_page(i)->show(); note->set_current_page(i); break; } } } //------------------------------------------------------------------------------ void MainForm::create_main_form_view_becb(const std::string &name, std::shared_ptr<bec::UIForm> form) { FormViewBase *view; if (_form_view_factories.find(name) != _form_view_factories.end()) { view = _form_view_factories[name](form); add_form_pane(view, TabOpenActive); } else { throw std::runtime_error("Form type not supported."); } } //------------------------------------------------------------------------------ void MainForm::destroy_main_form_view_becb(bec::UIForm *form) { } //------------------------------------------------------------------------------ void MainForm::tool_changed_becb(mdc::CanvasView *view) { ModelDiagramPanel *panel = get_panel_for_view(view); if (panel) { // update cursor in canvas panel->update_tool_cursor(); } } //------------------------------------------------------------------------------ void MainForm::handle_notification(const std::string &name, void *sender, base::NotificationInfo &info) { if (name == "GNFormTitleDidChange") { std::string form_id = info["form"]; Gtk::Notebook *note = get_upper_note(); for (int c = note->get_n_pages(), i = 1; i < c; i++) { FormViewBase *form = reinterpret_cast<FormViewBase *>(note->get_nth_page(i)->get_data("FormViewBase")); bec::UIForm *fui = form ? form->get_form() : 0; if (fui && fui->form_id() == form_id) { form->signal_title_changed().emit(info["title"]); break; } } } else if (name == "GNFocusChanged") { wb::WBContextUI::get()->get_command_ui()->revalidate_edit_menu_items(); } } const char *RefreshTypeStr[] = {"RefreshNeeded", "RefreshNothing", "RefreshSchemaNoReload", "RefreshNewDiagram", "RefreshSelection", "RefreshMessages", "RefreshCloseEditor", "RefreshNewModel", "RefreshOverviewNodeInfo", "RefreshOverviewNodeChildren", "RefreshDocument", "RefreshCloseDocument", "RefreshZoom", "RefreshTimer", "RefreshFinishEdit"}; //------------------------------------------------------------------------------ void MainForm::refresh_gui_becb(wb::RefreshType type, const std::string &arg_id, NativeHandle arg_ptr) { if (_exiting) return; // _overview is OverviewPanel switch (type) { case wb::RefreshNeeded: { _sig_flush_idle = Glib::signal_idle().connect(sigc::bind( sigc::bind_return(sigc::mem_fun(wb::WBContextUI::get()->get_wb(), &wb::WBContext::flush_idle_tasks), false), false)); break; } case wb::RefreshCloseDocument: { prepare_close_document(); wb::WBContextUI::get()->get_wb()->flush_idle_tasks(true); wb::WBContextUI::get()->get_wb()->close_document_finish(); handle_model_closed(); } case wb::RefreshNothing: break; case wb::RefreshSchemaNoReload: { ModelDiagramPanel *panel = dynamic_cast<ModelDiagramPanel *>(get_active_pane()); if (panel) panel->refresh_catalog(false); break; } case wb::RefreshNewDiagram: { ModelDiagramPanel *panel = dynamic_cast<ModelDiagramPanel *>(reinterpret_cast<FormViewBase *>(arg_ptr)); if (panel) panel->setup_navigator(); // switch_to_diagram(panel); break; } case wb::RefreshOverviewNodeInfo: { if (_model_panel && (arg_ptr == dynamic_cast<bec::UIForm *>(_model_panel->get_overview()->get_be()) || arg_ptr == NULL)) { _model_panel->get_overview()->refresh_node(bec::NodeId(arg_id)); } break; } case wb::RefreshOverviewNodeChildren: { if (_model_panel && (arg_ptr == dynamic_cast<bec::UIForm *>(wb::WBContextUI::get()->get_physical_overview()) || arg_ptr == NULL)) { if (arg_id.empty()) { _model_panel->get_overview()->show(); _model_panel->get_overview()->rebuild_all(); } _model_panel->get_overview()->refresh_children(bec::NodeId(arg_id)); } break; } case wb::RefreshDocument: { get_mainwindow()->set_title(wb::WBContextUI::get()->get_title()); ///_model_sidebar.refresh_usertypes(); break; } case wb::RefreshSelection: { FormViewBase *form = get_active_pane(); if (form == _model_panel) { if (_model_panel) _model_panel->selection_changed(); } else if (dynamic_cast<ModelDiagramPanel *>(form)) { ModelDiagramPanel *model = dynamic_cast<ModelDiagramPanel *>(form); model->selection_changed(); } break; } case wb::RefreshNewModel: handle_model_created(); wb::WBContextUI::get()->get_wb()->new_model_finish(); break; case wb::RefreshZoom: { ModelDiagramPanel *panel = dynamic_cast<ModelDiagramPanel *>(get_active_pane()); if (panel) panel->refresh_zoom(); break; } case wb::RefreshCloseEditor: _signal_close_editor.emit(arg_id); break; case wb::RefreshTimer: update_timer(); break; case wb::RefreshFinishEdits: { Gtk::Widget *focused = get_mainwindow()->get_focus(); if (focused) { if (dynamic_cast<Gtk::Entry *>(focused)) { Gtk::TreeView *tree = dynamic_cast<Gtk::TreeView *>(focused->get_parent()); if (!tree && focused->get_parent()) tree = dynamic_cast<Gtk::TreeView *>(focused->get_parent()->get_parent()); if (tree) { Gtk::TreePath path; Gtk::TreeViewColumn *column = 0; tree->get_cursor(path, column); if (column) { // this is not working... std::vector<Gtk::CellRenderer *> rends(column->get_cells()); for (std::vector<Gtk::CellRenderer *>::iterator iter = rends.begin(); iter != rends.end(); ++iter) { (*iter)->stop_editing(true); } // fallback tree->set_cursor(path, *column, false); } } } } } break; } } //------------------------------------------------------------------------------ void MainForm::handle_model_created() { wb::OverviewBE *overview_be = wb::WBContextUI::get()->get_physical_overview(); // Create the model overview panel _model_panel = ModelPanel::create(overview_be); _model_panel->set_close_editor_callback(sigc::bind(sigc::ptr_fun(close_plugin), wb::WBContextUI::get()->get_wb())); _model_panel->get_overview()->get_be()->set_frontend_data(dynamic_cast<FormViewBase *>(_model_panel)); _signal_close_editor.connect(sigc::hide_return(sigc::mem_fun(_model_panel, &ModelPanel::close_editors_for_object))); // even considering this widget is not managed, we need to reference it as later we can end up with crash because gtk3 // will release it // this will be deleted later in handle_model_closed // TODO: check if this can be removed after refactoring modeling #if GTK_VERSION_GE(3, 14) _model_panel->reference(); #endif add_form_pane(dynamic_cast<FormViewBase *>(_model_panel), TabOpenActive); _model_panel->get_overview()->rebuild_all(); } //------------------------------------------------------------------------------ void MainForm::handle_model_closed() { if (_model_panel != nullptr) { get_upper_note()->remove_page(*_model_panel); delete _model_panel; _model_panel = nullptr; } } //------------------------------------------------------------------------------ void MainForm::lock_gui_becb(bool lock) { _gui_locked = lock; } //------------------------------------------------------------------------------ void MainForm::switch_page(Gtk::Widget *, guint pagenum) { Gtk::Notebook *note = get_upper_note(); Gtk::Widget *page = note->get_nth_page(pagenum); if (page) { page->show(); bec::UIForm *uiform = static_cast<bec::UIForm *>(page->get_data("uiform")); wb::WBContextUI::get()->set_active_form(uiform); FormViewBase *form = static_cast<FormViewBase *>(page->get_data("FormViewBase")); if (form) form->on_activate(); } wb::WBContextUI::get()->get_command_ui()->revalidate_edit_menu_items(); } //============================================================================== ModelDiagramPanel *MainForm::get_panel_for_view(mdc::CanvasView *view) { bec::UIForm *form; if ((form = wb::WBContextUI::get()->get_wb()->get_model_context()->get_diagram_form(view))) return dynamic_cast<ModelDiagramPanel *>(static_cast<FormViewBase *>(form->get_frontend_data())); return 0; } void MainForm::add_plugin_pane(PluginEditorBase *pane) { // locate the active main tab Gtk::Notebook *note = get_upper_note(); int pagenum = note->get_current_page(); if (pagenum < 0) { g_warning("Cannot add plugin with no active tab"); return; } Gtk::Widget *page = note->get_nth_page(pagenum); if (page) { FormViewBase *form = reinterpret_cast<FormViewBase *>(page->get_data("FormViewBase")); if (form) { // add the editor to the active tab form->add_plugin_tab(pane); pane->set_data("ContainerForm", form); } } } void MainForm::bring_plugin_pane(PluginEditorBase *pane) { // locate the active main tab Gtk::Notebook *note = get_upper_note(); int pagenum = note->get_current_page(); if (pagenum < 0) { g_warning("Cannot add plugin with no active tab"); return; } FormViewBase *old_form = reinterpret_cast<FormViewBase *>(pane->get_data("ContainerForm")); Gtk::Widget *page = note->get_nth_page(pagenum); if (page) { FormViewBase *form = reinterpret_cast<FormViewBase *>(page->get_data("FormViewBase")); if (form && old_form != form && old_form->get_form()->get_form_context_name() != WB_CONTEXT_QUERY) { pane->reference(); if (old_form) old_form->remove_plugin_tab(pane); // remove from where it is now // add the editor to the active tab form->add_plugin_tab(pane); pane->unreference(); pane->set_data("ContainerForm", form); } } } static bool close_plugin_form(GdkEventAny *ev, PluginEditorBase *frame) { // wnd window is deleted inside closE_live_object_editor frame->close_live_object_editor(); return true; } void MainForm::add_plugin_form(PluginEditorBase *frame) { Gtk::Window *window = new Gtk::Window(); window->add(*frame); window->set_title(frame->get_title()); window->signal_delete_event().connect(sigc::bind(sigc::ptr_fun(close_plugin_form), frame)); frame->signal_title_changed().connect(sigc::mem_fun(window, &Gtk::Window::set_title)); int width = 800; int height = 600; window->resize(width, height); window->show_all(); } void MainForm::close_main_tab() { Gtk::Widget *focused = get_mainwindow()->get_focus(); Gtk::Notebook *upper = get_upper_note(); // go up the hierarchy to see if a child of the upper notebooks is focused while (focused && focused != upper) focused = focused->get_parent(); if (focused) { int curpagenum = upper->get_current_page(); if (curpagenum >= 0) { Gtk::Widget *page = upper->get_nth_page(curpagenum); bec::UIForm *form = reinterpret_cast<bec::UIForm *>(page->get_data("uiform")); if (form && form->get_form_context_name() == "home") return; close_tab(upper, page); } } } void MainForm::close_inner_tab() { Gtk::Widget *focused = get_mainwindow()->get_focus(); Gtk::Notebook *upper = get_upper_note(); // go up the hierarchy to see if a child of the upper notebooks is focused while (focused && focused != upper) focused = focused->get_parent(); if (focused) { int curpagenum = upper->get_current_page(); if (curpagenum >= 0) { Gtk::Widget *page = upper->get_nth_page(curpagenum); FormViewBase *panel = reinterpret_cast<FormViewBase *>(page->get_data("FormViewBase")); bec::UIForm *form = reinterpret_cast<bec::UIForm *>(page->get_data("uiform")); if (form && form->get_form_context_name() == "home") return; if (panel) panel->close_focused_tab(); } } } void MainForm::close_active_tab() { Gtk::Widget *focused = get_mainwindow()->get_focus(); Gtk::Notebook *upper = get_upper_note(); // go up the hierarchy to see if a child of the upper notebooks is focused while (focused && focused != upper) focused = focused->get_parent(); if (focused) { int curpagenum = upper->get_current_page(); if (curpagenum >= 0) { Gtk::Widget *page = upper->get_nth_page(curpagenum); FormViewBase *panel = reinterpret_cast<FormViewBase *>(page->get_data("FormViewBase")); bec::UIForm *form = reinterpret_cast<bec::UIForm *>(page->get_data("uiform")); if (form && form->get_form_context_name() == "home") return; if (panel) { if (!panel->close_focused_tab()) close_tab(upper, page); } else close_tab(upper, page); } } } static bool note_contains_page(Gtk::Notebook *note, Gtk::Widget *page) { for (int i = note->get_n_pages() - 1; i >= 0; --i) if (note->get_nth_page(i) == page) return true; return false; } bool MainForm::close_tab(Gtk::Notebook *note, Gtk::Widget *page) { // on_close should return true if the form should be closed/removed mforms::AppView *app_view = reinterpret_cast<mforms::AppView *>(page->get_data("AppView")); FormViewBase *form = reinterpret_cast<FormViewBase *>(page->get_data("FormViewBase")); if (app_view || form) { if ((app_view && app_view->on_close()) || (form && form->on_close())) { // can't use page_num, because it will dereference the pointer and it could be deleted (shouldn't be, but it is // happening) // if (note->page_num(*page) >= 0) if (note_contains_page(note, page)) { if (form) form->dispose(); note->remove_page(*page); page->unreference(); if (form == _model_panel) { delete _model_panel; _model_panel = nullptr; } } } else { for (int index = 0; index < note->get_n_pages(); ++index) { Gtk::Widget *page = note->get_nth_page(index); if (page->is_visible()) page->child_focus(Gtk::DIR_DOWN); } return false; } } else page->hide(); bool visible = false; for (int c = note->get_n_pages(), i = 0; i < c; i++) { if (note->get_nth_page(i)->is_visible()) { visible = true; break; } } if (!visible) note->hide(); return true; } void MainForm::append_tab_page(Gtk::Notebook *note, Gtk::Widget *widget, const std::string &title, TabStateInfo tabState, ActiveLabel **title_label_ret) { // Make a lookup of existing page with the same content bool already_have_page = false; for (int i = note->get_n_pages() - 1; i >= 0; --i) { if (note->get_nth_page(i) == widget) { already_have_page = true; break; } } if (!already_have_page) { ActiveLabel *label = Gtk::manage( new ActiveLabel(title, sigc::hide_return(sigc::bind(sigc::mem_fun(this, &MainForm::close_tab), note, widget)))); if (title_label_ret) *title_label_ret = label; mforms::Menu *menu = init_tab_menu(widget); label->set_menu(menu, true); menu->set_handler(sigc::bind(sigc::mem_fun(this, &MainForm::tab_menu_handler), label, widget, note)); if (g_object_is_floating(G_OBJECT(widget->gobj()))) g_object_ref_sink(G_OBJECT(widget->gobj())); note->append_page(*widget, *label); if (tabState == TabOpenActive) { // switch the current page when we're in idle _sig_set_current_page = Glib::signal_idle().connect(sigc::bind_return( sigc::bind(sigc::mem_fun(note, &Gtk::Notebook::set_current_page), note->get_n_pages() - 1), false)); } if (tabState != TabClosed) note->show(); } } //------------------------------------------------------------------------------ mforms::Menu *MainForm::init_tab_menu(Gtk::Widget *widget) { { mforms::Menu *m = new mforms::Menu(); m->add_item("Close Tab", "close tab"); m->add_item("Close Other Tabs", "close other tabs"); m->add_item("Close Other Tabs of This Type", "close similar"); return m; } } //------------------------------------------------------------------------------ void MainForm::tab_menu_handler(const std::string &action, ActiveLabel *label, Gtk::Widget *widget, Gtk::Notebook *note) { bec::UIForm *uiform = (bec::UIForm *)widget->get_data("uiform"); if (uiform) { if (action == "close tab") { _sig_close_tab = Glib::signal_idle().connect( sigc::bind_return(sigc::bind(sigc::mem_fun(this, &MainForm::close_tab), note, widget), false)); } else if (action == "close other tabs") { const int note_size = note->get_n_pages(); for (int i = note_size - 1; i >= 0; --i) { Gtk::Widget *cont = note->get_nth_page(i); bec::UIForm *cont_uiform = (bec::UIForm *)cont->get_data("uiform"); const std::string uiform_name = cont_uiform->get_form_context_name(); if (uiform_name != "home" && cont_uiform != uiform) { close_tab(note, cont); } } } else if (action == "close similar") { const int note_size = note->get_n_pages(); const std::string type = uiform->get_form_context_name(); for (int i = note_size - 1; i >= 0; --i) { Gtk::Widget *cont = note->get_nth_page(i); bec::UIForm *cont_uiform = (bec::UIForm *)cont->get_data("uiform"); const std::string uiform_type = cont_uiform->get_form_context_name(); if (uiform_type != "home" && cont != widget && type == uiform_type) { close_tab(note, cont); } } } } } FormViewBase *MainForm::get_active_pane() { int p = get_upper_note()->get_current_page(); if (p >= 0) { Gtk::Widget *page = get_upper_note()->get_nth_page(p); if (page) return reinterpret_cast<FormViewBase *>(page->get_data("FormViewBase")); } return 0; } void MainForm::add_form_pane(FormViewBase *panel, TabStateInfo tabState) { panel->get_panel()->set_data("FormViewBase", panel); panel->get_panel()->set_data("uiform", dynamic_cast<bec::UIForm *>(panel->get_form())); ActiveLabel *label = 0; append_tab_page(get_upper_note(), panel->get_panel(), panel->get_title(), tabState, &label); // Fix gtk3 issue... caused by gtk3: 399de111167c198a7d2ccbd459a2db7c6389181e get_upper_note()->set_current_page(get_upper_note()->page_num(*panel->get_panel())); if (tabState == TabClosed) panel->get_panel()->hide(); if (label) panel->signal_title_changed().connect(sigc::mem_fun(label, &ActiveLabel::set_text)); mforms::on_add_menubar_to_window(panel->get_form()->get_menubar(), get_mainwindow()); panel->reset_layout(); } void MainForm::prepare_close_document() { // close all diagram tabs so they stop receiving events (which can lead to a crash) Gtk::Notebook *note = get_upper_note(); for (int i = note->get_n_pages() - 1; i >= 0; --i) { Gtk::Widget *panel = note->get_nth_page(i); if (dynamic_cast<ModelDiagramPanel *>(panel)) panel->hide(); } // reset overview if (_model_panel != nullptr) _model_panel->get_overview()->reset(); } bool MainForm::fire_timer() { bec::GRTManager::get()->flush_timers(); update_timer(); return false; } void MainForm::update_timer() { int delay_ms = (int)(1000 * bec::GRTManager::get()->delay_for_next_timeout()); if (delay_ms >= 0) Glib::signal_timeout().connect(sigc::mem_fun(this, &MainForm::fire_timer), delay_ms); } //============================================================================== // Here go command handlers // //------------------------------------------------------------------------------ void MainForm::reset_layout() { /// } void MainForm::show_diagram_options() { DiagramSizeForm *form = DiagramSizeForm::create(); form->run(); form->hide(); delete form; } void MainForm::show_page_setup() { wb::WBContextUI::get()->get_wb()->execute_plugin("wb.print.setup"); } #include "mforms/mforms.h" #include "gtk/lf_view.h" static std::string get_resource_path(mforms::App *app, const std::string &file) { if (file.empty()) return bec::GRTManager::get()->get_data_file_path(""); if (file[0] == '/') return file; if (g_str_has_suffix(file.c_str(), ".png") || g_str_has_suffix(file.c_str(), ".xpm")) return bec::IconManager::get_instance()->get_icon_path(file); else if (g_str_has_suffix(file.c_str(), ".txt")) { // This is special handling for txt only on Linux. auto parts = base::split(bec::GRTManager::get()->get_basedir(), "/"); std::string last = parts.back() + (wb::WBContextUI::get()->get_wb()->is_commercial() ? "-commercial" : "-community"); parts.pop_back(); parts.push_back("doc"); parts.push_back(last); parts.push_back(file); return base::join(parts, "/"); } return bec::GRTManager::get()->get_data_file_path(file); } static std::string get_executable_path(mforms::App *app, const std::string &file) { std::string path = bec::GRTManager::get()->get_data_file_path(file); if (!path.empty() && base::file_exists(path)) return path; path = base::dirname(std::string(getenv("MWB_MODULE_DIR"))) + "/" + file; if (base::file_exists(path)) return path; const char *basedir = getenv("MWB_BASE_DIR"); if (basedir) { char *p = g_strdup_printf("%s/libexec/mysql-workbench/%s", basedir, file.c_str()); path = p; g_free(p); if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS)) return path; p = g_strdup_printf("%s/bin/%s", basedir, file.c_str()); path = p; g_free(p); if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS)) return path; p = g_strdup_printf("%s/libexec/%s", basedir, file.c_str()); path = p; g_free(p); if (g_file_test(path.c_str(), G_FILE_TEST_EXISTS)) return path; } return ""; } static base::Rect get_main_window_bounds(mforms::App *app) { Gtk::Window *w = get_mainwindow(); int x, y; w->get_window()->get_position(x, y); return base::Rect(x, y, w->get_width(), w->get_height()); } void MainForm::set_status_text(mforms::App *app, const std::string &text) { MainForm *self = reinterpret_cast<MainForm *>(app->get_data_ptr()); self->show_status_text_becb(text); } struct EventLoopFrame { int exit_code; bool timedout; bool ended; runtime::loop loop; }; static std::list<EventLoopFrame *> event_loop_exit_codes; static bool event_loop_timeout() { if (!event_loop_exit_codes.empty()) { EventLoopFrame *frame = event_loop_exit_codes.back(); if (!frame->timedout && !frame->ended) { frame->timedout = true; frame->loop.quit(); } } return false; } static int begin_event_loop(mforms::App *, float timeout) { EventLoopFrame frame; frame.exit_code = -1; frame.timedout = false; frame.ended = false; sigc::connection timeout_conn; if (timeout > 0.0) timeout_conn = Glib::signal_timeout().connect(sigc::ptr_fun(event_loop_timeout), (unsigned int)(timeout * 1000)); event_loop_exit_codes.push_back(&frame); frame.loop.run(); timeout_conn.disconnect(); if (event_loop_exit_codes.empty() || event_loop_exit_codes.back() != &frame) { g_warning("Internal inconsistency in begin_event_loop"); return -1; } if (!frame.ended && !frame.timedout) { // means something other than end_event_loop() called quit(), could mean the app is being quit.. runtime::app::get().quit(); return -1; } else { int rc = event_loop_exit_codes.back()->exit_code; event_loop_exit_codes.pop_back(); return rc; } } static void end_event_loop(mforms::App *, int rc) { if (event_loop_exit_codes.empty()) { g_warning("Attempt to exit unexisting event loop"); return; } if (!event_loop_exit_codes.back()->timedout) { event_loop_exit_codes.back()->exit_code = rc; event_loop_exit_codes.back()->ended = true; event_loop_exit_codes.back()->loop.quit(); } } static bool isDarkModeActive(mforms::App *) { // On Linux we can't just say if theme is dark or light return false; } static float backing_scale_factor(mforms::App *) { auto window = get_mainwindow(); double dpi = Gdk::Screen::get_default()->get_resolution(); int scaleFactor = Gdk::Screen::get_default()->get_monitor_scale_factor(Gdk::Screen::get_default()->get_monitor_at_window(window->get_window())); return dpi > 0 ? scaleFactor * dpi / defaultDpi : dpi; } void MainForm::setup_mforms_app() { mforms::ControlFactory *cf = mforms::ControlFactory::get_instance(); g_assert(cf); mforms::App::instantiate(this, false); mforms::App::get()->set_data(this); cf->_app_impl.get_resource_path = &get_resource_path; cf->_app_impl.get_executable_path = &get_executable_path; cf->_app_impl.set_status_text = &set_status_text; cf->_app_impl.get_application_bounds = &get_main_window_bounds; cf->_app_impl.enter_event_loop = &begin_event_loop; cf->_app_impl.exit_event_loop = &end_event_loop; cf->_app_impl.backing_scale_factor = &backing_scale_factor; cf->_app_impl.isDarkModeActive = &isDarkModeActive; } Gtk::Widget *MainForm::decorate_widget(Gtk::Widget *panel, bec::UIForm *form) { mforms::MenuBar *menu = form->get_menubar(); mforms::ToolBar *toolbar = form->get_toolbar(); Gtk::Box *top_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0)); if (menu) { Gtk::Widget *w = mforms::widget_for_menubar(menu); w->set_name(form->get_form_context_name()); top_box->pack_start(*w, false, true); w->show(); on_add_menubar_to_window(menu, get_mainwindow()); } if (toolbar) { Gtk::Widget *w = mforms::widget_for_toolbar(toolbar); top_box->pack_start(*w, false, true); w->show(); } top_box->pack_start(*panel, true, true); panel->show(); top_box->show(); return top_box; } static gpointer delete_appview(mforms::AppView *appview) { if (appview->is_managed()) appview->release(); else delete appview; return 0; } std::pair<int, int> MainForm::get_size() { Gtk::Widget *note = get_upper_note(); int w = note->get_width(); int h = note->get_height(); return std::pair<int, int>(w, h); } void MainForm::set_name(const std::string &name) { if (get_upper_note()) { Glib::RefPtr<Atk::Object> acc = get_upper_note()->get_accessible(); if (acc) acc->set_name(name); } } void MainForm::dock_view(mforms::AppView *view, const std::string &position, int) { g_return_if_fail(view != NULL); Gtk::Widget *w = mforms::widget_for_view(view); g_return_if_fail(w != NULL); if (position == "maintab" || position.empty()) { Gtk::Notebook *note = get_upper_note(); // Make a lookup of existing page with the same content bool already_have_page = false; for (int i = note->get_n_pages() - 1; i >= 0; --i) { Gtk::Widget *page; if ((page = note->get_nth_page(i)) && page->get_data("uiform") == dynamic_cast<bec::UIForm *>(view)) { already_have_page = true; break; } } if (already_have_page) return; if (view->release_on_add()) view->set_release_on_add(false); else view->retain(); if (!view->get_menubar()) view->set_menubar(mforms::manage(wb::WBContextUI::get()->get_command_ui()->create_menubar_for_context( view->is_main_form() ? view->get_form_context_name() : ""))); Gtk::Widget *decorated = reinterpret_cast<Gtk::Widget *>(w->get_data("DockDecoration")); if (!decorated) { decorated = decorate_widget(w, view); w->set_data("DockDecoration", decorated); // if (g_object_is_floating(decorated->gobj())) // g_object_ref_sink(decorated->gobj()); // turn the floating ref to a normal one } decorated->set_data("AppView", view); decorated->set_data("uiform", dynamic_cast<bec::UIForm *>(view)); decorated->add_destroy_notify_callback(view, (gpointer(*)(gpointer))delete_appview); if (dynamic_cast<bec::UIForm *>(view)->get_form_context_name() == "home") { Gtk::Widget *tab = Gtk::manage(new Gtk::Image(bec::IconManager::get_instance()->get_icon_path("WB_Home.png"))); tab->set_margin_left(10); tab->set_margin_right(10); note->append_page(*decorated, *tab); } else { ActiveLabel *label = 0; append_tab_page(get_upper_note(), decorated, "", TabOpenActive, &label); w->set_data("tablabel", label); } decorated->set_data("uiform", dynamic_cast<bec::UIForm *>(view)); w->set_data("containerNote", note); } } bool MainForm::select_view(mforms::AppView *view) { Gtk::Notebook *upper_note = get_upper_note(); for (int i = 0; i < upper_note->get_n_pages(); i++) { Gtk::Widget *page = upper_note->get_nth_page(i); if (page && reinterpret_cast<mforms::AppView *>(page->get_data("AppView")) == view) { upper_note->set_current_page(i); return true; } } return false; } void MainForm::undock_view(mforms::AppView *view) { g_return_if_fail(view != NULL); Gtk::Widget *w = mforms::widget_for_view(view); g_return_if_fail(w != NULL); Gtk::Notebook *note = reinterpret_cast<Gtk::Notebook *>(w->get_data("containerNote")); Gtk::Widget *decorated = reinterpret_cast<Gtk::Widget *>(w->get_data("DockDecoration")); if (note && decorated) { note->remove_page(*decorated); } view->release(); } void MainForm::set_view_title(mforms::AppView *view, const std::string &title) { g_return_if_fail(view != NULL); Gtk::Widget *w = mforms::widget_for_view(view); g_return_if_fail(w != NULL); ActiveLabel *label = reinterpret_cast<ActiveLabel *>(w->get_data("tablabel")); if (label) label->set_text(title); } mforms::AppView *MainForm::selected_view() { int i = get_upper_note()->get_current_page(); if (i >= 0) return view_at_index(i); return NULL; } int MainForm::view_count() { return get_upper_note()->get_n_pages(); } mforms::AppView *MainForm::view_at_index(int index) { Gtk::Widget *page = get_upper_note()->get_nth_page(index); if (page) return reinterpret_cast<mforms::AppView *>(page->get_data("AppView")); return NULL; }