]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiCommandBuffer.cpp
fix memory leaks
[lyx.git] / src / frontends / qt4 / GuiCommandBuffer.cpp
1 /**
2  * \file GuiCommandBuffer.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars
7  * \author Asger and Jürgen
8  * \author John Levon
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiCommandBuffer.h"
16
17 #include "GuiCommandEdit.h"
18 #include "GuiView.h"
19 #include "qt_helpers.h"
20
21 #include "BufferView.h"
22 #include "Cursor.h"
23 #include "LyXFunc.h"
24 #include "LyXAction.h"
25 #include "FuncRequest.h"
26
27 #include "support/lyxalgo.h"
28 #include "support/lstrings.h"
29
30 #include <QHBoxLayout>
31 #include <QKeyEvent>
32 #include <QLayout>
33 #include <QListWidget>
34 #include <QMouseEvent>
35 #include <QPixmap>
36 #include <QPushButton>
37 #include <QToolTip>
38 #include <QVBoxLayout>
39
40 using std::back_inserter;
41 using std::transform;
42 using std::string;
43 using std::vector;
44
45
46 namespace lyx {
47 namespace frontend {
48
49 using support::prefixIs;
50
51
52 namespace {
53
54 class QTempListBox : public QListWidget {
55 public:
56         QTempListBox() {
57                 //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
58                 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
59                 setWindowModality(Qt::WindowModal);
60                 setWindowFlags(Qt::Popup);
61                 setAttribute(Qt::WA_DeleteOnClose);
62         }
63 protected:
64         void mouseReleaseEvent(QMouseEvent * ev) {
65                 if (ev->x() < 0 || ev->y() < 0
66                     || ev->x() > width() || ev->y() > height()) {
67                         hide();
68                 } else {
69                         // emit signal
70                         itemPressed(currentItem());
71                 }
72         }
73
74         void keyPressEvent(QKeyEvent * ev) {
75                 if (ev->key() == Qt::Key_Escape) {
76                         hide();
77                         return;
78                 } else if (ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Space) {
79                         // emit signal
80                         itemPressed(currentItem());
81                 } else
82                         QListWidget::keyPressEvent(ev);
83         }
84 };
85
86 } // end of anon
87
88
89 GuiCommandBuffer::GuiCommandBuffer(GuiView * view)
90         : view_(view), history_pos_(history_.end())
91 {
92         transform(lyxaction.func_begin(), lyxaction.func_end(),
93                 back_inserter(commands_), firster());
94
95         QPixmap qpup(":/images/up.png");
96         QPixmap qpdown(":/images/down.png");
97
98         QVBoxLayout * top = new QVBoxLayout(this);
99         QHBoxLayout * layout = new QHBoxLayout(0);
100
101         QPushButton * up = new QPushButton(qpup, "", this);
102         up->setMaximumSize(24, 24);
103         QPushButton * down = new QPushButton(qpdown, "", this);
104         down->setToolTip(qt_("Next command"));
105         down->setMaximumSize(24, 24);
106         connect(down, SIGNAL(clicked()), this, SLOT(down()));
107
108         edit_ = new GuiCommandEdit(this);
109         edit_->setMinimumSize(edit_->sizeHint());
110         edit_->setFocusPolicy(Qt::ClickFocus);
111
112         connect(edit_, SIGNAL(escapePressed()), this, SLOT(cancel()));
113         connect(edit_, SIGNAL(returnPressed()), this, SLOT(dispatch()));
114         connect(edit_, SIGNAL(tabPressed()), this, SLOT(complete()));
115         connect(edit_, SIGNAL(upPressed()), this, SLOT(up()));
116         connect(edit_, SIGNAL(downPressed()), this, SLOT(down()));
117         connect(edit_, SIGNAL(hidePressed()), this, SLOT(hideParent()));
118
119         layout->addWidget(up, 0);
120         layout->addWidget(down, 0);
121         layout->addWidget(edit_, 10);
122         layout->setMargin(0);
123         top->addLayout(layout);
124         top->setMargin(0);
125         setFocusProxy(edit_);
126 }
127
128
129 void GuiCommandBuffer::cancel()
130 {
131         view_->setFocus();
132         edit_->setText(QString());
133 }
134
135
136 void GuiCommandBuffer::dispatch()
137 {
138         dispatch(fromqstr(edit_->text()));
139         view_->setFocus();
140         edit_->setText(QString());
141         edit_->clearFocus();
142 }
143
144
145 void GuiCommandBuffer::complete()
146 {
147         string const input = fromqstr(edit_->text());
148         string new_input;
149         vector<string> comp = completions(input, new_input);
150
151         if (comp.empty() && new_input == input) {
152                 // show_info_suffix(qt_("[no match]"), input);
153                 return;
154         }
155
156         if (comp.empty()) {
157                 edit_->setText(toqstr(new_input));
158         //      show_info_suffix(("[only completion]"), new_input + ' ');
159                 return;
160         }
161
162         edit_->setText(toqstr(new_input));
163
164         QTempListBox * list = new QTempListBox;
165
166         // For some reason the scrollview's contents are larger
167         // than the number of actual items...
168         vector<string>::const_iterator cit = comp.begin();
169         vector<string>::const_iterator end = comp.end();
170         for (; cit != end; ++cit)
171                 list->addItem(toqstr(*cit));
172
173         list->resize(list->sizeHint());
174         QPoint const pos = edit_->mapToGlobal(QPoint(0, 0));
175
176         int const y = std::max(0, pos.y() - list->height());
177
178         list->move(pos.x(), y);
179
180         connect(list, SIGNAL(itemPressed(QListWidgetItem *)),
181                 this, SLOT(complete_selected(QListWidgetItem *)));
182         connect(list, SIGNAL(itemActivated(QListWidgetItem *)),
183                 this, SLOT(complete_selected(QListWidgetItem *)));
184
185         list->show();
186         list->setFocus();
187 }
188
189
190 void GuiCommandBuffer::complete_selected(QListWidgetItem * item)
191 {
192         QWidget const * widget = static_cast<QWidget const *>(sender());
193         const_cast<QWidget *>(widget)->hide();
194         edit_->setText(item->text() + ' ');
195         edit_->activateWindow();
196         edit_->setFocus();
197 }
198
199
200 void GuiCommandBuffer::up()
201 {
202         string const input = fromqstr(edit_->text());
203         string const h = historyUp();
204
205         if (h.empty()) {
206         //      show_info_suffix(qt_("[Beginning of history]"), input);
207         } else {
208                 edit_->setText(toqstr(h));
209         }
210 }
211
212
213 void GuiCommandBuffer::down()
214 {
215         string const input = fromqstr(edit_->text());
216         string const h = historyDown();
217
218         if (h.empty()) {
219         //      show_info_suffix(qt_("[End of history]"), input);
220         } else {
221                 edit_->setText(toqstr(h));
222         }
223 }
224
225
226 void GuiCommandBuffer::hideParent()
227 {
228         view_->setFocus();
229         edit_->setText(QString());
230         edit_->clearFocus();
231         hide();
232 }
233
234
235 namespace {
236
237 class prefix_p {
238 public:
239         string p;
240         prefix_p(string const & s) : p(s) {}
241         bool operator()(string const & s) const { return prefixIs(s, p); }
242 };
243
244 } // end of anon namespace
245
246
247 string const GuiCommandBuffer::historyUp()
248 {
249         if (history_pos_ == history_.begin())
250                 return string();
251
252         return *(--history_pos_);
253 }
254
255
256 string const GuiCommandBuffer::historyDown()
257 {
258         if (history_pos_ == history_.end())
259                 return string();
260         if (history_pos_ + 1 == history_.end())
261                 return string();
262
263         return *(++history_pos_);
264 }
265
266
267 docstring const GuiCommandBuffer::getCurrentState() const
268 {
269         return view_->view()->cursor().currentState();
270 }
271
272
273 void GuiCommandBuffer::hide() const
274 {
275         FuncRequest cmd(LFUN_COMMAND_EXECUTE, "off");
276         theLyXFunc().setLyXView(view_);
277         lyx::dispatch(cmd);
278 }
279
280
281 vector<string> const
282 GuiCommandBuffer::completions(string const & prefix, string & new_prefix)
283 {
284         vector<string> comp;
285
286         copy_if(commands_.begin(), commands_.end(),
287                 back_inserter(comp), prefix_p(prefix));
288
289         if (comp.empty()) {
290                 new_prefix = prefix;
291                 return comp;
292         }
293
294         if (comp.size() == 1) {
295                 new_prefix = comp[0];
296                 return vector<string>();
297         }
298
299         // find maximal available prefix
300         string const tmp = comp[0];
301         string test = prefix;
302         if (tmp.length() > test.length())
303                 test += tmp[test.length()];
304         while (test.length() < tmp.length()) {
305                 vector<string> vtmp;
306                 copy_if(comp.begin(), comp.end(),
307                         back_inserter(vtmp), prefix_p(test));
308                 if (vtmp.size() != comp.size()) {
309                         test.erase(test.length() - 1);
310                         break;
311                 }
312                 test += tmp[test.length()];
313         }
314
315         new_prefix = test;
316         return comp;
317 }
318
319
320 void GuiCommandBuffer::dispatch(string const & str)
321 {
322         if (str.empty())
323                 return;
324
325         history_.push_back(str);
326         history_pos_ = history_.end();
327         FuncRequest func = lyxaction.lookupFunc(str);
328         func.origin = FuncRequest::COMMANDBUFFER;
329         theLyXFunc().setLyXView(view_);
330         lyx::dispatch(func);
331 }
332
333 } // namespace frontend
334 } // namespace lyx
335
336 #include "GuiCommandBuffer_moc.cpp"