comparison gui-main.cpp @ 0:dff751fb985c

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