Mercurial > jwe > qt-gui-with-push-parser
view gui-main.cpp @ 3:52c033864347
add build instructions to NOTES file
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 22 May 2019 16:21:06 -0400 |
parents | 08df60a01bc1 |
children | 0e154787183d |
line wrap: on
line source
#include <iostream> #include <sstream> #include <string> #include <cstdlib> #include <cstring> #include <QApplication> #include <QKeyEvent> #include <QTextDocument> #include <QTextEdit> #include "gui-main.h" #include "main.h" #include "gui-main.h" #include "parse.h" #include <readline/readline.h> #include <readline/history.h> namespace gui { static int available_char = 0; static inline int ctrl (int c) { return c & 0x1f; } static command_window *calc_interaction_window = 0; static int getc (FILE *) { int tmp = available_char; available_char = 0; return tmp; } static void redisplay (void) { if (calc_interaction_window) calc_interaction_window->redisplay (); } static void prep_term (int) { } static void deprep_term (void) { } static void accept_line (char *line) { if (calc_interaction_window) calc_interaction_window->accept (line ? line : ""); } static void display_completion_matches (char **matches, int num_matches, int /* max_length */) { if (calc_interaction_window) { std::ostringstream buf; if (num_matches > 1) buf << "\n"; for (int i = 1; i < num_matches; i++) buf << matches[i] << "\n"; calc_interaction_window->insert_at_end (buf.str ()); calc_interaction_window->redisplay (); } } static void readline_init (void) { rl_initialize (); rl_getc_function = getc; rl_redisplay_function = redisplay; rl_prep_term_function = prep_term; rl_deprep_term_function = deprep_term; rl_completion_display_matches_hook = display_completion_matches; rl_callback_handler_install (">> ", accept_line); } static void readline_fini (void) { rl_callback_handler_remove (); } command_window::command_window (QWidget *p) : QTextEdit (p), m_buffer (new QTextDocument ()), beg_mark (), prompt_mark () { setWindowTitle ("Qt::TextEdit example"); setMinimumSize (QSize (600, 400)); setDocument (m_buffer); connect (this, SIGNAL (result_available (double)), this, SLOT (handle_result (double))); connect (this, SIGNAL (input_char_available (int)), this, SLOT (handle_input_char (int))); insert_at_end ("Qt Example Calculator.\n" "Available operations: + - * / ^ ()\n" "Semicolon terminates statement.\n" "Up Arrow key moves to previous line in the command history.\n" "Down Arrow key moves to next line in the comand history.\n\n"); beg_mark = set_mark (); } // Accept an input line, parse and possibly execute it. void command_window::accept (const std::string& line) { if (calc::debug_mode) std::cerr << "accept: " << line << std::endl; insert_at_end ("\n"); if (! line.empty ()) { add_history (line.c_str ()); using_history (); // FIXME: what is the right thing to do with status returned // by parser. interpreter::parse_and_execute (line); } } // Redisplay current command line. void command_window::redisplay (void) { erase_line (); std::string line = rl_line_buffer ? rl_line_buffer : ""; std::string prompt = (rl_prompt && interpreter::beg_of_stmt) ? rl_prompt : ""; insert_line (prompt, line); scroll_to_bottom (); QTextCursor cursor = textCursor (); cursor.setPosition (prompt_mark + rl_point, QTextCursor::MoveAnchor); setTextCursor (cursor); } void command_window::insert_at_end (const std::string& text) { scroll_to_bottom (); insert_at_cursor (text); } void command_window::emit_error (const std::string& msg) { insert_at_end ("parse error: " + msg); rl_abort (0, 0); } void command_window::emit_result (double value) { emit result_available (value); } // FIXME: do we really need this extra function? void command_window::handle_result (double value) { insert_result (value); } void command_window::keyPressEvent (QKeyEvent *event) { if (! event) return; if (event->type () == QEvent::KeyPress) { int key = event->key (); switch (key) { case Qt::Key_Return: key = 0x0A; break; case Qt::Key_Backspace: key = 0x08; break; case Qt::Key_Tab: key = 0x09; break; case Qt::Key_Escape: key = 0x1b; break; case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_Right: case Qt::Key_Left: key = do_arrow_key (key); break; default: { switch (event->modifiers ()) { case Qt::ControlModifier: if (key > 0x3f && key < 0x7b) key &= 0x1f; else key = -1; break; default: { // Don't shoot me, this is just a demo... QString text = event->text (); QByteArray latin_text = text.toLatin1 (); key = latin_text[0]; } break; } } break; } if (key >= 0) emit input_char_available (key); } } void command_window::handle_input_char (int key) { available_char = key; rl_callback_read_char (); } int command_window::do_arrow_key (int arrow_key) { int retval = 0; available_char = 0x1b; rl_callback_read_char (); available_char = '['; rl_callback_read_char (); switch (arrow_key) { case Qt::Key_Up: retval = 'A'; break; case Qt::Key_Down: retval = 'B'; break; case Qt::Key_Right: retval = 'C'; break; case Qt::Key_Left: retval = 'D'; break; } return retval; } // Retrieve a command from the history list and insert it on the // current command. void command_window::history (bool up) { HIST_ENTRY *entry = up ? previous_history () : next_history (); if (entry) { erase_line (); std::string prompt = rl_prompt ? rl_prompt : ""; insert_line (prompt, entry->line); } else if (! up) erase_line (); scroll_to_bottom (); } void command_window::erase_line (void) { QTextCursor cursor = textCursor (); cursor.movePosition (QTextCursor::End); cursor.select (QTextCursor::LineUnderCursor); cursor.removeSelectedText (); setTextCursor (cursor); } void command_window::insert_at_cursor (const std::string& text) { QTextCursor cursor = textCursor (); cursor.insertText (QString::fromStdString (text)); setTextCursor (cursor); } void command_window::insert_line (const std::string& prompt, const std::string& line) { beg_mark = set_mark (); insert_at_cursor (prompt); prompt_mark = set_mark (); insert_at_cursor (line); } int command_window::set_mark (void) { return textCursor ().position (); } void command_window::insert_result (double value) { std::ostringstream buf; buf << "ans = " << value << "\n"; insert_at_cursor (buf.str ()); beg_mark = set_mark (); scroll_to_bottom (); } void command_window::scroll_to_bottom (void) { QTextCursor cursor = textCursor (); cursor.movePosition (QTextCursor::End); setTextCursor (cursor); } int main (int argc, char *argv[]) { interpreter::parser_init (); QApplication app (argc, argv); calc_interaction_window = new command_window (); calc_interaction_window->show (); readline_init (); int status = app.exec (); readline_fini (); interpreter::parser_fini (); return status; } void emit_error (const std::string& msg) { calc_interaction_window->emit_error (msg); } void emit_result (double value) { calc_interaction_window->emit_result (value); } } // -- Variable: rl_getc_func_t * rl_getc_function // If non-zero, Readline will call indirectly through this pointer to // get a character from the input stream. By default, it is set to // `rl_getc', the default Readline character input function (*note // Character Input::). // -- Variable: rl_voidfunc_t * rl_redisplay_function // If non-zero, Readline will call indirectly through this pointer to // update the display with the current contents of the editing buffer. // By default, it is set to `rl_redisplay', the default Readline // redisplay function (*note Redisplay::). // -- Variable: rl_vintfunc_t * rl_prep_term_function // If non-zero, Readline will call indirectly through this pointer to // initialize the terminal. The function takes a single argument, an // `int' flag that says whether or not to use eight-bit characters. // By default, this is set to `rl_prep_terminal' (*note Terminal // Management::). // -- Variable: rl_voidfunc_t * rl_deprep_term_function // If non-zero, Readline will call indirectly through this pointer to // reset the terminal. This function should undo the effects of // `rl_prep_term_function'. By default, this is set to // `rl_deprep_terminal' (*note Terminal Management::). // -- Function: void rl_callback_handler_install (const char *prompt, // rl_vcpfunc_t *lhandler) // Set up the terminal for readline I/O and display the initial // expanded value of PROMPT. Save the value of LHANDLER to use as a // function to call when a complete line of input has been entered. // The function takes the text of the line as an argument. // -- Function: void rl_callback_read_char (void) // Whenever an application determines that keyboard input is // available, it should call `rl_callback_read_char()', which will // read the next character from the current input source. If that // character completes the line, `rl_callback_read_char' will invoke // the LHANDLER function saved by `rl_callback_handler_install' to // process the line. Before calling the LHANDLER function, the // terminal settings are reset to the values they had before calling // `rl_callback_handler_install'. If the LHANDLER function returns, // the terminal settings are modified for Readline's use again. // `EOF' is indicated by calling LHANDLER with a `NULL' line. // -- Function: void rl_callback_handler_remove (void) // Restore the terminal to its initial state and remove the line // handler. This may be called from within a callback as well as // independently. If the LHANDLER installed by // `rl_callback_handler_install' does not exit the program, either // this function or the function referred to by the value of // `rl_deprep_term_function' should be called before the program // exits to reset the terminal settings. // -- Variable: rl_compdisp_func_t * rl_completion_display_matches_hook // If non-zero, then this is the address of a function to call when // completing a word would normally display the list of possible // matches. This function is called in lieu of Readline displaying // the list. It takes three arguments: (`char **'MATCHES, `int' // NUM_MATCHES, `int' MAX_LENGTH) where MATCHES is the array of // matching strings, NUM_MATCHES is the number of strings in that // array, and MAX_LENGTH is the length of the longest string in that // array. Readline provides a convenience function, // `rl_display_match_list', that takes care of doing the display to // Readline's output stream. That function may be called from this // hook.