]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Abstract retracing into a class of its own.
[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 "retracer.h"
9 #include "settingsdialog.h"
10
11 #include <qjson/parser.h>
12
13 #include <QAction>
14 #include <QDebug>
15 #include <QDir>
16 #include <QFileDialog>
17 #include <QLineEdit>
18 #include <QMessageBox>
19 #include <QProgressBar>
20 #include <QToolBar>
21 #include <QWebView>
22
23
24 MainWindow::MainWindow()
25     : QMainWindow(),
26       m_selectedEvent(0),
27       m_stateEvent(0),
28       m_jsonParser(new QJson::Parser())
29 {
30     m_ui.setupUi(this);
31     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
32
33     m_trace = new ApiTrace();
34     connect(m_trace, SIGNAL(startedLoadingTrace()),
35             this, SLOT(startedLoadingTrace()));
36     connect(m_trace, SIGNAL(finishedLoadingTrace()),
37             this, SLOT(finishedLoadingTrace()));
38
39     m_retracer = new Retracer(this);
40     connect(m_retracer, SIGNAL(finished(const QByteArray&)),
41             this, SLOT(replayFinished(const QByteArray&)));
42     connect(m_retracer, SIGNAL(error(const QString&)),
43             this, SLOT(replayError(const QString&)));
44
45     m_model = new ApiTraceModel();
46     m_model->setApiTrace(m_trace);
47     m_proxyModel = new ApiTraceFilter();
48     m_proxyModel->setSourceModel(m_model);
49     m_ui.callView->setModel(m_proxyModel);
50     m_ui.callView->setItemDelegate(new ApiCallDelegate);
51     m_ui.callView->resizeColumnToContents(0);
52     m_ui.callView->header()->swapSections(0, 1);
53     m_ui.callView->setColumnWidth(1, 42);
54
55     QToolBar *toolBar = addToolBar(tr("Navigation"));
56     m_filterEdit = new QLineEdit(toolBar);
57     toolBar->addWidget(m_filterEdit);
58
59     m_progressBar = new QProgressBar();
60     m_progressBar->setRange(0, 0);
61     statusBar()->addPermanentWidget(m_progressBar);
62     m_progressBar->hide();
63
64     m_ui.detailsDock->hide();
65     m_ui.stateDock->hide();
66
67     connect(m_ui.actionOpen, SIGNAL(triggered()),
68             this, SLOT(openTrace()));
69     connect(m_ui.actionQuit, SIGNAL(triggered()),
70             this, SLOT(close()));
71
72     connect(m_ui.actionReplay, SIGNAL(triggered()),
73             this, SLOT(replayStart()));
74     connect(m_ui.actionStop, SIGNAL(triggered()),
75             this, SLOT(replayStop()));
76     connect(m_ui.actionLookupState, SIGNAL(triggered()),
77             this, SLOT(lookupState()));
78        connect(m_ui.actionOptions, SIGNAL(triggered()),
79             this, SLOT(showSettings()));
80
81     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
82             this, SLOT(callItemSelected(const QModelIndex &)));
83     connect(m_filterEdit, SIGNAL(returnPressed()),
84             this, SLOT(filterTrace()));
85 }
86
87 void MainWindow::openTrace()
88 {
89     QString fileName =
90         QFileDialog::getOpenFileName(
91             this,
92             tr("Open Trace"),
93             QDir::homePath(),
94             tr("Trace Files (*.trace)"));
95
96     qDebug()<< "File name : " <<fileName;
97
98     newTraceFile(fileName);
99 }
100
101 void MainWindow::loadTrace(const QString &fileName)
102 {
103     if (!QFile::exists(fileName)) {
104         QMessageBox::warning(this, tr("File Missing"),
105                              tr("File '%1' doesn't exist.").arg(fileName));
106         return;
107     }
108     qDebug()<< "Loading  : " <<fileName;
109
110     m_progressBar->setValue(0);
111     newTraceFile(fileName);
112 }
113
114 void MainWindow::callItemSelected(const QModelIndex &index)
115 {
116     ApiTraceEvent *event =
117         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
118
119     if (event && event->type() == ApiTraceEvent::Call) {
120         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
121         m_ui.detailsWebView->setHtml(call->toHtml());
122         m_ui.detailsDock->show();
123         m_selectedEvent = call;
124     } else {
125         if (event && event->type() == ApiTraceEvent::Frame) {
126             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
127         } else
128             m_selectedEvent = 0;
129         m_ui.detailsDock->hide();
130     }
131     if (m_selectedEvent && !m_selectedEvent->state().isEmpty()) {
132         fillStateForFrame();
133     } else
134         m_ui.stateDock->hide();
135 }
136
137 void MainWindow::filterTrace()
138 {
139     m_proxyModel->setFilterString(m_filterEdit->text());
140 }
141
142 void MainWindow::replayStart()
143 {
144     replayTrace(false);
145 }
146
147 void MainWindow::replayStop()
148 {
149     m_retracer->terminate();
150     m_ui.actionStop->setEnabled(false);
151     m_ui.actionReplay->setEnabled(true);
152     m_ui.actionLookupState->setEnabled(true);
153 }
154
155 void MainWindow::newTraceFile(const QString &fileName)
156 {
157     m_traceFileName = fileName;
158     m_trace->setFileName(fileName);
159
160     if (m_traceFileName.isEmpty()) {
161         m_ui.actionReplay->setEnabled(false);
162         m_ui.actionLookupState->setEnabled(false);
163     } else {
164         m_ui.actionReplay->setEnabled(true);
165         m_ui.actionLookupState->setEnabled(true);
166     }
167 }
168
169 void MainWindow::replayFinished(const QByteArray &output)
170 {
171     m_ui.actionStop->setEnabled(false);
172     m_ui.actionReplay->setEnabled(true);
173     m_ui.actionLookupState->setEnabled(true);
174
175     if (m_retracer->captureState()) {
176         bool ok = false;
177         QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
178         parseState(parsedJson[QLatin1String("parameters")].toMap());
179     } else if (output.length() < 80) {
180         statusBar()->showMessage(output);
181     }
182     m_stateEvent = 0;
183 }
184
185 void MainWindow::replayError(const QString &message)
186 {
187     m_ui.actionStop->setEnabled(false);
188     m_ui.actionReplay->setEnabled(true);
189     m_ui.actionLookupState->setEnabled(true);
190     m_stateEvent = 0;
191
192     QMessageBox::warning(
193         this, tr("Replay Failed"), message);
194 }
195
196 void MainWindow::startedLoadingTrace()
197 {
198     Q_ASSERT(m_trace);
199     m_progressBar->show();
200     QFileInfo info(m_trace->fileName());
201     statusBar()->showMessage(
202         tr("Loading %1...").arg(info.fileName()));
203 }
204
205 void MainWindow::finishedLoadingTrace()
206 {
207     m_progressBar->hide();
208     if (!m_trace) {
209         return;
210     }
211     QFileInfo info(m_trace->fileName());
212     statusBar()->showMessage(
213         tr("Loaded %1").arg(info.fileName()), 3000);
214 }
215
216 void MainWindow::replayTrace(bool dumpState)
217 {
218     if (m_traceFileName.isEmpty())
219         return;
220
221     m_retracer->setFileName(m_traceFileName);
222     m_retracer->setCaptureState(dumpState);
223     if (m_retracer->captureState() && m_selectedEvent) {
224         int index = 0;
225         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
226             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index;
227         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
228             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(m_selectedEvent);
229             if (frame->calls.isEmpty()) {
230                 //XXX i guess we could still get the current state
231                 qDebug()<<"tried to get a state for an empty frame";
232                 return;
233             }
234             index = frame->calls.first()->index;
235         } else {
236             qDebug()<<"Unknown event type";
237             return;
238         }
239         m_retracer->setCaptureAtCallNumber(index);
240     }
241     m_retracer->start();
242
243     m_ui.actionStop->setEnabled(true);
244 }
245
246 void MainWindow::lookupState()
247 {
248     if (!m_selectedEvent) {
249         QMessageBox::warning(
250             this, tr("Unknown Event"),
251             tr("To inspect the state select an event in the event list."));
252         return;
253     }
254     m_stateEvent = m_selectedEvent;
255     replayTrace(true);
256 }
257
258 MainWindow::~MainWindow()
259 {
260     delete m_jsonParser;
261 }
262
263 void MainWindow::parseState(const QVariantMap &params)
264 {
265     QVariantMap::const_iterator itr;
266
267     m_stateEvent->setState(params);
268     m_model->stateSetOnEvent(m_stateEvent);
269     if (m_selectedEvent == m_stateEvent) {
270         fillStateForFrame();
271     } else {
272         m_ui.stateDock->hide();
273     }
274 }
275
276 static void
277 variantToString(const QVariant &var, QString &str)
278 {
279     if (var.type() == QVariant::List) {
280         QVariantList lst = var.toList();
281         str += QLatin1String("[");
282         for (int i = 0; i < lst.count(); ++i) {
283             QVariant val = lst[i];
284             variantToString(val, str);
285             if (i < lst.count() - 1)
286                 str += QLatin1String(", ");
287         }
288         str += QLatin1String("]");
289     } else if (var.type() == QVariant::Map) {
290         Q_ASSERT(!"unsupported state type");
291     } else if (var.type() == QVariant::Hash) {
292         Q_ASSERT(!"unsupported state type");
293     } else {
294         str += var.toString();
295     }
296 }
297
298 void MainWindow::fillStateForFrame()
299 {
300     QVariantMap::const_iterator itr;
301     QVariantMap params;
302
303     if (!m_selectedEvent || m_selectedEvent->state().isEmpty())
304         return;
305
306     m_ui.stateTreeWidget->clear();
307     params = m_selectedEvent->state();
308     QList<QTreeWidgetItem *> items;
309     for (itr = params.constBegin(); itr != params.constEnd(); ++itr) {
310         QString key = itr.key();
311         QString val;
312
313         variantToString(itr.value(), val);
314         //qDebug()<<"key = "<<key;
315         //qDebug()<<"val = "<<val;
316         QStringList lst;
317         lst += key;
318         lst += val;
319         items.append(new QTreeWidgetItem((QTreeWidget*)0, lst));
320     }
321     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
322     m_ui.stateDock->show();
323 }
324
325 void MainWindow::showSettings()
326 {
327     SettingsDialog dialog;
328     dialog.setFilterOptions(m_proxyModel->filterOptions());
329
330     if (dialog.exec() == QDialog::Accepted) {
331         m_proxyModel->setFilterOptions(dialog.filterOptions());
332     }
333 }
334
335 #include "mainwindow.moc"