]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Show important retrace messages long enough to read.
[apitrace] / gui / mainwindow.cpp
1 #include "mainwindow.h"
2
3 #include "apitrace.h"
4 #include "apitracecall.h"
5 #include "apicalldelegate.h"
6 #include "apitracemodel.h"
7 #include "apitracefilter.h"
8 #include "argumentseditor.h"
9 #include "imageviewer.h"
10 #include "jumpwidget.h"
11 #include "retracer.h"
12 #include "searchwidget.h"
13 #include "settingsdialog.h"
14 #include "shaderssourcewidget.h"
15 #include "tracedialog.h"
16 #include "traceprocess.h"
17 #include "thumbnail.h"
18 #include "ui_retracerdialog.h"
19 #include "vertexdatainterpreter.h"
20
21 #include <QAction>
22 #include <QApplication>
23 #include <QDebug>
24 #include <QDesktopServices>
25 #include <QDesktopWidget>
26 #include <QDir>
27 #include <QFileDialog>
28 #include <QLineEdit>
29 #include <QMessageBox>
30 #include <QProgressBar>
31 #include <QToolBar>
32 #include <QUrl>
33 #include <QVBoxLayout>
34 #include <QWebPage>
35 #include <QWebView>
36
37
38 MainWindow::MainWindow()
39     : QMainWindow(),
40       m_api(trace::API_GL),
41       m_initalCallNum(-1),
42       m_selectedEvent(0),
43       m_stateEvent(0),
44       m_nonDefaultsLookupEvent(0)
45 {
46     m_ui.setupUi(this);
47     initObjects();
48     initConnections();
49 }
50
51 void MainWindow::createTrace()
52 {
53     if (!m_traceProcess->canTrace()) {
54         QMessageBox::warning(
55             this,
56             tr("Unsupported"),
57             tr("Current configuration doesn't support tracing."));
58         return;
59     }
60
61     TraceDialog dialog;
62     if (dialog.exec() == QDialog::Accepted) {
63         qDebug()<< "App : " <<dialog.applicationPath();
64         qDebug()<< "  Arguments: "<<dialog.arguments();
65         m_traceProcess->setApi(dialog.api());
66         m_traceProcess->setExecutablePath(dialog.applicationPath());
67         m_traceProcess->setArguments(dialog.arguments());
68         m_traceProcess->start();
69     }
70 }
71
72 void MainWindow::openTrace()
73 {
74     QString fileName =
75             QFileDialog::getOpenFileName(
76                 this,
77                 tr("Open Trace"),
78                 QDir::homePath(),
79                 tr("Trace Files (*.trace)"));
80
81     if (!fileName.isEmpty() && QFile::exists(fileName)) {
82         newTraceFile(fileName);
83     }
84 }
85
86 void MainWindow::loadTrace(const QString &fileName, int callNum)
87 {
88     if (!QFile::exists(fileName)) {
89         QMessageBox::warning(this, tr("File Missing"),
90                              tr("File '%1' doesn't exist.").arg(fileName));
91         return;
92     }
93
94     m_initalCallNum = callNum;
95     newTraceFile(fileName);
96 }
97
98 void MainWindow::callItemSelected(const QModelIndex &index)
99 {
100     ApiTraceEvent *event =
101         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
102
103     if (event && event->type() == ApiTraceEvent::Call) {
104         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
105         m_ui.detailsDock->setWindowTitle(
106             tr("Details View. Frame %1, Call %2")
107             .arg(call->parentFrame() ? call->parentFrame()->number : 0)
108             .arg(call->index()));
109         m_ui.detailsWebView->setHtml(call->toHtml());
110         m_ui.detailsDock->show();
111         if (call->hasBinaryData()) {
112             QByteArray data =
113                 call->arguments()[call->binaryDataIndex()].toByteArray();
114             m_vdataInterpreter->setData(data);
115
116             QVector<QVariant> args = call->arguments();
117             for (int i = 0; i < call->argNames().count(); ++i) {
118                 QString name = call->argNames()[i];
119                 if (name == QLatin1String("stride")) {
120                     int stride = args[i].toInt();
121                     m_ui.vertexStrideSB->setValue(stride);
122                 } else if (name == QLatin1String("size")) {
123                     int components = args[i].toInt();
124                     m_ui.vertexComponentsSB->setValue(components);
125                 } else if (name == QLatin1String("type")) {
126                     QString val = args[i].toString();
127                     int textIndex = m_ui.vertexTypeCB->findText(val);
128                     if (textIndex >= 0) {
129                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
130                     }
131                 }
132             }
133         }
134         m_ui.vertexDataDock->setVisible(call->hasBinaryData());
135         m_selectedEvent = call;
136     } else {
137         if (event && event->type() == ApiTraceEvent::Frame) {
138             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
139         } else {
140             m_selectedEvent = 0;
141         }
142         m_ui.detailsDock->hide();
143         m_ui.vertexDataDock->hide();
144     }
145     if (m_selectedEvent && m_selectedEvent->hasState()) {
146         fillStateForFrame();
147     } else {
148         m_ui.stateDock->hide();
149     }
150 }
151
152 void MainWindow::callItemActivated(const QModelIndex &index) {
153     lookupState();
154 }
155
156 void MainWindow::replayStart()
157 {
158     if (m_trace->isSaving()) {
159         QMessageBox::warning(
160             this,
161             tr("Trace Saving"),
162             tr("QApiTrace is currently saving the edited trace file. "
163                "Please wait until it finishes and try again."));
164         return;
165     }
166     QDialog dlg;
167     Ui_RetracerDialog dlgUi;
168     dlgUi.setupUi(&dlg);
169
170     dlgUi.doubleBufferingCB->setChecked(
171         m_retracer->isDoubleBuffered());
172     dlgUi.errorCheckCB->setChecked(
173         !m_retracer->isBenchmarking());
174
175     if (dlg.exec() == QDialog::Accepted) {
176         m_retracer->setDoubleBuffered(
177             dlgUi.doubleBufferingCB->isChecked());
178         m_retracer->setBenchmarking(
179             !dlgUi.errorCheckCB->isChecked());
180         replayTrace(false, true);
181     }
182 }
183
184 void MainWindow::replayStop()
185 {
186     m_retracer->quit();
187     m_ui.actionStop->setEnabled(false);
188     m_ui.actionReplay->setEnabled(true);
189     m_ui.actionLookupState->setEnabled(true);
190     m_ui.actionShowThumbnails->setEnabled(true);
191 }
192
193 void MainWindow::newTraceFile(const QString &fileName)
194 {
195     qDebug()<< "Loading  : " <<fileName;
196
197     m_progressBar->setValue(0);
198     m_trace->setFileName(fileName);
199
200     if (fileName.isEmpty()) {
201         m_ui.actionReplay->setEnabled(false);
202         m_ui.actionLookupState->setEnabled(false);
203         m_ui.actionShowThumbnails->setEnabled(false);
204         setWindowTitle(tr("QApiTrace"));
205     } else {
206         QFileInfo info(fileName);
207         m_ui.actionReplay->setEnabled(true);
208         m_ui.actionLookupState->setEnabled(true);
209         m_ui.actionShowThumbnails->setEnabled(true);
210         setWindowTitle(
211             tr("QApiTrace - %1").arg(info.fileName()));
212     }
213 }
214
215 void MainWindow::replayFinished(const QString &message)
216 {
217     m_ui.actionStop->setEnabled(false);
218     m_ui.actionReplay->setEnabled(true);
219     m_ui.actionLookupState->setEnabled(true);
220     m_ui.actionShowThumbnails->setEnabled(true);
221
222     m_progressBar->hide();
223     statusBar()->showMessage(message, 2000);
224     m_stateEvent = 0;
225     m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors());
226     m_ui.errorsDock->setVisible(m_trace->hasErrors());
227     if (!m_trace->hasErrors()) {
228         m_ui.errorsTreeWidget->clear();
229     }
230 }
231
232 void MainWindow::replayError(const QString &message)
233 {
234     m_ui.actionStop->setEnabled(false);
235     m_ui.actionReplay->setEnabled(true);
236     m_ui.actionLookupState->setEnabled(true);
237     m_ui.actionShowThumbnails->setEnabled(true);
238     m_stateEvent = 0;
239     m_nonDefaultsLookupEvent = 0;
240
241     m_progressBar->hide();
242     statusBar()->showMessage(
243         tr("Replaying unsuccessful."), 2000);
244     QMessageBox::warning(
245         this, tr("Replay Failed"), message);
246 }
247
248 void MainWindow::startedLoadingTrace()
249 {
250     Q_ASSERT(m_trace);
251     m_progressBar->show();
252     QFileInfo info(m_trace->fileName());
253     statusBar()->showMessage(
254         tr("Loading %1...").arg(info.fileName()));
255 }
256
257 void MainWindow::finishedLoadingTrace()
258 {
259     m_progressBar->hide();
260     if (!m_trace) {
261         return;
262     }
263     m_api = m_trace->api();
264     QFileInfo info(m_trace->fileName());
265     statusBar()->showMessage(
266         tr("Loaded %1").arg(info.fileName()), 3000);
267     if (m_initalCallNum >= 0) {
268         m_trace->findCallIndex(m_initalCallNum);
269         m_initalCallNum = -1;
270     }
271 }
272
273 void MainWindow::replayTrace(bool dumpState, bool dumpThumbnails)
274 {
275     if (m_trace->fileName().isEmpty()) {
276         return;
277     }
278
279     m_retracer->setFileName(m_trace->fileName());
280     m_retracer->setAPI(m_api);
281     m_retracer->setCaptureState(dumpState);
282     m_retracer->setCaptureThumbnails(dumpThumbnails);
283     if (m_retracer->captureState() && m_selectedEvent) {
284         int index = 0;
285         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
286             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index();
287         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
288             ApiTraceFrame *frame =
289                 static_cast<ApiTraceFrame*>(m_selectedEvent);
290             if (frame->isEmpty()) {
291                 //XXX i guess we could still get the current state
292                 qDebug()<<"tried to get a state for an empty frame";
293                 return;
294             }
295             index = frame->lastCallIndex();
296         } else {
297             qDebug()<<"Unknown event type";
298             return;
299         }
300         m_retracer->setCaptureAtCallNumber(index);
301     }
302     m_retracer->start();
303
304     m_ui.actionStop->setEnabled(true);
305     m_progressBar->show();
306     if (dumpState || dumpThumbnails) {
307         if (dumpState && dumpThumbnails) {
308             statusBar()->showMessage(
309                 tr("Looking up the state and capturing thumbnails..."));
310         } else if (dumpState) {
311             statusBar()->showMessage(
312                 tr("Looking up the state..."));
313         } else if (dumpThumbnails) {
314             statusBar()->showMessage(
315                 tr("Capturing thumbnails..."));
316         }
317     } else {
318         statusBar()->showMessage(
319             tr("Replaying the trace file..."));
320     }
321 }
322
323 void MainWindow::lookupState()
324 {
325     if (!m_selectedEvent) {
326         QMessageBox::warning(
327             this, tr("Unknown Event"),
328             tr("To inspect the state select an event in the event list."));
329         return;
330     }
331     if (m_trace->isSaving()) {
332         QMessageBox::warning(
333             this,
334             tr("Trace Saving"),
335             tr("QApiTrace is currently saving the edited trace file. "
336                "Please wait until it finishes and try again."));
337         return;
338     }
339     m_stateEvent = m_selectedEvent;
340     replayTrace(true, false);
341 }
342
343 void MainWindow::showThumbnails()
344 {
345     replayTrace(false, true);
346 }
347
348 MainWindow::~MainWindow()
349 {
350     delete m_trace;
351     m_trace = 0;
352
353     delete m_proxyModel;
354     delete m_model;
355 }
356
357 static void
358 variantToString(const QVariant &var, QString &str)
359 {
360     if (var.type() == QVariant::List) {
361         QVector<QVariant> lst = var.toList().toVector();
362         str += QLatin1String("[");
363         for (int i = 0; i < lst.count(); ++i) {
364             QVariant val = lst[i];
365             variantToString(val, str);
366             if (i < lst.count() - 1)
367                 str += QLatin1String(", ");
368         }
369         str += QLatin1String("]");
370     } else if (var.type() == QVariant::Map) {
371         Q_ASSERT(!"unsupported state type");
372     } else if (var.type() == QVariant::Hash) {
373         Q_ASSERT(!"unsupported state type");
374     } else {
375         str += var.toString();
376     }
377 }
378
379 static QTreeWidgetItem *
380 variantToItem(const QString &key, const QVariant &var,
381               const QVariant &defaultVar);
382
383 static void
384 variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap,
385                   QList<QTreeWidgetItem *> &items)
386 {
387     QVariantMap::const_iterator itr;
388     for (itr = map.constBegin(); itr != map.constEnd(); ++itr) {
389         QString key = itr.key();
390         QVariant var = itr.value();
391         QVariant defaultVar = defaultMap[key];
392
393         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
394         if (item) {
395             items.append(item);
396         }
397     }
398 }
399
400 static void
401 variantListToItems(const QVector<QVariant> &lst,
402                    const QVector<QVariant> &defaultLst,
403                    QList<QTreeWidgetItem *> &items)
404 {
405     for (int i = 0; i < lst.count(); ++i) {
406         QString key = QString::number(i);
407         QVariant var = lst[i];
408         QVariant defaultVar;
409         
410         if (i < defaultLst.count()) {
411             defaultVar = defaultLst[i];
412         }
413
414         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
415         if (item) {
416             items.append(item);
417         }
418     }
419 }
420
421 static bool
422 isVariantDeep(const QVariant &var)
423 {
424     if (var.type() == QVariant::List) {
425         QVector<QVariant> lst = var.toList().toVector();
426         for (int i = 0; i < lst.count(); ++i) {
427             if (isVariantDeep(lst[i])) {
428                 return true;
429             }
430         }
431         return false;
432     } else if (var.type() == QVariant::Map) {
433         return true;
434     } else if (var.type() == QVariant::Hash) {
435         return true;
436     } else {
437         return false;
438     }
439 }
440
441 static QTreeWidgetItem *
442 variantToItem(const QString &key, const QVariant &var,
443               const QVariant &defaultVar)
444 {
445     if (var == defaultVar) {
446         return NULL;
447     }
448
449     QString val;
450
451     bool deep = isVariantDeep(var);
452     if (!deep) {
453         variantToString(var, val);
454     }
455
456     //qDebug()<<"key = "<<key;
457     //qDebug()<<"val = "<<val;
458     QStringList lst;
459     lst += key;
460     lst += val;
461
462     QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, lst);
463
464     if (deep) {
465         QList<QTreeWidgetItem *> children;
466         if (var.type() == QVariant::Map) {
467             QVariantMap map = var.toMap();
468             QVariantMap defaultMap = defaultVar.toMap();
469             variantMapToItems(map, defaultMap, children);
470         }
471         if (var.type() == QVariant::List) {
472             QVector<QVariant> lst = var.toList().toVector();
473             QVector<QVariant> defaultLst = defaultVar.toList().toVector();
474             variantListToItems(lst, defaultLst, children);
475         }
476         item->addChildren(children);
477     }
478
479     return item;
480 }
481
482 static void addSurfaceItem(const ApiSurface &surface,
483                            const QString &label,
484                            QTreeWidgetItem *parent,
485                            QTreeWidget *tree)
486 {
487     QIcon icon(QPixmap::fromImage(surface.thumb()));
488     QTreeWidgetItem *item = new QTreeWidgetItem(parent);
489     item->setIcon(0, icon);
490
491     int width = surface.size().width();
492     int height = surface.size().height();
493     QString descr =
494         QString::fromLatin1("%1, %2, %3 x %4")
495         .arg(label)
496         .arg(surface.formatName())
497         .arg(width)
498         .arg(height);
499
500     //item->setText(1, descr);
501     QLabel *l = new QLabel(descr, tree);
502     l->setWordWrap(true);
503     tree->setItemWidget(item, 1, l);
504
505     item->setData(0, Qt::UserRole, surface.image());
506 }
507
508 void MainWindow::fillStateForFrame()
509 {
510     if (!m_selectedEvent || !m_selectedEvent->hasState()) {
511         return;
512     }
513
514     if (m_nonDefaultsLookupEvent) {
515         m_ui.nonDefaultsCB->blockSignals(true);
516         m_ui.nonDefaultsCB->setChecked(true);
517         m_ui.nonDefaultsCB->blockSignals(false);
518     }
519
520     bool nonDefaults = m_ui.nonDefaultsCB->isChecked();
521     QVariantMap defaultParams;
522     if (nonDefaults) {
523         ApiTraceState defaultState = m_trace->defaultState();
524         defaultParams = defaultState.parameters();
525     }
526
527     const ApiTraceState &state = *m_selectedEvent->state();
528     m_ui.stateTreeWidget->clear();
529     QList<QTreeWidgetItem *> items;
530     variantMapToItems(state.parameters(), defaultParams, items);
531     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
532
533     QMap<QString, QString> shaderSources = state.shaderSources();
534     if (shaderSources.isEmpty()) {
535         m_sourcesWidget->setShaders(shaderSources);
536     } else {
537         m_sourcesWidget->setShaders(shaderSources);
538     }
539
540     m_ui.uniformsTreeWidget->clear();
541     QList<QTreeWidgetItem *> uniformsItems;
542     variantMapToItems(state.uniforms(), QVariantMap(), uniformsItems);
543     m_ui.uniformsTreeWidget->insertTopLevelItems(0, uniformsItems);
544
545     const QList<ApiTexture> &textures =
546         state.textures();
547     const QList<ApiFramebuffer> &fbos =
548         state.framebuffers();
549
550     m_ui.surfacesTreeWidget->clear();
551     if (textures.isEmpty() && fbos.isEmpty()) {
552         m_ui.surfacesTab->setDisabled(false);
553     } else {
554         m_ui.surfacesTreeWidget->setIconSize(QSize(THUMBNAIL_SIZE, THUMBNAIL_SIZE));
555         if (!textures.isEmpty()) {
556             QTreeWidgetItem *textureItem =
557                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
558             textureItem->setText(0, tr("Textures"));
559             if (textures.count() <= 6) {
560                 textureItem->setExpanded(true);
561             }
562
563             for (int i = 0; i < textures.count(); ++i) {
564                 const ApiTexture &texture =
565                     textures[i];
566                 addSurfaceItem(texture, texture.label(),
567                                textureItem,
568                                m_ui.surfacesTreeWidget);
569             }
570         }
571         if (!fbos.isEmpty()) {
572             QTreeWidgetItem *fboItem =
573                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
574             fboItem->setText(0, tr("Framebuffers"));
575             if (fbos.count() <= 6) {
576                 fboItem->setExpanded(true);
577             }
578
579             for (int i = 0; i < fbos.count(); ++i) {
580                 const ApiFramebuffer &fbo =
581                     fbos[i];
582                 addSurfaceItem(fbo, fbo.type(),
583                                fboItem,
584                                m_ui.surfacesTreeWidget);
585             }
586         }
587         m_ui.surfacesTab->setEnabled(true);
588     }
589     m_ui.stateDock->show();
590 }
591
592 void MainWindow::showSettings()
593 {
594     SettingsDialog dialog;
595     dialog.setAPI(m_api);
596     dialog.setFilterModel(m_proxyModel);
597
598     dialog.exec();
599
600     m_api = dialog.getAPI();
601 }
602
603 void MainWindow::openHelp(const QUrl &url)
604 {
605     QDesktopServices::openUrl(url);
606 }
607
608 void MainWindow::showSurfacesMenu(const QPoint &pos)
609 {
610     QTreeWidget *tree = m_ui.surfacesTreeWidget;
611     QTreeWidgetItem *item = tree->itemAt(pos);
612     if (!item) {
613         return;
614     }
615
616     QMenu menu(tr("Surfaces"), this);
617
618     QAction *act = menu.addAction(tr("View Image"));
619     act->setStatusTip(tr("View the currently selected surface"));
620     connect(act, SIGNAL(triggered()),
621             SLOT(showSelectedSurface()));
622
623     act = menu.addAction(tr("Save Image"));
624     act->setStatusTip(tr("Save the currently selected surface"));
625     connect(act, SIGNAL(triggered()),
626             SLOT(saveSelectedSurface()));
627
628     menu.exec(tree->viewport()->mapToGlobal(pos));
629 }
630
631 void MainWindow::showSelectedSurface()
632 {
633     QTreeWidgetItem *item =
634         m_ui.surfacesTreeWidget->currentItem();
635
636     if (!item) {
637         return;
638     }
639
640     ImageViewer *viewer = new ImageViewer(this);
641
642     QString title;
643     if (selectedCall()) {
644         title = tr("QApiTrace - Surface at %1 (%2)")
645                 .arg(selectedCall()->name())
646                 .arg(selectedCall()->index());
647     } else {
648         title = tr("QApiTrace - Surface Viewer");
649     }
650     viewer->setWindowTitle(title);
651
652     viewer->setAttribute(Qt::WA_DeleteOnClose, true);
653
654     QVariant var = item->data(0, Qt::UserRole);
655     QImage img = var.value<QImage>();
656     viewer->setImage(img);
657
658     viewer->show();
659     viewer->raise();
660     viewer->activateWindow();
661 }
662
663 void MainWindow::initObjects()
664 {
665     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
666     m_ui.uniformsTreeWidget->sortByColumn(0, Qt::AscendingOrder);
667
668     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
669     QVBoxLayout *layout = new QVBoxLayout;
670     layout->addWidget(m_sourcesWidget);
671     m_ui.shadersTab->setLayout(layout);
672
673     m_trace = new ApiTrace();
674     m_retracer = new Retracer(this);
675
676     m_vdataInterpreter = new VertexDataInterpreter(this);
677     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
678     m_vdataInterpreter->setStride(
679         m_ui.vertexStrideSB->value());
680     m_vdataInterpreter->setComponents(
681         m_ui.vertexComponentsSB->value());
682     m_vdataInterpreter->setStartingOffset(
683         m_ui.startingOffsetSB->value());
684     m_vdataInterpreter->setTypeFromString(
685         m_ui.vertexTypeCB->currentText());
686
687     m_model = new ApiTraceModel();
688     m_model->setApiTrace(m_trace);
689     m_proxyModel = new ApiTraceFilter();
690     m_proxyModel->setSourceModel(m_model);
691     m_ui.callView->setModel(m_proxyModel);
692     m_ui.callView->setItemDelegate(
693         new ApiCallDelegate(m_ui.callView));
694     m_ui.callView->resizeColumnToContents(0);
695     m_ui.callView->header()->swapSections(0, 1);
696     m_ui.callView->setColumnWidth(1, 42);
697     m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
698
699     m_progressBar = new QProgressBar();
700     m_progressBar->setRange(0, 100);
701     statusBar()->addPermanentWidget(m_progressBar);
702     m_progressBar->hide();
703
704     m_argsEditor = new ArgumentsEditor(this);
705
706     m_ui.detailsDock->hide();
707     m_ui.errorsDock->hide();
708     m_ui.vertexDataDock->hide();
709     m_ui.stateDock->hide();
710     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
711
712     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
713     tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
714
715     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
716
717     m_ui.detailsWebView->page()->setLinkDelegationPolicy(
718         QWebPage::DelegateExternalLinks);
719
720     m_jumpWidget = new JumpWidget(this);
721     m_ui.centralLayout->addWidget(m_jumpWidget);
722     m_jumpWidget->hide();
723
724     m_searchWidget = new SearchWidget(this);
725     m_ui.centralLayout->addWidget(m_searchWidget);
726     m_searchWidget->hide();
727
728     m_traceProcess = new TraceProcess(this);
729 }
730
731 void MainWindow::initConnections()
732 {
733     connect(m_trace, SIGNAL(startedLoadingTrace()),
734             this, SLOT(startedLoadingTrace()));
735     connect(m_trace, SIGNAL(loaded(int)),
736             this, SLOT(loadProgess(int)));
737     connect(m_trace, SIGNAL(finishedLoadingTrace()),
738             this, SLOT(finishedLoadingTrace()));
739     connect(m_trace, SIGNAL(startedSaving()),
740             this, SLOT(slotStartedSaving()));
741     connect(m_trace, SIGNAL(saved()),
742             this, SLOT(slotSaved()));
743     connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
744             this, SLOT(slotTraceChanged(ApiTraceEvent*)));
745     connect(m_trace, SIGNAL(findResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
746             this, SLOT(slotSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
747     connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)),
748             this, SLOT(slotFoundFrameStart(ApiTraceFrame*)));
749     connect(m_trace, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
750             this, SLOT(slotFoundFrameEnd(ApiTraceFrame*)));
751     connect(m_trace, SIGNAL(foundCallIndex(ApiTraceCall*)),
752             this, SLOT(slotJumpToResult(ApiTraceCall*)));
753
754     connect(m_retracer, SIGNAL(finished(const QString&)),
755             this, SLOT(replayFinished(const QString&)));
756     connect(m_retracer, SIGNAL(error(const QString&)),
757             this, SLOT(replayError(const QString&)));
758     connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
759             this, SLOT(replayStateFound(ApiTraceState*)));
760     connect(m_retracer, SIGNAL(foundThumbnails(const QList<QImage>&)),
761             this, SLOT(replayThumbnailsFound(const QList<QImage>&)));
762     connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
763             this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
764
765     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
766             m_vdataInterpreter, SLOT(interpretData()));
767     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
768             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
769     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
770             m_vdataInterpreter, SLOT(setStride(int)));
771     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
772             m_vdataInterpreter, SLOT(setComponents(int)));
773     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
774             m_vdataInterpreter, SLOT(setStartingOffset(int)));
775
776
777     connect(m_ui.actionNew, SIGNAL(triggered()),
778             this, SLOT(createTrace()));
779     connect(m_ui.actionOpen, SIGNAL(triggered()),
780             this, SLOT(openTrace()));
781     connect(m_ui.actionQuit, SIGNAL(triggered()),
782             this, SLOT(close()));
783
784     connect(m_ui.actionFind, SIGNAL(triggered()),
785             this, SLOT(slotSearch()));
786     connect(m_ui.actionGo, SIGNAL(triggered()),
787             this, SLOT(slotGoTo()));
788     connect(m_ui.actionGoFrameStart, SIGNAL(triggered()),
789             this, SLOT(slotGoFrameStart()));
790     connect(m_ui.actionGoFrameEnd, SIGNAL(triggered()),
791             this, SLOT(slotGoFrameEnd()));
792
793     connect(m_ui.actionReplay, SIGNAL(triggered()),
794             this, SLOT(replayStart()));
795     connect(m_ui.actionStop, SIGNAL(triggered()),
796             this, SLOT(replayStop()));
797     connect(m_ui.actionLookupState, SIGNAL(triggered()),
798             this, SLOT(lookupState()));
799     connect(m_ui.actionShowThumbnails, SIGNAL(triggered()),
800             this, SLOT(showThumbnails()));
801     connect(m_ui.actionOptions, SIGNAL(triggered()),
802             this, SLOT(showSettings()));
803
804     connect(m_ui.callView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
805             this, SLOT(callItemSelected(const QModelIndex &)));
806     connect(m_ui.callView, SIGNAL(doubleClicked(const QModelIndex &)),
807             this, SLOT(callItemActivated(const QModelIndex &)));
808     connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
809             this, SLOT(customContextMenuRequested(QPoint)));
810
811     connect(m_ui.surfacesTreeWidget,
812             SIGNAL(customContextMenuRequested(const QPoint &)),
813             SLOT(showSurfacesMenu(const QPoint &)));
814     connect(m_ui.surfacesTreeWidget,
815             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
816             SLOT(showSelectedSurface()));
817
818     connect(m_ui.detailsWebView, SIGNAL(linkClicked(const QUrl&)),
819             this, SLOT(openHelp(const QUrl&)));
820
821     connect(m_ui.nonDefaultsCB, SIGNAL(toggled(bool)),
822             this, SLOT(fillState(bool)));
823
824     connect(m_jumpWidget, SIGNAL(jumpTo(int)),
825             SLOT(slotJumpTo(int)));
826
827     connect(m_searchWidget,
828             SIGNAL(searchNext(const QString&, Qt::CaseSensitivity)),
829             SLOT(slotSearchNext(const QString&, Qt::CaseSensitivity)));
830     connect(m_searchWidget,
831             SIGNAL(searchPrev(const QString&, Qt::CaseSensitivity)),
832             SLOT(slotSearchPrev(const QString&, Qt::CaseSensitivity)));
833
834     connect(m_traceProcess, SIGNAL(tracedFile(const QString&)),
835             SLOT(createdTrace(const QString&)));
836     connect(m_traceProcess, SIGNAL(error(const QString&)),
837             SLOT(traceError(const QString&)));
838
839     connect(m_ui.errorsDock, SIGNAL(visibilityChanged(bool)),
840             m_ui.actionShowErrorsDock, SLOT(setChecked(bool)));
841     connect(m_ui.actionShowErrorsDock, SIGNAL(triggered(bool)),
842             m_ui.errorsDock, SLOT(setVisible(bool)));
843     connect(m_ui.errorsTreeWidget,
844             SIGNAL(itemActivated(QTreeWidgetItem*, int)),
845             this, SLOT(slotErrorSelected(QTreeWidgetItem*)));
846 }
847
848 void MainWindow::replayStateFound(ApiTraceState *state)
849 {
850     m_stateEvent->setState(state);
851     m_model->stateSetOnEvent(m_stateEvent);
852     if (m_selectedEvent == m_stateEvent ||
853         m_nonDefaultsLookupEvent == m_selectedEvent) {
854         fillStateForFrame();
855     } else {
856         m_ui.stateDock->hide();
857     }
858     m_nonDefaultsLookupEvent = 0;
859 }
860
861 void MainWindow::replayThumbnailsFound(const QList<QImage> &thumbnails)
862 {
863     m_ui.callView->setUniformRowHeights(false);
864     m_trace->bindThumbnailsToFrames(thumbnails);
865 }
866
867 void MainWindow::slotGoTo()
868 {
869     m_searchWidget->hide();
870     m_jumpWidget->show();
871 }
872
873 void MainWindow::slotJumpTo(int callNum)
874 {
875     m_trace->findCallIndex(callNum);
876 }
877
878 void MainWindow::createdTrace(const QString &path)
879 {
880     qDebug()<<"Done tracing "<<path;
881     newTraceFile(path);
882 }
883
884 void MainWindow::traceError(const QString &msg)
885 {
886     QMessageBox::warning(
887             this,
888             tr("Tracing Error"),
889             msg);
890 }
891
892 void MainWindow::slotSearch()
893 {
894     m_jumpWidget->hide();
895     m_searchWidget->show();
896 }
897
898 void MainWindow::slotSearchNext(const QString &str,
899                                 Qt::CaseSensitivity sensitivity)
900 {
901     ApiTraceCall *call = currentCall();
902     ApiTraceFrame *frame = currentFrame();
903
904     Q_ASSERT(call || frame);
905     if (!frame) {
906         frame = call->parentFrame();
907     }
908     Q_ASSERT(frame);
909
910     m_trace->findNext(frame, call, str, sensitivity);
911 }
912
913 void MainWindow::slotSearchPrev(const QString &str,
914                                 Qt::CaseSensitivity sensitivity)
915 {
916     ApiTraceCall *call = currentCall();
917     ApiTraceFrame *frame = currentFrame();
918
919     Q_ASSERT(call || frame);
920     if (!frame) {
921         frame = call->parentFrame();
922     }
923     Q_ASSERT(frame);
924
925     m_trace->findPrev(frame, call, str, sensitivity);
926 }
927
928 void MainWindow::fillState(bool nonDefaults)
929 {
930     if (nonDefaults) {
931         ApiTraceState defaultState = m_trace->defaultState();
932         if (defaultState.isEmpty()) {
933             m_ui.nonDefaultsCB->blockSignals(true);
934             m_ui.nonDefaultsCB->setChecked(false);
935             m_ui.nonDefaultsCB->blockSignals(false);
936             ApiTraceFrame *firstFrame =
937                 m_trace->frameAt(0);
938             if (!firstFrame) {
939                 return;
940             }
941             if (!firstFrame->isLoaded()) {
942                 m_trace->loadFrame(firstFrame);
943                 return;
944             }
945             ApiTraceCall *firstCall = firstFrame->calls().first();
946             ApiTraceEvent *oldSelected = m_selectedEvent;
947             m_nonDefaultsLookupEvent = m_selectedEvent;
948             m_selectedEvent = firstCall;
949             lookupState();
950             m_selectedEvent = oldSelected;
951         }
952     }
953     fillStateForFrame();
954 }
955
956 void MainWindow::customContextMenuRequested(QPoint pos)
957 {
958     QModelIndex index = m_ui.callView->indexAt(pos);
959
960     callItemSelected(index);
961     if (!index.isValid()) {
962         return;
963     }
964
965     ApiTraceEvent *event =
966         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
967     if (!event) {
968         return;
969     }
970
971     QMenu menu;
972     menu.addAction(QIcon(":/resources/media-record.png"),
973                    tr("Lookup state"), this, SLOT(lookupState()));
974     if (event->type() == ApiTraceEvent::Call) {
975         menu.addAction(tr("Edit"), this, SLOT(editCall()));
976     }
977
978     menu.exec(QCursor::pos());
979 }
980
981 void MainWindow::editCall()
982 {
983     if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) {
984         ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
985         m_argsEditor->setCall(call);
986         m_argsEditor->show();
987     }
988 }
989
990 void MainWindow::slotStartedSaving()
991 {
992     m_progressBar->show();
993     statusBar()->showMessage(
994         tr("Saving to %1").arg(m_trace->fileName()));
995 }
996
997 void MainWindow::slotSaved()
998 {
999     statusBar()->showMessage(
1000         tr("Saved to %1").arg(m_trace->fileName()), 2000);
1001     m_progressBar->hide();
1002 }
1003
1004 void MainWindow::slotGoFrameStart()
1005 {
1006     ApiTraceFrame *frame = currentFrame();
1007     ApiTraceCall *call = currentCall();
1008
1009     if (!frame && call) {
1010         frame = call->parentFrame();
1011     }
1012
1013     m_trace->findFrameStart(frame);
1014 }
1015
1016 void MainWindow::slotGoFrameEnd()
1017 {
1018     ApiTraceFrame *frame = currentFrame();
1019     ApiTraceCall *call = currentCall();
1020
1021     if (!frame && call) {
1022         frame = call->parentFrame();
1023     }
1024
1025     m_trace->findFrameEnd(frame);
1026 }
1027
1028 ApiTraceFrame * MainWindow::selectedFrame() const
1029 {
1030     if (m_selectedEvent) {
1031         if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
1032             return static_cast<ApiTraceFrame*>(m_selectedEvent);
1033         } else {
1034             Q_ASSERT(m_selectedEvent->type() == ApiTraceEvent::Call);
1035             ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
1036             return call->parentFrame();
1037         }
1038     }
1039     return NULL;
1040 }
1041
1042 void MainWindow::slotTraceChanged(ApiTraceEvent *event)
1043 {
1044     Q_ASSERT(event);
1045     if (event == m_selectedEvent) {
1046         if (event->type() == ApiTraceEvent::Call) {
1047             ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
1048             m_ui.detailsWebView->setHtml(call->toHtml());
1049         }
1050     }
1051 }
1052
1053 void MainWindow::slotRetraceErrors(const QList<ApiTraceError> &errors)
1054 {
1055     m_ui.errorsTreeWidget->clear();
1056
1057     foreach(ApiTraceError error, errors) {
1058         m_trace->setCallError(error);
1059
1060         QTreeWidgetItem *item =
1061             new QTreeWidgetItem(m_ui.errorsTreeWidget);
1062         item->setData(0, Qt::DisplayRole, error.callIndex);
1063         item->setData(0, Qt::UserRole, error.callIndex);
1064         QString type = error.type;
1065         type[0] = type[0].toUpper();
1066         item->setData(1, Qt::DisplayRole, type);
1067         item->setData(2, Qt::DisplayRole, error.message);
1068     }
1069 }
1070
1071 void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
1072 {
1073     if (current) {
1074         int callIndex =
1075             current->data(0, Qt::UserRole).toInt();
1076         m_trace->findCallIndex(callIndex);
1077     }
1078 }
1079
1080 ApiTraceCall * MainWindow::selectedCall() const
1081 {
1082     if (m_selectedEvent &&
1083         m_selectedEvent->type() == ApiTraceEvent::Call) {
1084         return static_cast<ApiTraceCall*>(m_selectedEvent);
1085     }
1086     return NULL;
1087 }
1088
1089 void MainWindow::saveSelectedSurface()
1090 {
1091     QTreeWidgetItem *item =
1092         m_ui.surfacesTreeWidget->currentItem();
1093
1094     if (!item || !m_trace) {
1095         return;
1096     }
1097
1098     QVariant var = item->data(0, Qt::UserRole);
1099     QImage img = var.value<QImage>();
1100
1101     QString imageIndex;
1102     if (selectedCall()) {
1103         imageIndex = tr("_call_%1")
1104                      .arg(selectedCall()->index());
1105     } else if (selectedFrame()) {
1106         ApiTraceCall *firstCall = selectedFrame()->call(0);
1107         if (firstCall) {
1108             imageIndex = tr("_frame_%1")
1109                          .arg(firstCall->index());
1110         } else {
1111             qDebug()<<"unknown frame number";
1112             imageIndex = tr("_frame_%1")
1113                          .arg(firstCall->index());
1114         }
1115     }
1116
1117     //which of the surfaces are we saving
1118     QTreeWidgetItem *parent = item->parent();
1119     int parentIndex =
1120         m_ui.surfacesTreeWidget->indexOfTopLevelItem(parent);
1121     if (parentIndex < 0) {
1122         parentIndex = 0;
1123     }
1124     int childIndex = 0;
1125     if (parent) {
1126         childIndex = parent->indexOfChild(item);
1127     } else {
1128         childIndex = m_ui.surfacesTreeWidget->indexOfTopLevelItem(item);
1129     }
1130
1131
1132     QString fileName =
1133         tr("%1%2-%3_%4.png")
1134         .arg(m_trace->fileName())
1135         .arg(imageIndex)
1136         .arg(parentIndex)
1137         .arg(childIndex);
1138     //qDebug()<<"save "<<fileName;
1139     img.save(fileName, "PNG");
1140     statusBar()->showMessage( tr("Saved '%1'").arg(fileName), 5000);
1141 }
1142
1143 void MainWindow::loadProgess(int percent)
1144 {
1145     m_progressBar->setValue(percent);
1146 }
1147
1148 void MainWindow::slotSearchResult(const ApiTrace::SearchRequest &request,
1149                                   ApiTrace::SearchResult result,
1150                                   ApiTraceCall *call)
1151 {
1152     switch (result) {
1153     case ApiTrace::SearchResult_NotFound:
1154         m_searchWidget->setFound(false);
1155         break;
1156     case ApiTrace::SearchResult_Found: {
1157         QModelIndex index = m_proxyModel->indexForCall(call);
1158
1159         if (index.isValid()) {
1160             m_ui.callView->setCurrentIndex(index);
1161             m_searchWidget->setFound(true);
1162         } else {
1163             //call is filtered out, so continue searching but from the
1164             // filtered call
1165             if (!call) {
1166                 qDebug()<<"Error: search success with no call";
1167                 return;
1168             }
1169 //            qDebug()<<"filtered! search from "<<call->searchText()
1170 //                   <<", call idx = "<<call->index();
1171
1172             if (request.direction == ApiTrace::SearchRequest::Next) {
1173                 m_trace->findNext(call->parentFrame(), call,
1174                                   request.text, request.cs);
1175             } else {
1176                 m_trace->findNext(call->parentFrame(), call,
1177                                   request.text, request.cs);
1178             }
1179         }
1180     }
1181         break;
1182     case ApiTrace::SearchResult_Wrapped:
1183         m_searchWidget->setFound(false);
1184         break;
1185     }
1186 }
1187
1188 ApiTraceFrame * MainWindow::currentFrame() const
1189 {
1190     QModelIndex index = m_ui.callView->currentIndex();
1191     ApiTraceEvent *event = 0;
1192
1193     if (!index.isValid()) {
1194         index = m_proxyModel->index(0, 0, QModelIndex());
1195         if (!index.isValid()) {
1196             qDebug()<<"no currently valid index";
1197             return 0;
1198         }
1199     }
1200
1201     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1202     Q_ASSERT(event);
1203     if (!event) {
1204         return 0;
1205     }
1206
1207     ApiTraceFrame *frame = 0;
1208     if (event->type() == ApiTraceCall::Frame) {
1209         frame = static_cast<ApiTraceFrame*>(event);
1210     }
1211     return frame;
1212 }
1213
1214 ApiTraceCall * MainWindow::currentCall() const
1215 {
1216     QModelIndex index = m_ui.callView->currentIndex();
1217     ApiTraceEvent *event = 0;
1218
1219     if (!index.isValid()) {
1220         index = m_proxyModel->index(0, 0, QModelIndex());
1221         if (!index.isValid()) {
1222             qDebug()<<"no currently valid index";
1223             return 0;
1224         }
1225     }
1226
1227     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1228     Q_ASSERT(event);
1229     if (!event) {
1230         return 0;
1231     }
1232
1233     ApiTraceCall *call = 0;
1234     if (event->type() == ApiTraceCall::Call) {
1235         call = static_cast<ApiTraceCall*>(event);
1236     }
1237
1238     return call;
1239
1240 }
1241
1242 void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame)
1243 {
1244     Q_ASSERT(frame->isLoaded());
1245     if (!frame || frame->isEmpty()) {
1246         return;
1247     }
1248
1249     QVector<ApiTraceCall*>::const_iterator itr;
1250     QVector<ApiTraceCall*> calls = frame->calls();
1251
1252     itr = calls.constBegin();
1253     while (itr != calls.constEnd()) {
1254         ApiTraceCall *call = *itr;
1255         QModelIndex idx = m_proxyModel->indexForCall(call);
1256         if (idx.isValid()) {
1257             m_ui.callView->setCurrentIndex(idx);
1258             break;
1259         }
1260         ++itr;
1261     }
1262 }
1263
1264 void MainWindow::slotFoundFrameEnd(ApiTraceFrame *frame)
1265 {
1266     Q_ASSERT(frame->isLoaded());
1267     if (!frame || frame->isEmpty()) {
1268         return;
1269     }
1270     QVector<ApiTraceCall*>::const_iterator itr;
1271     QVector<ApiTraceCall*> calls = frame->calls();
1272
1273     itr = calls.constEnd();
1274     do {
1275         --itr;
1276         ApiTraceCall *call = *itr;
1277         QModelIndex idx = m_proxyModel->indexForCall(call);
1278         if (idx.isValid()) {
1279             m_ui.callView->setCurrentIndex(idx);
1280             break;
1281         }
1282     } while (itr != calls.constBegin());
1283 }
1284
1285 void MainWindow::slotJumpToResult(ApiTraceCall *call)
1286 {
1287     QModelIndex index = m_proxyModel->indexForCall(call);
1288     if (index.isValid()) {
1289         m_ui.callView->setCurrentIndex(index);
1290     } else {
1291         statusBar()->showMessage(tr("Call has been filtered out."));
1292     }
1293 }
1294
1295 #include "mainwindow.moc"