Mercurial > jwe > qt-gui-with-push-parser
comparison gui-main.cpp @ 9:822a2fe5bb51
move command window to separate file and other refactoring
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 23 May 2019 12:36:26 -0400 |
parents | 04867eba6428 |
children | b652a5528fb1 |
comparison
equal
deleted
inserted
replaced
8:7c4b04a6346d | 9:822a2fe5bb51 |
---|---|
4 | 4 |
5 #include <cstdlib> | 5 #include <cstdlib> |
6 #include <cstring> | 6 #include <cstring> |
7 | 7 |
8 #include <QApplication> | 8 #include <QApplication> |
9 #include <QKeyEvent> | |
10 #include <QTextDocument> | |
11 #include <QTextEdit> | |
12 #include <QTimer> | |
13 | 9 |
10 #include "command-window.h" | |
14 #include "gui-main.h" | 11 #include "gui-main.h" |
15 #include "main.h" | |
16 | 12 |
17 #include "gui-main.h" | 13 namespace calc |
18 #include "interpreter.h" | |
19 #include "parser.h" | |
20 | |
21 #include <readline/readline.h> | |
22 #include <readline/history.h> | |
23 | |
24 namespace gui | |
25 { | 14 { |
26 static int available_char = 0; | 15 int gui_main (int argc, char *argv[]) |
27 | |
28 static command_window *calc_interaction_window = 0; | |
29 | |
30 static inline int ctrl (int c) | |
31 { | |
32 return c & 0x1f; | |
33 } | |
34 | |
35 static int getc (FILE *) | |
36 { | |
37 int tmp = available_char; | |
38 available_char = 0; | |
39 return tmp; | |
40 } | |
41 | |
42 static void redisplay (void) | |
43 { | |
44 if (calc_interaction_window) | |
45 emit calc_interaction_window->redisplay_signal (); | |
46 } | |
47 | |
48 static void prep_term (int) | |
49 { | |
50 } | |
51 | |
52 static void deprep_term (void) | |
53 { | |
54 } | |
55 | |
56 static void accept_line (char *line) | |
57 { | |
58 if (calc_interaction_window) | |
59 calc_interaction_window->accept_line (line ? line : ""); | |
60 } | |
61 | |
62 static void display_completion_matches (char **matches, int num_matches, | |
63 int /* max_length */) | |
64 { | |
65 if (calc_interaction_window) | |
66 { | |
67 std::ostringstream buf; | |
68 | |
69 if (num_matches > 1) | |
70 buf << "\n"; | |
71 | |
72 for (int i = 1; i < num_matches; i++) | |
73 buf << matches[i] << "\n"; | |
74 | |
75 calc_interaction_window->insert_at_end (buf.str ()); | |
76 | |
77 emit calc_interaction_window->redisplay_signal (); | |
78 } | |
79 } | |
80 | |
81 static void readline_init (void) | |
82 { | |
83 rl_initialize (); | |
84 | |
85 rl_getc_function = getc; | |
86 rl_redisplay_function = redisplay; | |
87 rl_prep_term_function = prep_term; | |
88 rl_deprep_term_function = deprep_term; | |
89 rl_completion_display_matches_hook = display_completion_matches; | |
90 | |
91 rl_callback_handler_install (">> ", accept_line); | |
92 } | |
93 | |
94 static void readline_fini (void) | |
95 { | |
96 rl_callback_handler_remove (); | |
97 } | |
98 | |
99 command_window::command_window (QWidget *p) | |
100 : QTextEdit (p), | |
101 m_buffer (new QTextDocument ()), | |
102 m_interpreter (new calc::qt_interpreter ()), | |
103 beg_mark (), prompt_mark () | |
104 { | |
105 setWindowTitle ("Qt::TextEdit example"); | |
106 | |
107 setMinimumSize (QSize (600, 400)); | |
108 | |
109 setDocument (m_buffer); | |
110 | |
111 connect (m_interpreter, SIGNAL (result_ready (double)), | |
112 this, SLOT (handle_result (double))); | |
113 | |
114 connect (m_interpreter, SIGNAL (error_signal (const QString&)), | |
115 this, SLOT (handle_error (const QString&))); | |
116 | |
117 connect (this, SIGNAL (input_char_available (int)), | |
118 this, SLOT (handle_input_char (int))); | |
119 | |
120 connect (this, SIGNAL (redisplay_signal (void)), | |
121 this, SLOT (redisplay (void))); | |
122 | |
123 connect (this, SIGNAL (accept_line_signal (const QString&)), | |
124 m_interpreter, SLOT (accept_input_line (const QString&))); | |
125 | |
126 insert_at_end | |
127 ("Qt Example Calculator.\n" | |
128 "Available operations: + - * / ^ ()\n" | |
129 "Semicolon terminates statement.\n" | |
130 "Up Arrow key moves to previous line in the command history.\n" | |
131 "Down Arrow key moves to next line in the comand history.\n\n"); | |
132 | |
133 beg_mark = set_mark (); | |
134 | |
135 // Defer initializing and executing the interpreter until after the main | |
136 // window and QApplication are running to prevent race conditions | |
137 QTimer::singleShot (0, m_interpreter, SLOT (execute (void))); | |
138 } | |
139 | |
140 // Accept an input line, parse and possibly execute it. | |
141 | |
142 void command_window::accept_line (const std::string& line) | |
143 { | |
144 if (calc::debug_mode) | |
145 std::cerr << "accept: " << line << std::endl; | |
146 | |
147 insert_at_end ("\n"); | |
148 | |
149 if (! line.empty ()) | |
150 { | |
151 add_history (line.c_str ()); | |
152 using_history (); | |
153 | |
154 emit accept_line_signal (QString::fromStdString (line)); | |
155 } | |
156 } | |
157 | |
158 void command_window::insert_at_end (const std::string& text) | |
159 { | |
160 scroll_to_bottom (); | |
161 | |
162 insert_at_cursor (text); | |
163 } | |
164 | |
165 void command_window::handle_error (const QString& msg) | |
166 { | |
167 insert_at_end ("parse error: " + msg.toStdString () + "\n"); | |
168 | |
169 rl_abort (0, 0); | |
170 } | |
171 | |
172 // FIXME: do we really need this extra function? | |
173 void command_window::handle_result (double value) | |
174 { | |
175 insert_result (value); | |
176 } | |
177 | |
178 // Redisplay current command line. | |
179 | |
180 void command_window::redisplay (void) | |
181 { | |
182 erase_line (); | |
183 | |
184 std::string line = rl_line_buffer ? rl_line_buffer : ""; | |
185 std::string prompt = (rl_prompt && parser::beg_of_stmt) ? rl_prompt : ""; | |
186 | |
187 insert_line (prompt, line); | |
188 | |
189 scroll_to_bottom (); | |
190 | |
191 QTextCursor cursor = textCursor (); | |
192 | |
193 cursor.setPosition (prompt_mark + rl_point, QTextCursor::MoveAnchor); | |
194 | |
195 setTextCursor (cursor); | |
196 } | |
197 | |
198 void command_window::keyPressEvent (QKeyEvent *event) | |
199 { | |
200 if (! event) | |
201 return; | |
202 | |
203 if (event->type () == QEvent::KeyPress) | |
204 { | |
205 int key = event->key (); | |
206 | |
207 switch (key) | |
208 { | |
209 case Qt::Key_Return: | |
210 key = 0x0A; | |
211 break; | |
212 | |
213 case Qt::Key_Backspace: | |
214 key = 0x08; | |
215 break; | |
216 | |
217 case Qt::Key_Tab: | |
218 key = 0x09; | |
219 break; | |
220 | |
221 case Qt::Key_Escape: | |
222 key = 0x1b; | |
223 break; | |
224 | |
225 case Qt::Key_Up: | |
226 case Qt::Key_Down: | |
227 case Qt::Key_Right: | |
228 case Qt::Key_Left: | |
229 key = do_arrow_key (key); | |
230 break; | |
231 | |
232 default: | |
233 { | |
234 switch (event->modifiers ()) | |
235 { | |
236 case Qt::ControlModifier: | |
237 if (key > 0x3f && key < 0x7b) | |
238 key &= 0x1f; | |
239 else | |
240 key = -1; | |
241 break; | |
242 | |
243 default: | |
244 { | |
245 // Don't shoot me, this is just a demo... | |
246 QString text = event->text (); | |
247 QByteArray latin_text = text.toLatin1 (); | |
248 key = latin_text[0]; | |
249 } | |
250 break; | |
251 } | |
252 } | |
253 break; | |
254 } | |
255 | |
256 if (key >= 0) | |
257 emit input_char_available (key); | |
258 } | |
259 } | |
260 | |
261 void command_window::handle_input_char (int key) | |
262 { | |
263 available_char = key; | |
264 rl_callback_read_char (); | |
265 } | |
266 | |
267 int command_window::do_arrow_key (int arrow_key) | |
268 { | |
269 int retval = 0; | |
270 | |
271 available_char = 0x1b; | |
272 rl_callback_read_char (); | |
273 | |
274 available_char = '['; | |
275 rl_callback_read_char (); | |
276 | |
277 switch (arrow_key) | |
278 { | |
279 case Qt::Key_Up: | |
280 retval = 'A'; | |
281 break; | |
282 | |
283 case Qt::Key_Down: | |
284 retval = 'B'; | |
285 break; | |
286 | |
287 case Qt::Key_Right: | |
288 retval = 'C'; | |
289 break; | |
290 | |
291 case Qt::Key_Left: | |
292 retval = 'D'; | |
293 break; | |
294 } | |
295 | |
296 return retval; | |
297 } | |
298 | |
299 // Retrieve a command from the history list and insert it on the | |
300 // current command. | |
301 | |
302 void command_window::history (bool up) | |
303 { | |
304 HIST_ENTRY *entry = up ? previous_history () : next_history (); | |
305 | |
306 if (entry) | |
307 { | |
308 erase_line (); | |
309 | |
310 std::string prompt = rl_prompt ? rl_prompt : ""; | |
311 | |
312 insert_line (prompt, entry->line); | |
313 } | |
314 else if (! up) | |
315 erase_line (); | |
316 | |
317 scroll_to_bottom (); | |
318 } | |
319 | |
320 void command_window::erase_line (void) | |
321 { | |
322 QTextCursor cursor = textCursor (); | |
323 | |
324 cursor.movePosition (QTextCursor::End); | |
325 cursor.select (QTextCursor::LineUnderCursor); | |
326 cursor.removeSelectedText (); | |
327 | |
328 setTextCursor (cursor); | |
329 } | |
330 | |
331 void command_window::insert_at_cursor (const std::string& text) | |
332 { | |
333 QTextCursor cursor = textCursor (); | |
334 | |
335 cursor.insertText (QString::fromStdString (text)); | |
336 | |
337 setTextCursor (cursor); | |
338 } | |
339 | |
340 void command_window::insert_line (const std::string& prompt, | |
341 const std::string& line) | |
342 { | |
343 beg_mark = set_mark (); | |
344 | |
345 insert_at_cursor (prompt); | |
346 | |
347 prompt_mark = set_mark (); | |
348 | |
349 insert_at_cursor (line); | |
350 } | |
351 | |
352 int command_window::set_mark (void) | |
353 { | |
354 return textCursor ().position (); | |
355 } | |
356 | |
357 void command_window::insert_result (double value) | |
358 { | |
359 std::ostringstream buf; | |
360 | |
361 buf << "ans = " << value << "\n"; | |
362 | |
363 insert_at_cursor (buf.str ()); | |
364 | |
365 beg_mark = set_mark (); | |
366 | |
367 scroll_to_bottom (); | |
368 } | |
369 | |
370 void command_window::scroll_to_bottom (void) | |
371 { | |
372 QTextCursor cursor = textCursor (); | |
373 | |
374 cursor.movePosition (QTextCursor::End); | |
375 | |
376 setTextCursor (cursor); | |
377 } | |
378 | |
379 int main (int argc, char *argv[]) | |
380 { | 16 { |
381 QApplication app (argc, argv); | 17 QApplication app (argc, argv); |
382 | 18 |
383 calc_interaction_window = new command_window (); | 19 command_window command_window; |
384 | 20 |
385 calc_interaction_window->show (); | 21 command_window.show (); |
386 | 22 |
387 readline_init (); | 23 readline_init (); |
388 | 24 |
389 int status = app.exec (); | 25 int status = app.exec (); |
390 | 26 |
391 readline_fini (); | 27 readline_fini (); |
392 | 28 |
393 return status; | 29 return status; |
394 } | 30 } |
395 } | 31 } |
396 | |
397 // -- Variable: rl_getc_func_t * rl_getc_function | |
398 // If non-zero, Readline will call indirectly through this pointer to | |
399 // get a character from the input stream. By default, it is set to | |
400 // `rl_getc', the default Readline character input function (*note | |
401 // Character Input::). | |
402 | |
403 // -- Variable: rl_voidfunc_t * rl_redisplay_function | |
404 // If non-zero, Readline will call indirectly through this pointer to | |
405 // update the display with the current contents of the editing buffer. | |
406 // By default, it is set to `rl_redisplay', the default Readline | |
407 // redisplay function (*note Redisplay::). | |
408 | |
409 // -- Variable: rl_vintfunc_t * rl_prep_term_function | |
410 // If non-zero, Readline will call indirectly through this pointer to | |
411 // initialize the terminal. The function takes a single argument, an | |
412 // `int' flag that says whether or not to use eight-bit characters. | |
413 // By default, this is set to `rl_prep_terminal' (*note Terminal | |
414 // Management::). | |
415 | |
416 // -- Variable: rl_voidfunc_t * rl_deprep_term_function | |
417 // If non-zero, Readline will call indirectly through this pointer to | |
418 // reset the terminal. This function should undo the effects of | |
419 // `rl_prep_term_function'. By default, this is set to | |
420 // `rl_deprep_terminal' (*note Terminal Management::). | |
421 | |
422 // -- Function: void rl_callback_handler_install (const char *prompt, | |
423 // rl_vcpfunc_t *lhandler) | |
424 // Set up the terminal for readline I/O and display the initial | |
425 // expanded value of PROMPT. Save the value of LHANDLER to use as a | |
426 // function to call when a complete line of input has been entered. | |
427 // The function takes the text of the line as an argument. | |
428 | |
429 // -- Function: void rl_callback_read_char (void) | |
430 // Whenever an application determines that keyboard input is | |
431 // available, it should call `rl_callback_read_char()', which will | |
432 // read the next character from the current input source. If that | |
433 // character completes the line, `rl_callback_read_char' will invoke | |
434 // the LHANDLER function saved by `rl_callback_handler_install' to | |
435 // process the line. Before calling the LHANDLER function, the | |
436 // terminal settings are reset to the values they had before calling | |
437 // `rl_callback_handler_install'. If the LHANDLER function returns, | |
438 // the terminal settings are modified for Readline's use again. | |
439 // `EOF' is indicated by calling LHANDLER with a `NULL' line. | |
440 | |
441 // -- Function: void rl_callback_handler_remove (void) | |
442 // Restore the terminal to its initial state and remove the line | |
443 // handler. This may be called from within a callback as well as | |
444 // independently. If the LHANDLER installed by | |
445 // `rl_callback_handler_install' does not exit the program, either | |
446 // this function or the function referred to by the value of | |
447 // `rl_deprep_term_function' should be called before the program | |
448 // exits to reset the terminal settings. | |
449 | |
450 // -- Variable: rl_compdisp_func_t * rl_completion_display_matches_hook | |
451 // If non-zero, then this is the address of a function to call when | |
452 // completing a word would normally display the list of possible | |
453 // matches. This function is called in lieu of Readline displaying | |
454 // the list. It takes three arguments: (`char **'MATCHES, `int' | |
455 // NUM_MATCHES, `int' MAX_LENGTH) where MATCHES is the array of | |
456 // matching strings, NUM_MATCHES is the number of strings in that | |
457 // array, and MAX_LENGTH is the length of the longest string in that | |
458 // array. Readline provides a convenience function, | |
459 // `rl_display_match_list', that takes care of doing the display to | |
460 // Readline's output stream. That function may be called from this | |
461 // hook. |