Permalink
Fetching contributors…
Cannot retrieve contributors at this time
19953 lines (18857 sloc) 889 KB
/*
ocelotgui -- Ocelot GUI Front End for MySQL or MariaDB
Version: 1.0.6
Last modified: August 7 2018
*/
/*
Copyright (c) 2014-2017 by Ocelot Computer Services Inc. 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 as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
The class named CodeEditor inside the ocelotgui program is taken and modified from
http://qt-project.org/doc/qt-4.8/widgets-codeeditor.html
and therefore CodeEditor's maker's copyright and BSD license provisions
are reproduced below. These provisions apply only for the
part of the CodeEditor class which is included by #include "codeeditor.h".
The program as a whole is copyrighted by Ocelot and
licensed under GPL version 2 as stated above.
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** $QT_BEGIN_LICENSE:BSD$
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
*/
#ifdef DEBUGGER
/*
The routine named debug_mdbug_install inside this program is taken and modified from
https://launchpad.net/mdbug, specifically http://bazaar.launchpad.net/~hp-mdbug-team/mdbug/trunk/view/head:/install.sql,
and therefore MDBug's maker's copyright and GPL version 2 license provisions
are reproduced below. These provisions apply only for the
part of the debug_mdbug_install routine which is marked within the program.
The program as a whole is copyrighted by Ocelot and
licensed under GPL version 2 as stated above.
This file is part of MDBug.
(c) Copyright 2012 Hewlett-Packard Development Company, L.P.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, see
<http://www.gnu.org/licenses>.
Linking MDBug statically or dynamically with other modules is making a combined work based on MDBug. Thus, the terms and
conditions of the GNU General Public License cover the whole combination.
In addition, as a special exception, the copyright holders of MDBug give you permission to combine MDBug with free
software programs or libraries that are released under the GNU LGPL and with code included in the standard release
of Eclipse under the Eclipse Public License version 1.0 (or modified versions of such code, with unchanged license).
You may copy and distribute such a system following the terms of the GNU GPL for MDBug and the licenses of the other
code concerned, provided that you include the source code of that other code when and as the GNU GPL requires
distribution of source code.
Note that people who make modified versions of MDBug are not obligated to grant this special exception for their
modified versions; it is their choice whether to do so. The GNU General Public License gives permission to release a
modified version without this exception; this exception also makes it possible to release a modified version which
carries forward this exception.
*/
#endif
/*
On Windows, ocelotgui is statically linked with libraries
MariaDB Connector C and Qt. See COPYING.thirdparty.
*/
/*
General comments
These are comments about the code.
The user manual and blog comments are elsewhere; look at README.md or manual.htm
or README.txt or ocelot.ca or ocelot.ca/blog or the repository that
this program was downloaded from, probably github.com/ocelot-inc/ocelotgui.
The code files are:
ocelotgui.pro small. used by Qt qmake
ocelotgui.h headers but also quite a lot of executable
ocelotgui.cpp executable
install_sql.cpp for creating debugger routines
ocelotgui.ui small. used to make ui_ocelotgui.h
codeeditor.h small. separated so license applicability is clear
hparse.h the recognizer
ostrings.h strings that contain English translatable text
There are three main widgets, which generally appear top-to-bottom on
the screen: history_edit_widget = an uncomplicated text edit which gets
retired statements and diagnostic messages; statement_edit_widget =
a class derived from text edit ("CodeEditor") which has code for a
prompt on the left and for syntax highlighting; result_grid_table_widget =
a class derived from widget ("ResultGrid") which has code for tabular
display of a result set and manipulation thereof. The result grid is
not based on Qt's high-level tools like QTableView, it is constructed
from basic building blocks (scroll bars and repeatedly-occurring
QTextEdits within QFrames). There is no use of Qt's "MySQL driver".
The code includes a tokenizer and recognizer for MySQL syntax.
The coding style is generally according to MySQL coding guidelines
http://dev.mysql.com/doc/internals/en/coding-guidelines.html
but lines may be long, and sometimes spaces occur at ends of lines,
and "if (x) y" may be preferred to "if (x) <newline> y".
The code was written by Ocelot Computer Services Inc. employees, except
for about 50 lines in the codeeditor.h CodeEditor section (Digia copyright / BSD license),
and except for most of the lines in install_sql.cpp (HP copyright / GPL license).
Other contributors will be acknowledged here and/or in a "Help" display.
The code #includes header files from MySQL/Oracle and from Qt/Digia,
and relies on the MySQL client library and the Qt core, gui,
and widgets libraries. Builds have been successful with several
Linux distros and gcc 4.6/4.7. Build instructions are in the user manual or
in a readme file
There are many comments. Searching for the word "Todo:" in the comments
will give some idea of what's especially weak and what's planned.
UTF8 Conversion
Everything which will be passed to MySQL will first be converted to UTF8.
Todo: Ensure that sending/receiving character set is UTF8, regardless of data language.
Currently we do say ocelot_set_charset_name= "utf8"; which is passed to mysql_options()
but the user can explicitly override that.
Todo: When receiving, only convert characters, not binary or numeric.
Todo: Handle Qt codecs for other character sets.
Todo: Figure out how to handle a literal preceded by an _introducer.
A typical set of instructions will look like this and have the comment
"See comment "UTF8 Conversion".
int tmp_len= QStr.toUtf8().size()); ... See how big UTF8 will be
char *tmp= new char[tmp_len + 1]; ... Allocate that amount
memcpy(tmp, QStr.toUtf8().constData()), tmp_len + 1); ... Copy including trail '\0' byte
... And later, say "delete []tmp" or hope for garbage collection
Todo: when I started, I wasn't aware that I could say
QString::fromUtf8 to copy a C string to a QString.
So there are several places where I copy to another temporary
C string and then assign. Such code should be replaced.
Todo: Allow right-click for changing individual widgets.
See end of program for comments re valgrind.
The usual ways to build are described in README.txt (actually README.md).
An unusual way would be with Qt 4.8 source-code libraries supplied by Digia:
Download 4.8 libraries via http://qt-project.org/downloads
This is a source download; you'll need to do ./configure and make.
Follow instructions in http://qt-project.org/doc/qt-4.8/install-x11.html
After make and make install you'll have /usr/local/Trolltech/Qt-4.8.6
At this point I got totally stuck with Qt Creator -- it refused to try to build with Qt 4.8.
And yes, I changed the kit, I changed the paths, but it was all a waste of a lot of time.
Well, if anyone else succeeds, let me know.
But I "succeeded" by exiting Qt Creator, creating a new make file, and building from the shell.
(I copy and then remove ui_ocelotgui.h, which may or may not have some importance that I missed.)
cd ~/ocelotgui
mv ui_ocelotgui.h ui_ocelotgui.h.bak
/usr/local/Trolltech/Qt-4.8.6/bin/qmake ocelotgui.pro
make
./ocelotgui
That ruins what Qt Creator expects when I get back to Qt Creator, so I get back to 5.1 with:
cd ~/ocelotgui
/home/pgulutzan/Qt/5.1.1/gcc_64/bin/qmake ./ocelotgui.pro
mv ui_ocelotgui.h ui_ocelotgui.h.bak
rm ui_ocelotgui.h
make (ignore failure)
re-enter Qt Creator. Build | Clean all. Build all. Run.
*/
#define MYSQL_MAIN_CONNECTION 0
#define MYSQL_DEBUGGER_CONNECTION 1
#define MYSQL_KILL_CONNECTION 2
#define MYSQL_REMOTE_CONNECTION 3
#define MYSQL_MAX_CONNECTIONS 4
#include "ostrings.h"
#include "ocelotgui.h"
#include "ui_ocelotgui.h"
/* Whenever you see STRING_LENGTH_512, think: here's a fixed arbitrary allocation, which should be be fixed up. */
#define STRING_LENGTH_512 512
/* Connect arguments and options */
static char* ocelot_host_as_utf8= 0; /* --host=s */
static char* ocelot_database_as_utf8= 0; /* --database=s */
static char* ocelot_user_as_utf8= 0; /* --user=s */
static char* ocelot_password_as_utf8= 0; /* --password[=s] */
static unsigned short ocelot_port= MYSQL_PORT; /* --port=n */
static char* ocelot_unix_socket_as_utf8= 0; /* --socket=s */
static unsigned int ocelot_protocol_as_int= 0; /* --protocol=s for MYSQL_OPT_PROTOCOL */
static char* ocelot_init_command_as_utf8= 0; /* --init_command=s for MYSQL_INIT_COMMAND */
/* Connect arguments below this point are minor and many are unsupported. */
static unsigned short ocelot_abort_source_on_error= 0; /* --abort_source_on_error (MariaDB) */
static unsigned short ocelot_auto_rehash= 1; /* --auto_rehash */
static unsigned short ocelot_auto_vertical_output= 0; /* --auto_vertical_output */
static unsigned short ocelot_batch= 0; /* --batch */
static unsigned short ocelot_binary_mode= 0; /* --binary_mode */
/* QString ocelot_bind_address */ /* --bind_address=s */
static char* ocelot_set_charset_dir_as_utf8= 0; /* --character_sets_dir=s for MYSQL_SET_CHARSET_DIR */
static unsigned short ocelot_result_grid_column_names= 1;/* --column_names */
static unsigned short ocelot_column_type_info= 0; /* --column_type_info */
static unsigned short ocelot_comments= 0; /* --comments */
static unsigned short ocelot_opt_compress= 0; /* --compress for MYSQL_OPT_COMPRESS */
static unsigned long int ocelot_opt_connect_timeout= 0; /* --connect_timeout = n for MYSQL_OPT_CONNECT_TIMEOUT */
/* QString ocelot_debug */ /* --debug[=s] */
static unsigned short ocelot_debug_check= 0; /* --debug_check */
static unsigned short ocelot_debug_info= 0; /* --debug_info */
static char* ocelot_default_auth_as_utf8= 0; /* --default_auth=s for MYSQL_DEFAULT_AUTH */
static char* ocelot_set_charset_name_as_utf8= 0; /* --default_character_set=s for MYSQL_SET_CHARSET_NAME */
/* exists as QString */ /* --defaults_extra_file=s */
/* exists as QString */ /* --defaults_file=s */
/* exists as QString */ /* --defaults_group_suffix=s */
/* exists as QString */ /* --delimiter=s */
static unsigned short ocelot_enable_cleartext_plugin= 0; /* --enable_cleartext_plugin for MYSQL_ENABLE_CLEARTEXT_PLUGIN */
/* QString ocelot_execute */ /* --execute=s */
static unsigned short ocelot_force= 0; /* --force */
static unsigned short ocelot_help= 0; /* --help */
static bool ocelot_history_hist_file_is_open= false;
static bool ocelot_history_hist_file_is_copied= false;
/* exists as QString */ /* --histfile=s */
/* exists as QString */ /* --histignore=s */
static unsigned short ocelot_html= 0; /* --html */
static unsigned short ocelot_ignore_spaces= 0; /* --ignore_spaces */
/* QString ocelot_ld_run_path */ /* --ld_run_path=s */
static unsigned short ocelot_line_numbers= 0; /* --line_numbers */
static unsigned short ocelot_opt_local_infile= 0; /* --local_infile[=n] for MYSQL_OPT_LOCAL_INFILE */
/* QString ocelot_login_path */ /* --login_path=s */
static int ocelot_log_level= 100; /* --ocelot_log_level */
static unsigned long int ocelot_max_allowed_packet= 16777216; /* --max_allowed_packet=n */
static unsigned long int ocelot_max_join_size= 1000000; /* --max_join_size = n */
static unsigned short ocelot_named_commands= 0; /* --named_commands */
static unsigned long int ocelot_net_buffer_length= 16384; /* --net_buffer_length=n */
static unsigned short ocelot_no_beep= 0; /* --no_beep */
static unsigned short ocelot_no_defaults= 0; /* --no_defaults */
static unsigned short ocelot_one_database= 0; /* --one-database */
/* QString ocelot_pager */ /* --pager[=s] */
static unsigned short ocelot_pipe= 0; /* --pipe */
static char* ocelot_plugin_dir_as_utf8= 0; /* --plugin_dir=s for MYSQL_PLUGIN_DIR */
static unsigned short ocelot_print_defaults= 0; /* --print_defaults */
/* QString ocelot_prompt */ /* --prompt=s */
static bool ocelot_prompt_is_default= true;
static unsigned short ocelot_quick= 0; /* --quick */
static unsigned short ocelot_raw= 0; /* --raw */
static unsigned int ocelot_opt_reconnect= 0; /* --reconnect for MYSQL_OPT_RECONNECT */ /* --reconnect */
static unsigned short ocelot_safe_updates= 0; /* --safe-updates or --i-am-a-dummy */
static unsigned short ocelot_secure_auth= 1; /* --secure_auth for MYSQL_SECURE_AUTH (default=true if version >= 5.6.5) */
static unsigned long int ocelot_select_limit= 0; /* --select_limit = n */
static char* ocelot_server_public_key_as_utf8= 0; /* --server_public_key=s for MYSQL_SERVER_PUBLIC_KEY */
static char* ocelot_shared_memory_base_name_as_utf8= 0; /* --shared_memory_base_name=s for MYSQL_SHARED_MEMORY_BASE_NAME */
static unsigned short ocelot_history_includes_warnings= 0; /* --show_warnings include warning(s) returned from statement? default = no. */
static unsigned short ocelot_sigint_ignore= 0; /* --sigint_ignore */
static unsigned short ocelot_silent= 0; /* --silent */
static char* ocelot_opt_ssl_as_utf8= 0; /* --ssl for CONNECT */
static char* ocelot_opt_ssl_ca_as_utf8= 0; /* --ssl-ca for MYSQL_OPT_SSL_CA */
static char* ocelot_opt_ssl_capath_as_utf8= 0; /* --ssl-capath for MYSQL_OPT_SSL_CAPATH. */
static char* ocelot_opt_ssl_cert_as_utf8= 0; /* --ssl-cert for MYSQL_OPT_SSL_CERT */
static char* ocelot_opt_ssl_cipher_as_utf8= 0; /* --ssl-cipher for MYSQL_OPT_SSL_CIPHER */
static char* ocelot_opt_ssl_crl_as_utf8= 0; /* --ssl-crl for MYSQL_OPT_SSL_CRL */
static char* ocelot_opt_ssl_crlpath_as_utf8= 0; /* --ssl-crlpath for MYSQL_OPT_SSL_CRLPATH */
static char* ocelot_opt_ssl_key_as_utf8= 0; /* --ssl-key for MYSQL_OPT_SSL_KEY */
static char* ocelot_opt_ssl_mode_as_utf8= 0; /* --ssl-mode for MYSQL_OPT_SSL_MODE */
static unsigned short int ocelot_opt_ssl_verify_server_cert= 0; /* --ssl-verify-server-cert for MYSQL_OPT_SSL_VERIFY_SERVER_CERT. --ssl-verify-server-cert (5.7) */
static unsigned short ocelot_syslog= 0; /* --syslog (5.7) */
static unsigned short ocelot_table= 0; /* --table */
static bool ocelot_history_tee_file_is_open= false; /* --tee for tee ... arg=history_tee_file_name*/
static unsigned short ocelot_unbuffered= 0; /* --unbuffered */
static unsigned short ocelot_verbose= 0; /* --verbose */
static unsigned short ocelot_version= 0; /* --version */
static unsigned short ocelot_result_grid_vertical= 0; /* --vertical for vertical */
static unsigned short ocelot_wait= 0; /* --wait ... actually this does nothing */
static unsigned short ocelot_xml= 0; /* --xml */
/*
For MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS + --connect-expired-password.
mysql client has this off by default, but ocelotgui has it on by default
so to turn it off say ocelotgui --skip_connect_expired_password
*/
static unsigned short ocelot_opt_can_handle_expired_passwords= 1;
/* Some items we allow, which are not available in mysql client */
static char* ocelot_opt_bind_as_utf8= 0; /* for MYSQL_OPT_BIND */
static char* ocelot_opt_connect_attr_delete_as_utf8= 0; /* for MYSQL_OPT_CONNECT_ATTR_DELETE */
static unsigned short int ocelot_opt_connect_attr_reset= 0; /* for MYSQL_OPT_CONNECT_ATTR_RESET */
static char* ocelot_read_default_file_as_utf8= 0; /* for MYSQL_READ_DEFAULT_FILE */
static char* ocelot_read_default_group_as_utf8= 0;/* for MYSQL_READ_DEFAULT_GROUP */
static unsigned int ocelot_opt_read_timeout= 0; /* for MYSQL_OPT_READ_TIMEOUT */
static unsigned short int ocelot_report_data_truncation= 0; /* for MYSQL_REPORT_DATA_TRUNCATION */
static unsigned short int ocelot_opt_use_result= 0; /* for MYSQL_OPT_USE_RESULT */
/* It's easy to increase ocelot_grid_tabs so more multi results are seen but don't make it ridiculous. */
static unsigned short int ocelot_grid_tabs= 16;
static unsigned short int ocelot_grid_actual_tabs= 0; /* Todo: move this, it's not an option. */
static unsigned short int ocelot_client_side_functions= 1;
static char ocelot_shortcut_connect[80]= "default";
static char ocelot_shortcut_exit[80]= "default";
static char ocelot_shortcut_undo[80]= "default";
static char ocelot_shortcut_redo[80]= "default";
static char ocelot_shortcut_cut[80]= "default";
static char ocelot_shortcut_copy[80]= "default";
static char ocelot_shortcut_paste[80]= "default";
static char ocelot_shortcut_select_all[80]= "default";
static char ocelot_shortcut_history_markup_previous[80]= "default";
static char ocelot_shortcut_history_markup_next[80]= "default";
static char ocelot_shortcut_format[80]= "default";
static char ocelot_shortcut_zoomin[80]= "default";
static char ocelot_shortcut_zoomout[80]= "default";
static char ocelot_shortcut_autocomplete[80]= "default";
static char ocelot_shortcut_execute[80]= "default";
static char ocelot_shortcut_kill[80]= "default";
static char ocelot_shortcut_next_window[80]= "default";
static char ocelot_shortcut_previous_window[80]= "default";
static char ocelot_shortcut_breakpoint[80]= "default";
static char ocelot_shortcut_continue[80]= "default";
static char ocelot_shortcut_next[80]= "default";
static char ocelot_shortcut_step[80]= "default";
static char ocelot_shortcut_clear[80]= "default";
static char ocelot_shortcut_debug_exit[80]= "default";
static char ocelot_shortcut_information[80]= "default";
static char ocelot_shortcut_refresh_server_variables[80]= "default";
static char ocelot_shortcut_refresh_user_variables[80]= "default";
static char ocelot_shortcut_refresh_variables[80]= "default";
static char ocelot_shortcut_refresh_call_stack[80]= "default";
/* Some items we allow, but the reasons we allow them are lost in the mists of time */
/* I gather that one is supposed to read the charset file. I don't think we do. */
static unsigned short ocelot_opt_named_pipe; /* for MYSQL_OPT_NAMED_PIPE */
static unsigned int ocelot_opt_write_timeout= 0; /* for MYSQL_OPT_WRITE_TIMEOUT */
static bool ocelot_detach_history_widget= false;
static bool ocelot_detach_result_grid_widget= false;
static bool ocelot_detach_debug_widget= false;
static int is_libmysqlclient_loaded= 0;
static int is_libcrypto_loaded= 0;
static void *libmysqlclient_handle= NULL;
static void *libcrypto_handle= NULL;
#ifdef DBMS_TARANTOOL
static int is_libtarantool_loaded= 0;
//static int is_libtarantoolnet_loaded= 0;
static void *libtarantool_handle= 0;
//static void *libtarantoolnet_handle= 0;
/* Todo: these shouldn't be global */
tnt_reply *tarantool_tnt_for_new_result_set;
bool tarantool_start_transaction_seen= false;
#endif
static unsigned int rehash_result_column_count= 0;
static unsigned int rehash_result_row_count= 0;
static char *rehash_result_set_copy= 0; /* gets a copy of mysql_res contents, if necessary */
static char **rehash_result_set_copy_rows= 0; /* dynamic-sized list of result_set_copy row offsets, if necessary */
int options_and_connect(unsigned int connection_number, char *database_as_utf8);
/* This should correspond to the version number in the comment at the start of this program. */
static const char ocelotgui_version[]="1.0.6"; /* For --version. Make sure it's in manual too. */
/* Todo: initialize this as we do for hparse_dbms_mask */
static unsigned short int dbms_version_mask;
/* Global mysql definitions */
static MYSQL mysql[MYSQL_MAX_CONNECTIONS];
static int connected[MYSQL_MAX_CONNECTIONS]= {0, 0, 0, 0};
pthread_t debug_thread_id;
bool debug_thread_exists= false;
static int connections_is_connected[MYSQL_MAX_CONNECTIONS]; /* == 1 if is connected */
static int connections_dbms[MYSQL_MAX_CONNECTIONS]; /* == DBMS_MYSQL or other DBMS_... value */
#ifdef DBMS_TARANTOOL
/* Global Tarantool definitions */
static struct tnt_stream *tnt[MYSQL_MAX_CONNECTIONS];
static int tarantool_errno[MYSQL_MAX_CONNECTIONS];
static char tarantool_errmsg[3072]; /* same size as hparse_errmsg? */
QString tarantool_server_name= "";
static long unsigned int tarantool_row_count[MYSQL_MAX_CONNECTIONS];
#endif
static ldbms *lmysql= 0;
static bool is_mysql_library_init_done= false;
unsigned int mysql_errno_result= 0;
static QString hparse_text_copy;
static QString hparse_token;
static int hparse_i;
static QString hparse_expected;
static int hparse_errno; /* 0=ok, 1=error, 2=error-unrecoverable */
static int hparse_errno_count;
static int hparse_token_type;
static int hparse_statement_type= -1;
static bool sql_mode_ansi_quotes= false;
/* hparse_f_accept can dump many expected tokens in hparse_errmsg */
static char hparse_errmsg[3072]; /* same size as tarantool_errmsg? */
static QString hparse_next_token, hparse_next_next_token;
static int hparse_next_token_type, hparse_next_next_token_type;
static QString hparse_next_next_next_token, hparse_next_next_next_next_token;
static int hparse_next_next_next_token_type, hparse_next_next_next_next_token_type;
static int hparse_begin_count;
static bool hparse_as_seen;
static bool hparse_create_trigger_seen;
static int hparse_count_of_accepts;
static int hparse_i_of_statement;
static int hparse_i_of_last_accepted;
static QString hparse_prev_token;
static bool hparse_like_seen;
static bool hparse_subquery_is_allowed;
static QString hparse_delimiter_str;
static bool hparse_sql_mode_ansi_quotes= false;
static unsigned short int hparse_dbms_mask= FLAG_VERSION_MYSQL_OR_MARIADB_ALL;
static bool hparse_is_in_subquery= false;
/* Suppress useless messages
https://bugreports.qt.io/browse/QTBUG-57180 (Windows startup)
https://github.com/MartinBriza/QGnomePlatform/issues/23 (Fedora)
Todo: consider also suppressing "OpenType support missing for script" */
#if (QT_VERSION >= 0x50000)
void dump_qtmessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (type == QtWarningMsg)
{
if (msg.contains("CreateFontFaceFromHDC")) return;
if (msg.contains("GtkDialog mapped without a transient parent")) return;
}
QByteArray localMsg = msg.toLocal8Bit();
printf("Qt message: %u: %s.\n", context.line, localMsg.constData());
if (type == QtFatalMsg) abort();
}
#endif
int main(int argc, char *argv[])
{
#if (defined(_WIN32) && (QT_VERSION >= 0x50000))
qInstallMessageHandler(dump_qtmessage);
#endif
QApplication main_application(argc, argv);
MainWindow w(argc, argv);
/* We depend on w being maximized in resizeEvent() */
w.showMaximized();
return main_application.exec();
}
MainWindow::MainWindow(int argc, char *argv[], QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
/* Maximum QString length = sizeof(int)/4. Maximum LONGBLOB length = 2**32. So 32 bit int is ok. */
assert(sizeof(int) >= 4);
#ifdef OCELOT_OS_LINUX
assert(MENU_FONT == 80); /* See kludge alert in ocelotgui.h Settings() */
#endif
assert(TOKEN_REFTYPE_MAX == 91); /* See comment after ocelotgui.h TOKEN_REFTYPE_MAX */
/* Initialization */
main_window_maximum_width= 0;
main_window_maximum_height= 0;
main_token_max_count= 0;
/*
setupUi() is in ui_ocelotgui.h, which is generated by a tool named "uic",
which is called by qmake because there is a line in ocelotgui.pro:
FORMS += ocelotgui.ui
I would love to get rid of the file and just put setupUi in ocelotgui.h,
but different versions of qmake produce different contents for ui_ocelotgui.h.
This is the routine that attaches the menubar to MainWindow, among other things.
*/
ui->setupUi(this);
/*
The Menu Bar did not appear with Ubuntu 14.04.
This might be the horrific bug
https://bugs.launchpad.net/ubuntu/+source/appmenu-qt5/+bug/1307619
But QT_QPA_PLATFORMTHEME= did not solve.
So insist that the menu goes in MainWindow.
*/
#ifdef OCELOT_OS_LINUX
ui->menuBar->setNativeMenuBar(false);
#endif
setWindowTitle("ocelotgui");
connections_is_connected[0]= 0;
mysql_res= 0;
/* client variable defaults */
/* Most settings done here might be overridden when connect_mysql_options_2 reads options. */
ocelot_dbms= "mysql";
connections_dbms[0]= DBMS_MYSQL;
ocelot_grid_max_row_lines= 5; /* obsolete? */ /* maximum number of lines in 1 row. warn if this is exceeded? */
ocelot_statement_prompt_background_color= s_color_list[COLOR_LIGHTGRAY*2 + 1]; /* set early because initialize_widget_statement() depends on this */
ocelot_grid_border_color= s_color_list[COLOR_BLACK*2 + 1];;
ocelot_grid_header_background_color= s_color_list[COLOR_LIGHTGRAY*2 + 1];
ocelot_grid_cell_drag_line_color= s_color_list[COLOR_LIGHTBLUE*2 + 1];;
ocelot_grid_cell_border_color= s_color_list[COLOR_BLACK*2 + 1];
ocelot_grid_cell_border_size= "1";
ocelot_grid_cell_drag_line_size= "5";
/* Probably result_grid_table_widget_saved_font only matters if the connection dialog box has to go up. */
QFont tmp_font;
// QFont *saved_font;
tmp_font= this->font();
// saved_font=&tmp_font;
// result_grid_table_widget= new ResultGrid(0, saved_font, this);
result_grid_tab_widget= new QTabWidget48(this); /* 2015-08-25 added "this" */
result_grid_tab_widget->hide();
main_layout= new QVBoxLayout();
history_edit_widget= new TextEditHistory(this); /* 2015-08-25 added "this" */
statement_edit_widget= new CodeEditor(this);
statement_edit_widget->is_debug_widget= false;
QFont fixed_font= get_fixed_font();
history_edit_widget->setFont(fixed_font);
statement_edit_widget->setFont(fixed_font);
#ifdef DEBUGGER
create_widget_debug();
#endif
hparse_f_parse_hint_line_create();
main_window= new QWidget(this); /* 2015-08-25 added "this" */
/*
Defaults for items that can be changed with Settings menu item.
First we get current values, after processing of any Qt options on the command line.
Then we make arbitrary colors for details that current values probably didn't supply.
The settings Red=string DarkGreen=Comment Green=object Blue=keyword DarkGray=operator
have been seen elsewhere, and also pink|magenta=built-in-function so keep that in reserve.
Then we read option files.
*/
set_current_colors_and_font(fixed_font); /* set ocelot_statement_text_color, ocelot_grid_text_color, etc. */
ocelot_statement_border_color= s_color_list[COLOR_BLACK*2 + 1];
ocelot_statement_highlight_literal_color= s_color_list[COLOR_RED*2 + 1];
ocelot_statement_highlight_identifier_color= s_color_list[COLOR_GREEN*2 + 1];
ocelot_statement_highlight_comment_color= s_color_list[COLOR_LIMEGREEN*2 + 1];
ocelot_statement_highlight_operator_color= s_color_list[COLOR_DARKGRAY*2 + 1];;
ocelot_statement_highlight_keyword_color= s_color_list[COLOR_BLUE*2 + 1];
ocelot_statement_highlight_current_line_color= s_color_list[COLOR_YELLOW*2 + 1];
ocelot_statement_highlight_function_color= s_color_list[COLOR_FUCHSIA*2 + 1];
ocelot_statement_syntax_checker= "1";
ocelot_statement_format_statement_indent= "2";
ocelot_statement_format_clause_indent= "4";
ocelot_statement_format_keyword_case= "upper";
ocelot_history_border_color= s_color_list[COLOR_BLACK*2 + 1];
ocelot_menu_border_color= s_color_list[COLOR_BLACK*2 + 1];
lmysql= new ldbms(); /* lmysql will be deleted in action_exit(). */
/* picking up possible settings options from argc+argv after setting initial defaults, so late */
/* as a result, ocelotgui --version and ocelotgui --help will look slow */
connect_mysql_options_2(argc, argv); /* pick up my.cnf and command-line MySQL-related options, if any */
if (ocelot_version != 0) /* e.g. if user said "ocelotgui --version" */
{
print_version();
exit(0);
}
if (ocelot_help != 0) /* e.g. if user said "ocelotgui --help" */
{
print_help();
exit(0);
}
for (int q_i= 0; strcmp(string_languages[q_i]," ") > 0; ++q_i)
{
QString s= string_languages[q_i];
if (QString::compare(ocelot_language, s, Qt::CaseInsensitive) == 0)
{
er_off= ER_END * q_i;
color_off= COLOR_END * q_i;
menu_off= MENU_END * q_i;
}
}
set_dbms_version_mask(ocelot_dbms);
for (int q_i= color_off; strcmp(s_color_list[q_i]," ") > 0; ++q_i) q_color_list.append(s_color_list[q_i]);
assign_names_for_colors();
make_style_strings();
//main_window->setStyleSheet(ocelot_menu_style_string);
ui->menuBar->setStyleSheet(ocelot_menu_style_string);
initialize_widget_history();
initialize_widget_statement();
result_grid_add_tab();
main_layout->addWidget(history_edit_widget);
main_layout->addWidget(result_grid_tab_widget);
main_layout->addWidget(statement_edit_widget);
#ifdef DEBUGGER
main_layout->addWidget(debug_top_widget);
#endif
main_layout->addWidget(hparse_line_edit);
main_window->setLayout(main_layout);
setCentralWidget(main_window);
create_menu(); /* Do this at a late stage because widgets must exist before we call connect() */
/*
If the command-line option was -p but not a password, then password input is necessary
so put up the connection dialog box. Otherwise try immediately to connect.
Todo: better messages so the user gets the hint: connection is necessary / password is necessary.
*/
if (ocelot_password_was_specified == 0)
{
statement_edit_widget->insertPlainText("CONNECT");
action_execute(1);
}
else
{
action_connect();
}
statement_edit_widget->setFocus(); /* Show user we're ready to accept a statement in the statement edit widget */
}
MainWindow::~MainWindow()
{
delete ui;
}
/*
We want to know the maximum widget size for dialog boxes.
We can get this from the second time resizeEvent happens
on MainWindow, which is asserted to be maximized.
*/
void MainWindow::resizeEvent(QResizeEvent *ev)
{
(void) ev; /* suppress "unused parameter" warning */
QSize main_window_size= size();
if (main_window_size.width() > main_window_maximum_width)
main_window_maximum_width= main_window_size.width();
if (main_window_size.height() > main_window_maximum_height)
main_window_maximum_height= main_window_size.height();
}
/*
Normally there is only one result set, and therefore only one tab,
and we hide the tabbing if there is only one tab. Abnormally there
is more than one because a stored procedure does more than one SELECT.
We call this at start, and we call this when we need a new tab.
We never delete. That might be bad because Settings changes will be slower.
We have:
ocelot_grid_tabs. The maximum. Default 16. Settable with --ocelot_grid_tabs=n.
result_grid_tab_widget. What we add the tabs to.
ocelot_grid_actual_tabs. When we successfully add a tab, this goes up.
Return 0 for success, 1 for failure (failure is probably because we hit maximum).
*/
int MainWindow::result_grid_add_tab()
{
ResultGrid *r;
int i_r= ocelot_grid_actual_tabs;
if ((ocelot_grid_actual_tabs >= ocelot_grid_tabs)
&& (ocelot_grid_tabs != 0))
return 1;
{
r= new ResultGrid(lmysql, this, true);
int new_tab_index= result_grid_tab_widget->addTab(r, QString::number(i_r + 1));
assert(new_tab_index == i_r);
r->hide(); /* Maybe this isn't necessary */
}
{
/* Todo: is this pointless? I don't catch fontchange anyway! */
r= qobject_cast<ResultGrid*>(result_grid_tab_widget->widget(i_r));
assert(r != 0);
r->installEventFilter(this); /* must catch fontChange, show, etc. */
r->grid_vertical_scroll_bar->installEventFilter(this);
}
{
r= qobject_cast<ResultGrid*>(result_grid_tab_widget->widget(i_r));
r->set_all_style_sheets(ocelot_grid_style_string, ocelot_grid_cell_drag_line_size, 0, false);
}
++ocelot_grid_actual_tabs;
return 0;
}
/*
Initialize statement_edit_widget. Assume "statement_edit_widget= new CodeEditor();" already done.
All user SQL input goes into statement_edit_widget.
This will be a CodeEditor, which is a class derived from QTextEdit, with a prompt on the left.
Todo: Check: after font change or main window resize, are line numbers and text in sync?
*/
void MainWindow::initialize_widget_statement()
{
statement_edit_widget_setstylesheet();
statement_edit_widget->setLineWrapMode(QPlainTextEdit::NoWrap);
/* statement_edit_widget->setAcceptRichText(false); */ /* Todo: test whether this works */
connect(statement_edit_widget->document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(action_statement_edit_widget_text_changed(int,int,int)));
statement_edit_widget_text_changed_flag= 0;
/*
Defaults.
The prompt can be changed with a "prompt" statement or by my.cnf contents.
The widget-left i.e. prompt bgcolor can be changed with a menu item.
*/
statement_edit_widget->statement_edit_widget_left_bgcolor= QColor(ocelot_statement_prompt_background_color);
statement_edit_widget->statement_edit_widget_left_treatment1_textcolor= QColor(ocelot_statement_text_color);
statement_edit_widget->prompt_default= ocelot_prompt; /* Todo: change to "\N [\d]>"? */
statement_edit_widget->prompt_as_input_by_user= statement_edit_widget->prompt_default;
statement_edit_widget->statement_count= 0;
statement_edit_widget->dbms_version= (QString)"";
statement_edit_widget->dbms_database= (QString)"";
statement_edit_widget->dbms_port= (QString)"";
statement_edit_widget->dbms_host= (QString)"";
statement_edit_widget->dbms_current_user= (QString)"";
statement_edit_widget->dbms_current_user_without_host= (QString)"";
statement_edit_widget->delimiter= ocelot_delimiter_str;
statement_edit_widget->result= (QString)"";
/*
Problem: the above statements don't cause recalculation of width of prompt on the left.
(Original calculation happens when widget is created.)
I can force recalculation by doing an "emit" here.
But I'm not sure this is the right way to do it.
*/
emit statement_edit_widget->update_prompt_width(0);
statement_edit_widget->installEventFilter(this);
statement_edit_widget->setMouseTracking(true);
}
/* Statement widget and debug widgets both use ocelot_statement_style_string. */
void MainWindow::statement_edit_widget_setstylesheet()
{
statement_edit_widget->setStyleSheet(ocelot_statement_style_string);
for (int debug_widget_index= 0; debug_widget_index < DEBUG_TAB_WIDGET_MAX; ++debug_widget_index)
{
if (debug_widget[debug_widget_index] != 0)
{
debug_widget[debug_widget_index]->setStyleSheet(ocelot_statement_style_string);
}
}
}
/*
Formatter -- minimal, but this format might be okay for debugger users.
It only works if the statement has been recognized.
All we're trying to accomplish is
"different statements different lines"
"block start causes indent"
"keywords upper case"
"lists of columns lined up in select/update/insert/set".
Cursor will go back to start.
Indentation is meaningless unless there is a fixed font.
Todo: Formatter is on the Edit menu but there is no shortcut key.
It would be easy to add a shortcut key with setShortcut()
or add a line in eventfilter() e.g.
if (key->key() == Qt::Key_1) { statement_edit_widget_formatter(); return true; }
But I haven't decided what the shortcut key should be.
Todo: stop giving up when you see DELIMITER, it can be figured out.
*/
void MainWindow::statement_edit_widget_formatter()
{
if (((ocelot_statement_syntax_checker.toInt()) & FLAG_FOR_HIGHLIGHTS) == 0) return;
int *output_offsets;
int i;
for (i= 0; main_token_lengths[i] != 0; ++i) ;
output_offsets= new int[i + 1];
QString text= statement_edit_widget->toPlainText(); /* or I could just pass this to tokenize() directly */
QString output= "";
int indent_base= 0;
int statement_indent= ocelot_statement_format_statement_indent.toInt();
int clause_indent= ocelot_statement_format_clause_indent.toInt();
QString keyword_case= ocelot_statement_format_keyword_case.toLower();
int token;
QString s;
int token_type;
for (i= 0; main_token_lengths[i] != 0; ++i)
{
if ((main_token_flags[i] & TOKEN_FLAG_IS_ERROR) != 0)
{
int st= 0;
if (i > 0) st= main_token_offsets[i - 1] + main_token_lengths[i - 1];
s= text.right(text.length() - st);
output.append(s);
break; /* Give up */
}
token_type= 0;
token= main_token_types[i];
if (token == TOKEN_TYPE_DELIMITER)
{
QString d= text.mid(main_token_offsets[i], main_token_lengths[i]);
output.append(d);
continue;
}
if (text.mid(main_token_offsets[i], 2).toUpper() == "\\G")
{
output.append(text.mid(main_token_offsets[i], 2));
++i;
continue;
}
if ((main_token_flags[i] & TOKEN_FLAG_IS_START_IN_COLUMN_LIST) != 0)
output_offsets[i]= output.length();
if ((main_token_flags[i] & TOKEN_FLAG_IS_END_IN_COLUMN_LIST) != 0)
{
output.append(",");
/* indent to where the column list began */
int indent_of_column= 0;
for (int k= i - 1; k >= 0; --k)
{
if ((main_token_flags[k] & TOKEN_FLAG_IS_START_IN_COLUMN_LIST) != 0)
{
int l= output_offsets[k];
for (; (l < output.length()) && (output.mid(l, 1) <= " "); ++l) ;
for (int m= l; ; --m)
{
if ((output.mid(m, 1) == "\n") || (m == 0))
{
if (m != 0) ++m;
indent_of_column= l - m;
break;
}
}
break;
}
}
output.append("\n");
for (int n= 0; n < indent_of_column; ++n) output.append(" ");
}
else
{
if (token == TOKEN_KEYWORD_DELIMITER)
{
if (output != "") output.append("\n");
QString d;
d= text.mid(main_token_offsets[i], main_token_lengths[i]);
QString e= d.toUpper();
if (e == "DELIMITER") output.append(e);
else output.append(d);
if (main_token_lengths[i + 1] != 0)
{
int j= main_token_offsets[i] + main_token_lengths[i];
d= text.mid(j, main_token_offsets[i + 1] - j);
if (d.contains("\n"))
{
continue; /* delimiter \n = an error, but possible */
}
output.append(" ");
++i;
d= text.mid(main_token_offsets[i], main_token_lengths[i]);
output.append(d);
while ((main_token_lengths[i + 1] != 0)
&& (main_token_offsets[i + 1] == main_token_offsets[i] + main_token_lengths[i]))
{
d= text.mid(main_token_offsets[i + 1], main_token_lengths[i + 1]);
output.append(d);
++i;
}
continue;
}
}
if (token == TOKEN_KEYWORD_PROMPT)
{
if (output != "") output.append("\n");
QString d;
d= text.mid(main_token_offsets[i], main_token_lengths[i]);
QString e= d.toUpper();
if (e == "PROMPT") output.append(e);
else output.append(d);
int k= i + 1;
while ((main_token_lengths[k] != 0)
&& ((main_token_flags[k] & TOKEN_FLAG_IS_START_STATEMENT) == 0)
&& ((main_token_flags[k] & TOKEN_FLAG_IS_ERROR) == 0))
++k;
--k;
if (k != i)
{
int j;
j= main_token_offsets[k] - (main_token_offsets[i] + main_token_lengths[i]) + main_token_lengths[k];
d= text.mid(main_token_offsets[i] + main_token_lengths[i], j);
output.append(d);
}
i= k;
continue;
}
if (((main_token_flags[i] & TOKEN_FLAG_IS_START_STATEMENT) != 0)
|| ((main_token_flags[i] & TOKEN_FLAG_IS_START_CLAUSE) != 0))
{
token_type= 1;
if ((token == TOKEN_KEYWORD_BEGIN)
|| (token == TOKEN_KEYWORD_CASE)
|| (token == TOKEN_KEYWORD_FOR_IN_FOR_STATEMENT) /* will only be true if MariaDB 10.3 */
|| (token == TOKEN_KEYWORD_IF)
|| (token == TOKEN_KEYWORD_LOOP)
|| (token == TOKEN_KEYWORD_REPEAT)
|| (token == TOKEN_KEYWORD_WHILE))
token_type= 3;
}
if (token_type > 0)
{
/* todo: check if (not already at indent-base) */
if (output != "") output.append("\n");
for (int k= 0; k < indent_base; ++k) output.append(" ");
if ((main_token_flags[i] & TOKEN_FLAG_IS_START_CLAUSE) != 0)
{
for (int k= 0; k < clause_indent; ++k) output.append(" ");
}
if (token == TOKEN_KEYWORD_END)
{
indent_base-= statement_indent;
}
if (token_type == 3)
{
indent_base+= statement_indent;
}
}
s= text.mid(main_token_offsets[i], main_token_lengths[i]);
QString p= "";
int p_flags= 0;
int p_type= 0;
if (i > 0)
{
p= text.mid(main_token_offsets[i - 1], main_token_lengths[i - 1]);
p_flags= main_token_flags[i - 1];
p_type= main_token_types[i - 1];
}
if (output.right(1) == "\\")
{
if (main_token_offsets[i] == main_token_offsets[i - 1] + main_token_lengths[i - 1])
{
output.append(s);
continue;
}
}
if (output.right(1) > " ")
{
if (main_token_types[i] == TOKEN_TYPE_OPERATOR)
{
if ((s == ",") || (s == ";") || (s == ")") || (s == "}") || (s == ".") || (s == ":")) {;}
else if (s == "(")
{
/* no space between function name and '('. mandatory. */
if ((p_flags & TOKEN_FLAG_IS_FUNCTION) != 0)
{
;
}
else if (p_type == TOKEN_TYPE_IDENTIFIER)
{
;
}
else if ((p_flags & TOKEN_FLAG_IS_DATA_TYPE) != 0)
{
;
}
else if ((p != "(")
&& ((main_token_flags[i] & TOKEN_FLAG_IS_NOT_AFTER_SPACE) == 0))
output.append(" ");
}
else output.append(" ");
}
else
{
if ((p == "(") || (p == "{") || (p == ".") || (p == "@") || (p == "~")) {;}
else if ((p == "-") || (p == "+"))
{
if ((main_token_flags[i - 1] & TOKEN_FLAG_IS_BINARY_PLUS_OR_MINUS) != 0)
{
output.append(" ");
}
}
else if (s == "@") {;}
else output.append(" ");
}
}
if (main_token_types[i] >= TOKEN_KEYWORDS_START)
{
if (keyword_case == "upper") s= s.toUpper();
if (keyword_case == "lower") s= s.toLower();
}
output.append(s);
}
}
/* Change statement widget contents with possibility of "undo" later */
QTextCursor cur(statement_edit_widget->textCursor());
cur.beginEditBlock();
cur.setPosition(0);
cur.select(QTextCursor::Document);
cur.removeSelectedText();
cur.insertText(output);
cur.endEditBlock();
delete [] output_offsets;
}
/*
The event filter, for detecting:
Has the user pressed on the vertical scroll bar of result_grid_widget?
Has the user pressed Enter or return on statement_edit_widget?
If the user presses Enter or Return on statement_edit_widget,
and the last non-comment token is the delimiter (normally ";" unless DELIMITER happened),
and the cursor is at the end i.e. after the delimiter,
that should cause execution.
Otherwise return false ...
I've seen examples where, instead of "return false;", the
instruction is "return QMainWindow::eventFilter(obj, event);".
I think the idea there is to go direct to the main processor
for text editing, bypassing other event filters.
Note: statement_edit_widget mouseMoveEvent handling is in ocelotgui.h
Todo: Consider: Perhaps this should not be in MainWindow:: but in CodeEditor::.
Todo: Consider: Perhaps this should be a menu item, not a filter event.
(There's already a menu item, but it's not for Enter|Return.)
There are a few "Ocelot keyword" items that do not require ";" or delimiter
provided they're the first word, for example "QUIT".
*/
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
return eventfilter_function(obj, event);
}
bool MainWindow::eventfilter_function(QObject *obj, QEvent *event)
{
// if (obj == result_grid_table_widget[0]->grid_vertical_scroll_bar)
// {
// /*
// Probably some of these events don't cause scroll bar value to change,
// I've only seen that happen for MouseMove and MouseButtonRelease.
// But that's harmless, it only means we call scroll_event for nothing.
// */
// if ((event->type() == QEvent::KeyPress)
// || (event->type() == QEvent::KeyRelease)
// || (event->type() == QEvent::MouseButtonDblClick)
// || (event->type() == QEvent::MouseButtonPress)
// || (event->type() == QEvent::MouseButtonRelease)
// || (event->type() == QEvent::MouseMove)
// || (event->type() == QEvent::MouseTrackingChange)) return (result_grid_table_widget->scroll_event());
// }
QString text;
if (event->type() == QEvent::FocusIn)
{
menu_activations(obj, QEvent::FocusIn);
return false;
}
if (event->type() == QEvent::FocusOut)
{
menu_activations(obj, QEvent::FocusOut);
return false;
}
{
ResultGrid* r;
for (int i_r= 0; i_r < ocelot_grid_actual_tabs; ++i_r)
{
r= qobject_cast<ResultGrid*>(result_grid_tab_widget->widget(i_r));
if (obj == r)
{
if (event->type() == QEvent::FontChange) return (r->fontchange_event());
if (event->type() == QEvent::Show) return (r->show_event());
}
if (obj == r->grid_vertical_scroll_bar)
{
return (r->vertical_scroll_bar_event(connections_dbms[0]));
}
}
}
#ifdef DEBUGGER
for (int debug_widget_index= 0; debug_widget_index < DEBUG_TAB_WIDGET_MAX; ++debug_widget_index)
{
if (obj == debug_widget[debug_widget_index])
{
if (event->type() == QEvent::MouseButtonPress)
{
action_debug_mousebuttonpress(event, debug_widget_index);
return false;
}
}
}
#endif
if (event->type() != QEvent::KeyPress) return false;
QKeyEvent *key= static_cast<QKeyEvent *>(event);
/* See comment with label "Shortcut Duplication" */
if (keypress_shortcut_handler(key, false) == true) return true;
if (obj != statement_edit_widget) return false;
if ((key->key() != Qt::Key_Enter) && (key->key() != Qt::Key_Return)) return false;
/* No delimiter needed if Ctrl+Enter, which we'll regard as a synonym for Ctrl+E */
if (key->modifiers() & Qt::ControlModifier)
{
action_execute(1);
return true;
}
/*
It's just Enter.
If it's not at the end, not counting white space, don't try to execute.
Anyway, don't force.
*/
if (statement_edit_widget->textCursor().atEnd() == false)
{
int cursor_position= statement_edit_widget->textCursor().position();
QString plain_text= statement_edit_widget->toPlainText();
for (int i= cursor_position; i < plain_text.size(); ++i)
{
if (plain_text.mid(i, 1) > " ") return false;
}
}
if (action_execute(0) == 1) return false;
return true;
}
/*
There are two reasons for keypress_shortcut_handler() to exist:
1: see comment = "Shortcut duplication"
2: we call not only from MainWindow::eventfilter_function()
but also from TextEditWidget::keyPressEvent().
If the keypress is a shortcut, we handle it and return true.
If it's not, then it's probably text input, we return false.
There is one situation that we do not handle:
TextEditWidget handles ocelot_shortcut_copy_keysequence separately.
Todo: Maybe event->matches() is a more standard way to compare.
Todo: Now there's useless code -- we have setShortcut() for most of
these combinations, but keypress_shortcut_handler() happens
first (?? I think), so they're useless. But a few things are
still handled by shortcuts, e.g. format, which is harmless
because there's only one widget that format works with.
Todo: can we get here for a disabled menu item?
Todo: if e.g. menu_edit_undo() does nothing because it can't find the
classname, it should return false so keypress_shortcut_handler()
can return false. Currently these things are all void.
Todo: the system hasn't been tested with detached debugger widgets
for which we've said SET ocelot_shortcut_...='something odd'.
*/
bool MainWindow::keypress_shortcut_handler(QKeyEvent *key, bool return_true_if_copy)
{
Qt::KeyboardModifiers modifiers= key->modifiers();
int qki= 0;
if ((modifiers & Qt::ControlModifier) != 0) qki= (qki | Qt::CTRL);
if ((modifiers & Qt::ShiftModifier) != 0) qki= (qki | Qt::SHIFT);
if ((modifiers & Qt::AltModifier) != 0) qki= (qki | Qt::ALT);
if ((modifiers & Qt::MetaModifier) != 0) qki= (qki | Qt::META);
QKeySequence qk= QKeySequence(key->key() | qki);
if ((qk == ocelot_shortcut_copy_keysequence)
&& (return_true_if_copy)) return true;
if (qk == ocelot_shortcut_connect_keysequence) { action_connect(); return true; }
if (qk == ocelot_shortcut_exit_keysequence) { action_exit(); return true; }
if (qk == ocelot_shortcut_undo_keysequence) { menu_edit_undo(); return true; }
if (qk == ocelot_shortcut_redo_keysequence) { menu_edit_redo(); return true; }
if (qk == ocelot_shortcut_cut_keysequence) { menu_edit_cut(); return true; }
if (qk == ocelot_shortcut_copy_keysequence) { menu_edit_copy(); return true; }
if (qk == ocelot_shortcut_paste_keysequence) { menu_edit_paste(); return true; }
if (qk == ocelot_shortcut_select_all_keysequence) { menu_edit_select_all(); return true; }
if (qk == ocelot_shortcut_history_markup_previous_keysequence) { history_markup_previous(); return true; }
if (qk == ocelot_shortcut_history_markup_next_keysequence) { history_markup_next(); return true; }
if (qk == ocelot_shortcut_execute_keysequence){ action_execute(1); return true; }
if (qk == ocelot_shortcut_zoomin_keysequence){menu_edit_zoomin(); return true; }
if (qk == ocelot_shortcut_zoomout_keysequence){menu_edit_zoomout(); return true; }
if (menu_edit_action_autocomplete->isEnabled() == true)
{
if (qk == ocelot_shortcut_autocomplete_keysequence) {
return menu_edit_autocomplete(); }
}
if (menu_run_action_kill->isEnabled() == true)
{
if (qk == ocelot_shortcut_kill_keysequence) { action_kill(); return true; }
}
if (qk == ocelot_shortcut_next_window_keysequence){action_option_next_window(); return true; }
if (qk == ocelot_shortcut_previous_window_keysequence){action_option_previous_window(); return true; }
if (menu_debug_action_breakpoint->isEnabled())
if (qk == ocelot_shortcut_breakpoint_keysequence) { action_debug_breakpoint(); return true; }
if (menu_debug_action_continue->isEnabled())
if (qk == ocelot_shortcut_continue_keysequence) { action_debug_continue(); return true; }
if (menu_debug_action_next->isEnabled())
if (qk == ocelot_shortcut_next_keysequence) { action_debug_next(); return true; }
if (menu_debug_action_step->isEnabled())
if (qk == ocelot_shortcut_step_keysequence) { action_debug_step(); return true; }
if (menu_debug_action_clear->isEnabled())
if (qk == ocelot_shortcut_clear_keysequence) { action_debug_clear(); return true; }
if (menu_debug_action_exit->isEnabled())
if (qk == ocelot_shortcut_debug_exit_keysequence) { action_debug_exit(); return true; }
if (menu_debug_action_information->isEnabled())
if (qk == ocelot_shortcut_information_keysequence) { action_debug_information(); return true; }
if (menu_debug_action_refresh_server_variables->isEnabled())
if (qk == ocelot_shortcut_refresh_server_variables_keysequence) { action_debug_refresh_server_variables(); return true; }
if (menu_debug_action_refresh_user_variables->isEnabled())
if (qk == ocelot_shortcut_refresh_user_variables_keysequence) { action_debug_refresh_user_variables(); return true; }
if (menu_debug_action_refresh_variables->isEnabled())
if (qk == ocelot_shortcut_refresh_variables_keysequence) { action_debug_refresh_variables(); return true; }
if (menu_debug_action_refresh_call_stack->isEnabled())
if (qk == ocelot_shortcut_refresh_call_stack_keysequence) { action_debug_refresh_call_stack(); return true; }
return false;
}
/*
We want to know: do we have a complete statement at the string start.
So we find out how many tokens are in the (first) statement.
We go forward to find the first non-comment token, the statement type.
We go backward to find the final non-comment token, the ; or delimiter -- or not.
Using these, and knowing what is ocelot_delimiter_str, we can decide if it's complete.
We also check for \G or \g.
*/
bool MainWindow::is_statement_complete(QString text)
{
int number_of_tokens_in_statement= 0;
int returned_begin_count= 0;
int i= 0;
int first_token_type= -1;
int first_token_i= 0;
//int last_token_type= -1;
QString first_token= "";
QString last_token= "";
QString second_last_token= "";
number_of_tokens_in_statement= get_next_statement_in_string(0, &returned_begin_count, false);
for (i= 0; i < number_of_tokens_in_statement; ++i)
{
int t= main_token_types[i];
if (t == TOKEN_TYPE_COMMENT_WITH_SLASH) continue;
if (t == TOKEN_TYPE_COMMENT_WITH_OCTOTHORPE) continue;
if (t == TOKEN_TYPE_COMMENT_WITH_MINUS) continue;
first_token_type= t;
first_token= text.mid(main_token_offsets[i], main_token_lengths[i]);
first_token_i= i;
break;
}
for (i= number_of_tokens_in_statement - 1; i >= 0; --i)
{
int t= main_token_types[i];
if (t == TOKEN_TYPE_COMMENT_WITH_SLASH) continue;
if (t == TOKEN_TYPE_COMMENT_WITH_OCTOTHORPE) continue;
if (t == TOKEN_TYPE_COMMENT_WITH_MINUS) continue;
//last_token_type= t;
last_token= text.mid(main_token_offsets[i], main_token_lengths[i]);
if (i > 0) second_last_token= text.mid(main_token_offsets[i - 1], main_token_lengths[i - 1]);
break;
}
/* No delimiter needed if first word in first statement of the input is an Ocelot keyword e.g. QUIT */
/* Todo: Check: does this mean that a client statement cannot be spread over two lines? */
if (is_client_statement(first_token_type, first_token_i, text) == true)
{
return true;
}
/* "go" or "ego", alone on the line, if --named-commands, is statement end */
/* Todo: but these are client statements so we won't ever get here */
/* Todo: I forget why we care. Is it in fact because the token is \G or \g? */
if ((ocelot_named_commands > 0)
&& ((first_token_type == TOKEN_KEYWORD_GO) || (first_token_type == TOKEN_KEYWORD_EGO)))
{
QString q;
for (int i_off= main_token_offsets[i] - 1;; --i_off)
{
if (i_off < 0) q= "\n";
else q= text.mid(i_off, 1);
if ((q == "\n") || (q == "\r"))
{
return true;
}
if (q != " ") break;
}
}
/* if create-routine && count-of-ENDs == count-of-BEGINS then ; is the end else ; is not the end */
if (ocelot_delimiter_str != ";") returned_begin_count= 0;
else
{
int token_count= get_next_statement_in_string(0, &returned_begin_count, false);
if (returned_begin_count == 0)
{
if (QString::compare(last_token, ";", Qt::CaseInsensitive) == 0)
{
/* Warning: this only works if we've gone through hparse */
/* plsql routines might have var|cursor|condition before begin */
if ((main_token_flags[token_count - 1] & TOKEN_FLAG_IS_PLSQL_DECLARE_SEMICOLON) != 0)
{
for (int k= token_count - 1;;--k)
{
if (main_token_types[k] == TOKEN_KEYWORD_BEGIN) break;
if (k == 0)
{
/* saw no begin but flag counts as a begin */
++returned_begin_count;
break;
}
}
}
if (returned_begin_count == 0) return true;
}
if (QString::compare(last_token, "G", Qt::CaseInsensitive) == 0)
{
if (QString::compare(second_last_token, "\\", Qt::CaseInsensitive) == 0)
{
return true;
}
}
}
}
if (last_token != ocelot_delimiter_str)
{
return false;
}
if (returned_begin_count > 0)
{
return false;
}
/* All conditions have been met. Tell caller to Execute, and eat the return key. */
return true;
}
/*
History
=======
The history widget history_edit_widget is an editable subclass of QTextEdit
which contains retired statements + errors/warnings, scrolling
so it looks not much different from the mysql retired-statement
scrolling.
However, our history does not include result sets (unless
result_set_for_history, described later).
These user-settable variables affect history_edit_widget:
ocelot_history_text|background|border_color default = system
ocelot_history_font_family|size|style|weight default = system
ocelot_history_includes_warnings default = 0 (no)
ocelot_history_max_row_count default = 0 (suppressed)
The statement is always followed by an error message,
but ocelot_history_includes_warnings is affected by ...
warnings (\W) Show warnings after every statement.
nowarning (\w) Don't show warnings after every statement.
If the prompt is included, then we should be saving time-of-day
for the first statement-line prompt and doing prompt_translate()
for each line of the statement when we copy it out.
The history_edit_widget is TextEditHistory which is derived from
QTextEdit, differing from statement_edit_widget which is CodeEditor
which is derived from QPlainTextEdit.
History menu items / commands:
* The usual edit menu items = cut, copy, paste, etc.
Therefore we don't really need to limit history size, users can clear.
* Previous Statement ^P and Next Statement ^N
? Possible alternatives: alt+up-arrow|alt+down-arrow, PgUp|PgDn
? Possible alternative: up-arrow if we're at top of statement widget
(which is more like what mysql client would do)
* disable if there's no previous / next
* if done on statement widget, brings in from history -- dunno if history should scroll when that happens
? when bringing in from history to statement, don't re-execute
* if user executes a restated statement, there's a behaviour choice: now the restated statement is last statement, or now the last-picked statement is still last statement
* Open | Save | Close | Delete (not implemented)
* file items, with the intent of keeping history
* you can go back in history by going back in a file
* format should be the same as MySQL log history
? but in that case, should display look like MySQL log too?
* Save means "save what's being shown, so it's affected by Hide
* Find | Goto
* you need these menu items for history soon, so might as well have them for statements too
* Hide prompt + Hide result + Hide result set
+ Settings: History Widget has different colors for prompt/statement/result/result set
Comments unrelated to HTML work:
Possible difficulty: our history is statement-at-a-time, not screen-at-a-time", if there are multi statements
Certain statements might be suppressed
* should Quit and Source go in history?
* should only data-change statements go in history?
* should client-statements be suppressed?
* this is a "Filter" matter
? to what extent is this useful for audit?
Wherever there's a menu item, there should also be a client-statement
* history must include comments even if they're not sent to the server (there's some mysql-option for that)
* Delimiter might change, so when you copy from history to statement, what should you do?
* When the debugger comes in, statements done by the debugger are a special history category
HTML in the history widget
Unlike statement_edit_widget, history_edit_widget is TextEditHistory
derived from QTextEdit and allows rich text, editable, containing HTML.
Each history entry has: prompt, statement, result, and possibly result set.
(Result set depends on ocelot_history_max_row_count, 0 by default, and
see copy_to_history, it's supposed to appear like mysql client result set.)
There are two kinds of markup:
(1) <span style="...">...</span> for changes to color + font.
Currently this is only used for history prompt bgcolor = statement prompt bgcolor.
(2) <a name='[mark]'> for showing what kind of text follows.
The markups are <a name='STATEMENT START'> ... <a name='STATEMENT END'>
<a name='PROMPT START'> ... <a name='PROMPT END'>
<a name='RESULT'> (ends when statement ends)
<a name='ENTITY'> (always an entity for a single & character)
Digia does not document or guarantee what it will do with <a>
and we're not using it for its intended function as anchor.
Example: "SELECT * FROM t <br> WHERE x < '&';" becomes
<a name='STATEMENT START'>
<span ...><a name='PROMPT'>mysql&gt;<a name='PROMPT END'></span>
SELECT * FROM t <br>
<a name='PROMPT'>&gt;<a name='PROMPT END'>
WHERE a &lt; '<a name='ENTITY'>&amp;';
<span ...><a name='RESULT'>OK</span>
<a name='STATEMENT END'>
Handle PgUp | PgDn by picking up next | previous STATEMENT START ... END.
When copying to history, change < and > and & to entities but mark if '&'.
Also: change ' and " to entities.
When copying from history, change entities to < and > and & unless marked.
Todo: hide prompt + result by putting whole thing within <a name='...'>.
In order to see the HTML, say:
QMessageBox msgBox;
msgBox.setTextFormat(Qt::PlainText);
msgBox.setText(history_edit_widget->toHtml());
msgBox.exec();
History is editable.
If the user inputs "<" when editing the history,
change that to an entity immediately -- but maybe Qt will do that.
Idea: use QTextCursor to change cursor in history if PgUp|PgDn in statement.
Idea: multiple types of result: warning, error, result set
*/
void MainWindow::initialize_widget_history()
{
history_edit_widget->setStyleSheet(ocelot_history_style_string);
history_edit_widget->setReadOnly(false); /* if history shouldn't be editable, set to "true" here */
history_edit_widget->hide(); /* hidden until a statement is executed */
history_markup_make_strings();
history_edit_widget->installEventFilter(this); /* must catch focusIn */
return;
}
/*
Set the strings that will be used for markup comments in history widget.
Call history_markup_make_strings() at program start.
Todo: At the moment these could be constants but I don't think they always will be.
Note: <br> in history_markup_statement_start is necessary; else prompt_start is ignored.
*/
void MainWindow::history_markup_make_strings()
{
history_markup_statement_start= "<a name=\"STATEMENT START\"></a><br>";
history_markup_statement_end= "<a name=\"STATEMENT END\"></a>";
history_markup_prompt_start= "<a name=\"PROMPT START\"></a>";
history_markup_prompt_end= "<a name=\"PROMPT END\"></a>";
history_markup_result= "<a name=\"RESULT\"></a>";
history_markup_entity= "<a name=\"ENTITY\"></a>";
}
/* The following will append the statement to history, line-at-a-time with prompts. */
/* It seems to work except that the prompt is not right-justified. */
/* is_interactive == false if we're reading from mysql_histfile during start */
/* Todo: right justify. Make it optional to show the prompt, unless prompt can be hidden. */
void MainWindow::history_markup_append(QString result_set_for_history, bool is_interactive)
{
QString plainTextEditContents;
QStringList statement_lines;
int statement_line_index;
QString history_statement;
plainTextEditContents= query_utf16; /* Todo: consider: why bother copying rather than using query_uitf16? */
statement_lines= plainTextEditContents.split("\n");
//statement_line_index= 0; /* Todo throw this useless line away */
/* Todo: There should be a better way to ensure that Qt realizes the whole widget is rich text. */
/* Todo: Some of this could be at start of history_edit_widget but what would happen if I cleared the whole area? */
/* Todo: background-color of prompt could be settable for history widget, rather than = statement background color. */
history_statement= "<i></i>"; /* hint that what's coming is HTML */
history_statement.append(history_markup_statement_start);
for (statement_line_index= 0; statement_line_index < statement_lines.count(); ++statement_line_index)
{
history_statement.append("<span style=\" background-color:");
history_statement.append(ocelot_statement_prompt_background_color);
history_statement.append(";\">");
history_statement.append(history_markup_prompt_start);
if (is_interactive == true) history_statement.append(history_markup_copy_for_history(statement_edit_widget->prompt_translate(statement_line_index + 1)));
else history_statement.append("-");
history_statement.append(history_markup_prompt_end);
history_statement.append("</span>");
history_statement.append(history_markup_copy_for_history(statement_lines[statement_line_index]));
history_statement.append("<br>");
}
history_statement.append(history_markup_result);
if (is_interactive == true)
history_statement.append(history_markup_copy_for_history(statement_edit_widget->result)); /* the main "OK" or error message */
else history_statement.append("--");
if (result_set_for_history > "")
{
history_statement.append("<pre>");
history_statement.append(history_markup_copy_for_history(result_set_for_history));
history_statement.append("</pre>");
}
history_statement.append(history_markup_statement_end);
history_edit_widget->append(history_statement);
history_markup_counter= 0;
if (is_interactive == false) return;
/* not related to markup, just a convenient place to call */
history_file_write("TEE", query_utf16);
history_file_write("HIST", query_utf16);
history_file_write("TEE", statement_edit_widget->result);
if (result_set_for_history > "")
history_file_write("TEE", result_set_for_history);
}
/* When copying to history, change < and > and & and " to entities. */
/* Change on 2015-03-16: change newline to <br>. */
QString MainWindow::history_markup_copy_for_history(QString inputs)
{
QString outputs;
QString c;
int i;
outputs= "";
for (i= 0; i < inputs.length(); ++i)
{
c= inputs.mid(i, 1);
if (c == "<") c= "&lt;";
if (c == ">") c= "&gt;";
if (c == "&") c= "&amp;";
if (c == "\"") c= "&quot;";
if (c == "\n") c= "<br>";
outputs.append(c);
}
return outputs;
}
/* In response to edit menu "Previous statement" or "Next statement",
make statement widget contents = a previous statement from history,
without executing.
When copying from history, change entities to < and > and & and "
But the job is much bigger than that.
Find "STATEMENT START".
Start copying.
Skip everything between "PROMPT START" and "PROMPT END".
Stop copying when you see "RESULT".
*/
void MainWindow::history_markup_previous()
{
++history_markup_counter;
if (history_markup_previous_or_next() == -1) --history_markup_counter;
}
void MainWindow::history_markup_next()
{
--history_markup_counter;
if (history_markup_previous_or_next() == -1) ++history_markup_counter;
}
int MainWindow::history_markup_previous_or_next()
{
QString outputs;
QString final_outputs;
int index_of_prompt_end;
int index_of_a_gt, index_of_lt_a;
int i;
QString c;
int search_start_point;
QString s;
int index_of_statement_start;
int index_of_result;
s= history_edit_widget->toHtml();
search_start_point= -1; /* search starting from end of string */
for (i= 0;;)
{
index_of_statement_start= s.lastIndexOf("\"STATEMENT START\"", search_start_point);
if (index_of_statement_start == -1) return -1; /* (statement start not found) */
index_of_result= s.indexOf("\"RESULT\"", index_of_statement_start);
if (index_of_result == -1) return -1; /* (result not found) */
++i;
if (i >= history_markup_counter) break; /* (we're at the right SELECT) */
search_start_point= index_of_statement_start - 1; /* keep going back */
}
outputs= "";
s= s.mid(index_of_statement_start, index_of_result - index_of_statement_start);
/* At this point s = whatever's between statement start and result */
/* But there might be a series of repeat(prompt-start prompt-end real-statement) till result */
for (;;)
{
index_of_prompt_end= s.indexOf("\"PROMPT END\"", 0);
if (index_of_prompt_end == -1) break;
index_of_a_gt= s.indexOf("a>", index_of_prompt_end) + 2;
if (index_of_a_gt == -1) break;
index_of_lt_a= s.indexOf("<a", index_of_a_gt);
if (index_of_lt_a == -1) break;
outputs.append(s.mid(index_of_a_gt, index_of_lt_a - index_of_a_gt));
s= s.mid(index_of_a_gt, -1);
}
/* At this point outputs = the statement but it might contain entities
so reverse whatever happened in history_markup_copy_for_history(),
and change <br> back to \r.
Todo: check what happens if original statement contains an entity.
*/
final_outputs= "";
for (i= 0; i < outputs.length(); ++i)
{
if (outputs.mid(i, 4) == "<br>")
{
final_outputs.append("\r");
i+= 3;
}
else if (outputs.mid(i, 6) == "<br />")
{
final_outputs.append("\r");
i+= 5;
}
else if (outputs.mid(i, 4) == "&lt;")
{
final_outputs.append("<");
i+= 3;
}
else if (outputs.mid(i, 4) == "&gt;")
{
final_outputs.append(">");
i+= 3;
}
else if (outputs.mid(i, 5) == "&amp;")
{
final_outputs.append("&");
i+= 4;
}
else if (outputs.mid(i, 6) == "&quot;")
{
final_outputs.append("\"");
i+= 5;
}
else
{
c= outputs.mid(i, 1);
final_outputs.append(c);
}
}
statement_edit_widget->setPlainText(final_outputs);
return 0;
}
/*
tee+hist
--------
TEE
* Code related to tee should have the comment somewhere = "for tee"
* bool ocelot_history_tee_file_is_open initially is false
* the options --tee=filename and --no-tee exist, and they are checked (I think)
* the client statements tee filename and notee will be seen
* there are no menu options (todo: decide whether this is a flaw)
* apparently the mysql client would flush, therefore we call flush() after writing
* the mysql client would include results from queries, but we do so only for TEE (todo: decide whether this is a flaw)
* there might be html in the output (todo: decide whether this is a flaw)
HIST
* read http://ocelot.ca/blog/blog/2015/08/04/mysql_histfile-and-mysql_history/
* bool ocelot_history_hist_file_is_open initially is false but it's opened if successful connect
* --batch or --silent or setting name to /deev/null turns history off
EITHER
* Ignore if filename is "", is "/dev/null", or is a link to "/dev/null"
*/
void MainWindow::history_file_write(QString history_type, QString text_line) /* see comment=tee+hist */
{
if (history_type == "TEE")
{
if (ocelot_history_tee_file_is_open == false) return;
}
else
{
if (ocelot_history_hist_file_is_open == false) return;
/* see Wildcard Matching section in http://doc.qt.io/qt-4.8/qregexp.html */
int qfrom= 0;
int qindex;
QString qs;
for (;;)
{
QRegExp rx;
if (qfrom >= ocelot_histignore.length()) break;
qindex= ocelot_histignore.indexOf(":", qfrom, Qt::CaseInsensitive);
if (qindex == -1) qindex= ocelot_histignore.length();
qs= ocelot_histignore.mid(qfrom, qindex - qfrom);
rx= QRegExp(qs);
rx.setPatternSyntax(QRegExp::Wildcard);
rx.setCaseSensitivity(Qt::CaseInsensitive);
if (rx.exactMatch(text_line) == true) return;
qfrom= qindex + 1;
}
}
QString s= text_line;
int query_len= s.toUtf8().size(); /* See comment "UTF8 Conversion" */
char *query= new char[query_len + 1];
memcpy(query, s.toUtf8().constData(), query_len + 1);
query[query_len]= '\0'; /* todo: think: is this necessary? */
if (history_type == "TEE")
{
ocelot_history_tee_file.write(query, strlen(query));
ocelot_history_tee_file.write("\n", strlen("\n"));
ocelot_history_tee_file.flush();
}
else
{
ocelot_history_hist_file.write(query, strlen(query));
ocelot_history_hist_file.write("\n", strlen("\n"));
ocelot_history_hist_file.flush();
}
delete []query;
}
int MainWindow::history_file_start(QString history_type, QString file_name) /* see comment=tee+hist */
{
QString file_name_to_open;
if (history_type == "TEE")
{
file_name_to_open= ocelot_history_tee_file_name;
if (ocelot_history_tee_file_is_open == true)
{
ocelot_history_tee_file.close();
ocelot_history_tee_file_is_open= false;
}
}
else
{
file_name_to_open= ocelot_history_hist_file_name;
if (ocelot_history_hist_file_is_open == true)
{
ocelot_history_hist_file.close();
ocelot_history_hist_file_is_open= false;
}
if (ocelot_batch != 0) return 1; /* if --batch happened, no history */
if (ocelot_silent != 0) return 1; /* if --silent happened, no history */
}
if (file_name != "") file_name_to_open= file_name;
int query_len= file_name_to_open.toUtf8().size(); /* See comment "UTF8 Conversion" */
char *query= new char[query_len + 1];
memcpy(query, file_name_to_open.toUtf8().constData(), query_len + 1);
query[query_len]= '\0'; /* todo: think: is this necessary? */
/* If file name == "/dev/null" or something that links to "/dev/null", don't try to open. */
if (file_name_to_open == "/dev/null") { delete []query; return 0; }
#ifdef OCELOT_OS_LINUX
char tmp_link_file[9 + 1];
if (readlink(query, tmp_link_file, 9 + 1) == 9)
{
if (memcmp(tmp_link_file, "/dev/null", 9) == 0) {delete []query; return 0; }
}
#endif
bool open_result;
if (history_type == "TEE")
{
ocelot_history_tee_file.setFileName(query);
open_result= ocelot_history_tee_file.open(QIODevice::Append | QIODevice::Text);
delete []query;
if (open_result == false) return 0;
ocelot_history_tee_file_is_open= true;
ocelot_history_tee_file_name= file_name_to_open;
return 1;
}
else
{
ocelot_history_hist_file.setFileName(query);
open_result= ocelot_history_hist_file.open(QIODevice::Append | QIODevice::Text);
delete []query;
if (open_result == false) return 0;
ocelot_history_hist_file_is_open= true;
ocelot_history_hist_file_name= file_name_to_open;
return 1;
}
return 0;
}
void MainWindow::history_file_stop(QString history_type) /* see comment=tee+hist */
{
if (history_type == "TEE")
{
ocelot_history_tee_file.close();
ocelot_history_tee_file_is_open= false;
}
else
{
ocelot_history_hist_file.close();
ocelot_history_hist_file_is_open= false;
}
}
/*
This is putting in the history widget, indeed, BUT ...
2. The history widget seems to grow when I type something for the first time
4. Make sure there's no disaster if file is /dev/null or blank.
5. ^P doesn't work, and that's probably because we depend on markup to see statement start.
TODO: Fix for ^P
Nothing happens if --batch or --silent
We try to open the history file during each connect.
If we successfully open, but only the first time, we copy its lines to the history widget.
3. We have to limit the number of lines, since the file might be big.
So we do an fseek to the last 10000 bytes of the file, then skip the first line from there.
*/
#define HISTFILESIZE 10000
void MainWindow::history_file_to_history_widget() /* see comment=tee+hist */
{
FILE *history_file;
if (ocelot_batch != 0) return; /* if --batch happened, no history */
if (ocelot_silent != 0) return; /* if --silent happened, no history */
if (ocelot_history_hist_file_is_copied == true) return; /* we've alredy done this */
//if (ocelot_history_hist_file_is_open == false) return;
{
int query_len= ocelot_history_hist_file_name.toUtf8().size(); /* See comment "UTF8 Conversion" */
char *query= new char[query_len + 1];
memcpy(query, ocelot_history_hist_file_name.toUtf8().constData(), query_len + 1);
query[query_len]= '\0'; /* todo: think: is this necessary? */
history_file= fopen(query, "r");
delete []query;
}
if (history_file == NULL) return;
if (fseek(history_file, 0 , SEEK_END) == 0)
{
int file_size = ftell(history_file);
if (file_size > HISTFILESIZE) fseek(history_file, file_size - HISTFILESIZE, SEEK_SET);
else fseek(history_file, 0, SEEK_SET);
}
char line[4096];
while(fgets(line, sizeof line, history_file) != NULL) /* put all non-"" in history widget */
{
query_utf16= line;
query_utf16= query_utf16.trimmed();
if (query_utf16 > "") history_markup_append("", false);
}
fclose(history_file);
ocelot_history_hist_file_is_copied= true;
query_utf16= er_strings[er_off + ER_START_OF_SESSION];
}
/*
Shortcut duplication
Example:
In create_menu():
connect(menu_file_action_exit, SIGNAL(triggered()), this, SLOT(action_exit()));
menu_file_action_exit->setShortcut(QKeySequence::Quit);
In eventFilter():
if (key->matches(QKeySequence::Quit))
{
action_exit();
return true;
}
With Ubuntu 12.04 the menu shortcut is executed.
With Ubuntu 15.04 the event filter is executed.
That is: the duplication in the event filter is a workaround for the fact that,
on some distro versions, some menu shortcuts are missed. Maybe it's a Qt bug.
Maybe it's an Ubuntu bug: https://bugs.launchpad.net/ubuntu/+source/texmaker/+bug/1386111
*/
/*
Create the menu.
Apparently the menubar already exists because of the call to UiSetup() earlier. Incomprehensible.
Todo: Consider making a table with the menu settings. Something like this ...
Table: ocelot.actions
Window Menu_position Menu_item_position Menu_name Menu_item_name Name Caption/Hint Menu Key Toolbar Action
------ ------------- ------------------ --------- -------------- ---- ------------ ---- --- ------- ------
Main 1 1 File E&xit exit
Main 1 1 File Exitrun Execute Edit &E lightning-bolt
This is creation -- assume there are no existing menus.
There could also be alter -- wipe existing actions as part of user customization, then call this.
We do not want users to get rid of "About" box that shows copyrights etc.
?? How do I get rid of a QAction?
Also set up more with setShortcuts(), setStatusTip(), ...
Also add action to toolbox
This would addMenu() multiple times for the same menu, so I use a different name each time.
I should show what the shortcuts are; unfortunately for most actions the shortcut differs according to platform
Maybe instead of "Execute ctrl+E" I should be using "Run ctrl+R"
Sure, I tried using & for various menu items e.g. E&xit -- but then Alt+x did not work!
*/
void MainWindow::create_menu()
{
menu_file= ui->menuBar->addMenu(menu_strings[menu_off + MENU_FILE]);
/* Todo: consider adding fileMenu = new QMenu(tr("&File"), this); -*/
menu_file_action_connect= menu_file->addAction(menu_strings[menu_off + MENU_FILE_CONNECT]);
connect(menu_file_action_connect, SIGNAL(triggered()), this, SLOT(action_connect()));
shortcut("ocelot_shortcut_connect", "", false, true);
menu_file_action_exit= menu_file->addAction(menu_strings[menu_off + MENU_FILE_EXIT]);
connect(menu_file_action_exit, SIGNAL(triggered()), this, SLOT(action_exit()));
shortcut("ocelot_shortcut_exit", "", false, true);
menu_edit= ui->menuBar->addMenu(menu_strings[menu_off + MENU_EDIT]);
menu_edit_action_undo= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_UNDO]);
connect(menu_edit_action_undo, SIGNAL(triggered()), this, SLOT(menu_edit_undo()));
shortcut("ocelot_shortcut_undo", "", false, true);
menu_edit_action_redo= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_REDO]);
connect(menu_edit_action_redo, SIGNAL(triggered()), this, SLOT(menu_edit_redo()));
shortcut("ocelot_shortcut_redo", "", false, true);
menu_edit->addSeparator();
menu_edit_action_cut= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_CUT]);
connect(menu_edit_action_cut, SIGNAL(triggered()), this, SLOT(menu_edit_cut()));
shortcut("ocelot_shortcut_cut", "", false, true);
menu_edit_action_copy= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_COPY]);
connect(menu_edit_action_copy, SIGNAL(triggered()), this, SLOT(menu_edit_copy()));
shortcut("ocelot_shortcut_copy", "", false, true);
menu_edit_action_paste= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_PASTE]);
connect(menu_edit_action_paste, SIGNAL(triggered()), this, SLOT(menu_edit_paste()));
shortcut("ocelot_shortcut_paste", "", false, true);
menu_edit->addSeparator();
menu_edit_action_select_all= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_SELECT_ALL]);
connect(menu_edit_action_select_all, SIGNAL(triggered()), this, SLOT(menu_edit_select_all()));
shortcut("ocelot_shortcut_select_all", "", false, true);
menu_edit_action_history_markup_previous= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_PREVIOUS_STATEMENT]);
connect(menu_edit_action_history_markup_previous, SIGNAL(triggered()), this, SLOT(history_markup_previous()));
shortcut("ocelot_shortcut_history_markup_previous", "", false, true);
menu_edit_action_history_markup_next= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_NEXT_STATEMENT]);
connect(menu_edit_action_history_markup_next, SIGNAL(triggered()), this, SLOT(history_markup_next()));
shortcut("ocelot_shortcut_history_markup_next", "", false, true);
menu_edit_action_formatter= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_FORMAT]);
connect(menu_edit_action_formatter, SIGNAL(triggered()), this, SLOT(statement_edit_widget_formatter()));
shortcut("ocelot_shortcut_format", "", false, true);
menu_edit_action_zoomin= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_ZOOMIN]);
connect(menu_edit_action_zoomin, SIGNAL(triggered()), this, SLOT(menu_edit_zoomin()));
shortcut("ocelot_shortcut_zoomin", "", false, true);
menu_edit_action_zoomout= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_ZOOMOUT]);
connect(menu_edit_action_zoomout, SIGNAL(triggered()), this, SLOT(menu_edit_zoomout()));
shortcut("ocelot_shortcut_zoomout", "", false, true);
menu_edit_action_autocomplete= menu_edit->addAction(menu_strings[menu_off + MENU_EDIT_AUTOCOMPLETE]);
connect(menu_edit_action_autocomplete, SIGNAL(triggered()), this, SLOT(menu_edit_autocomplete_via_menu()));
shortcut("ocelot_shortcut_autocomplete", "", false, true);
menu_run= ui->menuBar->addMenu(menu_strings[menu_off + MENU_RUN]);
menu_run_action_execute= menu_run->addAction(menu_strings[menu_off + MENU_RUN_EXECUTE]);
connect(menu_run_action_execute, SIGNAL(triggered()), this, SLOT(action_execute_force()));
shortcut("ocelot_shortcut_execute", "", false, true);
menu_run_action_kill= menu_run->addAction(menu_strings[menu_off + MENU_RUN_KILL]);
connect(menu_run_action_kill, SIGNAL(triggered()), this, SLOT(action_kill()));
shortcut("ocelot_shortcut_kill", "", false, true);
menu_run_action_kill->setEnabled(false);
menu_settings= ui->menuBar->addMenu(menu_strings[menu_off + MENU_SETTINGS]);
menu_settings_action_menu= menu_settings->addAction(menu_strings[menu_off + MENU_SETTINGS_MENU]);
menu_settings_action_history= menu_settings->addAction(menu_strings[menu_off + MENU_SETTINGS_HISTORY_WIDGET]);
menu_settings_action_grid= menu_settings->addAction(menu_strings[menu_off + MENU_SETTINGS_GRID_WIDGET]);
menu_settings_action_statement= menu_settings->addAction(menu_strings[menu_off + MENU_SETTINGS_STATEMENT_WIDGET]);
menu_settings_action_extra_rule_1= menu_settings->addAction(menu_strings[menu_off + MENU_SETTINGS_EXTRA_RULE_1]);
connect(menu_settings_action_menu, SIGNAL(triggered()), this, SLOT(action_menu()));
connect(menu_settings_action_history, SIGNAL(triggered()), this, SLOT(action_history()));
connect(menu_settings_action_grid, SIGNAL(triggered()), this, SLOT(action_grid()));
connect(menu_settings_action_statement, SIGNAL(triggered()), this, SLOT(action_statement()));
connect(menu_settings_action_extra_rule_1, SIGNAL(triggered()), this, SLOT(action_extra_rule_1()));
menu_options= ui->menuBar->addMenu(menu_strings[menu_off + MENU_OPTIONS]);
menu_options_action_option_detach_history_widget= menu_options->addAction(menu_strings[menu_off + MENU_OPTIONS_DETACH_HISTORY_WIDGET]);
menu_options_action_option_detach_history_widget->setCheckable(true);
menu_options_action_option_detach_history_widget->setChecked(ocelot_detach_history_widget);
connect(menu_options_action_option_detach_history_widget, SIGNAL(triggered(bool)), this, SLOT(action_option_detach_history_widget(bool)));
menu_options_action_option_detach_result_grid_widget= menu_options->addAction(menu_strings[menu_off + MENU_OPTIONS_DETACH_RESULT_GRID_WIDGET]);
menu_options_action_option_detach_result_grid_widget->setCheckable(true);
menu_options_action_option_detach_result_grid_widget->setChecked(ocelot_detach_result_grid_widget);
connect(menu_options_action_option_detach_result_grid_widget, SIGNAL(triggered(bool)), this, SLOT(action_option_detach_result_grid_widget(bool)));
menu_options_action_option_detach_debug_widget= menu_options->addAction(menu_strings[menu_off + MENU_OPTIONS_DETACH_DEBUG_WIDGET]);
menu_options_action_option_detach_debug_widget->setCheckable(true);
menu_options_action_option_detach_debug_widget->setChecked(ocelot_detach_debug_widget);
connect(menu_options_action_option_detach_debug_widget, SIGNAL(triggered(bool)), this, SLOT(action_option_detach_debug_widget(bool)));
menu_options_action_next_window= menu_options->addAction(menu_strings[menu_off + MENU_OPTIONS_NEXT_WINDOW]);
connect(menu_options_action_next_window, SIGNAL(triggered(bool)), this, SLOT(action_option_next_window()));
shortcut("ocelot_shortcut_next_window", "", false, true);
menu_options_action_previous_window= menu_options->addAction(menu_strings[menu_off + MENU_OPTIONS_PREVIOUS_WINDOW]);
connect(menu_options_action_previous_window, SIGNAL(triggered(bool)), this, SLOT(action_option_previous_window()));
shortcut("ocelot_shortcut_previous_window", "", false, true);
#ifdef DEBUGGER
menu_debug= ui->menuBar->addMenu(menu_strings[menu_off + MENU_DEBUG]);
// menu_debug_action_install= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_INSTALL]);
// connect(menu_debug_action_install, SIGNAL(triggered()), this, SLOT(action_debug_install()));
//=shortcut(?) menu_debug_action_install->setShortcut(QKeySequence(tr("Alt+A")));
// menu_debug_action_setup= menu_debug->addAction(tr("Setup"));
// connect(menu_debug_action_setup, SIGNAL(triggered()), this, SLOT(action_debug_setup()));
//=shortcut(?) menu_debug_action_setup->setShortcut(QKeySequence(tr("Alt+5")));
// menu_debug_action_debug= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG]);
// connect(menu_debug_action_debug, SIGNAL(triggered()), this, SLOT(action_debug_debug()));
//=shortcut(?) menu_debug_action_debug->setShortcut(QKeySequence(tr("Alt+3")));
menu_debug_action_breakpoint= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_BREAKPOINT]);
connect(menu_debug_action_breakpoint, SIGNAL(triggered()), this, SLOT(action_debug_breakpoint()));
shortcut("ocelot_shortcut_breakpoint", "", false, true);
menu_debug_action_continue= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_CONTINUE]);
connect(menu_debug_action_continue, SIGNAL(triggered()), this, SLOT(action_debug_continue()));
shortcut("ocelot_shortcut_continue", "", false, true);
// menu_debug_action_leave= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_LEAVE);
// connect(menu_debug_action_leave, SIGNAL(triggered()), this, SLOT(action_debug_leave()));
//=shortcut(?) menu_debug_action_leave->setShortcut(QKeySequence(tr("Alt+B")));
menu_debug_action_next= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_NEXT]);
connect(menu_debug_action_next, SIGNAL(triggered()), this, SLOT(action_debug_next()));
shortcut("ocelot_shortcut_next", "", false, true);
// menu_debug_action_skip= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_SKIP);
// connect(menu_debug_action_skip, SIGNAL(triggered()), this, SLOT(action_debug_skip()));
//=shortcut(?) menu_debug_action_skip->setShortcut(QKeySequence(tr("Alt+4")));
menu_debug_action_step= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_STEP]);
connect(menu_debug_action_step, SIGNAL(triggered()), this, SLOT(action_debug_step()));
shortcut("ocelot_shortcut_step", "", false, true);
menu_debug_action_clear= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_CLEAR]);
connect(menu_debug_action_clear, SIGNAL(triggered()), this, SLOT(action_debug_clear()));
shortcut("ocelot_shortcut_clear", "", false, true);
// menu_debug_action_delete= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_DELETE]);
// connect(menu_debug_action_delete, SIGNAL(triggered()), this, SLOT(action_debug_delete()));
//=shortcut(?) menu_debug_action_delete->setShortcut(QKeySequence(tr("Alt+G")));
menu_debug_action_exit= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_EXIT]);
connect(menu_debug_action_exit, SIGNAL(triggered()), this, SLOT(action_debug_exit()));
shortcut("ocelot_shortcut_debug_exit", "", false, true);
menu_debug_action_information= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_INFORMATION]);
connect(menu_debug_action_information, SIGNAL(triggered()), this, SLOT(action_debug_information()));
shortcut("ocelot_shortcut_information", "", false, true);
menu_debug_action_refresh_server_variables= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_REFRESH_SERVER_VARIABLES]);
connect(menu_debug_action_refresh_server_variables, SIGNAL(triggered()), this, SLOT(action_debug_refresh_server_variables()));
shortcut("ocelot_shortcut_refresh_server_variables", "", false, true);
menu_debug_action_refresh_user_variables= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_REFRESH_USER_VARIABLES]);
connect(menu_debug_action_refresh_user_variables, SIGNAL(triggered()), this, SLOT(action_debug_refresh_user_variables()));
shortcut("ocelot_shortcut_refresh_user_variables", "", false, true);
menu_debug_action_refresh_variables= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_REFRESH_VARIABLES]);
connect(menu_debug_action_refresh_variables, SIGNAL(triggered()), this, SLOT(action_debug_refresh_variables()));
shortcut("ocelot_shortcut_refresh_variables", "", false, true);
menu_debug_action_refresh_call_stack= menu_debug->addAction(menu_strings[menu_off + MENU_DEBUG_REFRESH_CALL_STACK]);
connect(menu_debug_action_refresh_call_stack, SIGNAL(triggered()), this, SLOT(action_debug_refresh_call_stack()));
shortcut("ocelot_shortcut_refresh_call_stack", "", false, true);
debug_menu_enable_or_disable(TOKEN_KEYWORD_BEGIN); /* Disable most of debug menu */
#endif
menu_help= ui->menuBar->addMenu(menu_strings[menu_off + MENU_HELP]);
menu_help_action_about= menu_help->addAction(menu_strings[menu_off + MENU_HELP_ABOUT]);
connect(menu_help_action_about, SIGNAL(triggered()), this, SLOT(action_about()));
menu_help_action_the_manual= menu_help->addAction(menu_strings[menu_off + MENU_HELP_THE_MANUAL]);
connect(menu_help_action_the_manual, SIGNAL(triggered()), this, SLOT(action_the_manual()));
/* Qt says I should also do "addSeparator" if Motif style. Harmless. */
ui->menuBar->addSeparator();
/* exitAction->setPriority(QAction::LowPriority); */
menu_help_action_libmysqlclient= menu_help->addAction(menu_strings[menu_off + MENU_HELP_LIBMYSQLCLIENT]);
connect(menu_help_action_libmysqlclient, SIGNAL(triggered()), this, SLOT(action_libmysqlclient()));
menu_help_action_settings= menu_help->addAction(menu_strings[menu_off + MENU_HELP_SETTINGS]);
connect(menu_help_action_settings, SIGNAL(triggered()), this, SLOT(action_settings()));
}
/*
Shortcuts -- set variable value, or do setShortcut().
Example:
At the beginning of this program we have a declaration
static char ocelot_shortcut_exit[80]= "default";
The figure 80 is big enough for "Ctrl+Shift+Alt+F12\0" (19) 4 times
but not for multiple keys.
We can change it with a setting in a .cnf file e.g. .my.cnf:
ocelot_shortcut_key = 'Alt+L'
We can change it with a SET statement:
SET ocelot_shortcut_exit = 'Ctrl+q';
or
SET ocelot_shortcut_exit = 'default';
or
SET ocelot_shortcut_exit = 'ALT+A,Alt+B';
See also http://doc.qt.io/qt-4.8/qkeysequence.html#fromString
I have tested
SET ocelot_shortcut_exit = 'Alt+L';
and it works (File|Exit menu shows Alt+L, and Alt+L causes quit).
Thus this changes what's in create_menu(), above.
Called from create_menu(),execute_client_statement(),connect_set_variable().
Return 1 if it's ocelot_shortcut_exit etc., even if it fails.
You might want to "set" e.g. ocelot_shortcut_exit= value;
You might want to "do" e.g. menu_file_action_exit->setShortcut(value);
You might want to both "set" and "do".
Todo: ensure two keys don't have the same action (error check).
Todo: ensure string is valid -- error checks and warnings|errors
(now we check for displayable ASCII or F1-F12, is that okay?)
Todo: syntax checker should see this.
Todo: all the other shortcut keys.
Todo: also, something for "what to execute" e.g. SQL statement.
Todo: return 0 if token1 first characters are not "ocelot_shortcut_"
Todo: The format shortcut should be Ctrl+Shift+F, but isn't.
See the comment about this in ocelotgui.cpp.
Todo: There is no GUI "shortcut editor" here, but such things exist:
see doc.qt.io/qt-5/qkeysequenceedit.html#details.
We could show a table with current settings, and let
users pick or type in a key sequence.
Warn: Although we can handle multiple-key shortcuts,
the workaround for the Qt/Ubuntu bug in eventfilter_function
only looks at one key.
Warn: We don't say default = QKeySequence::Zoomin for zoomin, because
it is Ctrl++ (Qt::CTRL + Qt::Key_Plus). But + requires shift.
It seems more common to use without shift, which is Ctrl+=.
*/
int MainWindow::shortcut(QString token1, QString token3, bool is_set, bool is_do)
{
QString target= token1.toLower();
QString source;
char source_as_utf8[80*4];
if (target.mid(0, 16) != "ocelot_shortcut_") return 0;
if (is_set)
{
source= connect_stripper(token3, false);
if (source.length() >= 80) return -1;
source= source.toLower();
strcpy(source_as_utf8, source.toUtf8());
if (strlen(source_as_utf8) >= 80) return -1;
if (strcmp(source_as_utf8, "default") != 0)
{
QKeySequence k= QKeySequence(source_as_utf8);
if ((k.count() < 1) || (k.count() > 4)) return -1;
if (k.isEmpty()) return -1;
if (k.toString() < " ") return -1;
for (unsigned int i= 0; i < (unsigned) k.count(); ++i)
{
int qi= k.operator[](i);
qi= (qi & ~(Qt::CTRL | Qt::SHIFT | Qt::ALT | Qt::META));
bool is_ok= false;
if ((qi >= Qt::Key_Space) && (qi <= Qt::Key_ydiaeresis)) is_ok= true;
if ((qi >= Qt::Key_F1) && (qi <= Qt::Key_F12)) is_ok= true;
if (is_ok == false) return -1;
}
}
}
if (target == "ocelot_shortcut_connect")
{
if (is_set) strcpy(ocelot_shortcut_connect, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_connect, "default") == 0)
ocelot_shortcut_connect_keysequence= QKeySequence::Open;
else
ocelot_shortcut_connect_keysequence= QKeySequence(ocelot_shortcut_connect);
menu_file_action_connect->setShortcut(ocelot_shortcut_connect_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_exit")
{
if (is_set) strcpy(ocelot_shortcut_exit, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_exit, "default") == 0)
/* With Puppy Linux --non-KDE non-Gnome -- QKeySequence::Quit fails. */
ocelot_shortcut_exit_keysequence= QKeySequence("Ctrl+Q");
else
ocelot_shortcut_exit_keysequence= QKeySequence(ocelot_shortcut_exit);
menu_file_action_exit->setShortcut(ocelot_shortcut_exit_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_undo")
{
if (is_set) strcpy(ocelot_shortcut_undo, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_undo, "default") == 0)
ocelot_shortcut_undo_keysequence= QKeySequence::Undo;
else
ocelot_shortcut_undo_keysequence= QKeySequence(ocelot_shortcut_undo);
menu_edit_action_undo->setShortcut(ocelot_shortcut_undo_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_redo")
{
if (is_set) strcpy(ocelot_shortcut_redo, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_redo, "default") == 0)
ocelot_shortcut_redo_keysequence= QKeySequence::Redo;
else
ocelot_shortcut_redo_keysequence= QKeySequence(ocelot_shortcut_redo);
menu_edit_action_redo->setShortcut(ocelot_shortcut_redo_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_cut")
{
if (is_set) strcpy(ocelot_shortcut_cut, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_cut, "default") == 0)
ocelot_shortcut_cut_keysequence= QKeySequence::Cut;
else
ocelot_shortcut_cut_keysequence= QKeySequence(ocelot_shortcut_cut);
menu_edit_action_cut->setShortcut(ocelot_shortcut_cut_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_copy")
{
if (is_set) strcpy(ocelot_shortcut_copy, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_copy, "default") == 0)
ocelot_shortcut_copy_keysequence= QKeySequence::Copy;
else
ocelot_shortcut_copy_keysequence= QKeySequence(ocelot_shortcut_copy);
menu_edit_action_copy->setShortcut(ocelot_shortcut_copy_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_paste")
{
if (is_set) strcpy(ocelot_shortcut_paste, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_paste, "default") == 0)
ocelot_shortcut_paste_keysequence= QKeySequence::Paste;
else
ocelot_shortcut_paste_keysequence= QKeySequence(ocelot_shortcut_paste);
menu_edit_action_paste->setShortcut(ocelot_shortcut_paste_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_select_all")
{
if (is_set) strcpy(ocelot_shortcut_select_all, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_select_all, "default") == 0)
ocelot_shortcut_select_all_keysequence= QKeySequence::SelectAll;
else
ocelot_shortcut_select_all_keysequence= QKeySequence(ocelot_shortcut_select_all);
menu_edit_action_select_all->setShortcut(ocelot_shortcut_select_all_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_history_markup_previous")
{
if (is_set) strcpy(ocelot_shortcut_history_markup_previous, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_history_markup_previous, "default") == 0)
ocelot_shortcut_history_markup_previous_keysequence= QKeySequence("Ctrl+P");
else
ocelot_shortcut_history_markup_previous_keysequence= QKeySequence(ocelot_shortcut_history_markup_previous);
menu_edit_action_history_markup_previous->setShortcut(ocelot_shortcut_history_markup_previous_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_history_markup_next")
{
if (is_set) strcpy(ocelot_shortcut_history_markup_next, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_history_markup_next, "default") == 0)
ocelot_shortcut_history_markup_next_keysequence= QKeySequence("Ctrl+N");
else
ocelot_shortcut_history_markup_next_keysequence= QKeySequence(ocelot_shortcut_history_markup_next);
menu_edit_action_history_markup_next->setShortcut(ocelot_shortcut_history_markup_next_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_format")
{
if (is_set) strcpy(ocelot_shortcut_format, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_format, "default") == 0)
ocelot_shortcut_format_keysequence= QKeySequence("Alt+Shift+F");
else
ocelot_shortcut_format_keysequence= QKeySequence(ocelot_shortcut_format);
menu_edit_action_formatter->setShortcut(ocelot_shortcut_format_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_zoomin")
{
if (is_set) strcpy(ocelot_shortcut_zoomin, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_zoomin, "default") == 0)
ocelot_shortcut_zoomin_keysequence= QKeySequence("Ctrl+=");
else
ocelot_shortcut_zoomin_keysequence= QKeySequence(ocelot_shortcut_zoomin);
menu_edit_action_zoomin->setShortcut(ocelot_shortcut_zoomin_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_zoomout")
{
if (is_set) strcpy(ocelot_shortcut_zoomout, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_zoomout, "default") == 0)
ocelot_shortcut_zoomout_keysequence= QKeySequence::ZoomOut;
else
ocelot_shortcut_zoomout_keysequence= QKeySequence(ocelot_shortcut_zoomout);
menu_edit_action_zoomout->setShortcut(ocelot_shortcut_zoomout_keysequence);
}
return 1;
}
/* Todo: Tab is like mysql but a poor default choice. Try Ctrl+Space? */
if (target == "ocelot_shortcut_autocomplete")
{
if (is_set) strcpy(ocelot_shortcut_autocomplete, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_autocomplete, "default") == 0)
ocelot_shortcut_autocomplete_keysequence= QKeySequence("Tab");
else
ocelot_shortcut_autocomplete_keysequence= QKeySequence(ocelot_shortcut_autocomplete);
menu_edit_action_autocomplete->setShortcut(ocelot_shortcut_autocomplete_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_execute")
{
if (is_set) strcpy(ocelot_shortcut_execute, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_execute, "default") == 0)
ocelot_shortcut_execute_keysequence= QKeySequence("Ctrl+E");
else
ocelot_shortcut_execute_keysequence= QKeySequence(ocelot_shortcut_execute);
menu_run_action_execute->setShortcut(ocelot_shortcut_execute_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_kill")
{
if (is_set) strcpy(ocelot_shortcut_kill, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_kill, "default") == 0)
ocelot_shortcut_kill_keysequence= QKeySequence("Ctrl+C");
else
ocelot_shortcut_kill_keysequence= QKeySequence(ocelot_shortcut_kill);
menu_run_action_kill->setShortcut(ocelot_shortcut_kill_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_next_window")
{
if (is_set) strcpy(ocelot_shortcut_next_window, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_next_window, "default") == 0)
ocelot_shortcut_next_window_keysequence= QKeySequence::NextChild;
else
ocelot_shortcut_next_window_keysequence= QKeySequence(ocelot_shortcut_next_window);
menu_options_action_next_window->setShortcut(ocelot_shortcut_next_window_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_previous_window")
{
if (is_set) strcpy(ocelot_shortcut_previous_window, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_previous_window, "default") == 0)
ocelot_shortcut_previous_window_keysequence= QKeySequence::PreviousChild;
else
ocelot_shortcut_previous_window_keysequence= QKeySequence(ocelot_shortcut_previous_window);
menu_options_action_previous_window->setShortcut(ocelot_shortcut_previous_window_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_breakpoint")
{
if (is_set) strcpy(ocelot_shortcut_breakpoint, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_breakpoint, "default") == 0)
ocelot_shortcut_breakpoint_keysequence= QKeySequence("Alt+1");
else
ocelot_shortcut_breakpoint_keysequence= QKeySequence(ocelot_shortcut_breakpoint);
menu_debug_action_breakpoint->setShortcut(ocelot_shortcut_breakpoint_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_continue")
{
if (is_set) strcpy(ocelot_shortcut_continue, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_continue, "default") == 0)
ocelot_shortcut_continue_keysequence= QKeySequence("Alt+2");
else
ocelot_shortcut_continue_keysequence= QKeySequence(ocelot_shortcut_continue);
menu_debug_action_continue->setShortcut(ocelot_shortcut_continue_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_next")
{
if (is_set) strcpy(ocelot_shortcut_next, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_next, "default") == 0)
ocelot_shortcut_next_keysequence= QKeySequence("Alt+3");
else
ocelot_shortcut_next_keysequence= QKeySequence(ocelot_shortcut_next);
menu_debug_action_next->setShortcut(ocelot_shortcut_next_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_step")
{
if (is_set) strcpy(ocelot_shortcut_step, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_step, "default") == 0)
ocelot_shortcut_step_keysequence= QKeySequence("Alt+5");
else
ocelot_shortcut_step_keysequence= QKeySequence(ocelot_shortcut_step);
menu_debug_action_step->setShortcut(ocelot_shortcut_step_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_clear")
{
if (is_set) strcpy(ocelot_shortcut_clear, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_clear, "default") == 0)
ocelot_shortcut_clear_keysequence= QKeySequence("Alt+6");
else
ocelot_shortcut_clear_keysequence= QKeySequence(ocelot_shortcut_clear);
menu_debug_action_clear->setShortcut(ocelot_shortcut_clear_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_debug_exit")
{
if (is_set) strcpy(ocelot_shortcut_debug_exit, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_debug_exit, "default") == 0)
ocelot_shortcut_debug_exit_keysequence= QKeySequence("Alt+7");
else
ocelot_shortcut_debug_exit_keysequence= QKeySequence(ocelot_shortcut_debug_exit);
menu_debug_action_exit->setShortcut(ocelot_shortcut_debug_exit_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_information")
{
if (is_set) strcpy(ocelot_shortcut_information, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_information, "default") == 0)
ocelot_shortcut_information_keysequence= QKeySequence("Alt+8");
else
ocelot_shortcut_information_keysequence= QKeySequence(ocelot_shortcut_information);
menu_debug_action_information->setShortcut(ocelot_shortcut_information_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_refresh_server_variables")
{
if (is_set) strcpy(ocelot_shortcut_refresh_server_variables, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_refresh_server_variables, "default") == 0)
ocelot_shortcut_refresh_server_variables_keysequence= QKeySequence("Alt+9");
else
ocelot_shortcut_refresh_server_variables_keysequence= QKeySequence(ocelot_shortcut_refresh_server_variables);
menu_debug_action_refresh_server_variables->setShortcut(ocelot_shortcut_refresh_server_variables_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_refresh_user_variables")
{
if (is_set) strcpy(ocelot_shortcut_refresh_user_variables, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_refresh_user_variables, "default") == 0)
ocelot_shortcut_refresh_user_variables_keysequence= QKeySequence("Alt+0");
else
ocelot_shortcut_refresh_user_variables_keysequence= QKeySequence(ocelot_shortcut_refresh_user_variables);
menu_debug_action_refresh_user_variables->setShortcut(ocelot_shortcut_refresh_user_variables_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_refresh_variables")
{
if (is_set) strcpy(ocelot_shortcut_refresh_variables, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_refresh_variables, "default") == 0)
ocelot_shortcut_refresh_variables_keysequence= QKeySequence("Alt+A");
else
ocelot_shortcut_refresh_variables_keysequence= QKeySequence(ocelot_shortcut_refresh_variables);
menu_debug_action_refresh_variables->setShortcut(ocelot_shortcut_refresh_variables_keysequence);
}
return 1;
}
if (target == "ocelot_shortcut_refresh_call_stack")
{
if (is_set) strcpy(ocelot_shortcut_refresh_call_stack, source_as_utf8);
if (is_do)
{
if (strcmp(ocelot_shortcut_refresh_call_stack, "default") == 0)
ocelot_shortcut_refresh_call_stack_keysequence= QKeySequence("Alt+B");
else
ocelot_shortcut_refresh_call_stack_keysequence= QKeySequence(ocelot_shortcut_refresh_call_stack);
menu_debug_action_refresh_call_stack->setShortcut(ocelot_shortcut_refresh_call_stack_keysequence);
}
}
return 0;
}
/*
Edit Menu Dispatcher
We have only one edit menu, but we have multiple edit widgets.
So the connect-signal-slot code in create_menu() jumps to menu_edit slots.
The alternatives were: Multiple Document Interface, or multiple windows. I thought this was easier.
For example, for cut: the slot is menu_edit_cut, it gets the focused widget and calls its cut().
But Undo and Redo are complicated: for some widgets they're enabled, for some widgets they're not.
TODO: We are not enabling|disabling edit menu items properly. We should be saying (examples):
Initially, menu_edit_action_cut->setEnabled(false);
When creating any editor widget,
connect(widget, SIGNAL(copyAvailable(bool)),menu_edit_action_cut, SLOT(setEnabled(bool)));
When we want to know if something can be pasted,
connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(processClipboardChange()));
qMimeData* x= clipboard->mimeData();
qStringList* y= x->formats();
int z= y->size();
if (z > 0) menu_edit_action_paste->setEnabled(); << wrong, you only know if paste is possible
If something is selected and focus changes: de-select
TODO: I have no idea what "else if (strcmp(class_name, "+") == 0)"
is for, maybe it's a bug. Compare menu_edit_redo().
*/
void MainWindow::menu_edit_undo()
{
QWidget* focus_widget= QApplication::focusWidget();
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
action_undo();
else if (strcmp(class_name, "TextEditWidget") == 0)
qobject_cast<TextEditWidget*>(focus_widget)->undo();
else if (strcmp(class_name, "TextEditHistory") == 0)
qobject_cast<TextEditHistory*>(focus_widget)->undo();
else if (strcmp(class_name, "QTextEdit") == 0)
qobject_cast<QTextEdit*>(focus_widget)->undo();
else if (strcmp(class_name, "+") == 0)
qobject_cast<QTextEdit*>(focus_widget)->undo();
}
void MainWindow::menu_edit_redo()
{
QWidget* focus_widget= QApplication::focusWidget();
if (focus_widget == 0) return; /* This would be unexpected */
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
action_redo();
else if (strcmp(class_name, "TextEditWidget") == 0)
qobject_cast<TextEditWidget*>(focus_widget)->redo();
else if (strcmp(class_name, "TextEditHistory") == 0)
qobject_cast<TextEditHistory*>(focus_widget)->redo();
else if (strcmp(class_name, "QTextEdit") == 0)
qobject_cast<QTextEdit*>(focus_widget)->redo();
}
void MainWindow::menu_edit_cut()
{
QWidget* focus_widget= QApplication::focusWidget();
if (focus_widget == 0) return; /* This would be unexpected */
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
qobject_cast<CodeEditor*>(focus_widget)->cut();
else if (strcmp(class_name, "TextEditWidget") == 0)
qobject_cast<TextEditWidget*>(focus_widget)->cut();
else if (strcmp(class_name, "TextEditHistory") == 0)
qobject_cast<TextEditHistory*>(focus_widget)->cut();
else if (strcmp(class_name, "QTextEdit") == 0)
qobject_cast<QTextEdit*>(focus_widget)->cut();
}
void MainWindow::menu_edit_copy()
{
QWidget* focus_widget= QApplication::focusWidget();
if (focus_widget == 0) return; /* This would be unexpected */
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
qobject_cast<CodeEditor*>(focus_widget)->copy();
else if (strcmp(class_name, "TextEditWidget") == 0)
/* see TextEditWidget::copy() later in this file */
qobject_cast<TextEditWidget*>(focus_widget)->copy();
else if (strcmp(class_name, "TextEditHistory") == 0)
qobject_cast<TextEditHistory*>(focus_widget)->copy();
else if (strcmp(class_name, "QTextEdit") == 0)
qobject_cast<QTextEdit*>(focus_widget)->copy();
}
void MainWindow::menu_edit_paste()
{
QWidget* focus_widget= QApplication::focusWidget();
if (focus_widget == 0) return; /* This would be unexpected */
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
qobject_cast<CodeEditor*>(focus_widget)->paste();
else if (strcmp(class_name, "TextEditWidget") == 0)
qobject_cast<TextEditWidget*>(focus_widget)->paste();
if (strcmp(class_name, "TextEditHistory") == 0)
qobject_cast<TextEditHistory*>(focus_widget)->paste();
else if (strcmp(class_name, "QTextEdit") == 0)
qobject_cast<QTextEdit*>(focus_widget)->paste();
}
void MainWindow::menu_edit_select_all()
{
QWidget* focus_widget= QApplication::focusWidget();
if (focus_widget == 0) return; /* This would be unexpected */
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
qobject_cast<CodeEditor*>(focus_widget)->selectAll();
else if (strcmp(class_name, "TextEditWidget") == 0)
qobject_cast<TextEditWidget*>(focus_widget)->selectAll();
else if (strcmp(class_name, "TextEditHistory") == 0)
qobject_cast<TextEditHistory*>(focus_widget)->selectAll();
else if (strcmp(class_name, "QTextEdit") == 0)
qobject_cast<QTextEdit*>(focus_widget)->selectAll();
}
/*
zoom in/out won't work by calling default zoomIn()/zoomOut(), probably
because we use style sheets. So we'll get the current style
sheet, add an increment (currently always +1 or -1), change
the style sheet. This does not affect what's set with Settings.
That's deliberate. Zooming is temporary, setting is permanent.
Re extracting: Compare what we do with get_font_from_style_sheet().
Todo: Changing font size won't affect images, we need special handling.
Todo: Maybe we should qobject_cast for setStyleSheet() too.
Todo: BUG: If you just click on a ResultGrid cell, thinking you have
given it the focus, and then you click Ctrl++ or Ctrl-- i.e.
the shortcut not the menu item, it doesn't recognize it.
Click away, then come back, and it does recognize it.
*/
void MainWindow::menu_edit_zoominorout(int increment)
{
QWidget* focus_widget= QApplication::focusWidget();
if (focus_widget == 0) return; /* This would be unexpected */
QString old_stylesheet;
const char *class_name= focus_widget->metaObject()->className();
if (strcmp(class_name, "CodeEditor") == 0)
old_stylesheet= qobject_cast<CodeEditor*>(focus_widget)->styleSheet();
else if (strcmp(class_name, "TextEditWidget") == 0)
old_stylesheet= qobject_cast<TextEditWidget*>(focus_widget)->styleSheet();
else if (strcmp(class_name, "TextEditHistory") == 0)
old_stylesheet= qobject_cast<TextEditHistory*>(focus_widget)->styleSheet();
else if (strcmp(class_name, "QTextEdit") == 0)
old_stylesheet= qobject_cast<QTextEdit*>(focus_widget)->styleSheet();
else return; /* This would be unexpected. */
int i= old_stylesheet.indexOf("font-size:");
if (i == -1) return;
i+= sizeof("font-size:");
int j= old_stylesheet.indexOf("pt", i);
if (j == -1) return;
QString old_font_size= old_stylesheet.mid(i-1, j-(i-1));
int font_size= old_font_size.toInt();
font_size+= increment;
if (font_size < FONT_SIZE_MIN) font_size= FONT_SIZE_MIN;
if (font_size > FONT_SIZE_MAX) font_size= FONT_SIZE_MAX;
QString new_stylesheet= old_stylesheet.mid(0, i-1);
new_stylesheet.append(QString::number(font_size));
new_stylesheet.append(old_stylesheet.mid(j,old_stylesheet.size()-j));
focus_widget->setStyleSheet(new_stylesheet);
return;
}
void MainWindow::menu_edit_zoomin()
{
menu_edit_zoominorout(FONT_SIZE_ZOOM_INCREMENT);
}
void MainWindow::menu_edit_zoomout()
{
menu_edit_zoominorout(-FONT_SIZE_ZOOM_INCREMENT);
}
/*
The autocomplete shortcut is handled by event filter which
calls keypress_shortcut_handler() which calls menu_edit_autocomplete()
directly. Therefore we can only get here if the user chooses
the autocomplete menu item rather than press a shortcut key.
And in that case we don't care if the return is false.
*/
void MainWindow::menu_edit_autocomplete_via_menu()
{
menu_edit_autocomplete();
}
/*
Called from edit menu choice = autocomplete or keypress_shortcut_handler().
Depends on rehash, ocelot_auto_rehash etc. e.g. was REHASH called.
The extra checking here applies because default key is Tab Qt::Key_Tab,
we want to return false if we don't handle it as a shortcut,
so that it will simply be added to the widget contents.
*/
bool MainWindow::menu_edit_autocomplete()
{
QString text;
if (ocelot_auto_rehash > 0)
{
if (hparse_line_edit->isHidden() == false)
{
if (statement_edit_widget->hasFocus() == true)
{
QString s= hparse_line_edit->text();
int word_start= s.indexOf(": ", 0);
int word_end= s.indexOf(" ", word_start + 2);
if (word_end == -1) word_end= s.size();
QString word= s.mid(word_start + 2, (word_end - word_start) - 1);
int i;
for (i= 0; main_token_lengths[i] != 0; ++i)
{
if ((main_token_flags[i] & TOKEN_FLAG_IS_ERROR) != 0) break;
}
if ((main_token_flags[i] & TOKEN_FLAG_IS_ERROR) == 0) return true;
text= statement_edit_widget->toPlainText();
if (word.left(1) != "[")
{
int offset;
if (main_token_lengths[i] == 0)
{
offset= text.size();
word= " " + word;
}
else offset= main_token_offsets[i];
QString new_text= text.left(offset);
new_text.append(word);
int rest_start= offset + main_token_lengths[i];
new_text.append(text.right(text.size() - rest_start));
statement_edit_widget->setPlainText(new_text);
QTextCursor c= statement_edit_widget->textCursor();
c.movePosition(QTextCursor::End);
statement_edit_widget->setTextCursor(c);
return true;
}
}
}
}
return false;
}
/*
The required size of main_token_offsets|lengths|types|flags is
#-of-tokens + 1. We allocate lots more than enough after calculating
x = (size-in-bytes + 1) rounded up to nearest 1000.
We never reduce and perhaps that will appear to be a memory leak.
todo: check if 'new' fails.
*/
void MainWindow::main_token_new(int text_size)
{
unsigned int desired_count;
desired_count= text_size + 1;
if (desired_count >= main_token_max_count)
{
if (main_token_max_count != 0)
{
delete [] main_token_offsets;
delete [] main_token_lengths;
delete [] main_token_types;
delete [] main_token_flags;
delete [] main_token_pointers;
delete [] main_token_reftypes;
}
desired_count= (desired_count - (desired_count % 1000)) + 1000;
main_token_offsets= new int[desired_count];
main_token_lengths= new int[desired_count];
main_token_lengths[0]= 0;
main_token_types= new int[desired_count];
main_token_flags= new unsigned int[desired_count];
main_token_pointers= new int[desired_count];
main_token_reftypes= new unsigned char[desired_count];
main_token_max_count= desired_count;
}
}
/* I'm now defining HAVE_PUSH_AND_POP for the debugger stub */
#define HAVE_PUSH_AND_POP 1
#ifdef HAVE_PUSH_AND_POP
/*
Saving and restoring the main_token variables.
This was conceived for a trick with subqueries that I didn't do,
but $setup uses it now. The problem is that main_token_*
variables are global (okay, bad early laziness, but refactoring
now would be hard). So save them when you're about to parse
something else e.g. a local subquery, and restore when the
subquery is done. Optimistically I call this push + pop, but
in fact can only save one level. Expect text to be saved elsewhere.
*/
void MainWindow::main_token_push()
{
assert(sizeof(main_token_offsets[0] == sizeof(int)));
assert(sizeof(main_token_lengths[0] == sizeof(int)));
assert(sizeof(main_token_types[0] == sizeof(int)));
assert(sizeof(main_token_flags[0] == sizeof(unsigned int)));
assert(sizeof(main_token_pointers[0] == sizeof(int)));
assert(sizeof(main_token_reftypes[0] == sizeof(unsigned char)));
saved_main_token_count_in_all= main_token_count_in_all;
saved_main_token_count_in_statement= main_token_count_in_statement;
saved_main_token_number= main_token_number;
saved_main_token_offsets= new int[saved_main_token_count_in_all];
saved_main_token_lengths= new int[saved_main_token_count_in_all];
saved_main_token_types= new int[saved_main_token_count_in_all];
saved_main_token_flags= new unsigned int[saved_main_token_count_in_all];
saved_main_token_pointers= new int[saved_main_token_count_in_all];
saved_main_token_reftypes= new unsigned char[saved_main_token_count_in_all];
memcpy(saved_main_token_offsets, main_token_offsets, saved_main_token_count_in_all * sizeof(int));
memcpy(saved_main_token_lengths, main_token_lengths, saved_main_token_count_in_all * sizeof(int));
memcpy(saved_main_token_types, main_token_types, saved_main_token_count_in_all * sizeof(int));
memcpy(saved_main_token_flags, main_token_flags, saved_main_token_count_in_all * sizeof(unsigned int));
memcpy(saved_main_token_pointers, main_token_pointers, saved_main_token_count_in_all * sizeof(int));
memcpy(saved_main_token_reftypes, main_token_reftypes, saved_main_token_count_in_all * sizeof(unsigned char));
}
void MainWindow::main_token_pop()
{
memcpy(main_token_offsets, saved_main_token_offsets, saved_main_token_count_in_all * sizeof(int));
memcpy(main_token_lengths, saved_main_token_lengths, saved_main_token_count_in_all * sizeof(int));
memcpy(main_token_types, saved_main_token_types, saved_main_token_count_in_all * sizeof(int));
memcpy(main_token_flags, saved_main_token_flags, saved_main_token_count_in_all * sizeof(unsigned int));
memcpy(main_token_pointers, saved_main_token_pointers, saved_main_token_count_in_all * sizeof(int));
memcpy(main_token_reftypes, saved_main_token_reftypes, saved_main_token_count_in_all * sizeof(unsigned char));
delete [] saved_main_token_reftypes;
delete [] saved_main_token_pointers;
delete [] saved_main_token_flags;
delete [] saved_main_token_types;
delete [] saved_main_token_lengths;
delete [] saved_main_token_offsets;
main_token_count_in_all= saved_main_token_count_in_all;
main_token_count_in_statement= saved_main_token_count_in_statement;
main_token_number= saved_main_token_number;
}
#endif
/*
ACTIONS
! All action_ functions must be in the "public slots" area of ocelotgui.h
*/
/*
action_statement_edit_widget_text_changed() is a slot.
Actually the connect() was for statement_edit_widget->document()'s
contentsChanged(), rather than statement_edit_widget's textChanged(),
but I don't see any difference.
This routine can cause the document to change, causing a signal
and we get action_statement_edit_widget_text_changed() again.
It would be infinite. We have two ways to prevent the loop, and we
use them both (yes it's redundant but this loop really worries me):
exit if a global flag is on
disconnect to ensure this slot won't be activated by signals that it itself generates.
Todo: A hang occurs sometimes, and log() suggests it's in this routine.
That's why there are so many log() and assert() invocations.
Until May 2017 I used blockSignals(true)+blockSignals(false) instead
of disconnect+connect; also I set statement_edit_widget_text_changed_flag= 1;
within the routine; I hope these changes are safe.
*/
void MainWindow::action_statement_edit_widget_text_changed(int position,int chars_removed,int chars_added)
{
log("action_statement_edit_widget_text_changed start", 90);
if (statement_edit_widget_text_changed_flag != 0)
{
position_for_redo= position;
chars_removed_for_redo= chars_removed;
chars_added_for_redo= chars_added;
return;
}
log("action_statement_edit_widget_text_changed after flag check", 90);
//statement_edit_widget_text_changed_flag= 1;
//statement_edit_widget->document()->blockSignals(true);
disconnect(statement_edit_widget->document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(action_statement_edit_widget_text_changed(int,int,int)));
QString text;
int i;
int pos;
/* Syntax highlighting */
text= statement_edit_widget->toPlainText(); /* or I could just pass this to tokenize() directly */
/* Todo: avoid total tokenize() + tokens_to_keywords() if user is just adding at end */
main_token_new(text.size());
tokenize(text.data(),
text.size(),
main_token_lengths, main_token_offsets, main_token_max_count,
(QChar*)"33333", 1, ocelot_delimiter_str, 1);
tokens_to_keywords(text, 0, sql_mode_ansi_quotes);
if (((ocelot_statement_syntax_checker.toInt()) & FLAG_FOR_HIGHLIGHTS) != 0)
{
hparse_f_multi_block(text); /* recognizer */
}
log("action_statement_edit_widget_text_changed after hparse_f_multi_block", 90);
/* This "sets" the colour, it does not "merge" it. */
/* Do not try to set underlines, they won't go away. */
QTextDocument *pDoc= statement_edit_widget->document();
QTextCursor cur(pDoc);
/* cur.select (QTextCursor::Document); */ /* desperate attempt to fix so undo/redo is not destroyed ... does not work */
QTextCharFormat format_of_literal;
format_of_literal.setForeground(QColor(qt_color(ocelot_statement_highlight_literal_color)));
QTextCharFormat format_of_identifier;
format_of_identifier.setForeground(QColor(qt_color(ocelot_statement_highlight_identifier_color)));
QTextCharFormat format_of_comment;
format_of_comment.setForeground(QColor(qt_color(ocelot_statement_highlight_comment_color)));
QTextCharFormat format_of_operator;
format_of_operator.setForeground(QColor(qt_color(ocelot_statement_highlight_operator_color)));
QTextCharFormat format_of_reserved_word;
format_of_reserved_word.setForeground(QColor(qt_color(ocelot_statement_highlight_keyword_color)));
QTextCharFormat format_of_function;
format_of_function.setForeground(QColor(qt_color(ocelot_statement_highlight_function_color)));
QTextCharFormat format_of_other;
format_of_other.setForeground(QColor(qt_color(ocelot_statement_text_color)));
pos= 0;
/* cur.setPosition(pos, QTextCursor::KeepAnchor); */
cur.joinPreviousEditBlock(); /* was cur.beginEditBlock() till 2017-07-23 */
/* ought to affect undo/redo stack? */
{
/* This sets everything to normal format / no underline. Gets overridden by token formats. */
QTextCharFormat format_of_white_space;
cur.setPosition(0, QTextCursor::MoveAnchor);
cur.setPosition(text.size(), QTextCursor::KeepAnchor);
format_of_white_space= format_of_other;
format_of_white_space.setUnderlineStyle(QTextCharFormat::NoUnderline);
cur.setCharFormat(format_of_white_space);
}
log("action_statement_edit_widget_text_changed loop start", 90);
for (i= 0; main_token_lengths[i] != 0; ++i)
{
assert(main_token_lengths[i] > 0);
assert(main_token_lengths[i] <= text.size());
assert(main_token_offsets[i] >= 0);
assert(main_token_offsets[i] <= text.size());
assert(pos >= 0);
assert(pos <= text.size());
assert(i >= 0);
assert(i <= (int) main_token_max_count);
QTextCharFormat format_of_current_token;
/* Todo: find out why this is necessary. It looks redundant but without it there's trouble. */
for (; pos < main_token_offsets[i]; ++pos)
{
cur.setPosition(pos, QTextCursor::MoveAnchor);
format_of_current_token= format_of_other;
format_of_current_token.setUnderlineStyle(QTextCharFormat::NoUnderline);
cur.setCharFormat(format_of_current_token);
/* cur.clearSelection(); */
}
int t= main_token_types[i];
if (t == TOKEN_TYPE_LITERAL_WITH_SINGLE_QUOTE) format_of_current_token= format_of_literal;
if (t == TOKEN_TYPE_LITERAL_WITH_DOUBLE_QUOTE) format_of_current_token= format_of_literal;
if (t == TOKEN_TYPE_LITERAL_WITH_DIGIT) format_of_current_token= format_of_literal;
/* literal_with_brace == literal */
if (t == TOKEN_TYPE_LITERAL_WITH_BRACE) format_of_current_token= format_of_literal; /* obsolete? */
if (t == TOKEN_TYPE_IDENTIFIER_WITH_BACKTICK) format_of_current_token= format_of_identifier;
if (t == TOKEN_TYPE_IDENTIFIER_WITH_DOUBLE_QUOTE) format_of_current_token= format_of_identifier;
if (t == TOKEN_TYPE_IDENTIFIER) format_of_current_token= format_of_identifier;
if (t == TOKEN_TYPE_IDENTIFIER_WITH_AT) format_of_current_token= format_of_identifier;
if (t == TOKEN_TYPE_COMMENT_WITH_SLASH) format_of_current_token= format_of_comment;
if (t == TOKEN_TYPE_COMMENT_WITH_OCTOTHORPE) format_of_current_token= format_of_comment;
if (t == TOKEN_TYPE_COMMENT_WITH_MINUS) format_of_current_token= format_of_comment;
if (t == TOKEN_TYPE_OPERATOR) format_of_current_token= format_of_operator;
if (t >= TOKEN_KEYWORDS_START)
{
if (((main_token_flags[i] & TOKEN_FLAG_IS_FUNCTION) != 0)
&& (main_token_lengths[i + 1] == 1)
&& (text.mid(main_token_offsets[i + 1], 1) == "("))
{
format_of_current_token= format_of_function;
}
else format_of_current_token= format_of_reserved_word;
}
if (t == TOKEN_TYPE_OTHER) format_of_current_token= format_of_other;
/* Todo: consider using SpellCheckUnderline instead of WaveUnderline. */
if ((main_token_flags[i] & TOKEN_FLAG_IS_ERROR) != 0)
{
format_of_current_token.setUnderlineStyle(QTextCharFormat::WaveUnderline);
format_of_current_token.setUnderlineColor(Qt::red);
}
else
{
//format_of_current_token.setUnderlineStyle(QTextCharFormat::NoUnderline);
}
cur.setPosition(pos, QTextCursor::MoveAnchor);
cur.setPosition(pos + main_token_lengths[i], QTextCursor::KeepAnchor);
cur.setCharFormat(format_of_current_token);
pos+= main_token_lengths[i];
}
log("action_statement_edit_widget_text_changed loop end", 90);
cur.endEditBlock();
/* Todo: consider what to do about trailing whitespace. */
widget_sizer(); /* Perhaps adjust relative sizes of the main widgets. */
log("action_statement_edit_widget_text_changed after widget_sizer", 90);
//statement_edit_widget->document()->blockSignals(false);
connect(statement_edit_widget->document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(action_statement_edit_widget_text_changed(int,int,int)));
//statement_edit_widget_text_changed_flag= 0;
log("action_statement_edit_widget_text_changed end", 90);
}
/*
For menu item "connect" we said connect(...SLOT(action_connect())));
By default this is on and associated with File|Connect menu item.
Actually connect will be attempted depending on defaults and command-line parameters.
*/
void MainWindow::action_connect()
{
action_connect_once(tr("File|Connect"));
}
/*
If we're connecting, action_connect() calls action_connect_once with arg = "File|Connect".
If we're printing for --help, print_help calls action_connect_once with arg = "Print".
Todo:
If user types OK and there's an error, repeat the dialog box with a new message e.g. "Connect failed ...".
This is called from program-start!
This should put "CONNECT" in the statement widget and cause its execution, so it shows up on the history widget.
*/
void MainWindow::action_connect_once(QString message)
{
int column_count;
QString *row_form_label;
int *row_form_type;
int *row_form_is_password;
QString *row_form_data;
QString *row_form_width;
QString row_form_title;
QString row_form_message;
int i;
Row_form_box *co;
row_form_label= 0;
row_form_type= 0;
row_form_is_password= 0;
row_form_data= 0;
row_form_width= 0;
column_count= 84; /* If you add or remove items, you have to change this */
row_form_label= new QString[column_count];
row_form_type= new int[column_count];
row_form_is_password= new int[column_count];
row_form_data= new QString[column_count];
row_form_width= new QString[column_count];
row_form_label[i=0]= "host"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_host; row_form_width[i]= 80;
row_form_label[++i]= "port"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_port); row_form_width[i]= 4;
row_form_label[++i]= "user"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_user; row_form_width[i]= 80;
row_form_label[++i]= "database"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_database; row_form_width[i]= 80;
row_form_label[++i]= "socket"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_unix_socket; row_form_width[i]= 80;
row_form_label[++i]= "password"; row_form_type[i]= 0; row_form_is_password[i]= 1; row_form_data[i]= ocelot_password; row_form_width[i]= 80;
row_form_label[++i]= "protocol"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_protocol; row_form_width[i]= 80;
row_form_label[++i]= "init_command"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_init_command; row_form_width[i]= 80;
row_form_label[++i]= "abort_source_on_error"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_abort_source_on_error); row_form_width[i]= 5;
row_form_label[++i]= "auto_rehash"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_auto_rehash); row_form_width[i]= 5;
row_form_label[++i]= "auto_vertical_output"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_auto_vertical_output); row_form_width[i]= 5;
row_form_label[++i]= "batch"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_batch); row_form_width[i]= 5;
row_form_label[++i]= "binary_mode"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_binary_mode); row_form_width[i]= 5;
row_form_label[++i]= "bind_address"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_bind_address; row_form_width[i]= 5;
row_form_label[++i]= "character_sets_dir"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_set_charset_dir; row_form_width[i]= 5;
row_form_label[++i]= "column_names"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_result_grid_column_names); row_form_width[i]= 5;
row_form_label[++i]= "column_type_info"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_column_type_info); row_form_width[i]= 5;
row_form_label[++i]= "comments"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_comments); row_form_width[i]= 5;
row_form_label[++i]= "compress"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_opt_compress); row_form_width[i]= 5;
row_form_label[++i]= "connect_expired_password"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_opt_can_handle_expired_passwords); row_form_width[i]= 5;
row_form_label[++i]= "connect_timeout"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_opt_connect_timeout); row_form_width[i]= 5;
row_form_label[++i]= "debug"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_debug; row_form_width[i]= 5;
row_form_label[++i]= "debug_check"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_debug_check); row_form_width[i]= 5;
row_form_label[++i]= "debug_info"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_debug_info); row_form_width[i]= 5;
row_form_label[++i]= "default_auth"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_default_auth; row_form_width[i]= 5;
row_form_label[++i]= "default_character_set"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_set_charset_name; row_form_width[i]= 5;
row_form_label[++i]= "defaults_extra_file"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_defaults_extra_file; row_form_width[i]= 5;
row_form_label[++i]= "defaults_file"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_defaults_file; row_form_width[i]= 5;
row_form_label[++i]= "defaults_group_suffix"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_defaults_group_suffix; row_form_width[i]= 5;
row_form_label[++i]= "delimiter"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_delimiter_str; row_form_width[i]= 5;
row_form_label[++i]= "enable_cleartext_plugin"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_enable_cleartext_plugin); row_form_width[i]= 5;
row_form_label[++i]= "execute"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_execute; row_form_width[i]= 5;
row_form_label[++i]= "force"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_force); row_form_width[i]= 5;
row_form_label[++i]= "help"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_help); row_form_width[i]= 5;
row_form_label[++i]= "histfile"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_history_hist_file_name; row_form_width[i]= 5;
row_form_label[++i]= "histignore"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_histignore; row_form_width[i]= 5;
row_form_label[++i]= "html"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_html); row_form_width[i]= 5;
row_form_label[++i]= "ignore_spaces"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_ignore_spaces); row_form_width[i]= 5;
row_form_label[++i]= "ld_run_path"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_ld_run_path; row_form_width[i]= 5;
if (is_libmysqlclient_loaded != 0) row_form_type[i]= (row_form_type[i] | READONLY_FLAG);
row_form_label[++i]= "line_numbers"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_line_numbers); row_form_width[i]= 5;
row_form_label[++i]= "local_infile"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_opt_local_infile); row_form_width[i]= 5;
row_form_label[++i]= "login_path"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_login_path; row_form_width[i]= 5;
row_form_label[++i]= "max_allowed_packet"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_max_allowed_packet); row_form_width[i]= 5;
row_form_label[++i]= "max_join_size"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_max_join_size); row_form_width[i]= 5;
row_form_label[++i]= "named_commands"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_named_commands); row_form_width[i]= 5;
row_form_label[++i]= "net_buffer_length"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_net_buffer_length); row_form_width[i]= 5;
row_form_label[++i]= "no_beep"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_no_beep); row_form_width[i]= 5;
row_form_label[++i]= "no_defaults"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_no_defaults); row_form_width[i]= 5;
row_form_label[++i]= "one_database"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_one_database); row_form_width[i]= 5;
row_form_label[++i]= "pager"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_pager; row_form_width[i]= 5;
row_form_label[++i]= "pipe"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_pipe); row_form_width[i]= 5;
row_form_label[++i]= "plugin_dir"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_plugin_dir; row_form_width[i]= 5;
row_form_label[++i]= "print_defaults"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_print_defaults); row_form_width[i]= 5;
row_form_label[++i]= "prompt"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_prompt; row_form_width[i]= 5;
row_form_label[++i]= "quick"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_quick); row_form_width[i]= 5;
row_form_label[++i]= "raw"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_raw); row_form_width[i]= 5;
row_form_label[++i]= "reconnect"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_opt_reconnect); row_form_width[i]= 5;
row_form_label[++i]= "safe_updates"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_safe_updates); row_form_width[i]= 5;
row_form_label[++i]= "secure_auth"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_secure_auth); row_form_width[i]= 5;
row_form_label[++i]= "select_limit"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_select_limit); row_form_width[i]= 5;
row_form_label[++i]= "server_public_key"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_server_public_key; row_form_width[i]= 5;
row_form_label[++i]= "shared_memory_base_name"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_shared_memory_base_name; row_form_width[i]= 5;
/* It used to crash if I said number(ocelot_history_includes_warnings). Problem has disappeared. */
row_form_label[++i]= "show_warnings"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_history_includes_warnings); row_form_width[i]= 5;
row_form_label[++i]= "sigint_ignore"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_sigint_ignore); row_form_width[i]= 5;
row_form_label[++i]= "silent"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_silent); row_form_width[i]= 5;
row_form_label[++i]= "ssl"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl; row_form_width[i]= 5;
row_form_label[++i]= "ssl_ca"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_ca; row_form_width[i]= 5;
row_form_label[++i]= "ssl_capath"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_capath; row_form_width[i]= 5;
row_form_label[++i]= "ssl_cert"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_cert; row_form_width[i]= 5;
row_form_label[++i]= "ssl_cipher"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_cipher; row_form_width[i]= 5;
row_form_label[++i]= "ssl_crl"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_crl; row_form_width[i]= 5;
row_form_label[++i]= "ssl_crlpath"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_crlpath; row_form_width[i]= 5;
row_form_label[++i]= "ssl_key"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_key; row_form_width[i]= 5;
row_form_label[++i]= "ssl_mode"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_opt_ssl_mode; row_form_width[i]= 5;
row_form_label[++i]= "ssl_verify_server_cert"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_opt_ssl_verify_server_cert); row_form_width[i]= 5;
row_form_label[++i]= "syslog"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_syslog); row_form_width[i]= 5;
row_form_label[++i]= "table"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_table); row_form_width[i]= 5;
row_form_label[++i]= "tee"; row_form_type[i]= 0; row_form_is_password[i]= 0; row_form_data[i]= ocelot_history_tee_file_name; row_form_width[i]= 5;
row_form_label[++i]= "unbuffered"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_unbuffered); row_form_width[i]= 5;
row_form_label[++i]= "verbose"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_verbose); row_form_width[i]= 5;
row_form_label[++i]= "version"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_version); row_form_width[i]= 5;
row_form_label[++i]= "vertical"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_result_grid_vertical); row_form_width[i]= 5;
row_form_label[++i]= "wait"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_wait); row_form_width[i]= 5;
row_form_label[++i]= "xml"; row_form_type[i]= NUM_FLAG; row_form_is_password[i]= 0; row_form_data[i]= QString::number(ocelot_xml); row_form_width[i]= 5;
assert(i == column_count - 1);
if (message == "Print")
{
char output_string[5120];
for (int j= 0; j < i; ++j)
{
strcpy(output_string, row_form_label[j].toUtf8());
printf("%-34s", output_string);
strcpy(output_string, row_form_data[j].toUtf8());
printf("%s\n", output_string);
}
}
else /* if (message == "File|Connect") */
{
row_form_title= menu_strings[menu_off + MENU_CONNECTION_DIALOG_BOX];
row_form_message= message;
co= new Row_form_box(column_count, row_form_label,
row_form_type,
row_form_is_password, row_form_data,
// row_form_width,
row_form_title,
menu_strings[menu_off + MENU_FILE_CONNECT_HEADING],
this);
co->exec();
if (co->is_ok == 1)
{
ocelot_host= row_form_data[0].trimmed();
ocelot_port= to_long(row_form_data[1].trimmed());
ocelot_user= row_form_data[2].trimmed();
ocelot_database= row_form_data[3].trimmed();
ocelot_unix_socket= row_form_data[4].trimmed();
ocelot_password= row_form_data[5].trimmed();
ocelot_protocol= row_form_data[6].trimmed(); ocelot_protocol_as_int= get_ocelot_protocol_as_int(ocelot_protocol);
ocelot_init_command= row_form_data[7].trimmed();
ocelot_abort_source_on_error= to_long(row_form_data[8].trimmed());
ocelot_auto_rehash= to_long(row_form_data[9].trimmed());
i= 10;
ocelot_auto_vertical_output= to_long(row_form_data[i++].trimmed());
ocelot_batch= to_long(row_form_data[i++].trimmed());
ocelot_binary_mode= to_long(row_form_data[i++].trimmed());
ocelot_bind_address= row_form_data[i++].trimmed();
ocelot_set_charset_dir= row_form_data[i++].trimmed(); /* "character_sets_dir" */
ocelot_result_grid_column_names= to_long(row_form_data[i++].trimmed());
ocelot_column_type_info= to_long(row_form_data[i++].trimmed());
ocelot_comments= to_long(row_form_data[i++].trimmed());
ocelot_opt_compress= to_long(row_form_data[i++].trimmed());
ocelot_opt_can_handle_expired_passwords= to_long(row_form_data[i++].trimmed());
ocelot_opt_connect_timeout= to_long(row_form_data[i++].trimmed());
ocelot_debug= row_form_data[i++].trimmed();
ocelot_debug_check= to_long(row_form_data[i++].trimmed());
ocelot_debug_info= to_long(row_form_data[i++].trimmed());
ocelot_default_auth= row_form_data[i++].trimmed();
ocelot_set_charset_name= row_form_data[i++].trimmed(); /* "default_character_set" */
ocelot_defaults_extra_file= row_form_data[i++].trimmed();
ocelot_defaults_file= row_form_data[i++].trimmed();
ocelot_defaults_group_suffix= row_form_data[i++].trimmed();
ocelot_delimiter_str= row_form_data[i++].trimmed();
ocelot_enable_cleartext_plugin= to_long(row_form_data[i++].trimmed());
ocelot_execute= row_form_data[i++].trimmed();
ocelot_force= to_long(row_form_data[i++].trimmed());
ocelot_help= to_long(row_form_data[i++].trimmed());
ocelot_history_hist_file_name= row_form_data[i++].trimmed();
ocelot_histignore= row_form_data[i++].trimmed();
ocelot_html= to_long(row_form_data[i++].trimmed());
ocelot_ignore_spaces= to_long(row_form_data[i++].trimmed());
ocelot_ld_run_path= row_form_data[i++].trimmed();
ocelot_line_numbers= to_long(row_form_data[i++].trimmed());
ocelot_opt_local_infile= to_long(row_form_data[i++].trimmed());
ocelot_login_path= row_form_data[i++].trimmed();
ocelot_max_allowed_packet= to_long(row_form_data[i++].trimmed());
ocelot_max_join_size= to_long(row_form_data[i++].trimmed());
ocelot_named_commands= to_long(row_form_data[i++].trimmed());
ocelot_net_buffer_length= to_long(row_form_data[i++].trimmed());
ocelot_no_beep= to_long(row_form_data[i++].trimmed());
ocelot_no_defaults= to_long(row_form_data[i++].trimmed());
ocelot_one_database= to_long(row_form_data[i++].trimmed());
ocelot_pager= row_form_data[i++].trimmed();
ocelot_pipe= to_long(row_form_data[i++].trimmed());
ocelot_plugin_dir= row_form_data[i++].trimmed();
ocelot_print_defaults= to_long(row_form_data[i++].trimmed());
ocelot_prompt= row_form_data[i++].trimmed();
ocelot_quick= to_long(row_form_data[i++].trimmed());
ocelot_raw= to_long(row_form_data[i++].trimmed());
ocelot_opt_reconnect= to_long(row_form_data[i++].trimmed());
ocelot_safe_updates= to_long(row_form_data[i++].trimmed());
ocelot_secure_auth= to_long(row_form_data[i++].trimmed());
ocelot_select_limit= to_long(row_form_data[i++].trimmed());
ocelot_server_public_key= row_form_data[i++].trimmed();
ocelot_shared_memory_base_name= row_form_data[i++].trimmed();
ocelot_history_includes_warnings= to_long(row_form_data[i++].trimmed());
ocelot_sigint_ignore= to_long(row_form_data[i++].trimmed());
ocelot_silent= to_long(row_form_data[i++].trimmed());
ocelot_opt_ssl= row_form_data[i++].trimmed();
ocelot_opt_ssl_ca= row_form_data[i++].trimmed();
ocelot_opt_ssl_capath= row_form_data[i++].trimmed();
ocelot_opt_ssl_cert= row_form_data[i++].trimmed();
ocelot_opt_ssl_cipher= row_form_data[i++].trimmed();
ocelot_opt_ssl_crl= row_form_data[i++].trimmed();
ocelot_opt_ssl_crlpath= row_form_data[i++].trimmed();
ocelot_opt_ssl_key= row_form_data[i++].trimmed();
ocelot_opt_ssl_mode= row_form_data[i++].trimmed();
ocelot_opt_ssl_verify_server_cert= to_long(row_form_data[i++].trimmed());
ocelot_syslog= to_long(row_form_data[i++].trimmed());
ocelot_table= to_long(row_form_data[i++].trimmed());
ocelot_history_tee_file_name= row_form_data[i++].trimmed();
ocelot_unbuffered= to_long(row_form_data[i++].trimmed());
ocelot_verbose= to_long(row_form_data[i++].trimmed());
ocelot_version= row_form_data[i++].trimmed().toInt();
ocelot_result_grid_vertical= to_long(row_form_data[i++].trimmed());
ocelot_wait= to_long(row_form_data[i++].trimmed());
ocelot_xml= to_long(row_form_data[i++].trimmed());
assert(i == column_count);
/* This should ensure that a record goes to the history widget */
/* Todo: clear statement_edit_widget first */
statement_edit_widget->insertPlainText("CONNECT");
action_execute(1);
if (ocelot_init_command > "")
{
statement_edit_widget->insertPlainText(ocelot_init_command);
action_execute(1);
}
}
delete(co);
}
if (row_form_width != 0) delete [] row_form_width;
if (row_form_data != 0) delete [] row_form_data;
if (row_form_is_password != 0) delete [] row_form_is_password;
if (row_form_type != 0) delete [] row_form_type;
if (row_form_label != 0) delete [] row_form_label;
}
/*
For menu item "exit" we said connect(...SLOT(action_exit())));
By default this is on and associated with File|Exit menu item.
Stop the program.
*/
void MainWindow::action_exit()
{
log("action_exit start", 90);
if (ocelot_dbms.contains("tarantool", Qt::CaseInsensitive))
{
/* Todo: if there was a successful connection, close it */
#ifdef OCELOT_OS_LINUX
#ifdef DBMS_TARANTOOL
#if (OCELOT_THIRD_PARTY != 1)
if (is_libtarantool_loaded == 1) { dlclose(libtarantool_handle); is_libtarantool_loaded= 0; }
//if (is_libtarantoolnet_loaded == 1) { dlclose(libtarantoolnet_handle); is_libtarantoolnet_loaded= 0; }
#endif
#endif
#endif
}
else
{
#ifdef DEBUGGER
/* Get rid of debuggee if it's still around. */
/* Todo: this might not be enough. Maybe you should be intercepting the "close window" event. */
if (menu_debug_action_exit->isEnabled() == true) action_debug_exit();
#endif
/* Usually this closes main connection, kill connection or debug connection probably are = 0 */
for (int i= 0; i < MYSQL_MAX_CONNECTIONS; ++i)
{
if (connected[i] != 0)
{
lmysql->ldbms_mysql_close(&mysql[i]);
connected[i]= 0;
}
}
if (is_mysql_library_init_done == true)
{
/*
This assumes mysql_thread_end() was done for any debugger or kill threads,
but we don't call mysql_thread_end() for the main thread (is that okay?).
If we don't call mysql_library_end(), we'll get a few extra valgrind complaints.
*/
lmysql->ldbms_mysql_library_end();
is_mysql_library_init_done= false;
}
/* Some code added 2015-08-25 due to valgrind */
if (lmysql != 0) { delete lmysql; lmysql= 0; }
#ifdef OCELOT_OS_LINUX
if (is_libmysqlclient_loaded == 1) { dlclose(libmysqlclient_handle); is_libmysqlclient_loaded= 0; }
if (is_libcrypto_loaded == 1) { dlclose(libcrypto_handle); is_libcrypto_loaded= 0; }
#endif
}
delete_utf8_copies();
log("action_exit mid", 90);
close();
log("action_exit end", 90);
}
/*
detach
Would "floating" be a better word than "detached"? or "undock"? or "detachable"?
Todo: We need a client statement for detached, e.g. SET result_grid_detached = 1; or "detach".
Todo: Change border, size, title, frame width, position. Perhaps in Settings dialog.
Todo: Menu keys should be slotted to the new window as well as the main window. E.g. control-Q.
Todo: How to bring to front / bring to back? Currently it's always in front of main widget.
Todo: title bar of result grid widget could show part of query and number of rows.
Todo: Maybe these should be detached by default, or always detached.
Todo: Check: Do you lose the parent? If that happens, there will be memory leaks.
Todo: Detached widgets optionally could have their own menus. Lots of work.
* Similar to create_menu() with all addActions() work, but mostly
we can (after separating actions and adding #defines) copy with
QAction *actionb = menu_edit->actions().at(EDIT_MENU_UNDO);
* In every routine that uses action_edit_menu_undo etc., pass
QMenu object so you can use actions().at.
Otherwise click on menu changes focus to undetached widget.
*/
/*
Flags for setWindowFlags().
Doesn't include Qt::WindowCloseButtonHint so there will be no close button.
Doesn't include Qt::WindowStaysOnTopHint but in fact it will be on top of other widgets of app.
*/
#define DETACHED_WINDOW_FLAGS Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint
/* menu item = Options|detach history widget */
void MainWindow::action_option_detach_history_widget(bool checked)
{
bool is_visible= history_edit_widget->isVisible();
ocelot_detach_history_widget= checked;
if (checked)
{
menu_options_action_option_detach_history_widget->setText("attach history widget");
history_edit_widget->setWindowFlags(Qt::Window | DETACHED_WINDOW_FLAGS);
history_edit_widget->setWindowTitle("history widget");
history_edit_widget->detach_start();
}
else
{
menu_options_action_option_detach_history_widget->setText("detach history widget");
history_edit_widget->setWindowFlags(Qt::Widget);
history_edit_widget->detach_stop();
}
if (is_visible) history_edit_widget->show();
}
/* menu item = Options|detach result grid widget */
void MainWindow::action_option_detach_result_grid_widget(bool checked)
{
bool is_visible= result_grid_tab_widget->isVisible();
ocelot_detach_result_grid_widget= checked;
if (checked)
{
menu_options_action_option_detach_result_grid_widget->setText("attach result grid widget");
result_grid_tab_widget->setWindowFlags(Qt::Window | DETACHED_WINDOW_FLAGS);
result_grid_tab_widget->setWindowTitle("result grid widget");
}
else
{
menu_options_action_option_detach_result_grid_widget->setText("detach result grid widget");
result_grid_tab_widget->setWindowFlags(Qt::Widget);
}
if (is_visible) result_grid_tab_widget->show();
}
/* menu item = Options|detach debug widget */
void MainWindow::action_option_detach_debug_widget(bool checked)
{
bool is_visible= debug_tab_widget->isVisible();