frontend/common/grt_shell_window.cpp (1,646 lines of code) (raw):
/*
* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
*
* 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 "grt_python_debugger.h"
#include "grt_shell_window.h"
#include "base/file_functions.h"
#include "base/string_utilities.h"
#include "base/wb_iterators.h"
#include "base/notifications.h"
#include "base/log.h"
#include <errno.h>
DEFAULT_LOG_DOMAIN("grtshell")
#include "grt/common.h"
#include "grt_code_editor.h"
#include "grt_plugin_wizard.h"
#include "workbench/wb_context.h"
#include "mforms/app.h"
#include "mforms/imagebox.h"
#include <glib/gstdio.h>
using namespace base;
using namespace bec;
using namespace mforms;
#define EDITOR_TAB_OFFSET 2
//--------------------------------------------------------------------------------------------------
GRTShellWindow::GRTShellWindow(wb::WBContext *context)
: mforms::Form(mforms::Form::main_form(),
(mforms::FormFlag)(mforms::FormResizable | mforms::FormMinimizable | mforms::FormHideOnClose)),
_context(context),
_toolbar(true),
_content(false),
_padding_box(false),
_hsplitter(true),
_side_tab(mforms::TabViewPalette),
_main_tab(mforms::TabViewDocumentClosable),
_global_box1(false),
_global_box2(false),
_global_splitter(false),
_global_combo(mforms::SelectorCombobox),
_global_tree(mforms::TreeDefault),
_global_list(mforms::TreeFlatList),
_classes_box(false),
_classes_splitter(false),
_classes_tree(mforms::TreeNoBorder),
_classes_text(mforms::VerticalScrollBar),
_modules_splitter(false),
_modules_tree(mforms::TreeNoBorder),
_modules_text(mforms::VerticalScrollBar),
_notifs_splitter(false),
_notifs_tree(mforms::TreeNoBorder),
_notifs_text(mforms::VerticalScrollBar),
_right_splitter(false),
_shell_box(false),
_shell_text(mforms::VerticalScrollBar),
_shell_hbox(true),
#ifdef _MSC_VER
_side_header_panel(mforms::FilledHeaderPanel),
_lower_tab(mforms::TabViewPalette),
_lower_header_panel(mforms::FilledHeaderPanel),
#else
_lower_tab(mforms::TabViewDocument),
#endif
_output_text(mforms::VerticalScrollBar),
_snippet_splitter(false),
_snippet_text(),
_userSnippetsLoaded(false),
_snippetClicked(false) {
set_title(("Workbench Scripting Shell"));
set_name("Shell Window");
setInternalName("shell_window");
// Minimum size for the entire window.
set_size(800, 600);
set_content(&_content);
set_menubar(&_menu);
scoped_connect(signal_closed(), std::bind(&GRTShellWindow::shell_closed, this));
set_on_close(std::bind(&GRTShellWindow::can_close, this));
_content.add(&_toolbar, false, true);
// setup the menubar
{
mforms::MenuItem *menu = mforms::manage(new mforms::MenuItem("File"));
mforms::MenuItem *item;
_menu.add_submenu(menu);
item = menu->add_item_with_title("New...", std::bind(&GRTShellWindow::add_new_script, this), "New", "");
item->set_shortcut("Modifier+N");
menu->add_item_with_title("New Script", std::bind(&GRTShellWindow::add_editor, this, true, "python"), "New Script", "");
item = menu->add_item_with_title("Open...", std::bind(&GRTShellWindow::open_script_file, this), "Open", "");
item->set_shortcut("Modifier+O");
menu->add_separator();
item = menu->add_item_with_title("Save", std::bind(&GRTShellWindow::save_file, this, false), "Save", "");
item->set_shortcut("Modifier+S");
item = menu->add_item_with_title("Save As...", std::bind(&GRTShellWindow::save_file, this, true), "Save As", "");
item->set_shortcut("Modifier+Shift+S");
menu->add_separator();
item = menu->add_item_with_title("Close Script", std::bind(&GRTShellWindow::close_tab, this), "Close Script", "");
item->set_shortcut("Modifier+W");
item = menu->add_item_with_title("Close Window", std::bind(&GRTShellWindow::close, this), "Close Window", "");
#ifdef _MSC_VER
item->set_shortcut("Control+F4");
#else
item->set_shortcut("Modifier+Shift+W");
#endif
menu = mforms::manage(new mforms::MenuItem("Edit"));
_menu.add_submenu(menu);
item = menu->add_item_with_title("Cut", std::bind(&GRTShellWindow::cut, this), "Cut", "");
item->set_shortcut("Modifier+X");
item = menu->add_item_with_title("Copy", std::bind(&GRTShellWindow::copy, this), "Copy", "");
item->set_shortcut("Modifier+C");
item = menu->add_item_with_title("Paste", std::bind(&GRTShellWindow::paste, this), "Paste", "");
item->set_shortcut("Modifier+V");
item = menu->add_item_with_title("Select All", std::bind(&GRTShellWindow::select_all, this), "Select All", "");
item->set_shortcut("Modifier+A");
menu->add_separator();
item = menu->add_item_with_title("Find...", std::bind(&GRTShellWindow::show_find_panel, this), "Find", "");
item->set_shortcut("Modifier+F");
item = menu->add_item_with_title("Replace...", std::bind(&GRTShellWindow::show_replace_panel, this), "Replace", "");
#if defined(__APPLE__)
item->set_shortcut("Command+Option+F");
#else
item->set_shortcut("Modifier+H");
#endif
menu = mforms::manage(new mforms::MenuItem("Script"));
_menu.add_submenu(menu);
item = menu->add_item_with_title("Run", std::bind(&GRTShellWindow::execute_file, this), "Run", "");
item->set_shortcut("Modifier+R");
item->set_name("Run");
item->setInternalName("run");
}
#ifdef _MSC_VER
_content.add(&_padding_box, true, true);
_padding_box.add(&_hsplitter, true, true);
_padding_box.set_padding(6);
set_back_color("#283752");
_hsplitter.set_back_color("#283752");
_side_header_panel.add(&_side_tab);
_side_header_panel.set_back_color("#283752");
_hsplitter.add(&_side_header_panel);
#else
_content.add(&_hsplitter, true, true);
_hsplitter.add(&_side_tab);
#endif
scoped_connect(_side_tab.signal_tab_changed(), std::bind(&GRTShellWindow::side_tab_changed, this));
_hsplitter.add(&_right_splitter);
// Side bar consists of 4 pages: files, globals tree, classes tree and modules tree.
// Setup toolbar.
#ifdef _MSC_VER
_toolbar.set_size(-1, 24);
_toolbar.set_back_color("#BCC7D8");
#endif
_toolbar.set_padding(2);
_toolbar.set_spacing(4);
add_tool_button("tiny_new_doc.png", std::bind(&GRTShellWindow::add_new_script, this),
_("Create a new file from a template"));
add_tool_button("tiny_load.png", std::bind(&GRTShellWindow::open_script_file, this), _("Open a script file"));
add_tool_separator();
_save_button = add_tool_button("tiny_save.png", std::bind(&GRTShellWindow::save_file, this, false), _("Save file"));
_save_as_button = add_tool_button("tiny_saveas.png", std::bind(&GRTShellWindow::save_file, this, true),
_("Save file with a new name"));
add_tool_separator();
// if (_editing_module)
{
// add_tool_button("tiny_refresh.png", std::bind(&GRTCodeEditor::execute, this),
// "Refresh the module");
} // else
{
_run_button = add_tool_button("qe_sql-editor-tb-icon_execute.png", std::bind(&GRTShellWindow::execute_file, this),
_("Execute script"));
_continue_button = add_tool_button("debug_continue.png", std::bind(&GRTShellWindow::debug_continue, this),
_("Continue execution until next breakpoint"));
_pause_button =
add_tool_button("debug_pause.png", std::bind(&GRTShellWindow::debug_pause, this), "Pause script execution");
_step_button =
add_tool_button("debug_step.png", std::bind(&GRTShellWindow::debug_step, this), _("Step to next statement"));
_step_into_button = add_tool_button("debug_step_into.png", std::bind(&GRTShellWindow::debug_step_into, this),
_("Step into next function"));
_step_out_button = add_tool_button("debug_step_out.png", std::bind(&GRTShellWindow::debug_step_out, this),
_("Finish execution of current function"));
_stop_button =
add_tool_button("debug_stop.png", std::bind(&GRTShellWindow::debug_stop, this), _("Stop executing script"));
_continue_button->show(false);
_step_button->set_enabled(false);
_step_into_button->set_enabled(false);
_step_out_button->set_enabled(false);
_continue_button->set_enabled(false);
_stop_button->set_enabled(false);
_pause_button->set_enabled(false);
}
#if !defined(_MSC_VER) && !defined(__APPLE__)
// TODO: remove as soon as all platforms support closable tabs.
_close_script_tab_button =
add_tool_button("Discard.png", std::bind(&GRTShellWindow::close_tab, this), _("Close this script tab"), false);
#else
_close_script_tab_button = NULL;
#endif
add_tool_separator();
_clear_script_output_button = add_tool_button(
"clear_output.png", std::bind(&mforms::TextBox::set_value, &_output_text, ""), _("Clear script output"), false);
add_tool_button("snippet_add.png", std::bind(&GRTShellWindow::add_snippet, this), _("Add a new snippet"));
_snippet_delete_button =
add_tool_button("snippet_del.png", std::bind(&GRTShellWindow::del_snippet, this), _("Delete the selected snippet"));
_snippet_copy_button = add_tool_button("snippet_clipboard.png", std::bind(&GRTShellWindow::copy_snippet, this),
_("Copy snippet text to the clipboard"));
add_tool_separator();
_show_find_button =
add_tool_button("qe_sql-editor-tb-icon_find.png", std::bind(&GRTShellWindow::show_find_panel, this),
_("Show the Find panel for the editor"));
_show_find_button->set_enabled(false);
// Files
mforms::Box *files_box = mforms::manage(new mforms::Box(false));
#ifdef _MSC_VER
_files_tree = mforms::manage(new mforms::TreeView(mforms::TreeNoBorder | mforms::TreeNoHeader));
#else
_files_tree = mforms::manage(new mforms::TreeView(mforms::TreeDefault | mforms::TreeNoHeader));
#endif
_files_menu.add_item_with_title("Add New File",
std::bind(&GRTShellWindow::file_menu_activate, this, "file-from-template"), "Add New File", "");
_files_menu.add_item_with_title("Open Script File",
std::bind(&GRTShellWindow::file_menu_activate, this, "open-script"), "Open Script File", "");
_files_menu.add_separator();
_files_menu.add_item_with_title("Delete Script",
std::bind(&GRTShellWindow::file_menu_activate, this, "delete-script"), "Delete Script", "");
_files_tree->set_context_menu(&_files_menu);
_files_tree->add_column(IconStringColumnType, "", 400, false);
_files_tree->end_columns();
scoped_connect(_files_tree->signal_node_activated(),
std::bind(&GRTShellWindow::file_list_activated, this, std::placeholders::_1, std::placeholders::_2));
files_box->add(_files_tree, true, true);
#ifdef _MSC_VER
files_box->set_back_color("#FFFFFF");
files_box->set_padding(0, 0, 0, 2);
#endif
_side_tab.add_page(files_box, "Files");
// 1) Globals tree
#ifdef _MSC_VER
_global_splitter.set_back_color("#FFFFFF");
_hsplitter.set_back_color("#283752");
//_global_splitter.set_padding(0, 0, 0, 2); TODO: might require work around since we removed padding from View.
#endif
_side_tab.add_page(&_global_splitter, "Globals");
_global_splitter.add(&_global_box1, 0);
_global_splitter.add(&_global_box2, 0);
#ifndef _MSC_VER
_global_box1.set_spacing(4);
_global_box2.set_spacing(4);
#endif
_global_box1.add(&_global_combo, false, false);
_global_box1.add(&_global_tree, true, true);
_global_box2.add(&_global_entry, false, true);
_global_entry.set_read_only(true);
#if defined(_MSC_VER) | defined(__APPLE__)
_global_entry.set_back_color("#FFFFFF");
#endif
_global_box2.add(&_global_list, true, true);
_global_list.add_column(mforms::IconStringColumnType, "Name", 100);
_global_list.add_column(mforms::StringColumnType, "Value", 100);
_global_list.end_columns();
_global_tree.add_column(mforms::IconStringColumnType, "GRT Globals", 200, false);
_global_tree.add_column(mforms::StringColumnType, "Type", 100, false);
_global_tree.end_columns();
scoped_connect(_global_tree.signal_expand_toggle(),
std::bind(&GRTShellWindow::globals_expand_toggle, this, std::placeholders::_1, std::placeholders::_2));
scoped_connect(_global_tree.signal_changed(), std::bind(&GRTShellWindow::global_selected, this));
scoped_connect(_global_combo.signal_changed(), std::bind(&GRTShellWindow::refresh_globals_tree, this));
_inspector = 0;
_global_menu.add_item_with_title(_("Copy Value"), std::bind(&GRTShellWindow::handle_global_menu, this, "copy_value"), "Copy Value", "");
_global_menu.add_item_with_title(_("Copy Path"), std::bind(&GRTShellWindow::handle_global_menu, this, "copy_path"), "Copy Path", "");
_global_menu.add_item_with_title(_("Copy Path for Python"),
std::bind(&GRTShellWindow::handle_global_menu, this, "copy_path_py"), "Copy Path for Python", "");
_global_tree.set_context_menu(&_global_menu);
// 2) Classes tree
#ifdef _MSC_VER
_classes_splitter.set_back_color("#FFFFFF");
//_classes_splitter.set_padding(0, 0, 0, 2); TODO: might require workaround.
#endif
_side_tab.add_page(&_classes_splitter, "Classes");
_classes_splitter.add(&_classes_box, 0);
_classes_box.set_spacing(4);
_classes_box.add(&_classes_sorting, false, true);
_classes_box.add(&_classes_tree, true, true);
_classes_splitter.add(&_classes_text, 0);
_classes_text.set_read_only(true);
_classes_text.set_back_color("#FFFFFF");
_classes_tree.add_column(mforms::IconStringColumnType, "Name", 150, false);
_classes_tree.add_column(mforms::StringColumnType, "Type", 100, false);
_classes_tree.add_column(mforms::StringColumnType, "Caption", 100, false);
_classes_tree.end_columns();
scoped_connect(_classes_tree.signal_changed(), std::bind(&GRTShellWindow::class_selected, this));
_classes_sorting.add_item("Group by Name");
_classes_sorting.add_item("Group by Hierarchy");
_classes_sorting.add_item("Group by Package");
scoped_connect(_classes_sorting.signal_changed(), std::bind(&GRTShellWindow::refresh_classes_tree, this));
// 3) Modules tree
#ifdef _MSC_VER
_modules_splitter.set_back_color("#FFFFFF");
//_modules_splitter.set_padding(0, 0, 0, 2); TODO: might require workaround.
#endif
_side_tab.add_page(&_modules_splitter, "Modules");
_modules_splitter.add(&_modules_tree, 0);
_modules_splitter.add(&_modules_text, 0);
_modules_text.set_read_only(true);
_modules_text.set_back_color("#FFFFFF");
_modules_tree.add_column(mforms::IconStringColumnType, "GRT Modules", 220, false);
_modules_tree.end_columns();
scoped_connect(_modules_tree.signal_changed(), std::bind(&GRTShellWindow::module_selected, this));
_right_splitter.add(&_main_tab);
// 4) Notifications tree
#ifdef _MSC_VER
_notifs_splitter.set_back_color("#FFFFFF");
//_modules_splitter.set_padding(0, 0, 0, 2); TODO: might require workaround.
#endif
_side_tab.add_page(&_notifs_splitter, "Notifications");
_notifs_splitter.add(&_notifs_tree, 0);
_notifs_splitter.add(&_notifs_text, 0);
_notifs_text.set_read_only(true);
_notifs_text.set_back_color("#FFFFFF");
_notifs_tree.add_column(mforms::IconStringColumnType, "Notifications", 220, false);
_notifs_tree.end_columns();
scoped_connect(_notifs_tree.signal_changed(), std::bind(&GRTShellWindow::notif_selected, this));
#ifdef _MSC_VER
_right_splitter.set_back_color("#283752");
_lower_header_panel.add(&_lower_tab);
_right_splitter.add(&_lower_header_panel);
_lower_header_panel.set_title(_("Debugging"));
_lower_header_panel.set_back_color("#283752");
#else
_right_splitter.add(&_lower_tab);
#endif
// setup shell
#ifdef _MSC_VER
_snippet_list = mforms::manage(new TreeView(mforms::TreeNoBorder | mforms::TreeFlatList));
#else
_snippet_list = mforms::manage(new TreeView(mforms::TreeDefault | mforms::TreeFlatList));
#endif
_shell_box.add(&_shell_text, true, true);
_shell_text.set_monospaced(true);
_shell_text.set_read_only(true);
#if defined(_MSC_VER) | defined(__APPLE__)
_shell_text.set_front_color("#FFFFFF");
_shell_text.set_back_color("#000000");
#endif
_shell_text.set_padding(2);
_shell_box.add(&_shell_hbox, false, true);
_shell_hbox.add(&_shell_prompt, false, true);
_shell_hbox.add(&_shell_entry, true, true);
_main_tab.add_page(&_shell_box, "Shell", false);
scoped_connect(_shell_entry.signal_action(), std::bind(&GRTShellWindow::shell_action, this, std::placeholders::_1));
// snippets tab
#ifdef _MSC_VER
_snippet_splitter.set_back_color("#283752");
#else
_snippet_splitter.set_padding(8);
#endif
//_snippet_box.set_spacing(8);
_snippet_splitter.add(_snippet_list, 50, true);
_snippet_splitter.add(&_snippet_text, 50, false);
_snippet_menu.add_item_with_title("Execute Snippet",
std::bind(&GRTShellWindow::snippet_menu_activate, this, "execute"), "Execute Snippet", "");
_snippet_menu.add_item_with_title("Send to Script Editor",
std::bind(&GRTShellWindow::snippet_menu_activate, this, "new_with_snippet"), "Send to Script Editor", "");
_snippet_menu.add_separator();
_snippet_menu.add_item_with_title("Copy to Clipboard",
std::bind(&GRTShellWindow::snippet_menu_activate, this, "copy_clipboard"), "Copy to Clipboard", "");
_snippet_menu.add_separator();
_snippet_menu.add_item_with_title("Delete Snippet",
std::bind(&GRTShellWindow::snippet_menu_activate, this, "delete"), "Delete Snippet", "");
_snippet_list->set_context_menu(&_snippet_menu);
scoped_connect(_snippet_list->signal_changed(), std::bind(&GRTShellWindow::snippet_selected, this));
_snippet_text.set_language(LanguagePython);
scoped_connect(_snippet_text.signal_changed(),
std::bind(&GRTShellWindow::snippet_changed, this, std::placeholders::_1, std::placeholders::_2));
_snippet_list->add_column(mforms::StringColumnType, "Snippet", 500, false);
_snippet_list->end_columns();
_main_tab.add_page(&_snippet_splitter, "Snippets", false);
scoped_connect(_main_tab.signal_tab_closing(),
std::bind(&GRTShellWindow::on_tab_closing, this, std::placeholders::_1));
scoped_connect(_main_tab.signal_tab_changed(), std::bind(&GRTShellWindow::on_tab_changed, this));
//
_output_text.set_read_only(true);
_output_text.set_monospaced(true);
_lower_tab.add_page(&_output_text, "Output");
_debugger = new PythonDebugger(this, &_lower_tab);
try {
_debugger->init_pdb();
} catch (const std::exception &exc) {
logError("Could not initialize the debugger: %s\n", exc.what());
add_output("Could not initialize the debugger\n");
delete _debugger;
_debugger = 0;
}
bec::GRTManager::get()->run_once_when_idle(std::bind(&GRTShellWindow::set_splitter_positions, this));
bec::GRTManager::get()->get_shell()->set_ready_handler(
std::bind(&GRTShellWindow::handle_prompt, this, std::placeholders::_1));
bec::GRTManager::get()->get_shell()->set_output_handler(
std::bind(&GRTShellWindow::handle_output, this, std::placeholders::_1));
on_tab_changed();
snippet_selected();
side_tab_changed();
}
bool GRTShellWindow::can_close() {
// Because there's no other way to know if the window is about to close, we'll use this event to stop debugger.
if (_stop_button->is_enabled() && _debugger)
_debugger->stop();
// GRTShellWindow is about to close so we ask each editor if it's fine to quit.
return request_quit();
}
void GRTShellWindow::set_splitter_positions() {
_hsplitter.set_divider_position(300);
_global_splitter.set_divider_position(400);
_modules_splitter.set_divider_position(400);
_classes_splitter.set_divider_position(400);
_notifs_splitter.set_divider_position(400);
_snippet_splitter.set_divider_position(200);
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::shell_action(mforms::TextEntryAction action) {
switch (action) {
case mforms::EntryActivate: {
std::string command = _shell_entry.get_string_value();
_shell_entry.set_value("");
// _completion->add_completion_text(command);
command += '\n';
bec::GRTManager::get()->get_shell()->write(grt::GRT::get()->get_shell()->get_prompt() + " " + command);
bec::GRTManager::get()->get_shell()->process_line_async(command);
break;
}
case mforms::EntryKeyUp: {
std::string line;
if (bec::GRTManager::get()->get_shell()->previous_history_line(_shell_entry.get_string_value(), line))
_shell_entry.set_value(line);
break;
}
case mforms::EntryCKeyUp:
break;
case mforms::EntryKeyDown: {
std::string line;
if (bec::GRTManager::get()->get_shell()->next_history_line(line))
_shell_entry.set_value(line);
break;
}
case mforms::EntryCKeyDown:
break;
case mforms::EntryEscape:
break;
}
}
void GRTShellWindow::show(bool flag) {
if (flag)
refresh_all();
load_state();
mforms::Form::show(flag);
}
void GRTShellWindow::refresh_all() {
refresh_files();
int idx = 0;
std::string root = _global_tree.root_node()->get_tag();
std::vector<std::string> l = bec::GRTManager::get()->get_shell()->get_grt_tree_bookmarks();
_global_combo.clear();
for (std::vector<std::string>::const_iterator i = l.begin(); i != l.end(); ++i, ++idx) {
_global_combo.add_item(*i);
if (root == *i)
_global_combo.set_selected(idx);
}
// refresh values
refresh_globals_tree();
global_selected();
// refresh _struct
refresh_classes_tree();
// refresh modules
refresh_modules_tree();
refresh_notifs_list();
_script_extension = ".py";
_comment_prefix = "# ";
refresh_snippets();
}
//--------------------------------------------------------------------------------------------------
bool GRTShellWindow::capture_output(const grt::Message &msg, void *sender, bool send_to_output) {
if (msg.type == grt::OutputMsg) {
if (bec::GRTManager::get()->in_main_thread()) {
if (send_to_output)
add_output(msg.text);
else
handle_output(msg.text); // sends to shell window
} else {
if (send_to_output)
bec::GRTManager::get()->run_once_when_idle(std::bind(&GRTShellWindow::add_output, this, msg.text));
else
bec::GRTManager::get()->run_once_when_idle(std::bind(&GRTShellWindow::handle_output, this, msg.text));
}
return true;
}
return false;
}
void GRTShellWindow::execute_file() {
GRTCodeEditor *editor = get_active_editor();
if (!editor)
return;
grt::GRT::get()->pushMessageHandler(
new grt::SlotHolder(std::bind(&GRTShellWindow::capture_output, this, std::placeholders::_1, std::placeholders::_2, true)));
if (_debugger && g_str_has_suffix(editor->get_path().c_str(), ".py")) {
_run_button->show(false);
_continue_button->show(true);
_pause_button->set_enabled(true);
_debugger->run(editor);
_run_button->show(true);
_continue_button->show(false);
_step_button->set_enabled(false);
_step_into_button->set_enabled(false);
_step_out_button->set_enabled(false);
_continue_button->set_enabled(false);
_stop_button->set_enabled(false);
_pause_button->set_enabled(false);
} else
try {
editor->execute();
} catch (const std::exception &exc) {
logError("Error during execution of script: %s\n", exc.what());
add_output("There were errors during execution. Please review log messages.\n");
}
grt::GRT::get()->popMessageHandler();
}
void GRTShellWindow::debug_step() {
GRTCodeEditor *editor = get_active_editor();
if (editor && _debugger && g_str_has_suffix(editor->get_path().c_str(), ".py")) {
if (_debugger->program_stopped())
_debugger->step();
else {
// start the program stopping at the 1st line
grt::GRT::get()->pushMessageHandler(
new grt::SlotHolder(std::bind(&GRTShellWindow::capture_output, this, std::placeholders::_1, std::placeholders::_2, true)));
_run_button->show(false);
_continue_button->show(true);
_pause_button->set_enabled(true);
_debugger->run(editor, true);
_run_button->show(true);
_continue_button->show(false);
_step_button->set_enabled(true);
_step_into_button->set_enabled(false);
_step_out_button->set_enabled(false);
_continue_button->set_enabled(false);
_stop_button->set_enabled(false);
_pause_button->set_enabled(false);
grt::GRT::get()->popMessageHandler();
}
}
}
void GRTShellWindow::debug_step_into() {
if (_debugger)
_debugger->step_into();
}
void GRTShellWindow::debug_step_out() {
if (_debugger)
_debugger->step_out();
}
void GRTShellWindow::debug_continue() {
if (_debugger)
_debugger->continue_();
}
void GRTShellWindow::debug_stop() {
if (_debugger)
_debugger->stop();
}
void GRTShellWindow::debug_pause() {
if (_debugger)
_debugger->pause();
}
void GRTShellWindow::save_file(bool save_as) {
GRTCodeEditor *editor = get_active_editor();
if (editor)
editor->save(save_as);
}
void GRTShellWindow::close_tab() {
GRTCodeEditor *editor = get_active_editor();
if (editor) {
if (editor->can_close())
close_editor(editor);
}
}
void GRTShellWindow::show_find_panel() {
GRTCodeEditor *editor = get_active_editor();
if (editor) {
editor->get_editor()->show_find_panel(false);
}
}
void GRTShellWindow::show_replace_panel() {
GRTCodeEditor *editor = get_active_editor();
if (editor) {
editor->get_editor()->show_find_panel(true);
}
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::side_tab_changed() {
#ifdef _MSC_VER
static std::string side_bar_titles[] = {_("File Browser"), _("Globals Tree"), _("Classes List"), _("Modules List"),
_("Notifications")};
_side_header_panel.set_title(side_bar_titles[_side_tab.get_active_tab()]);
#endif
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::handle_output(const std::string &text) {
_shell_text.append_text(text, true);
}
void GRTShellWindow::handle_error(const std::string &text, const std::string &detail) {
_shell_text.append_text(text);
_shell_text.append_text(detail);
}
void GRTShellWindow::handle_prompt(const std::string &text) {
_shell_prompt.set_text(text);
}
void GRTShellWindow::global_selected() {
if (_inspector) {
delete _inspector;
_inspector = 0;
}
mforms::TreeNodeRef selected;
try {
if ((selected = _global_tree.get_selected_node())) {
grt::ValueRef value(get_global_at_node(selected));
if (value.is_valid()) {
_inspector = ValueInspectorBE::create(value, false, false);
refresh_global_list();
}
_global_entry.set_value(get_global_path_at_node(selected));
} else
_global_entry.set_value("");
} catch (std::exception &exc) {
logError("Exception when selecting item in globals tree: %s\n", exc.what());
}
}
void GRTShellWindow::class_selected() {
mforms::TreeNodeRef selected;
if ((selected = _classes_tree.get_selected_node()))
_classes_text.set_value(get_class_node_description(selected));
else
_classes_text.set_value("");
}
void GRTShellWindow::module_selected() {
mforms::TreeNodeRef selected;
if ((selected = _modules_tree.get_selected_node())) {
std::string text(get_module_node_description(selected));
_modules_text.set_value(text);
} else
_modules_text.set_value("");
}
void GRTShellWindow::notif_selected() {
mforms::TreeNodeRef selected;
if ((selected = _notifs_tree.get_selected_node()) && selected->get_parent() != _notifs_tree.root_node()) {
std::string text;
std::string name = selected->get_string(0);
base::NotificationCenter::NotificationHelp info =
base::NotificationCenter::get()->get_registered_notification(name);
text = base::strfmt(
"%s (%s)\n"
"%s\n\n"
"Sender: %s\n\n"
"Extra Info Dictionary:\n%s",
name.c_str(), info.context.c_str(), info.summary.c_str(), info.sender.empty() ? "NULL" : info.sender.c_str(),
info.info.empty() ? "No additional info is sent" : info.info.c_str());
_notifs_text.set_value(text);
} else
_notifs_text.set_value("");
}
void GRTShellWindow::handle_global_menu(const std::string &action) {
mforms::TreeNodeRef selected;
if ((selected = _global_tree.get_selected_node())) {
if (action == "copy_value") {
grt::ValueRef value(get_global_at_node(selected));
mforms::Utilities::set_clipboard_text(value.debugDescription());
} else if (action == "copy_path") {
mforms::Utilities::set_clipboard_text(get_global_path_at_node(selected));
} else if (action == "copy_path_py") {
std::string path = "grt.root";
std::vector<std::string> parts;
parts = base::split(get_global_path_at_node(selected), "/");
for (base::const_range<std::vector<std::string> > p(parts); p; ++p) {
if (p->empty())
continue;
if (isdigit(p->at(0)))
path.append("[").append(*p).append("]");
else
path.append(".").append(*p);
}
mforms::Utilities::set_clipboard_text(path);
}
}
}
void GRTShellWindow::save_snippets() {
// If the user snippets were not loaded yet, a save is invalid
if (!_userSnippetsLoaded || _snippetClicked)
return;
std::string path = base::makePath(bec::GRTManager::get()->get_user_datadir(), "shell_snippets" + _script_extension);
std::fstream file(path, std::fstream::out | std::fstream::trunc);
if (!file.is_open()) {
_shell_text.append_text(base::strfmt("Cannot save snippets to %s: %s", path.c_str(), g_strerror(errno)));
return;
}
int c = _snippet_list->root_node()->count();
for (int i = _global_snippet_count; i < c; i++) {
std::string snippet = _snippet_list->root_node()->get_child(i)->get_tag();
if (i > _global_snippet_count)
file << std::endl;
file << " " << base::replaceString(snippet, "\n", "\n ") << std::endl;
}
}
void GRTShellWindow::load_snippets_from(const std::string &path) {
FILE *f = base_fopen(path.c_str(), "r");
if (f) {
char line[4096];
while (fgets(line, sizeof(line), f)) {
std::string script = line + 1;
char *ptr = strchr(line, '\n');
if (ptr)
*ptr = 0;
std::string name = line + 1;
while (fgets(line, sizeof(line) - 1, f) && line[0] == ' ') {
script.append(line + 1);
}
// Remove the last line break, we added that, not the user.
if (script.size() > 0)
script.erase(script.size() - 1, 1);
mforms::TreeNodeRef node = _snippet_list->add_node();
node->set_string(0, name);
node->set_tag(script);
}
fclose(f);
}
}
void GRTShellWindow::refresh_snippets() {
_snippet_list->clear();
load_snippets_from(bec::GRTManager::get()->get_data_file_path("shell_snippets" + _script_extension + ".txt"));
_global_snippet_count = _snippet_list->root_node()->count();
load_snippets_from(base::makePath(bec::GRTManager::get()->get_user_datadir(), "shell_snippets" + _script_extension));
_userSnippetsLoaded = true;
snippet_selected();
}
void GRTShellWindow::open_script_file() {
mforms::FileChooser chooser(mforms::OpenFile);
chooser.set_title(_("Open GRT Script"));
if (chooser.run_modal()) {
open_file_in_editor(chooser.get_path(), true);
}
}
bool GRTShellWindow::execute_script(const std::string &script, const std::string &language) {
bool result = bec::GRTManager::get()->get_shell()->run_script(script, language);
save_state();
return result;
}
void GRTShellWindow::add_snippet() {
std::string snippet = _comment_prefix + " new snippet\n";
mforms::TreeNodeRef node = _snippet_list->add_node();
node->set_tag(snippet);
_snippet_list->select_node(node);
snippet_selected(); // force snippet to be displayed
save_snippets();
save_state();
}
void GRTShellWindow::del_snippet() {
mforms::TreeNodeRef node = _snippet_list->get_selected_node();
if (node) {
node->remove_from_parent();
snippet_selected();
save_snippets();
}
}
void GRTShellWindow::copy_snippet() {
mforms::TreeNodeRef node = _snippet_list->get_selected_node();
if (node)
mforms::Utilities::set_clipboard_text(node->get_tag());
}
void GRTShellWindow::scriptize_snippet() {
mforms::TreeNodeRef node = _snippet_list->get_selected_node();
if (node) {
std::string snippet = node->get_tag();
std::string language = "python";
GRTCodeEditor *editor = add_editor(true, language);
editor->set_text(snippet);
}
}
bool run_return_true(std::function<void(const std::string &)> f, const std::string ¶m) {
f(param);
return true;
}
void GRTShellWindow::run_snippet() {
mforms::TreeNodeRef node = _snippet_list->get_selected_node();
if (node) {
std::string script = node->get_tag();
// auto-select the tab where output goes
_main_tab.set_active_tab(0);
handle_output("Running snippet...\n");
// redirect snippet output to the shell
grt::GRT::get()->pushMessageHandler(
new grt::SlotHolder(std::bind(&GRTShellWindow::capture_output, this, std::placeholders::_1, std::placeholders::_2, false)));
try {
std::string language = "python";
bool ret = execute_script(script, language);
grt::GRT::get()->popMessageHandler();
if (!ret) {
handle_output("Snippet execution finished with an error\n");
} else {
handle_output("...execution finished\n");
}
} catch (const std::exception &exc) {
grt::GRT::get()->popMessageHandler();
handle_output("Exception caught while executing snippet:\n");
handle_output(std::string(exc.what()).append("\n"));
}
}
save_state();
}
void GRTShellWindow::snippet_selected() {
bool read_only = false;
_snippetClicked = true;
_snippet_text.set_features(mforms::FeatureReadOnly, false); // Necessary to be able to change the text.
int sel = _snippet_list->get_selected_row();
if (sel < 0) {
_snippet_delete_button->set_enabled(false);
_snippet_copy_button->set_enabled(false);
_snippet_text.set_value("");
read_only = true;
for (int i = 0; i < 6; i++)
_snippet_menu.get_item(i)->set_enabled(false);
} else {
if (sel < _global_snippet_count) {
read_only = true;
_snippet_delete_button->set_enabled(false);
for (int i = 0; i < 6; i++) {
if (i != 5)
_snippet_menu.get_item(i)->set_enabled(true);
else
_snippet_menu.get_item(i)->set_enabled(false); // 5 is delete
}
} else {
_snippet_delete_button->set_enabled(true);
for (int i = 0; i < 6; i++)
_snippet_menu.get_item(i)->set_enabled(true);
}
mforms::TreeNodeRef node(_snippet_list->get_selected_node());
if (node)
_snippet_text.set_value(node->get_tag());
_snippet_copy_button->set_enabled(true);
}
_snippet_text.set_features(mforms::FeatureReadOnly, read_only);
_snippetClicked = false;
}
void GRTShellWindow::snippet_changed(int line, int linesAdded) {
std::string snippet = _snippet_text.get_string_value();
mforms::TreeNodeRef node = _snippet_list->get_selected_node();
if (node) {
node->set_tag(snippet);
std::string::size_type p = snippet.find('\n');
if (p != std::string::npos)
snippet = snippet.substr(0, p);
node->set_string(0, snippet);
save_snippets();
}
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::snippet_menu_activate(const std::string &action) {
if (action == "execute")
run_snippet();
else if (action == "new_with_snippet")
scriptize_snippet();
else if (action == "copy_clipboard")
copy_snippet();
else if (action == "delete")
del_snippet();
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::file_menu_activate(const std::string &action) {
if (action == "file-from-template")
add_new_script();
else if (action == "open-script")
open_script_file();
else if (action == "delete-script")
delete_selected_file();
}
//--------------------------------------------------------------------------------------------------
GRTCodeEditor *GRTShellWindow::add_editor(bool is_script, const std::string &language) {
GRTCodeEditor *editor = manage(new GRTCodeEditor(this, !is_script, language));
_editors.push_back(editor);
int page = _main_tab.add_page(editor, editor->get_title());
_main_tab.set_active_tab(page);
save_state();
if (language == "python" && _debugger)
_debugger->editor_added(editor);
return editor;
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::close_editor(GRTCodeEditor *editor) {
for (std::vector<GRTCodeEditor *>::iterator iter = _editors.begin(); iter != _editors.end(); ++iter) {
if ((*iter) == editor) {
_editors.erase(iter);
break;
}
}
if (_debugger)
_debugger->editor_closed(editor);
_main_tab.remove_page(editor);
save_state();
}
void GRTShellWindow::open_file_in_editor(const std::string &path, bool is_script) {
if (get_editor_for(path, true) != NULL)
return;
std::string language = ""; // No syntax highlighting if file extension is unknown.
if (g_str_has_suffix(path.c_str(), ".py"))
language = "python"; // Python script
else if (g_str_has_suffix(path.c_str(), ".sql") || g_str_has_suffix(path.c_str(), ".qbquery"))
language = "sql";
// Show warning messages if applicable...
if (language == "") {
std::string text =
base::strfmt(_("The file %s has an unsupported extension for this script editor."), path.c_str());
if (Utilities::show_message_and_remember(_("Unsupported File Format"), text, _("OK"), _("Cancel"), "",
"ShellWindowUnknownLanguageFile", "") == mforms::ResultCancel)
return;
} else if (language == "sql") {
if (Utilities::show_message_and_remember(
_("Unsupported Execution"), _("This script editor is meant for developing Workbench plugins and scripts. SQL "
"scripts should be opened and executed in the SQL Editor."),
_("OK"), _("Cancel"), "", "ShellWindowSqlLanguageFile", "") == mforms::ResultCancel)
return;
}
GRTCodeEditor *editor = add_editor(is_script, language);
if (!editor->load(path)) {
close_editor(editor);
return;
}
#ifdef _DEBUG
editor->test_markup();
#endif
}
GRTCodeEditor *GRTShellWindow::show_file_at_line(const std::string &path, int line) {
open_file_in_editor(path, true);
GRTCodeEditor *editor = get_editor_for(path, true);
if (!editor)
add_output(base::strfmt("Cannot open file %s", path.c_str()));
else {
ssize_t start, length;
editor->get_editor()->get_range_of_line(line, start, length);
editor->get_editor()->set_selection(start, 0);
}
return editor;
}
void GRTShellWindow::add_new_script() {
NewPluginDialog wizard(this, bec::GRTManager::get()->get_data_file_path("script_templates"));
std::string path;
std::string code;
bool is_script;
std::string language;
if (wizard.run(path, code, is_script, language)) {
GRTCodeEditor *editor = add_editor(is_script, language);
if (!path.empty() && base::basename(path) == path)
path = base::makePath(bec::GRTManager::get()->get_user_script_path(), path);
editor->set_path(path);
editor->set_text(code);
}
save_state();
}
bool GRTShellWindow::add_output(const std::string &text) {
_output_text.append_text(text, true);
return true;
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::set_editor_title(GRTCodeEditor *editor, const std::string &title) {
int index = _main_tab.get_page_index(editor);
if (index >= 0)
_main_tab.set_tab_title(index, editor->get_title());
}
//--------------------------------------------------------------------------------------------------
/**
* Called from the UI context when WB is about to quit. Check if we have pending changes.
* Return true if we are clear, false otherwise.
*/
bool GRTShellWindow::request_quit() {
std::vector<GRTCodeEditor *>::reverse_iterator editor;
while ((editor = _editors.rbegin()) != _editors.rend()) {
if (!(*editor)->can_close())
return false;
else
close_editor(*editor);
}
return true;
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::add_files_from_dir(mforms::TreeNodeRef parent, const std::string &dirname, bool is_script) {
GDir *dir = g_dir_open(dirname.c_str(), 0, NULL);
if (!dir)
return;
while (const gchar *name = g_dir_read_name(dir)) {
if (g_str_has_suffix(name, ".py")) {
mforms::TreeNodeRef node = parent->add_child();
node->set_string(0, name);
if (is_script)
node->set_tag(std::string("s").append(dirname).append(G_DIR_SEPARATOR_S).append(name));
else
node->set_tag(std::string("m").append(dirname).append(G_DIR_SEPARATOR_S).append(name));
}
}
g_dir_close(dir);
}
void GRTShellWindow::refresh_files() {
mforms::TreeNodeRef node;
_files_tree->clear();
node = _files_tree->root_node()->add_child();
node->set_string(0, "User Scripts");
node->set_icon_path(0, "folder");
add_files_from_dir(node, bec::GRTManager::get()->get_user_script_path(), true);
node->expand();
node = _files_tree->root_node()->add_child();
node->set_string(0, "User Modules");
node->set_icon_path(0, "folder");
add_files_from_dir(node, bec::GRTManager::get()->get_user_module_path(), false);
node->expand();
node = _files_tree->root_node()->add_child();
node->set_string(0, "User Libraries");
node->set_icon_path(0, "folder");
add_files_from_dir(node, bec::GRTManager::get()->get_user_library_path(), true);
node->expand();
}
void GRTShellWindow::file_list_activated(mforms::TreeNodeRef node, int column) {
if (node) {
std::string path = node->get_tag();
if (!path.empty()) {
open_file_in_editor(path.substr(1), path[0] == 's');
}
}
}
void GRTShellWindow::on_file_save(const std::string &file) {
refresh_files();
if (_debugger)
_debugger->refresh_file(file);
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::delete_selected_file() {
mforms::TreeNodeRef node(_files_tree->get_selected_node());
if (node) {
std::string path = node->get_tag();
if (!path.empty()) {
std::string fn = path.substr(1);
if (mforms::Utilities::show_message(
_("Delete File"),
base::strfmt(_("Really delete '%s' from disk? This operation cannot be undone."), fn.c_str()), _("Delete"),
_("Cancel")) == mforms::ResultOk) {
::g_remove(fn.c_str());
::g_remove((fn + 'c').c_str());
refresh_files();
}
}
}
}
//--------------------------------------------------------------------------------------------------
mforms::Button *GRTShellWindow::add_tool_button(const std::string &image, const std::function<void()> &action,
const std::string &tooltip, bool left) {
App *app = App::get();
Button *b = manage(new Button(ToolButton));
b->set_icon(app->get_resource_path(image));
b->set_tooltip(tooltip);
#ifdef __APPLE__
b->set_size(-1, 24);
#endif
scoped_connect(b->signal_clicked(), action);
if (left)
_toolbar.add(b, false, true);
else
_toolbar.add_end(b, false, true);
return b;
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::add_tool_separator() {
App *app = App::get();
ImageBox *image = manage(new ImageBox());
image->set_image(app->get_resource_path("statusbar_separator.png"));
image->set_image_align(MiddleCenter);
_toolbar.add(image, false, true);
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::load_state() {
int x = _context->read_state("left", "scripting-shell", 100);
int y = _context->read_state("top", "scripting-shell", 100);
int width = _context->read_state("width", "scripting-shell", 800);
int height = _context->read_state("height", "scripting-shell", 600);
set_size(width, height);
set_position(x, y);
// Restore divider positions.
_hsplitter.set_divider_position(_context->read_state("main-splitter", "scripting-shell", 250));
_global_splitter.set_divider_position(_context->read_state("global-splitter", "scripting-shell", 400));
_modules_splitter.set_divider_position(_context->read_state("modules-splitter", "scripting-shell", 400));
_classes_splitter.set_divider_position(_context->read_state("classes-splitter", "scripting-shell", 400));
_snippet_splitter.set_divider_position(_context->read_state("snippets-splitter", "scripting-shell", 400));
_shell_text.set_font(bec::GRTManager::get()->get_app_option_string("workbench.scripting.ScriptingShell:Font"));
_snippet_text.set_font(bec::GRTManager::get()->get_app_option_string("workbench.scripting.ScriptingEditor:Font"));
for (std::vector<GRTCodeEditor *>::iterator editor = _editors.begin(); editor != _editors.end(); editor++)
(*editor)->set_font(bec::GRTManager::get()->get_app_option_string("workbench.scripting.ScriptingEditor:Font"));
_lower_tab_height = _context->read_state("editor-splitter", "scripting-shell", 400);
on_tab_changed();
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::save_state() {
// Store form's size and position.
_context->save_state("left", "scripting-shell", get_x());
_context->save_state("top", "scripting-shell", get_y());
_context->save_state("width", "scripting-shell", get_width());
_context->save_state("height", "scripting-shell", get_height());
// Store all divider positions.
_context->save_state("main-splitter", "scripting-shell", _hsplitter.get_divider_position());
_context->save_state("global-splitter", "scripting-shell", _global_splitter.get_divider_position());
_context->save_state("modules-splitter", "scripting-shell", _modules_splitter.get_divider_position());
_context->save_state("classes-splitter", "scripting-shell", _classes_splitter.get_divider_position());
_context->save_state("snippet-splitter", "scripting-shell", _snippet_splitter.get_divider_position());
}
//--------------------------------------------------------------------------------------------------
/**
* Triggered when the shell window was closed by the user. We can use this event to store our state.
*/
void GRTShellWindow::shell_closed() {
save_state();
}
//--------------------------------------------------------------------------------------------------
/**
* Triggered when a tab is about to close. Don't allow shell and snippets to close and check if
* editors are dirty.
*/
bool GRTShellWindow::on_tab_closing(int index) {
if (index == 0 || index == 1)
return false;
GRTCodeEditor *editor = _editors[index - EDITOR_TAB_OFFSET];
if (editor->can_close()) {
close_editor(editor);
return true;
}
return false;
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::on_tab_changed() {
GRTCodeEditor *editor = get_active_editor();
mforms::MenuItem *_run = _menu.find_item("run");
if (editor) {
bool exec_enabled = (editor->get_language() == "python");
_save_button->set_enabled(true);
_save_as_button->set_enabled(true);
_run_button->set_enabled(exec_enabled);
if (_run)
_run->set_enabled(exec_enabled);
_step_button->set_enabled(exec_enabled);
_clear_script_output_button->set_enabled(true);
if (_close_script_tab_button)
_close_script_tab_button->set_enabled(true);
_show_find_button->set_enabled(true);
_right_splitter.set_expanded(false, true);
} else {
_save_button->set_enabled(false);
_save_as_button->set_enabled(false);
_run_button->set_enabled(false);
if (_run)
_run->set_enabled(false);
_step_button->set_enabled(false);
_clear_script_output_button->set_enabled(false);
if (_close_script_tab_button)
_close_script_tab_button->set_enabled(false);
_show_find_button->set_enabled(false);
_right_splitter.set_expanded(false, false);
}
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::activate_output_tab() {
_lower_tab.set_active_tab(0);
}
//--------------------------------------------------------------------------------------------------
/**
* Returns the editor which is currently editing the given file.
*/
GRTCodeEditor *GRTShellWindow::get_editor_for(const std::string &path, bool select_tab) {
#ifdef _MSC_VER
// We probably would need g_utf8_normalize too if we want it really good, but since this is
// supposed to be a temporary solution...
gchar *path1 = g_utf8_strdown(path.c_str(), -1);
for (std::vector<GRTCodeEditor *>::iterator editor = _editors.begin(); editor != _editors.end(); editor++) {
gchar *path2 = g_utf8_strdown((*editor)->get_path().c_str(), -1);
if (g_utf8_collate(path1, path2) == 0) {
if (select_tab)
_main_tab.set_active_tab((int)(editor - _editors.begin() + EDITOR_TAB_OFFSET));
g_free(path2);
return *editor;
}
g_free(path2);
}
g_free(path1);
#else
for (std::vector<GRTCodeEditor *>::iterator editor = _editors.begin(); editor != _editors.end(); editor++) {
if ((*editor)->get_path() == path) {
if (select_tab)
_main_tab.set_active_tab(int(editor - _editors.begin() + EDITOR_TAB_OFFSET));
return *editor;
}
}
#endif
return NULL;
}
GRTCodeEditor *GRTShellWindow::get_active_editor() {
int index = _main_tab.get_active_tab() - EDITOR_TAB_OFFSET;
if (index >= 0 && index < (int)_editors.size())
return _editors[index];
return 0;
}
//--------------------------------------------------------------------------------------------------
template <class C>
struct CompareNamedObject {
bool operator()(C *a, C *b) {
return a->name() < b->name();
}
};
void GRTShellWindow::refresh_modules_tree() {
IconManager *im = IconManager::get_instance();
std::string mod_icon = im->get_icon_path("grt_module.png");
;
std::string fun_icon = im->get_icon_path("grt_function.png");
;
_modules_tree.clear();
std::vector<grt::Module *> modules(grt::GRT::get()->get_modules());
std::sort(modules.begin(), modules.end(), CompareNamedObject<grt::Module>());
for (std::vector<grt::Module *>::const_iterator m = modules.begin(); m != modules.end(); ++m) {
mforms::TreeNodeRef mod_node = _modules_tree.add_node();
const std::vector<grt::Module::Function> functions((*m)->get_functions());
if ((*m)->description().empty())
mod_node->set_string(0, (*m)->name());
else
mod_node->set_string(0, (*m)->name() + " *");
mod_node->set_icon_path(0, mod_icon);
mod_node->set_tag("m");
for (std::vector<grt::Module::Function>::const_iterator f = functions.begin(); f != functions.end(); ++f) {
mforms::TreeNodeRef fun_node = mod_node->add_child();
fun_node->set_string(0, f->name);
fun_node->set_icon_path(0, fun_icon);
}
}
}
std::string GRTShellWindow::get_module_node_description(const mforms::TreeNodeRef &node) {
std::string value;
if (node->get_parent() == _modules_tree.root_node()) {
std::string name = node->get_string(0);
if (!name.empty() && name[name.size() - 1] == '*')
name = name.substr(0, name.size() - 2);
grt::Module *module = grt::GRT::get()->get_module(name);
if (module) {
std::string descr;
descr.append("Module: " + module->name() + "\n");
descr.append("Path: " + module->path() + "\n");
descr.append("Language: " + module->get_loader()->get_loader_name() + "\n");
descr.append("Extends: " + module->extends() + "\n");
descr.append("Implements: ");
for (std::vector<std::string>::const_iterator iter = module->get_interfaces().begin();
iter != module->get_interfaces().end(); ++iter) {
descr.append(*iter).append("\n");
}
descr.append("\n\n").append(module->description());
value = descr;
}
} else {
std::string name = node->get_parent()->get_string(0);
if (!name.empty() && name[name.size() - 1] == '*')
name = name.substr(0, name.size() - 2);
grt::Module *module = grt::GRT::get()->get_module(name);
if (module) {
const grt::Module::Function *func = module->get_function(node->get_string(0));
value = base::strfmt("Function:\n %s %s(%s)\n\n", fmt_type_spec(func->ret_type).c_str(), func->name.c_str(),
fmt_arg_spec_list(func->arg_types).c_str());
value.append("Arguments:\n");
std::string args;
for (grt::ArgSpecList::const_iterator arg = func->arg_types.begin(); arg != func->arg_types.end(); ++arg) {
if (!arg->name.empty())
args.append(" - ").append(arg->name).append(": ").append(arg->doc).append("\n");
else
args.append(" - ").append(fmt_type_spec(arg->type)).append("\n");
}
value.append(args);
value.append("\n").append(func->description);
}
}
return value;
}
//--------------------------------------------------------------------------------------------------
void GRTShellWindow::refresh_classes_tree() {
_classes_tree.clear();
switch (_classes_sorting.get_selected_index()) {
case 0:
refresh_classes_tree_by_name();
break;
case 1:
refresh_classes_tree_by_hierarchy();
break;
case 2:
refresh_classes_tree_by_package();
break;
}
}
static std::string struct_member_icon(grt::TypeSpec type) {
IconManager *im = IconManager::get_instance();
switch (type.base.type) {
case grt::ListType:
return im->get_icon_path("grt_list.png");
case grt::DictType:
return im->get_icon_path("grt_dict.png");
case grt::ObjectType:
return im->get_icon_path("grt_object.png");
default:
return im->get_icon_path("grt_simple_type.png");
}
return im->get_icon_path("grt_simple_type.png");
}
struct SortableClassMember {
std::string name;
std::string caption;
std::string type;
std::string icon;
std::string tag;
bool operator<(const SortableClassMember &o) const {
return name < o.name;
}
};
static void scan_class_members(mforms::TreeNodeRef node, grt::MetaClass *gstruct) {
IconManager *im = IconManager::get_instance();
std::vector<SortableClassMember> members;
for (grt::MetaClass::MethodList::const_iterator mem = gstruct->get_methods_partial().begin();
mem != gstruct->get_methods_partial().end(); ++mem) {
SortableClassMember m;
m.name = mem->second.name;
m.caption = gstruct->get_member_attribute(mem->second.name, "caption");
m.type = mem->second.ret_type.base.type == grt::AnyType ? "void" : grt::fmt_type_spec(mem->second.ret_type);
m.icon = im->get_icon_path("grt_function.png");
std::string value;
value = base::strfmt("Function:\n %s %s(%s)\n", m.type.c_str(), mem->second.name.c_str(),
fmt_arg_spec_list(mem->second.arg_types).c_str());
value.append(gstruct->get_member_attribute(mem->second.name, "caption")).append("\n");
value.append("Arguments:\n");
std::string args;
for (grt::ArgSpecList::const_iterator arg = mem->second.arg_types.begin(); arg != mem->second.arg_types.end();
++arg) {
if (!arg->name.empty())
args.append(" - ").append(arg->name).append(": ").append(arg->doc).append("\n");
else
args.append(" - ").append(fmt_type_spec(arg->type)).append("\n");
}
value.append(args);
value.append("\n").append(gstruct->get_member_attribute(mem->second.name, "desc"));
m.tag = value;
members.push_back(m);
}
for (grt::MetaClass::MemberList::const_iterator mem = gstruct->get_members_partial().begin();
mem != gstruct->get_members_partial().end(); ++mem) {
SortableClassMember m;
m.name = mem->second.name;
m.caption = gstruct->get_member_attribute(mem->second.name, "caption");
m.type = grt::fmt_type_spec(mem->second.type);
m.icon = struct_member_icon(mem->second.type);
m.tag = base::strfmt("Member:\n %s %s\n%s\n\n", m.type.c_str(), m.name.c_str(),
gstruct->get_member_attribute(mem->second.name, "desc").c_str());
members.push_back(m);
}
std::sort(members.begin(), members.end());
for (std::vector<SortableClassMember>::const_iterator i = members.begin(); i != members.end(); ++i) {
mforms::TreeNodeRef mnode = node->add_child();
mnode->set_string(0, i->name);
mnode->set_string(1, i->type);
mnode->set_string(2, i->caption);
mnode->set_icon_path(0, i->icon);
mnode->set_tag(i->tag);
}
}
void GRTShellWindow::refresh_classes_tree_by_name() {
std::list<grt::MetaClass *> metaclasses(grt::GRT::get()->get_metaclasses());
std::string struct_icon = IconManager::get_instance()->get_icon_path("grt_struct.png");
metaclasses.sort(CompareNamedObject<grt::MetaClass>());
for (std::list<grt::MetaClass *>::const_iterator iter = metaclasses.begin(); iter != metaclasses.end(); ++iter) {
grt::MetaClass *gstruct = *iter;
mforms::TreeNodeRef node;
node = _classes_tree.add_node();
node->set_tag(base::strfmt("Class:\n %s %s\n\n%s", gstruct->name().c_str(),
gstruct->parent() ? base::strfmt("(%s)", gstruct->parent()->name().c_str()).c_str() : "",
(*iter)->get_attribute("desc").c_str()));
node->set_string(0, gstruct->name());
node->set_string(2, gstruct->get_attribute("caption"));
node->set_icon_path(0, struct_icon);
scan_class_members(node, gstruct);
}
}
static void scan_subclasses(const std::list<grt::MetaClass *> &metaclasses, mforms::TreeNodeRef parnode,
grt::MetaClass *parent) {
std::string struct_icon = IconManager::get_instance()->get_icon_path("grt_struct.png");
for (std::list<grt::MetaClass *>::const_iterator iter = metaclasses.begin(); iter != metaclasses.end(); ++iter) {
mforms::TreeNodeRef node;
if ((*iter)->parent() != parent)
continue;
node = parnode->add_child();
node->set_tag((*iter)->get_attribute("desc"));
node->set_string(0, (*iter)->name());
node->set_string(2, (*iter)->get_attribute("caption"));
node->set_icon_path(0, struct_icon);
scan_class_members(node, *iter);
// add child structs
scan_subclasses(metaclasses, node, *iter);
}
}
void GRTShellWindow::refresh_classes_tree_by_hierarchy() {
std::list<grt::MetaClass *> metaclasses(grt::GRT::get()->get_metaclasses());
metaclasses.sort(CompareNamedObject<grt::MetaClass>());
scan_subclasses(metaclasses, _classes_tree.root_node(),
grt::GRT::get()->get_metaclass(grt::internal::Object::static_class_name()));
}
void GRTShellWindow::refresh_classes_tree_by_package() {
IconManager *im = IconManager::get_instance();
std::map<std::string, mforms::TreeNodeRef> package_nodes;
std::list<grt::MetaClass *> metaclasses(grt::GRT::get()->get_metaclasses());
metaclasses.sort(CompareNamedObject<grt::MetaClass>());
std::string struct_icon = im->get_icon_path("grt_struct.png");
for (std::list<grt::MetaClass *>::const_iterator iter = metaclasses.begin(); iter != metaclasses.end(); ++iter) {
std::string pkgname = (*iter)->name();
std::string::size_type p = pkgname.rfind('.');
if (p != std::string::npos)
pkgname = pkgname.substr(0, p);
else
pkgname = "";
mforms::TreeNodeRef pkgnode = package_nodes[pkgname];
if (!pkgnode) {
pkgnode = _classes_tree.add_node();
pkgnode->set_string(0, pkgname);
pkgnode->set_icon_path(0, "folder");
package_nodes[pkgname] = pkgnode;
}
mforms::TreeNodeRef node = pkgnode->add_child();
node->set_tag((*iter)->get_attribute("desc"));
node->set_string(0, (*iter)->name());
node->set_string(2, (*iter)->get_attribute("caption"));
node->set_icon_path(0, struct_icon);
scan_class_members(node, *iter);
}
}
std::string GRTShellWindow::get_class_node_description(const mforms::TreeNodeRef &selected) {
return selected->get_tag();
}
//--------------------------------------------------------------------------------------------------
static bool find_expandable_member(const grt::MetaClass::Member *member, bool *expandable) {
if (!grt::is_simple_type(member->type.base.type))
*expandable = true;
return !*expandable;
}
static void globals_get_node_info(const grt::ValueRef &value, std::string &type, std::string &icon, bool &expandable) {
IconManager *im = IconManager::get_instance();
type = grt::type_to_str(value.type());
expandable = false;
switch (value.type()) {
case grt::ListType: {
grt::BaseListRef l(grt::BaseListRef::cast_from(value));
std::string struct_name;
if (l.content_type() != grt::AnyType) {
type += " [";
if (l.content_type() == grt::ObjectType) {
if (l.content_class_name().empty()) {
type += "object";
struct_name = "";
} else {
type += "object:" + l.content_class_name();
struct_name = l.content_class_name();
}
} else {
if (l.content_type() == grt::AnyType)
type += "*";
else
type += grt::type_to_str(l.content_type());
}
type += "]";
}
if (!struct_name.empty())
icon = im->get_icon_path(im->get_icon_id(grt::GRT::get()->get_metaclass(struct_name), Icon16, "many_$"));
if (icon.empty())
icon = im->get_icon_path("grt_list.png");
for (size_t c = l.count(), i = 0; i < c; i++) {
if (!grt::is_simple_type(l[i].type())) {
expandable = true;
break;
}
}
} break;
case grt::DictType: {
grt::DictRef d(grt::DictRef::cast_from(value));
if (d.content_type() != grt::AnyType) {
type += " [";
if (d.content_type() == grt::ObjectType) {
type += "object:" + d.content_class_name();
icon = im->get_icon_path(im->get_icon_id(grt::GRT::get()->get_metaclass(d.content_class_name()), Icon16));
} else
type += grt::type_to_str(d.content_type());
type += "]";
}
if (icon.empty())
icon = im->get_icon_path("grt_dict.png");
for (grt::DictRef::const_iterator iter = d.begin(); iter != d.end(); ++iter) {
if (!grt::is_simple_type(iter->second.type())) {
expandable = true;
break;
}
}
} break;
case grt::ObjectType: {
grt::ObjectRef o(grt::ObjectRef::cast_from(value));
type += ":" + std::string(o.class_name());
icon = im->get_icon_path(im->get_icon_id(o, Icon16));
if (icon.empty())
icon = im->get_icon_path("grt_object.png");
grt::MetaClass *meta = o.get_metaclass();
meta->foreach_member(std::bind(&find_expandable_member, std::placeholders::_1, &expandable));
} break;
default:
icon = im->get_icon_path("grt_simple_type.png");
break;
}
}
static void globals_rescan_list(mforms::TreeNodeRef &node, const std::string &path, const grt::BaseListRef &value) {
char buffer[30];
node->remove_children();
for (size_t i = 0; i < value.count(); i++) {
grt::ValueRef v = value.get(i);
std::string label;
sprintf(buffer, "%lu", (long unsigned)i);
if (v.is_valid() && !grt::is_simple_type(v.type())) {
mforms::TreeNodeRef child = node->add_child();
std::string type;
std::string icon;
bool expandable;
globals_get_node_info(v, type, icon, expandable);
child->set_tag(buffer);
child->set_string(0, label.empty() ? buffer : label);
child->set_string(1, type);
child->set_icon_path(0, icon);
if (v.type() == grt::ObjectType && label.empty()) {
grt::ObjectRef o(grt::ObjectRef::cast_from(v));
std::string s = std::string("[") + buffer + "]";
try {
if (o.has_member("name") && o.get_string_member("name") != "")
s.append(" ").append(o.get_string_member("name"));
} catch (grt::type_error &) {
s.append(" ").append("?");
}
child->set_string(0, s);
}
if (expandable)
child->add_child();
}
}
}
static void globals_rescan_dict(mforms::TreeNodeRef &node, const std::string &path, const grt::DictRef &value) {
node->remove_children();
for (grt::DictRef::const_iterator item = value.begin(); item != value.end(); ++item) {
std::string key(item->first);
grt::ValueRef v(item->second);
std::string label;
if (v.is_valid() && !grt::is_simple_type(v.type())) {
mforms::TreeNodeRef child = node->add_child();
std::string type;
std::string icon;
bool expandable;
globals_get_node_info(v, type, icon, expandable);
child->set_tag(key);
child->set_string(0, label.empty() ? key : label);
child->set_string(1, type);
child->set_icon_path(0, icon);
if (v.type() == grt::ObjectType && label.empty()) {
grt::ObjectRef o(grt::ObjectRef::cast_from(v));
if (o.has_member("name") && o.get_string_member("name") != "")
child->set_string(0, o.get_string_member("name"));
else
child->set_string(0, "[" + child->get_tag() + "]");
}
if (expandable)
child->add_child();
}
}
}
static bool globals_rescan_member(const grt::MetaClass::Member *mem, mforms::TreeNodeRef &node,
const grt::ObjectRef &value) {
std::string name(mem->name);
grt::ValueRef v(value.get_member(name));
std::string label;
if (v.is_valid() && !is_simple_type(v.type())) {
mforms::TreeNodeRef child = node->add_child();
std::string type;
std::string icon;
bool expandable;
globals_get_node_info(v, type, icon, expandable);
child->set_tag(name);
child->set_string(0, label.empty() ? name : label);
child->set_string(1, type);
child->set_icon_path(0, icon);
if (expandable)
child->add_child();
}
return true;
}
static void globals_rescan_object(mforms::TreeNodeRef &node, const std::string &path, const grt::ObjectRef &value) {
grt::MetaClass *meta = value.get_metaclass();
node->remove_children();
meta->foreach_member(std::bind(&globals_rescan_member, std::placeholders::_1, node, value));
}
static void globals_rescan_value(mforms::TreeNodeRef &node, const std::string &path, const grt::ValueRef &value) {
switch (value.type()) {
case grt::ListType:
globals_rescan_list(node, path, grt::BaseListRef::cast_from(value));
break;
case grt::DictType:
globals_rescan_dict(node, path, grt::DictRef::cast_from(value));
break;
case grt::ObjectType:
globals_rescan_object(node, path, grt::ObjectRef::cast_from(value));
break;
default:
break;
}
}
void GRTShellWindow::refresh_globals_tree() {
std::string path = _global_combo.get_string_value();
if (path.empty())
path = "/";
try {
grt::ValueRef value = grt::GRT::get()->get(path);
if (value.is_valid()) {
_global_tree.clear();
mforms::TreeNodeRef root = _global_tree.add_node();
std::string type;
std::string icon;
bool expandable;
globals_get_node_info(value, type, icon, expandable);
root->set_string(0, path);
root->set_string(1, type);
root->set_icon_path(0, icon);
root->set_tag(path);
globals_rescan_value(root, path, value);
// root->expand();
}
} catch (const grt::bad_item &) {
// ignore
}
}
void GRTShellWindow::globals_expand_toggle(const mforms::TreeNodeRef &node, bool expanded) {
if (expanded) {
grt::ValueRef value = get_global_at_node(node);
if (value.is_valid()) {
mforms::TreeNodeRef mnode = node;
globals_rescan_value(mnode, mnode->get_tag(), value);
}
}
}
grt::ValueRef GRTShellWindow::get_global_at_node(const mforms::TreeNodeRef &node) {
return grt::GRT::get()->get(get_global_path_at_node(node));
}
std::string GRTShellWindow::get_global_path_at_node(const mforms::TreeNodeRef &node) {
std::string path;
mforms::TreeNodeRef parent = node;
while (parent != _global_tree.root_node()) {
if (parent->get_tag() == "/")
path = "/" + path;
else {
if (path.empty())
path = parent->get_tag();
else
path = parent->get_tag() + "/" + path;
}
parent = parent->get_parent();
}
return path;
}
void GRTShellWindow::refresh_global_list() {
_global_list.clear();
if (_inspector) {
for (size_t c = _inspector->count(), i = 0; i < c; i++) {
mforms::TreeNodeRef node = _global_list.add_node();
std::string value;
_inspector->get_field(i, 0, value);
node->set_string(0, value);
_inspector->get_field(i, 1, value);
node->set_string(1, value);
value = IconManager::get_instance()->get_icon_path(_inspector->get_field_icon(i, 0, Icon16));
node->set_icon_path(0, value);
}
}
}
void GRTShellWindow::refresh_notifs_list() {
const std::map<std::string, base::NotificationCenter::NotificationHelp> &info =
base::NotificationCenter::get()->get_registered_notifications();
std::map<std::string, std::vector<std::string> > contexts;
_notifs_tree.clear();
for (std::map<std::string, base::NotificationCenter::NotificationHelp>::const_iterator i = info.begin();
i != info.end(); ++i)
contexts[i->second.context].push_back(i->first);
for (std::map<std::string, std::vector<std::string> >::const_iterator iter = contexts.begin(); iter != contexts.end();
++iter) {
mforms::TreeNodeRef node = _notifs_tree.add_node();
node->set_string(0, iter->first);
node->set_icon_path(0, "folder");
for (std::vector<std::string>::const_iterator n = iter->second.begin(); n != iter->second.end(); ++n) {
mforms::TreeNodeRef nnode = node->add_child();
nnode->set_string(0, *n);
}
node->expand();
}
}
void GRTShellWindow::cut() {
GRTCodeEditor *editor = get_active_editor();
if (editor)
editor->get_editor()->cut();
else if (_shell_entry.has_focus())
_shell_entry.cut();
}
void GRTShellWindow::copy() {
GRTCodeEditor *editor = get_active_editor();
if (editor)
editor->get_editor()->copy();
else if (_shell_entry.has_focus())
_shell_entry.copy();
}
void GRTShellWindow::paste() {
GRTCodeEditor *editor = get_active_editor();
if (editor)
editor->get_editor()->paste();
else if (_shell_entry.has_focus())
_shell_entry.paste();
}
void GRTShellWindow::select_all() {
GRTCodeEditor *editor = get_active_editor();
if (editor)
editor->get_editor()->select_all();
else if (_shell_entry.has_focus())
_shell_entry.select(base::Range(0, (size_t)-1));
}