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