Menu item "Checkout" spawns a dialog, thus requires ellipsis
[qgit4/redivivus.git] / src / inputdialog.cpp
blob8022c657b271ff275ce48dd17a1a56734a846ffd
1 #include "inputdialog.h"
2 #include "common.h"
3 #include <QLineEdit>
4 #include <QTextEdit>
5 #include <QComboBox>
6 #include <QGridLayout>
7 #include <QLabel>
8 #include <QDialogButtonBox>
9 #include <QPushButton>
10 #include <QCompleter>
11 #include <QListView>
12 #include <QStringListModel>
14 namespace QGit {
16 struct InputDialog::WidgetItem {
17 WidgetItem() : widget(NULL) {}
18 void init(QWidget* w, const char *name) {
19 widget = w;
20 prop_name = name;
23 const char *prop_name; // property name
24 QWidget *widget;
25 int start, end;
28 QString parseString(const QString &value, const InputDialog::VariableMap &vars) {
29 if (value.startsWith('$')) return vars.value(value.mid(1), QString()).toString();
30 else return value;
32 QStringList parseStringList(const QString &value, const InputDialog::VariableMap &vars) {
33 QStringList values = value.split(',');
34 QStringList result;
35 for (QStringList::iterator it=values.begin(), end=values.end(); it!=end; ++it) {
36 if (it->startsWith('$')) result.append(vars.value(value.mid(1), QStringList()).toStringList());
37 else result.append(*it);
39 return result;
42 class RefNameValidator : public QValidator {
43 public:
44 RefNameValidator(bool allowEmpty=false, QObject *parent=0)
45 : QValidator(parent)
46 , invalid("[ ~^:\?*[]")
47 , allowEmpty(allowEmpty)
50 void fixup(QString& input) const;
51 State validate(QString & input, int & pos) const;
52 private:
53 const QRegExp invalid;
54 bool allowEmpty;
57 void RefNameValidator::fixup(QString &input) const
59 // remove invalid chars
60 input.replace(invalid, "");
61 input.replace("/.","/"); // no dot after slash
62 input.replace("..","."); // no two dots in a row
63 input.replace("//","/"); // no two slashes in a row
64 input.replace("@{", "@"); // no sequence @{
67 QValidator::State RefNameValidator::validate(QString &input, int &pos) const
69 // https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
70 // automatically remove invalid chars
71 QString front = input.left(pos); fixup(front);
72 QString rear = input.mid(pos); fixup(rear);
73 input = front + rear;
74 // keep cursor were it was
75 pos = front.length();
77 QString fixed(input); fixup(fixed);
78 if (fixed != input) return Invalid;
80 // empty string or single @ are not allowed
81 if ((input.isEmpty() && !allowEmpty) || input == "@")
82 return Intermediate;
83 return Acceptable;
87 InputDialog::InputDialog(const QString &cmd, const VariableMap &variables,
88 const QString &title, QWidget *parent, Qt::WindowFlags f)
89 : QDialog(parent, f)
90 , cmd(cmd)
92 this->setWindowTitle(title);
93 QGridLayout *layout = new QGridLayout(this);
95 QRegExp re("%(([a-z_]+)([[]([a-z ,]+)[]])?:)?([^%=]+)(=[^%]+)?%");
96 int start = 0;
97 int row = 0;
98 while ((start = re.indexIn(cmd, start)) != -1) {
99 const QString type = re.cap(2);
100 const QStringList opts = re.cap(4).split(',', QString::SkipEmptyParts);
101 const QString name = re.cap(5);
102 const QString value = re.cap(6).mid(1);
103 if (widgets.count(name)) { // widget already created
104 if (!type.isEmpty()) dbs("token must not be redefined: " + name);
105 continue;
108 WidgetItemPtr item (new WidgetItem());
109 item->start = start;
110 item->end = start = start + re.matchedLength();
112 if (type == "combobox") {
113 QComboBox *w = new QComboBox(this);
114 w->addItems(parseStringList(value, variables));
115 if (opts.contains("editable")) w->setEditable(true);
116 w->setMinimumWidth(100);
117 if (opts.contains("ref")) {
118 w->setValidator(new RefNameValidator(opts.contains("empty")));
119 validators.insert(name, w->validator());
120 connect(w, SIGNAL(editTextChanged(QString)), this, SLOT(validate()));
122 item->init(w, "currentText");
123 } else if (type == "listbox") {
124 QListView *w = new QListView(this);
125 w->setModel(new QStringListModel(parseStringList(value, variables)));
126 item->init(w, NULL);
127 } else if (type == "lineedit" || type == "") {
128 QLineEdit *w = new QLineEdit(this);
129 w->setText(parseString(value, variables));
130 QStringList values = parseStringList(value, variables);
131 if (!values.isEmpty()) // use default string list as
132 w->setCompleter(new QCompleter(values));
133 if (opts.contains("ref")) {
134 w->setValidator(new RefNameValidator(opts.contains("empty")));
135 validators.insert(name, w->validator());
136 connect(w, SIGNAL(textEdited(QString)), this, SLOT(validate()));
138 item->init(w, "text");
139 } else if (type == "textedit") {
140 QTextEdit *w = new QTextEdit(this);
141 w->setText(parseString(value, variables));
142 item->init(w, "plainText");
143 } else {
144 dbs("unknown widget type: " + type);
145 continue;
147 widgets.insert(name, item);
148 if (name.startsWith('_')) { // _name triggers hiding of label
149 layout->addWidget(item->widget, row, 1);
150 } else {
151 layout->addWidget(new QLabel(name + ":"), row, 0);
152 layout->addWidget(item->widget, row, 1);
154 ++row;
156 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
157 layout->addWidget(buttons, row, 0, 1, 2);
158 okButton = buttons->button(QDialogButtonBox::Ok);
160 connect(okButton, SIGNAL(pressed()), this, SLOT(accept()));
161 connect(buttons->button(QDialogButtonBox::Cancel), SIGNAL(pressed()), this, SLOT(reject()));
162 validate();
165 QWidget *InputDialog::widget(const QString &token)
167 WidgetItemPtr item = widgets.value(token);
168 return item ? item->widget : NULL;
171 QVariant InputDialog::value(const QString &token) const
173 WidgetItemPtr item = widgets.value(token);
174 if (!item) {
175 dbs("unknown token: " + token);
176 return QString();
178 return item->widget->property(item->prop_name);
181 bool InputDialog::validate()
183 bool result=true;
184 for (QMap<QString, const QValidator*>::const_iterator
185 it=validators.begin(), end=validators.end(); result && it != end; ++it) {
186 QString val = value(it.key()).toString();
187 int pos=0;
188 if (it.value()->validate(val, pos) != QValidator::Acceptable)
189 result=false;
191 okButton->setEnabled(result);
192 return result;
195 QString InputDialog::replace(const VariableMap &variables) const
197 QString result = cmd;
198 for (WidgetMap::const_iterator it = widgets.begin(), end = widgets.end(); it != end; ++it) {
199 QString token = "%" + it.key() + "%";
200 WidgetItemPtr item = it.value();
201 QString value = item->widget->property(item->prop_name).toString();
202 result.replace(item->start, item->end - item->start, value); // replace main token
203 result.replace(token, value); // replace all other occurences of %name%
205 for (VariableMap::const_iterator it=variables.begin(), end=variables.end(); it != end; ++it) {
206 QString token = "$" + it.key();
207 QString val = it.value().type() == QVariant::StringList ? it.value().toStringList().join(" ")
208 : it.value().toString();
209 result.replace(token, val);
211 return result;
214 } // namespace QGit