]> git.cworth.org Git - apitrace/blob - gui/argumentseditor.cpp
Switch more places from qlist to qvector.
[apitrace] / gui / argumentseditor.cpp
1 #include "argumentseditor.h"
2
3 #include "apitracecall.h"
4
5 #include <QDebug>
6 #include <QDoubleSpinBox>
7 #include <QItemEditorFactory>
8 #include <QSpinBox>
9
10 #include <limits.h>
11 #include <float.h>
12
13
14 static bool
15 isVariantEditable(const QVariant &var)
16 {
17     if (var.canConvert<ApiArray>()) {
18         ApiArray array = var.value<ApiArray>();
19         QList<QVariant> vals = array.values();
20         if (vals.isEmpty())
21             return false;
22         else
23             return isVariantEditable(vals.first());
24     }
25     switch (var.userType()) {
26     case QVariant::Bool:
27     case QVariant::Int:
28     case QVariant::UInt:
29     case QVariant::LongLong:
30     case QVariant::ULongLong:
31     case QVariant::Double:
32         return true;
33     default:
34         return false;
35     }
36 }
37
38 static bool
39 isVariantStringArray(const QVariant &var)
40 {
41     if (var.isNull() || var.userType() != QMetaType::type("ApiArray"))
42         return false;
43
44     ApiArray array = var.value<ApiArray>();
45     QList<QVariant> origValues = array.values();
46     if (origValues.isEmpty() ||
47         origValues.first().userType() != QVariant::String)
48         return false;
49
50     return true;
51 }
52
53 ArgumentsItemEditorFactory::ArgumentsItemEditorFactory()
54     : QItemEditorFactory()
55 {
56 }
57
58 QWidget * ArgumentsItemEditorFactory::createEditor(QVariant::Type type,
59                                                    QWidget *parent) const
60 {
61     switch (type) {
62     case QVariant::Bool: {
63         BooleanComboBox *cb = new BooleanComboBox(parent);
64         cb->setFrame(false);
65         return cb;
66     }
67     case QVariant::UInt: {
68         QSpinBox *sb = new QSpinBox(parent);
69         sb->setFrame(false);
70         sb->setMaximum(INT_MAX);
71         return sb; }
72     case QVariant::Int: {
73         QSpinBox *sb = new QSpinBox(parent);
74         sb->setFrame(false);
75         sb->setMinimum(INT_MIN);
76         sb->setMaximum(INT_MAX);
77         return sb;
78     }
79     case QVariant::ULongLong: {
80         QSpinBox *sb = new QSpinBox(parent);
81         sb->setFrame(false);
82         sb->setMaximum(INT_MAX);
83         return sb; }
84     case QVariant::LongLong: {
85         QSpinBox *sb = new QSpinBox(parent);
86         sb->setFrame(false);
87         sb->setMinimum(INT_MIN);
88         sb->setMaximum(INT_MAX);
89         return sb;
90     }
91     case QVariant::Pixmap:
92         return new QLabel(parent);
93     case QVariant::Double: {
94         QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
95         sb->setFrame(false);
96         sb->setMinimum(-DBL_MAX);
97         sb->setMaximum(DBL_MAX);
98         sb->setDecimals(8);
99         return sb;
100     }
101     default:
102         break;
103     }
104     return 0;
105 }
106
107 QByteArray
108 ArgumentsItemEditorFactory::valuePropertyName(QVariant::Type type) const
109 {
110     switch (type) {
111     case QVariant::Bool:
112         return "currentIndex";
113     case QVariant::UInt:
114     case QVariant::Int:
115     case QVariant::LongLong:
116     case QVariant::ULongLong:
117     case QVariant::Double:
118         return "value";
119 #if 0
120     case QVariant::String:
121 #endif
122     default:
123         return "text";
124     }
125 }
126
127 BooleanComboBox::BooleanComboBox(QWidget *parent)
128     : QComboBox(parent)
129 {
130     addItem(tr("False"));
131     addItem(tr("True"));
132 }
133
134 void BooleanComboBox::setValue(bool value)
135 {
136     setCurrentIndex(value ? 1 : 0);
137 }
138
139 bool BooleanComboBox::value() const
140 {
141     return (currentIndex() == 1);
142 }
143
144 ArgumentsEditor::ArgumentsEditor(QWidget *parent)
145     : QDialog(parent),
146       m_model(new QStandardItemModel()),
147       m_call(0)
148 {
149     init();
150 }
151
152 ArgumentsEditor::~ArgumentsEditor()
153 {
154     delete m_model;
155 }
156
157 void ArgumentsEditor::setCall(ApiTraceCall *call)
158 {
159     m_call = call;
160     setupCall();
161 }
162
163 ApiTraceCall * ArgumentsEditor::call() const
164 {
165     return m_call;
166 }
167
168 void ArgumentsEditor::init()
169 {
170     m_ui.setupUi(this);
171
172     connect(m_ui.selectStringCB, SIGNAL(currentIndexChanged(int)),
173             SLOT(currentSourceChanged(int)));
174     connect(m_ui.glslEdit, SIGNAL(textChanged()),
175             SLOT(sourceChanged()));
176     connect(m_ui.revertButton, SIGNAL(clicked()),
177             SLOT(revert()));
178
179     m_ui.argsTree->setModel(m_model);
180     QItemEditorFactory *factory =
181         new ArgumentsItemEditorFactory();
182
183     QItemEditorFactory::setDefaultFactory(factory);
184 }
185
186 void ArgumentsEditor::setupCall()
187 {
188     m_model->clear();
189
190     QStringList headers;
191     headers.append(tr("Argument"));
192     headers.append(tr("Value"));
193     m_model->setColumnCount(2);
194     m_model->setHorizontalHeaderLabels(headers);
195     m_ui.argsTabWidget->removeTab(
196         m_ui.argsTabWidget->indexOf(m_ui.shaderTab));
197
198     if (!m_call)
199         return;
200
201     m_ui.callLabel->setText(m_call->name());
202     QStandardItem *rootItem = m_model->invisibleRootItem();
203     for (int i = 0; i < m_call->argNames().count(); ++i) {
204         QString argName = m_call->argNames()[i];
205         QVariant val = m_call->arguments()[i];
206         QStandardItem *nameItem = new QStandardItem(argName);
207         nameItem->setFlags(nameItem->flags() ^ Qt::ItemIsEditable);
208         QList<QStandardItem*> topRow;
209         topRow.append(nameItem);
210
211         if (val.canConvert<ApiArray>()) {
212             ApiArray array = val.value<ApiArray>();
213             QList<QVariant> vals = array.values();
214
215             QVariant firstVal = vals.value(0);
216             if (firstVal.userType() == QVariant::String) {
217                 m_ui.argsTabWidget->addTab(
218                     m_ui.shaderTab, argName);
219                 setupShaderEditor(vals);
220                 delete nameItem;
221                 continue;
222             } else if (isVariantEditable(firstVal)) {
223                 for (int i = 0; i < vals.count(); ++i) {
224                     QList<QStandardItem*> row;
225
226                     QStandardItem *idx = new QStandardItem();
227                     idx->setFlags(idx->flags() ^ Qt::ItemIsEditable);
228                     idx->setText(tr("%1)").arg(i));
229
230                     QStandardItem *col = new QStandardItem();
231                     col->setFlags(col->flags() | Qt::ItemIsEditable);
232                     col->setData(vals[i], Qt::DisplayRole);
233                     row.append(idx);
234                     row.append(col);
235                     nameItem->appendRow(row);
236                 }
237             } else {
238                 qDebug()<<"\tUnsupported array = "<<firstVal;
239                 delete nameItem;
240                 continue;
241             }
242         } else  if (val.canConvert<ApiPointer>()) {
243             ApiPointer ptr = val.value<ApiPointer>();
244             QStandardItem *item = new QStandardItem();
245             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
246             item->setText(ptr.toString());
247             QIcon icon(":/resources/emblem-locked.png");
248             item->setIcon(icon);
249             item->setToolTip(tr("Argument is read-only"));
250             topRow.append(item);
251         } else if (val.canConvert<ApiEnum>()) {
252             ApiEnum en = val.value<ApiEnum>();
253             QStandardItem *item = new QStandardItem();
254             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
255             item->setText(en.toString());
256             QIcon icon(":/resources/emblem-locked.png");
257             item->setIcon(icon);
258             item->setToolTip(tr("Argument is read-only"));
259             topRow.append(item);
260         } else if (val.canConvert<ApiBitmask>()) {
261             ApiBitmask mask = val.value<ApiBitmask>();
262             QStandardItem *item = new QStandardItem();
263             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
264             item->setText(mask.toString());
265             QIcon icon(":/resources/emblem-locked.png");
266             item->setIcon(icon);
267             item->setToolTip(tr("Argument is read-only"));
268             topRow.append(item);
269         } else if (val.canConvert<ApiStruct>()) {
270             ApiStruct str = val.value<ApiStruct>();
271             QStandardItem *item = new QStandardItem();
272             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
273             item->setText(str.toString());
274             QIcon icon(":/resources/emblem-locked.png");
275             item->setIcon(icon);
276             item->setToolTip(tr("Argument is read-only"));
277             topRow.append(item);
278         } else if (val.userType() == QVariant::ByteArray) {
279             QByteArray ba = val.value<QByteArray>();
280             QStandardItem *item = new QStandardItem();
281             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
282             item->setText(
283                 tr("<binary data, size = %1 bytes>").arg(ba.size()));
284             QIcon icon(":/resources/emblem-locked.png");
285             item->setIcon(icon);
286             item->setToolTip(tr("Argument is read-only"));
287             topRow.append(item);
288         } else {
289             QStandardItem *item
290                 = new QStandardItem();
291
292             if (isVariantEditable(val)) {
293                 item->setFlags(item->flags() | Qt::ItemIsEditable);
294             } else {
295                 QIcon icon(":/resources/emblem-locked.png");
296                 item->setIcon(icon);
297                 item->setFlags(item->flags() ^ Qt::ItemIsEditable);
298                 item->setToolTip(tr("Argument is read-only"));
299             }
300             item->setData(val, Qt::DisplayRole);
301             topRow.append(item);
302         }
303         rootItem->appendRow(topRow);
304     }
305 }
306
307 void ArgumentsEditor::setupShaderEditor(const QList<QVariant> &sources)
308 {
309     m_ui.selectStringCB->clear();
310     m_ui.glslEdit->clear();
311     for (int i = 0; i < sources.count(); ++i) {
312         m_ui.selectStringCB->addItem(
313             tr("Shader string: %1").arg(i),
314             sources[i]);
315     }
316     m_ui.selectStringCB->setCurrentIndex(0);
317 }
318
319 void ArgumentsEditor::currentSourceChanged(int idx)
320 {
321     QVariant source = m_ui.selectStringCB->itemData(idx);
322     QString str = source.toString();
323     m_ui.glslEdit->setPlainText(source.toString());
324     m_ui.lengthLabel->setText(
325         tr("%1").arg(str.length()));
326 }
327
328 void ArgumentsEditor::sourceChanged()
329 {
330     QString str = m_ui.glslEdit->toPlainText();
331     m_ui.lengthLabel->setText(
332         tr("%1").arg(str.length()));
333
334     m_ui.selectStringCB->setItemData(
335         m_ui.selectStringCB->currentIndex(),
336         str);
337 }
338
339 void ArgumentsEditor::accept()
340 {
341     QStringList argNames = m_call->argNames();
342     QVector<QVariant> originalValues = m_call->arguments();
343     QVector<QVariant> newValues;
344     bool changed = false;
345     for (int i = 0; i < argNames.count(); ++i) {
346         bool valChanged = false;
347         QString argName = argNames[i];
348         QVariant argValue = originalValues[i];
349         QVariant editorValue = valueForName(argName, argValue, &valChanged);
350
351         newValues.append(editorValue);
352 #if 0
353         qDebug()<<"Arg = "<<argName;
354         qDebug()<<"\toriginal = "<<argValue;
355         qDebug()<<"\teditor   = "<<editorValue;
356         qDebug()<<"\tchanged  = "<<valChanged;
357 #endif
358         if (valChanged)
359             changed = true;
360     }
361     if (changed)
362         m_call->setEditedValues(newValues);
363     QDialog::accept();
364 }
365
366 QVariant ArgumentsEditor::valueForName(const QString &name,
367                                        const QVariant &originalValue,
368                                        bool *changed) const
369 {
370     QVariant val;
371
372     *changed = false;
373
374     //Handle string arrays specially
375     if (isVariantStringArray(originalValue)) {
376         ApiArray array = originalValue.value<ApiArray>();
377         return arrayFromEditor(array, changed);
378     }
379
380     if (!isVariantEditable(originalValue)) {
381         return originalValue;
382     }
383
384     for (int topRow = 0; topRow < m_model->rowCount(); ++topRow) {
385         QModelIndex nameIdx = m_model->index(topRow, 0, QModelIndex());
386         QString argName = nameIdx.data().toString();
387         /* we display shaders in a separate widget so
388          * the ordering might be different */
389         if (argName == name) {
390             if (originalValue.userType() == QMetaType::type("ApiArray")) {
391                 ApiArray array = originalValue.value<ApiArray>();
392                 val = arrayFromIndex(nameIdx, array, changed);
393             } else {
394                 QModelIndex valIdx = m_model->index(topRow, 1, QModelIndex());
395                 val = valIdx.data();
396                 if (val != originalValue)
397                     *changed = true;
398             }
399         }
400     }
401     return val;
402 }
403
404 QVariant ArgumentsEditor::arrayFromIndex(const QModelIndex &parentIndex,
405                                          const ApiArray &origArray,
406                                          bool *changed) const
407 {
408     QList<QVariant> origValues = origArray.values();
409
410     *changed = false;
411
412     if (origValues.isEmpty())
413         return QVariant::fromValue(ApiArray());
414
415     QList<QVariant> lst;
416     for (int i = 0; i < origValues.count(); ++i) {
417         QModelIndex valIdx = m_model->index(i, 1, parentIndex);
418         QVariant var = valIdx.data();
419         QVariant origValue = origValues[i];
420         if (var != origValue)
421             *changed = true;
422         //qDebug()<<"\t\tarray "<<i<<") "<<var;
423         lst.append(var);
424     }
425     return QVariant::fromValue(ApiArray(lst));
426 }
427
428 QVariant ArgumentsEditor::arrayFromEditor(const ApiArray &origArray,
429                                           bool *changed) const
430 {
431     QList<QVariant> vals;
432     QList<QVariant> origValues = origArray.values();
433
434     Q_ASSERT(isVariantStringArray(QVariant::fromValue(origArray)));
435     *changed = false;
436     //shaders
437     for (int i = 0; i < m_ui.selectStringCB->count(); ++i) {
438         QVariant val = m_ui.selectStringCB->itemData(i);
439         QVariant origValue = origValues[i];
440         if (origValue != val)
441             *changed = true;
442         vals.append(val);
443     }
444     return QVariant::fromValue(ApiArray(vals));
445 }
446
447 void ArgumentsEditor::revert()
448 {
449     m_call->revert();
450     setupCall();
451 }
452
453 #include "argumentseditor.moc"