]> git.lyx.org Git - lyx.git/blob - src/minibuffer.C
96d00c99a460277aff6230b1ba307700721c230b
[lyx.git] / src / minibuffer.C
1 /* ###########################################################################
2  *
3  *                 The MiniBuffer Class
4  *                 read minibuffer.h for more
5  *                 information.
6  * 
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-2000 The LyX Team.  
9  * 
10  * ###########################################################################
11  */
12
13 #include <config.h>
14
15 #include <iostream>
16
17 #ifdef __GNUG__
18 #pragma implementation
19 #endif
20
21 #include "minibuffer.h"
22
23 #include "support/lyxalgo.h"
24 #include "support/filetools.h"
25 #include "LyXView.h"
26 #include "gettext.h"
27 #include "LyXAction.h"
28 #include "BufferView.h"
29
30
31 using SigC::slot;
32 using std::vector;
33
34 extern LyXAction lyxaction;
35
36
37 namespace {
38
39 struct prefix {
40         string p;
41         prefix(string const & s) 
42                 : p(s) {}
43         bool operator()(string const & s) const {
44                 return prefixIs(s, p);
45         }
46 };
47
48 } // end of anon namespace
49
50
51 MiniBuffer::MiniBuffer(LyXView * o, FL_Coord x, FL_Coord y,
52                        FL_Coord h, FL_Coord w)
53         : stored_(false), owner_(o), state_(spaces)
54 {
55         add(FL_NORMAL_INPUT, x, y, h, w);
56         timer.setTimeout(6000);
57         timer.timeout.connect(slot(this, &MiniBuffer::init));
58         stored_timer.setTimeout(1500);
59         stored_timer.timeout.connect(slot(this, &MiniBuffer::stored_slot));
60         deactivate();
61 }
62
63
64 void MiniBuffer::stored_slot() 
65 {
66         if (stored_) {
67                 stored_ = false;
68                 fl_set_input(the_buffer, stored_input.c_str());
69         }
70 }
71
72
73 void MiniBuffer::stored_set(string const & str) 
74 {
75         stored_input = str;
76         stored_ = true;
77         stored_timer.start();
78 }
79
80
81 int MiniBuffer::peek_event(FL_OBJECT * ob, int event, int key)
82 {
83         switch (event) {
84         case FL_KEYBOARD:
85         {
86                 char const * tmp = fl_get_input(ob);
87                 string input = tmp ? tmp : "";
88                 if (stored_) {
89                         stored_timer.stop();
90                         input = stored_input;
91                         fl_set_input(ob, input.c_str());
92                         stored_ = false;
93                 }
94                 
95                 switch (key) {
96                 case XK_Down:
97                         if (hist_iter != history_->end()) {
98                                 ++hist_iter;
99                         }
100                         if (hist_iter == history_->end()) {
101                                 // no further history
102                                 stored_set(input);
103                                 fl_set_input(ob, _("[End of history]"));
104                         } else {
105                                 fl_set_input(ob, (*hist_iter).c_str());
106                         }
107                         return 1; 
108                 case XK_Up:
109                         if (hist_iter == history_->begin()) {
110                                 // no further history
111                                 stored_set(input);
112                                 fl_set_input(ob, _("[Beginning of history]"));
113                         } else {
114                                 --hist_iter;
115                                 fl_set_input(ob, (*hist_iter).c_str());
116                         }
117                         return 1; 
118                 case 9:
119                 case XK_Tab:
120                 {
121                         // Completion handling.
122                         
123                         vector<string> comp;
124                         lyx::copy_if(completion_.begin(),
125                                      completion_.end(),
126                                      std::back_inserter(comp), prefix(input));
127
128                         if (comp.empty()) {
129                                 // No matches
130                                 string const tmp = input + _(" [no match]");
131                                 stored_set(input);
132                                 fl_set_input(ob, tmp.c_str());
133                         } else if (comp.size() == 1) {
134                                 // Perfect match
135                                 string const tmp =
136                                         comp[0] + _(" [sole completion]");
137                                 stored_set(comp[0]);
138                                 fl_set_input(ob, tmp.c_str());
139                         } else {
140                                 // More that one match
141                                 // Find maximal avaliable prefix
142                                 string const tmp = comp[0];
143                                 string test(input);
144                                 test += tmp[test.length()];
145                                 while (test.length() < tmp.length()) {
146                                         vector<string> vtmp;
147                                         lyx::copy_if(comp.begin(),
148                                                      comp.end(),
149                                                      std::back_inserter(vtmp),
150                                                      prefix(test));
151                                         if (vtmp.size() != comp.size()) {
152                                                 test.erase(test.length() - 1);
153                                                 break;
154                                         }
155                                         test += tmp[test.length()];
156                                 }
157                                 fl_set_input(ob, test.c_str());
158                                 
159                                 // How should the possible matches
160                                 // be visualized?
161                                 std::copy(comp.begin(), comp.end(),
162                                           std::ostream_iterator<string>(std::cerr, "\n"));
163                         }
164                         return 1; 
165                 }
166                 case 27:
167                 case XK_Escape:
168                         // Abort
169                         owner_->view()->focus(true);
170                         init();
171                         deactivate();
172                         //escape.emit();
173                         return 1; 
174                 case 13:
175                 case XK_Return:
176                 {
177                         // First check for match
178                         vector<string>::const_iterator cit =
179                                 std::find(completion_.begin(),
180                                           completion_.end(),
181                                           input);
182                         if (cit == completion_.end()) {
183                                 // no such func/item
184                                 stored_set(input);
185                                 string const tmp = input + _(" [no match]");
186                                 fl_set_input(ob, tmp.c_str());
187                         } else {
188                                 // Return the inputted string
189                                 deactivate();
190                                 owner_->view()->focus(true);
191                                 history_->push_back(input);
192                                 stringReady.emit(input);
193                         }
194                         return 1;
195                 }
196                 case XK_space:
197                 {
198                         // Depending on the input state spaces might not
199                         // be allowed.
200                         switch (state_) {
201                         case spaces:
202                                 return 0;
203                         case nospaces:
204                         {
205                                 stored_set(input);
206                                 string const tmp = input + _(" [no match]");
207                                 fl_set_input(ob, tmp.c_str());
208                                 return 1;
209                         }
210                         }
211                         
212                 }
213                 
214                 default:
215                         return 0;
216                 }
217         }
218         default:
219                 //lyxerr << "Unhandled minibuffer event!" << endl;
220                 break;
221         }
222         
223         return 0;
224 }
225
226
227 extern "C"
228 int C_MiniBuffer_peek_event(FL_OBJECT * ob, int event, 
229                             FL_Coord, FL_Coord,
230                             int key, void * /*xev*/)
231 {
232         MiniBuffer * mini = static_cast<MiniBuffer*>(ob->u_vdata);
233         return mini->peek_event(ob, event, key);
234 }
235
236
237 void MiniBuffer::prepare()
238 {
239         text.erase();
240         fl_set_input(the_buffer, "");
241         activate();
242         fl_set_focus_object(owner_->getForm(), the_buffer);
243 }
244
245
246 FL_OBJECT * MiniBuffer::add(int type, FL_Coord x, FL_Coord y,
247                            FL_Coord w, FL_Coord h)
248 {
249         FL_OBJECT * obj;
250         
251         the_buffer = obj = fl_add_input(type, x, y, w, h, text.c_str());
252         fl_set_object_boxtype(obj, FL_DOWN_BOX);
253         fl_set_object_resize(obj, FL_RESIZE_ALL);
254         fl_set_object_gravity(obj, SouthWestGravity, SouthEastGravity);
255         fl_set_object_color(obj, FL_MCOL, FL_MCOL);
256         fl_set_object_lsize(obj, FL_NORMAL_SIZE);
257         
258         // To intercept Up, Down, Table for history
259         fl_set_object_prehandler(obj, C_MiniBuffer_peek_event);
260         obj->u_vdata = this;
261         obj->wantkey = FL_KEY_TAB;
262
263         fl_set_input(the_buffer, text.c_str());
264         
265         return obj;
266 }
267
268
269 void MiniBuffer::message(string const & str) 
270 {
271         timer.restart();
272         string const ntext = strip(str);
273         if (!the_buffer->focus) {
274                 fl_set_input(the_buffer, ntext.c_str());
275                 text = ntext;
276         }
277 }
278
279
280 void MiniBuffer::messagePush(string const & str) 
281 {
282         text_stored = text;
283         message(str);
284 }
285
286
287 void MiniBuffer::messagePop()
288 {
289         if (!text_stored.empty()) {
290                 message(text_stored);
291                 text_stored.erase();
292         }
293 }
294
295
296 void MiniBuffer::addSet(string const & s1, string const & s2)
297 {
298         string const str = text + ' ' +  s1 + ' ' + s2;
299         message(str);
300 }
301
302
303 void MiniBuffer::getString(State spaces,
304                            vector<string> const & completion,
305                            vector<string> & history)
306 {
307         state_ = spaces;
308         completion_ = completion;
309         history_ = &history;
310         hist_iter = history_->end();
311         prepare();
312 }
313
314
315 void MiniBuffer::init()
316 {
317         // If we have focus, we don't want to change anything.
318         if (the_buffer->focus)
319                 return;
320
321         timeout.emit();
322         timer.stop();
323 }
324
325
326 void MiniBuffer::activate()
327 {
328         fl_activate_object(the_buffer);
329         fl_redraw_object(the_buffer);
330 }
331
332
333 void MiniBuffer::deactivate()
334 {
335         fl_redraw_object(the_buffer);
336         fl_deactivate_object(the_buffer);
337 }
338
339
340 void MiniBuffer::redraw() 
341 {
342         fl_redraw_object(the_buffer);
343 }