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