]> git.cworth.org Git - apitrace/blob - gui/argumentseditor.cpp
Lots of various cosmetic changes to the call editing.
[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 }
147
148 void ArgumentsEditor::setCall(ApiTraceCall *call)
149 {
150     m_call = call;
151     setupCall();
152 }
153
154 ApiTraceCall * ArgumentsEditor::call() const
155 {
156     return m_call;
157 }
158
159 void ArgumentsEditor::init()
160 {
161     m_ui.setupUi(this);
162
163     connect(m_ui.selectStringCB, SIGNAL(currentIndexChanged(int)),
164             SLOT(currentSourceChanged(int)));
165     connect(m_ui.glslEdit, SIGNAL(textChanged()),
166             SLOT(sourceChanged()));
167     connect(m_ui.revertButton, SIGNAL(clicked()),
168             SLOT(revert()));
169
170     m_ui.argsTree->setModel(m_model);
171     QItemEditorFactory *factory =
172         new ArgumentsItemEditorFactory();
173
174     QItemEditorFactory::setDefaultFactory(factory);
175 }
176
177 void ArgumentsEditor::setupCall()
178 {
179     m_model->clear();
180
181     QStringList headers;
182     headers.append(tr("Argument"));
183     headers.append(tr("Value"));
184     m_model->setColumnCount(2);
185     m_model->setHorizontalHeaderLabels(headers);
186     m_ui.argsTabWidget->removeTab(
187         m_ui.argsTabWidget->indexOf(m_ui.shaderTab));
188
189     if (!m_call)
190         return;
191
192     m_ui.callLabel->setText(m_call->name());
193     QStandardItem *rootItem = m_model->invisibleRootItem();
194     for (int i = 0; i < m_call->argNames().count(); ++i) {
195         QString argName = m_call->argNames()[i];
196         QVariant val = m_call->arguments()[i];
197         QStandardItem *nameItem = new QStandardItem(argName);
198         nameItem->setFlags(nameItem->flags() ^ Qt::ItemIsEditable);
199         QList<QStandardItem*> topRow;
200         topRow.append(nameItem);
201
202         if (val.canConvert<ApiArray>()) {
203             ApiArray array = val.value<ApiArray>();
204             QList<QVariant> vals = array.values();
205
206             QVariant firstVal = vals.value(0);
207             if (firstVal.userType() == QVariant::String) {
208                 m_ui.argsTabWidget->addTab(
209                     m_ui.shaderTab, argName);
210                 setupShaderEditor(vals);
211                 delete nameItem;
212                 continue;
213             } else if (isVariantEditable(firstVal)) {
214                 for (int i = 0; i < vals.count(); ++i) {
215                     QList<QStandardItem*> row;
216
217                     QStandardItem *idx = new QStandardItem();
218                     idx->setFlags(idx->flags() ^ Qt::ItemIsEditable);
219                     idx->setText(tr("%1)").arg(i));
220
221                     QStandardItem *col = new QStandardItem();
222                     col->setFlags(col->flags() | Qt::ItemIsEditable);
223                     col->setData(vals[i], Qt::DisplayRole);
224                     row.append(idx);
225                     row.append(col);
226                     nameItem->appendRow(row);
227                 }
228             } else {
229                 qDebug()<<"\tUnsupported array = "<<firstVal;
230                 delete nameItem;
231                 continue;
232             }
233         } else  if (val.canConvert<ApiPointer>()) {
234             ApiPointer ptr = val.value<ApiPointer>();
235             QStandardItem *item = new QStandardItem();
236             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
237             item->setText(ptr.toString());
238             QIcon icon(":/resources/emblem-locked.png");
239             item->setIcon(icon);
240             item->setToolTip(tr("Argument is read-only"));
241             topRow.append(item);
242         } else if (val.canConvert<ApiEnum>()) {
243             ApiEnum en = val.value<ApiEnum>();
244             QStandardItem *item = new QStandardItem();
245             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
246             item->setText(en.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<ApiBitmask>()) {
252             ApiBitmask mask = val.value<ApiBitmask>();
253             QStandardItem *item = new QStandardItem();
254             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
255             item->setText(mask.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<ApiStruct>()) {
261             ApiStruct str = val.value<ApiStruct>();
262             QStandardItem *item = new QStandardItem();
263             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
264             item->setText(str.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.userType() == QVariant::ByteArray) {
270             QByteArray ba = val.value<QByteArray>();
271             QStandardItem *item = new QStandardItem();
272             item->setFlags(item->flags() ^ Qt::ItemIsEditable);
273             item->setText(
274                 tr("<binary data, size = %1 bytes>").arg(ba.size()));
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 {
280             QStandardItem *item
281                 = new QStandardItem();
282
283             if (isVariantEditable(val)) {
284                 item->setFlags(item->flags() | Qt::ItemIsEditable);
285             } else {
286                 QIcon icon(":/resources/emblem-locked.png");
287                 item->setIcon(icon);
288                 item->setFlags(item->flags() ^ Qt::ItemIsEditable);
289                 item->setToolTip(tr("Argument is read-only"));
290             }
291             item->setData(val, Qt::DisplayRole);
292             topRow.append(item);
293         }
294         rootItem->appendRow(topRow);
295     }
296 }
297
298 void ArgumentsEditor::setupShaderEditor(const QList<QVariant> &sources)
299 {
300     m_ui.selectStringCB->clear();
301     m_ui.glslEdit->clear();
302     for (int i = 0; i < sources.count(); ++i) {
303         m_ui.selectStringCB->addItem(
304             tr("Shader string: %1").arg(i),
305             sources[i]);
306     }
307     m_ui.selectStringCB->setCurrentIndex(0);
308 }
309
310 void ArgumentsEditor::currentSourceChanged(int idx)
311 {
312     QVariant source = m_ui.selectStringCB->itemData(idx);
313     QString str = source.toString();
314     m_ui.glslEdit->setPlainText(source.toString());
315     m_ui.lengthLabel->setText(
316         tr("%1").arg(str.length()));
317 }
318
319 void ArgumentsEditor::sourceChanged()
320 {
321     QString str = m_ui.glslEdit->toPlainText();
322     m_ui.lengthLabel->setText(
323         tr("%1").arg(str.length()));
324
325     m_ui.selectStringCB->setItemData(
326         m_ui.selectStringCB->currentIndex(),
327         str);
328 }
329
330 void ArgumentsEditor::accept()
331 {
332     QStringList argNames = m_call->argNames();
333     QList<QVariant> originalValues = m_call->arguments();
334     QList<QVariant> newValues;
335     bool changed = false;
336     for (int i = 0; i < argNames.count(); ++i) {
337         bool valChanged = false;
338         QString argName = argNames[i];
339         QVariant argValue = originalValues[i];
340         QVariant editorValue = valueForName(argName, argValue, &valChanged);
341         newValues.append(editorValue);
342 #if 0
343         qDebug()<<"Arg = "<<argName;
344         qDebug()<<"\toriginal = "<<argValue;
345         qDebug()<<"\teditor   = "<<editorValue;
346         qDebug()<<"\tchanged  = "<<valChanged;
347 #endif
348         if (valChanged)
349             changed = true;
350     }
351     if (changed)
352         m_call->setEditedValues(newValues);
353     QDialog::accept();
354 }
355
356 QVariant ArgumentsEditor::valueForName(const QString &name,
357                                        const QVariant &originalValue,
358                                        bool *changed) const
359 {
360     QVariant val;
361
362     *changed = false;
363
364     //Handle string arrays specially
365     if (isVariantStringArray(originalValue)) {
366         ApiArray array = originalValue.value<ApiArray>();
367         return arrayFromEditor(array, changed);
368     }
369
370     for (int topRow = 0; topRow < m_model->rowCount(); ++topRow) {
371         QModelIndex nameIdx = m_model->index(topRow, 0, QModelIndex());
372         QString argName = nameIdx.data().toString();
373         /* we display shaders in a separate widget so
374          * the ordering might be different */
375         if (argName == name) {
376             if (originalValue.userType() == QMetaType::type("ApiArray")) {
377                 ApiArray array = originalValue.value<ApiArray>();
378                 val = arrayFromIndex(nameIdx, array, changed);
379             } else {
380                 QModelIndex valIdx = m_model->index(topRow, 1, QModelIndex());
381                 val = valIdx.data();
382                 if (val != originalValue)
383                     *changed = true;
384             }
385         }
386     }
387     return val;
388 }
389
390 QVariant ArgumentsEditor::arrayFromIndex(const QModelIndex &parentIndex,
391                                          const ApiArray &origArray,
392                                          bool *changed) const
393 {
394     QList<QVariant> origValues = origArray.values();
395
396     *changed = false;
397
398     if (origValues.isEmpty())
399         return QVariant::fromValue(ApiArray());
400
401     QList<QVariant> lst;
402     for (int i = 0; i < origValues.count(); ++i) {
403         QModelIndex valIdx = m_model->index(i, 1, parentIndex);
404         QVariant var = valIdx.data();
405         QVariant origValue = origValues[i];
406         if (var != origValue)
407             *changed = true;
408         //qDebug()<<"\t\tarray "<<i<<") "<<var;
409         lst.append(var);
410     }
411     return QVariant::fromValue(ApiArray(lst));
412 }
413
414 QVariant ArgumentsEditor::arrayFromEditor(const ApiArray &origArray,
415                                           bool *changed) const
416 {
417     QList<QVariant> vals;
418     QList<QVariant> origValues = origArray.values();
419
420     Q_ASSERT(isVariantStringArray(QVariant::fromValue(origArray)));
421     *changed = false;
422     //shaders
423     for (int i = 0; i < m_ui.selectStringCB->count(); ++i) {
424         QVariant val = m_ui.selectStringCB->itemData(i);
425         QVariant origValue = origValues[i];
426         if (origValue != val)
427             *changed = true;
428         vals.append(val);
429     }
430     return QVariant::fromValue(ApiArray(vals));
431 }
432
433 void ArgumentsEditor::revert()
434 {
435     m_call->revert();
436     setupCall();
437 }
438
439 #include "argumentseditor.moc"