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