]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XMiniBuffer.C
A clean-up of the minibuffer code; see xforms/ChangeLog. Qt code seems Ok
[lyx.git] / src / frontends / xforms / XMiniBuffer.C
1 // -*- C++ -*-
2 /**
3  * \file XMiniBuffer.C
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Lars
8  * \author Asger and Juergen
9  *
10  * Full author contact details are available in file CREDITS
11  */
12
13 #include <config.h>
14
15 #include "XMiniBuffer.h"
16 #include "DropDown.h"
17 #include "ControlCommandBuffer.h"
18
19 #include "gettext.h"
20
21 #include "frontends/Timeout.h"
22
23 #include <boost/bind.hpp>
24
25 #include <vector>
26
27 #ifndef CXX_GLOBAL_CSTD
28 using std::isprint;
29 #endif
30
31 using std::endl;
32 using std::vector;
33
34
35 namespace {
36
37 /// This creates the input widget for the minibuffer
38 FL_OBJECT * create_input_box(void * parent, int type,
39                              FL_Coord, FL_Coord, FL_Coord, FL_Coord);
40
41 } // namespace anon
42
43
44 XMiniBuffer::XMiniBuffer(ControlCommandBuffer & control,
45                          FL_Coord x, FL_Coord y, FL_Coord h, FL_Coord w)
46         : controller_(control),
47           info_shown_(false)
48 {
49         input_ = create_input_box(this, FL_NORMAL_INPUT, x, y, h, w);
50         info_timer_.reset(new Timeout(1500));
51         idle_timer_.reset(new Timeout(6000));
52         info_con = info_timer_->timeout.connect(boost::bind(&XMiniBuffer::info_timeout, this));
53         idle_con = idle_timer_->timeout.connect(boost::bind(&XMiniBuffer::idle_timeout, this));
54         idle_timer_->start();
55         messageMode();
56 }
57
58
59 // This is here so that scoped ptr will not require a complete type.
60 XMiniBuffer::~XMiniBuffer()
61 {}
62
63
64 // thanks for nothing, xforms (recursive creation not allowed)
65 void XMiniBuffer::dd_init()
66 {
67         dropdown_.reset(new DropDown(input_));
68         result_con = dropdown_->result.connect(boost::bind(&XMiniBuffer::set_complete_input, this, _1));
69         keypress_con = dropdown_->keypress.connect(boost::bind(&XMiniBuffer::append_char, this, _1));
70 }
71
72
73 int XMiniBuffer::peek_event(FL_OBJECT * ob, int event,
74                             int key, XEvent * /*xev*/)
75 {
76         switch (event) {
77         case FL_FOCUS:
78                 messageMode(false);
79                 break;
80         case FL_UNFOCUS:
81                 messageMode();
82                 break;
83         case FL_KEYBOARD:
84         {
85                 string input;
86                 if (info_shown_) {
87                         info_timer_->stop();
88                         info_timeout();
89                 }
90
91                 char const * tmp = fl_get_input(ob);
92                 input = tmp ? tmp : "";
93
94                 switch (key) {
95                 case XK_Down:
96 #ifdef XK_KP_Down
97                 case XK_KP_Down:
98 #endif
99                 {
100                         string const h(controller_.historyDown());
101                         if (h.empty()) {
102                                 show_info(_("[End of history]"), input, false);
103                         } else {
104                                 set_input(h);
105                         }
106                         return 1;
107                 }
108
109                 case XK_Up:
110 #ifdef XK_KP_Up
111                 case XK_KP_Up:
112 #endif
113                 {
114                         string const h(controller_.historyUp());
115                         if (h.empty()) {
116                                 show_info(_("[Beginning of history]"), input, false);
117                         } else {
118                                 set_input(h);
119                         }
120                         return 1;
121                 }
122
123                 case 9:
124                 case XK_Tab:
125                 {
126                         string new_input;
127                         vector<string> comp = controller_.completions(input, new_input);
128
129                         if (comp.empty() && new_input == input) {
130                                 show_info(_("[no match]"), input);
131                                 break;
132                         }
133
134                         if (comp.empty()) {
135                                 set_input(new_input);
136                                 show_info(_("[only completion]"), new_input + ' ');
137                                 break;
138                         }
139
140                         set_input(new_input);
141
142                         int x,y,w,h;
143                         fl_get_wingeometry(fl_get_real_object_window(input_),
144                                            &x, &y, &w, &h);
145
146                         // asynchronous completion
147                         int const air = input_->x;
148                         x += air;
149                         y += h - (input_->h + air);
150                         w = input_->w;
151                         dropdown_->select(comp, x, y, w);
152                         return 1;
153                 }
154                 case 27:
155                 case XK_Escape:
156                         messageMode();
157                         return 1;
158                 case 13:
159                 case XK_Return:
160 #ifdef XK_KP_Enter
161                 case XK_KP_Enter:
162 #endif
163                 {
164                         messageMode();
165                         redraw();
166                         controller_.dispatch(input);
167                         return 1;
168                 }
169                 default:
170                         return 0;
171                 }
172         }
173         default:
174                 break;
175         }
176
177         return 0;
178 }
179
180
181 void XMiniBuffer::freeze()
182 {
183         // we must prevent peek_event, or we get an unfocus() when the
184         // containing form gets destroyed
185         fl_set_object_prehandler(input_, 0);
186 }
187
188
189 void XMiniBuffer::show_info(string const & info, string const & input, bool append)
190 {
191         stored_input_ = input;
192         info_shown_ = true;
193         if (append)
194                 set_input(input + ' ' + info);
195         else
196                 set_input(info);
197         info_timer_->start();
198 }
199
200
201 void XMiniBuffer::idle_timeout()
202 {
203         set_input(controller_.getCurrentState());
204 }
205
206
207 void XMiniBuffer::info_timeout()
208 {
209         info_shown_ = false;
210         set_input(stored_input_);
211 }
212
213
214 bool XMiniBuffer::isEditingMode() const
215 {
216         return input_->focus;
217 }
218
219
220 void XMiniBuffer::messageMode(bool on)
221 {
222         set_input("");
223         if (!on) {
224                 fl_activate_object(input_);
225                 fl_set_focus_object(input_->form, input_);
226                 redraw();
227                 idle_timer_->stop();
228         } else {
229                 if (isEditingMode()) {
230                         // focus back to the workarea
231                         fl_set_focus_object(input_->form, 0);
232                         idle_timer_->start();
233                 }
234         }
235 }
236
237
238 void XMiniBuffer::redraw()
239 {
240         fl_redraw_object(input_);
241         XFlush(fl_display);
242 }
243
244
245 void XMiniBuffer::append_char(char c)
246 {
247         if (!c || !isprint(c))
248                 return;
249
250         char const * tmp = fl_get_input(input_);
251         string str = tmp ? tmp : "";
252
253         str += c;
254
255         fl_set_input(input_, str.c_str());
256 }
257
258
259 void XMiniBuffer::set_complete_input(string const & str)
260 {
261         if (!str.empty()) {
262                 // add a space so the user can type
263                 // an argument immediately
264                 set_input(str + ' ');
265         }
266 }
267
268
269 void XMiniBuffer::message(string const & str)
270 {
271         if (!isEditingMode())
272                 set_input(str);
273 }
274
275
276 void XMiniBuffer::set_input(string const & str)
277 {
278         fl_set_input(input_, str.c_str());
279 }
280
281
282 namespace {
283
284 extern "C"
285 int C_XMiniBuffer_peek_event(FL_OBJECT * ob, int event,
286                              FL_Coord, FL_Coord,
287                              int key, void * xev)
288 {
289         XMiniBuffer * mini = static_cast<XMiniBuffer*>(ob->u_vdata);
290         return mini->peek_event(ob, event, key, static_cast<XEvent *>(xev));
291 }
292
293
294 FL_OBJECT * create_input_box(void * parent, int type,
295                              FL_Coord x, FL_Coord y, FL_Coord w, FL_Coord h)
296 {
297         FL_OBJECT * obj = fl_add_input(type, x, y, w, h, "");
298         fl_set_object_boxtype(obj, FL_DOWN_BOX);
299         fl_set_object_resize(obj, FL_RESIZE_ALL);
300         fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
301         fl_set_object_color(obj, FL_MCOL, FL_MCOL);
302         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
303
304         // To intercept Up, Down, Table for history
305         fl_set_object_prehandler(obj, C_XMiniBuffer_peek_event);
306         obj->u_vdata = parent;
307         obj->wantkey = FL_KEY_TAB;
308
309         return obj;
310 }
311
312 } // namespace anon