]> git.lyx.org Git - features.git/blob - src/frontends/qt/GuiCommandBuffer.cpp
Correct white space.
[features.git] / src / frontends / qt / 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 "GuiApplication.h"
18 #include "GuiCommandEdit.h"
19 #include "GuiView.h"
20 #include "qt_helpers.h"
21
22 #include "BufferView.h"
23 #include "Cursor.h"
24 #include "LyX.h"
25 #include "LyXAction.h"
26 #include "FuncRequest.h"
27 #include "Session.h"
28
29 #include "support/lstrings.h"
30
31 #include <QHBoxLayout>
32 #include <QKeyEvent>
33 #include <QListWidget>
34 #include <QMouseEvent>
35 #include <QPixmap>
36 #include <QPushButton>
37 #include <QToolTip>
38 #include <QVBoxLayout>
39
40 using namespace std;
41 using namespace lyx::support;
42
43 namespace lyx {
44 namespace frontend {
45
46 namespace {
47
48 class QTempListBox : public QListWidget {
49 public:
50         QTempListBox() {
51                 //setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
52                 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
53                 setWindowModality(Qt::WindowModal);
54                 setWindowFlags(Qt::Popup);
55                 setAttribute(Qt::WA_DeleteOnClose);
56         }
57 protected:
58         bool event(QEvent * ev) override {
59                 if (ev->type() == QEvent::MouseButtonPress) {
60                         QMouseEvent * me = static_cast<QMouseEvent *>(ev);
61 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
62                         if (me->position().x() < 0 || me->position().y() < 0
63                             || me->position().x() > width() || me->position().y() > height())
64 #else
65                         if (me->x() < 0 || me->y() < 0
66                             || me->x() > width() || me->y() > height())
67 #endif
68                                 hide();
69                         return true;
70                 }
71                 return QListWidget::event(ev);
72         }
73
74         void keyPressEvent(QKeyEvent * ev) override {
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                         itemClicked(currentItem());
81                 } else
82                         QListWidget::keyPressEvent(ev);
83         }
84 };
85
86 } // namespace
87
88
89 GuiCommandBuffer::GuiCommandBuffer(GuiView * view)
90         : view_(view)
91 {
92         for (auto const & name_code : lyxaction) {
93                 commands_.push_back(name_code.first);
94         }
95
96         QPixmap qpup = getPixmap("images/", "up", "svgz,png");
97         QPixmap qpdown = getPixmap("images/", "down", "svgz,png");
98
99         QVBoxLayout * top = new QVBoxLayout(this);
100         QHBoxLayout * layout = new QHBoxLayout(0);
101
102         edit_ = new GuiCommandEdit(this);
103         edit_->setMinimumSize(edit_->sizeHint());
104         edit_->setFocusPolicy(Qt::ClickFocus);
105
106         int height = max(24, 2 * (edit_->sizeHint().height() / 2));
107         QSize button_size = QSize(height, height);
108         QSize icon_size = button_size - QSize(4, 4);
109
110         upPB = new QPushButton(qpup, "", this);
111         upPB->setToolTip(qt_("List of previous commands"));
112         upPB->setMaximumSize(button_size);
113         upPB->setIconSize(icon_size);
114         downPB = new QPushButton(qpdown, "", this);
115         downPB->setToolTip(qt_("Next command"));
116         downPB->setMaximumSize(button_size);
117         downPB->setIconSize(icon_size);
118         downPB->setEnabled(false);
119         connect(downPB, SIGNAL(clicked()), this, SLOT(down()));
120         connect(upPB, SIGNAL(pressed()), this, SLOT(listHistoryUp()));
121
122         connect(edit_, SIGNAL(returnPressed()), this, SLOT(dispatch()));
123         connect(edit_, SIGNAL(tabPressed()), this, SLOT(complete()));
124         connect(edit_, SIGNAL(upPressed()), this, SLOT(up()));
125         connect(edit_, SIGNAL(downPressed()), this, SLOT(down()));
126         connect(edit_, SIGNAL(escapePressed()), this, SLOT(hideParent()));
127
128         layout->addWidget(upPB, 0);
129         layout->addWidget(downPB, 0);
130         layout->addWidget(edit_, 10);
131 #if QT_VERSION < 0x060000
132         layout->setMargin(0);
133 #else
134         layout->setContentsMargins(0, 0, 0, 0);
135 #endif
136         top->addLayout(layout);
137 #if QT_VERSION < 0x060000
138         top->setMargin(0);
139 #else
140         top->setContentsMargins(0, 0, 0, 0);
141 #endif
142         setFocusProxy(edit_);
143
144         LastCommandsSection::LastCommands last_commands
145                 = theSession().lastCommands().getcommands();
146         LastCommandsSection::LastCommands::const_iterator it
147                 = last_commands.begin();
148         LastCommandsSection::LastCommands::const_iterator end
149                 = last_commands.end();
150
151         upPB->setEnabled(it != end);
152
153         for(; it != end; ++it)
154                 history_.push_back(*it);
155         history_pos_ = history_.end();
156 }
157
158
159 void GuiCommandBuffer::dispatch()
160 {
161         std::string const cmd = fromqstr(edit_->text());
162         if (!cmd.empty())
163                 theSession().lastCommands().add(cmd);
164         DispatchResult const & dr = dispatch(cmd);
165         if (!dr.error()) {
166                 view_->setFocus();
167                 edit_->setText(QString());
168                 edit_->clearFocus();
169                 // If the toolbar was "auto", it is not needed anymore
170                 view_->resetCommandExecute();
171         }
172 }
173
174
175 void GuiCommandBuffer::listHistoryUp()
176 {
177         if (history_.size()==1) {
178                 edit_->setText(toqstr(history_.back()));
179                 upPB->setEnabled(false);
180                 return;
181         }
182         QPoint const & pos = upPB->mapToGlobal(QPoint(0, 0));
183         showList(history_, pos, true);
184 }
185
186
187 void GuiCommandBuffer::complete()
188 {
189         string const input = fromqstr(edit_->text());
190         string new_input;
191         vector<string> const & comp = completions(input, new_input);
192
193         if (comp.empty()) {
194                 if (new_input != input)
195                         edit_->setText(toqstr(new_input));
196                 return;
197         }
198
199         edit_->setText(toqstr(new_input));
200         QPoint const & pos = edit_->mapToGlobal(QPoint(0, 0));
201         showList(comp, pos);
202 }
203
204 void GuiCommandBuffer::showList(vector<string> const & list,
205         QPoint const & pos, bool reversed) const
206 {
207         QTempListBox * listBox = new QTempListBox;
208
209         // For some reason the scrollview's contents are larger
210         // than the number of actual items...
211         vector<string>::const_iterator cit = list.begin();
212         vector<string>::const_iterator end = list.end();
213         for (; cit != end; ++cit) {
214                 if (reversed)
215                         listBox->insertItem(0, toqstr(*cit));
216                 else
217                         listBox->addItem(toqstr(*cit));
218         }
219
220         listBox->resize(listBox->sizeHint());
221
222         int const y = max(0, pos.y() - listBox->height());
223         listBox->move(pos.x(), y);
224
225         connect(listBox, SIGNAL(itemClicked(QListWidgetItem *)),
226                 this, SLOT(itemSelected(QListWidgetItem *)));
227         connect(listBox, SIGNAL(itemActivated(QListWidgetItem *)),
228                 this, SLOT(itemSelected(QListWidgetItem *)));
229
230         listBox->show();
231         listBox->setFocus();
232 }
233
234
235 void GuiCommandBuffer::itemSelected(QListWidgetItem * item)
236 {
237         QWidget const * widget = static_cast<QWidget const *>(sender());
238         const_cast<QWidget *>(widget)->hide();
239         edit_->setText(item->text()+ ' ');
240         edit_->activateWindow();
241         edit_->setFocus();
242 }
243
244
245 void GuiCommandBuffer::up()
246 {
247         string const h = historyUp();
248
249         if (!h.empty())
250                 edit_->setText(toqstr(h));
251
252         upPB->setEnabled(history_pos_ != history_.begin());
253         downPB->setEnabled(history_pos_ != history_.end());
254 }
255
256
257 void GuiCommandBuffer::down()
258 {
259         string const h = historyDown();
260
261         if (!h.empty())
262                 edit_->setText(toqstr(h));
263
264         downPB->setEnabled(!history_.empty()
265                            && history_pos_ != history_.end() - 1);
266         upPB->setEnabled(history_pos_ != history_.begin());
267 }
268
269
270 void GuiCommandBuffer::hideParent()
271 {
272         view_->setFocus();
273         view_->resetCommandExecute();
274         edit_->setText(QString());
275         edit_->clearFocus();
276 }
277
278
279 string const GuiCommandBuffer::historyUp()
280 {
281         if (history_pos_ == history_.begin())
282                 return string();
283
284         return *(--history_pos_);
285 }
286
287
288 string const GuiCommandBuffer::historyDown()
289 {
290         if (history_pos_ == history_.end())
291                 return string();
292         if (history_pos_ + 1 == history_.end())
293                 return string();
294
295         return *(++history_pos_);
296 }
297
298
299 vector<string> const
300 GuiCommandBuffer::completions(string const & prefix, string & new_prefix)
301 {
302         vector<string> comp;
303         for (auto const & cmd : commands_) {
304                 if (prefixIs(cmd, prefix))
305                         comp.push_back(cmd);
306         }
307
308         if (comp.empty()) {
309                 new_prefix = prefix;
310                 return comp;
311         }
312
313         if (comp.size() == 1) {
314                 new_prefix = comp[0];
315                 return vector<string>();
316         }
317
318         // find maximal available prefix
319         string const tmp = comp[0];
320         string test = prefix;
321         if (tmp.length() > test.length())
322                 test += tmp[test.length()];
323         while (test.length() < tmp.length()) {
324                 vector<string> vtmp;
325                 for (auto const & cmd : comp) {
326                         if (prefixIs(cmd, test))
327                                 vtmp.push_back(cmd);
328                 }
329                 if (vtmp.size() != comp.size()) {
330                         test.erase(test.length() - 1);
331                         break;
332                 }
333                 test += tmp[test.length()];
334         }
335
336         new_prefix = test;
337         return comp;
338 }
339
340
341 DispatchResult const & GuiCommandBuffer::dispatch(string const & str)
342 {
343         if (str.empty()) {
344                 static DispatchResult empty_dr;
345                 return empty_dr;
346         }
347
348         history_.push_back(trim(str));
349         history_pos_ = history_.end();
350         upPB->setEnabled(history_pos_ != history_.begin());
351         downPB->setEnabled(history_pos_ != history_.end());
352         FuncRequest func = lyxaction.lookupFunc(str);
353         func.setOrigin(FuncRequest::COMMANDBUFFER);
354         return lyx::dispatch(func);
355 }
356
357 } // namespace frontend
358 } // namespace lyx
359
360 #include "moc_GuiCommandBuffer.cpp"