]> sigrok.org Git - sigrok-qt.git/blob - mainwindow.cpp
.gitignore: Add missing entries.
[sigrok-qt.git] / mainwindow.cpp
1 /*
2  * This file is part of the sigrok project.
3  *
4  * Copyright (C) 2010-2011 Uwe Hermann <uwe@hermann-uwe.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20
21 extern "C" {
22 #include <sigrokdecode.h>
23 }
24
25 #include <QDebug>
26 #include <QMessageBox>
27 #include <QFileDialog>
28 #include <QProgressDialog>
29 #include <QDockWidget>
30 #include <QScrollBar>
31 #include "mainwindow.h"
32 #include "ui_mainwindow.h"
33 #include "configform.h"
34 #include "ui_configform.h"
35 #include "channelform.h"
36 #include "ui_channelform.h"
37 #include "decodersform.h"
38 #include "ui_decodersform.h"
39 #include "decoderstackform.h"
40 #include "ui_decoderstackform.h"
41
42 extern "C" {
43 /* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */
44 #define __STDC_FORMAT_MACROS
45 #include <inttypes.h>
46 #include <stdint.h>
47 #include <stdarg.h>
48 #include <glib.h>
49 #include <libsigrok/libsigrok.h>
50 #include <sigrokdecode.h>
51 }
52
53 #define DOCK_VERTICAL   0
54 #define DOCK_HORIZONTAL 1
55
56 uint64_t limit_samples = 0; /* FIXME */
57
58 QProgressDialog *progress = NULL;
59
60 /* TODO: Documentation. */
61 extern "C" {
62 static int logger(void *cb_data, int loglevel, const char *format, va_list args)
63 {
64         QString s;
65
66         if (loglevel > srd_log_loglevel_get())
67                 return SRD_OK;
68
69         s.vsprintf(format, args);
70
71         MainWindow *mw = (MainWindow *)cb_data;
72         mw->ui->plainTextEdit->appendPlainText(QString("srd: ").append(s));
73
74         return SRD_OK;
75 }
76 }
77
78 MainWindow::MainWindow(QWidget *parent)
79         : QMainWindow(parent), ui(new Ui::MainWindow)
80 {
81         struct sr_dev_driver **drivers;
82         int i;
83
84         devices = NULL;
85         currentLA = -1;
86         numChannels = -1;
87         configChannelTitleBarLayout = DOCK_VERTICAL; /* Vertical layout */
88         for (int i = 0; i < NUMCHANNELS; ++i)
89                 dockWidgets[i] = NULL;
90
91         ui->setupUi(this);
92
93         /* FIXME */
94         QMainWindow::setCentralWidget(ui->mainWidget);
95
96         srd_log_loglevel_set(SRD_LOG_SPEW);
97
98         if (srd_log_callback_set(logger, (void *)this) != SRD_OK) {
99                 qDebug() << "ERROR: srd_log_handler_set() failed.";
100                 return; /* TODO? */
101         }
102         qDebug() << "srd_log_handler_set() call successful.";
103
104         /* Initialize all libsigrok drivers. */
105         drivers = sr_driver_list();
106         for (i = 0; drivers[i]; i++) {
107                 if (sr_driver_init(drivers[i]) != SR_OK) {
108                         qDebug("Failed to initialize driver %s", drivers[i]->name);
109                         return;
110                 }
111         }
112
113         // this->setDockOptions(QMainWindow::AllowNestedDocks);
114 }
115
116 MainWindow::~MainWindow()
117 {
118         srd_exit();
119         sr_exit();
120
121         delete ui;
122 }
123
124 void MainWindow::setupDockWidgets(void)
125 {
126         /* TODO: Do not create new dockWidgets if we already have them. */
127
128         /* TODO: Kill any old dockWidgets before creating new ones? */
129
130         for (int i = 0; i < getNumChannels(); ++i) {
131                 channelForms[i] = new ChannelForm(this);
132                 channelForms[i]->setChannelNumber(i);
133
134                 dockWidgets[i] = new QDockWidget(this);
135                 dockWidgets[i]->setAllowedAreas(Qt::BottomDockWidgetArea);
136
137                 QDockWidget::DockWidgetFeatures f;
138                 f = QDockWidget::DockWidgetClosable |
139                     QDockWidget::DockWidgetMovable |
140                     QDockWidget::DockWidgetFloatable;
141                 if (configChannelTitleBarLayout == DOCK_VERTICAL)
142                         f |= QDockWidget::DockWidgetVerticalTitleBar;
143                 dockWidgets[i]->setFeatures(f);
144                 dockWidgets[i]->setWidget(channelForms[i]);
145                 addDockWidget(Qt::BottomDockWidgetArea, dockWidgets[i],
146                               Qt::Vertical);
147
148                 /* Update labels upon changes. */
149                 QObject::connect(channelForms[i],
150                         SIGNAL(sampleStartChanged(QString)),
151                         ui->labelSampleStart, SLOT(setText(QString)));
152                 QObject::connect(channelForms[i],
153                         SIGNAL(sampleEndChanged(QString)),
154                         ui->labelSampleEnd, SLOT(setText(QString)));
155                 QObject::connect(channelForms[i],
156                         SIGNAL(scaleFactorChanged(QString)),
157                         ui->labelScaleFactor, SLOT(setText(QString)));
158
159                 /* Redraw channels upon changes. */
160                 QObject::connect(channelForms[i],
161                         SIGNAL(sampleStartChanged(QString)),
162                         channelForms[i], SLOT(generatePainterPath()));
163                 QObject::connect(channelForms[i],
164                         SIGNAL(sampleEndChanged(QString)),
165                         channelForms[i], SLOT(generatePainterPath()));
166                 QObject::connect(channelForms[i],
167                         SIGNAL(scaleFactorChanged(QString)),
168                         channelForms[i], SLOT(generatePainterPath()));
169
170                 // dockWidgets[i]->show();
171
172 #if 0
173                 /* If the user renames a channel, adapt the dock title. */
174                 QObject::connect(lineEdits[i], SIGNAL(textChanged(QString)),
175                                  dockWidgets[i], SLOT(setWindowTitle(QString)));
176 #endif
177         }
178
179         /* For now, display only one scrollbar which scrolls all channels. */
180         QDockWidget* scrollWidget = new QDockWidget(this);
181         scrollWidget->setAllowedAreas(Qt::BottomDockWidgetArea);
182         horizontalScrollBar = new QScrollBar(this);
183         horizontalScrollBar->setOrientation(Qt::Horizontal);
184
185         QDockWidget::DockWidgetFeatures f;
186         if (configChannelTitleBarLayout == DOCK_VERTICAL)
187                 f |= QDockWidget::DockWidgetVerticalTitleBar;
188         scrollWidget->setFeatures(f);
189         scrollWidget->setWidget(horizontalScrollBar);
190         addDockWidget(Qt::BottomDockWidgetArea, scrollWidget,
191                               Qt::Vertical);
192
193         for (int i = 0; i < getNumChannels(); ++i) {
194                 /* The scrollbar scrolls all channels. */
195                 connect(horizontalScrollBar, SIGNAL(valueChanged(int)),
196                                 channelForms[i], SLOT(setScrollBarValue(int)));
197         }
198 }
199
200 GSList *MainWindow::getDevices(void)
201 {
202         return devices;
203 }
204
205 void MainWindow::setCurrentLA(int la)
206 {
207         currentLA = la;
208 }
209
210 int MainWindow::getCurrentLA(void)
211 {
212         return currentLA;
213 }
214
215 void MainWindow::setNumChannels(int ch)
216 {
217         numChannels = ch;
218 }
219
220 int MainWindow::getNumChannels(void)
221 {
222         return numChannels;
223 }
224
225 void MainWindow::on_actionAbout_triggered()
226 {
227         GSList *l;
228         struct sr_dev_driver **drivers;
229         struct sr_input_format **inputs;
230         struct sr_output_format **outputs;
231         struct srd_decoder *dec;
232
233         QString s = tr("%1 %2<br />%3<br /><a href=\"%4\">%4</a>\n<p>")
234                 .arg(QApplication::applicationName())
235                 .arg(QApplication::applicationVersion())
236                 .arg(tr("GNU GPL, version 2 or later"))
237                 .arg(QApplication::organizationDomain());
238
239         s.append("<b>" + tr("Supported hardware drivers:") + "</b><table>");
240         drivers = sr_driver_list();
241         for (int i = 0; drivers[i]; ++i) {
242                 s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
243                          .arg(QString(drivers[i]->name))
244                          .arg(QString(drivers[i]->longname)));
245         }
246         s.append("</table><p>");
247
248         s.append("<b>" + tr("Supported input formats:") + "</b><table>");
249         inputs = sr_input_list();
250         for (int i = 0; inputs[i]; ++i) {
251                 s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
252                          .arg(QString(inputs[i]->id))
253                          .arg(QString(inputs[i]->description)));
254         }
255         s.append("</table><p>");
256
257         s.append("<b>" + tr("Supported output formats:") + "</b><table>");
258         outputs = sr_output_list();
259         for (int i = 0; outputs[i]; ++i) {
260                 s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
261                         .arg(QString(outputs[i]->id))
262                         .arg(QString(outputs[i]->description)));
263         }
264         s.append("</table><p>");
265
266         s.append("<b>" + tr("Supported protocol decoders:") + "</b><table>");
267         for (l = srd_decoder_list(); l; l = l->next) {
268                 dec = (struct srd_decoder *)l->data;
269                 s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
270                          .arg(QString(dec->id))
271                          .arg(QString(dec->longname)));
272         }
273         s.append("</table>");
274
275         QMessageBox::about(this, tr("About"), s);
276 }
277
278 void MainWindow::on_actionAbout_Qt_triggered()
279 {
280         QMessageBox::aboutQt(this, tr("About Qt"));
281 }
282
283 void MainWindow::on_actionPreferences_triggered()
284 {
285         ConfigForm *form = new ConfigForm();
286         form->show();
287 }
288
289 void MainWindow::on_actionScan_triggered()
290 {
291         QString s;
292         GSList *l, *tmpdevs;
293         int num_devs, pos, i;
294         struct sr_dev_driver **drivers;
295         struct sr_dev_inst *sdi;
296         char *di_num_probes, *str;
297         struct sr_samplerates *samplerates;
298         const static float mult[] = { 2.f, 2.5f, 2.f };
299
300         statusBar()->showMessage(tr("Scanning for logic analyzers..."), 2000);
301
302         if (devices) {
303                 /* TODO: tell drivers to clean up all instances */
304                 g_free(devices);
305         }
306         devices = NULL;
307
308         /* Scan all drivers for all devices. */
309         drivers = sr_driver_list();
310         for (i = 0; drivers[i]; i++) {
311                 tmpdevs = sr_driver_scan(drivers[i], NULL);
312                 for (l = tmpdevs; l; l = l->next)
313                         devices = g_slist_append(devices, l->data);
314                 g_slist_free(tmpdevs);
315         }
316         num_devs = g_slist_length(devices);
317
318         ui->comboBoxLA->clear();
319         for (int i = 0; i < num_devs; ++i) {
320                 sdi = (struct sr_dev_inst *)g_slist_nth_data(devices, i);
321                 ui->comboBoxLA->addItem(sdi->driver->name); /* TODO: Full name */
322         }
323
324         if (num_devs == 0) {
325                 s = tr("No supported logic analyzer found.");
326                 statusBar()->showMessage(s, 2000);
327                 return;
328         } else if (num_devs == 1) {
329                 s = tr("Found supported logic analyzer: ");
330                 sdi = (struct sr_dev_inst *)g_slist_nth_data(devices, 0 /* opt_dev */);
331                 s.append(sdi->driver->name);
332                 statusBar()->showMessage(s, 2000);
333         } else {
334                 /* TODO: Allow user to select one of the devices. */
335                 s = tr("Found multiple logic analyzers: ");
336                 for (int i = 0; i < num_devs; ++i) {
337                         sdi = (struct sr_dev_inst *)g_slist_nth_data(devices, i);
338                         s.append(sdi->driver->name);
339                         if (i != num_devs - 1)
340                                 s.append(", ");
341                 }
342                 statusBar()->showMessage(s, 2000);
343                 // return;
344         }
345
346         sdi = (struct sr_dev_inst *)g_slist_nth_data(devices, 0 /* opt_dev */);
347
348         setCurrentLA(0 /* TODO */);
349
350         if (sr_info_get(sdi->driver, SR_DI_NUM_PROBES,
351                         (const void **)&di_num_probes, sdi) == SR_OK)
352                 setNumChannels(GPOINTER_TO_INT(di_num_probes));
353         else
354                 setNumChannels(8); /* FIXME: Error handling. */
355
356         ui->comboBoxLA->clear();
357         ui->comboBoxLA->addItem(sdi->driver->name); /* TODO: Full name */
358
359         s = QString(tr("Channels: %1")).arg(getNumChannels());
360         ui->labelChannels->setText(s);
361
362         if (sr_info_get(sdi->driver, SR_DI_SAMPLERATES,
363                         (const void **)&samplerates, sdi) != SR_OK || !samplerates) {
364                 /* TODO: Error handling. */
365         }
366
367         /* Populate the combobox with supported samplerates. */
368         ui->comboBoxSampleRate->clear();
369         if (!samplerates) {
370                 ui->comboBoxSampleRate->addItem("No samplerate");
371                 ui->comboBoxSampleRate->setEnabled(false);
372         } else if (samplerates->list != NULL) {
373                 for (int i = 0; samplerates->list[i]; ++i) {
374                         str = sr_samplerate_string(samplerates->list[i]);
375                         s = QString(str);
376                         g_free(str);
377                         ui->comboBoxSampleRate->insertItem(0, s,
378                                 QVariant::fromValue(samplerates->list[i]));
379                 }
380                 ui->comboBoxSampleRate->setEnabled(true);
381         } else {
382                 pos = 0;
383                 for (uint64_t r = samplerates->low; r <= samplerates->high; ) {
384                         str = sr_samplerate_string(r);
385                         s = QString(str);
386                         g_free(str);
387                         ui->comboBoxSampleRate->insertItem(0, s,
388                                                 QVariant::fromValue(r));
389                         r *= mult[pos++];
390                         pos %= 3;
391                 }
392                 ui->comboBoxSampleRate->setEnabled(true);
393         }
394         ui->comboBoxSampleRate->setCurrentIndex(0);
395
396         /* FIXME */
397         ui->comboBoxNumSamples->clear();
398         ui->comboBoxNumSamples->addItem("100", 100); /* For testing... */
399         ui->comboBoxNumSamples->addItem("3000000", 3000000);
400         ui->comboBoxNumSamples->addItem("2000000", 2000000);
401         ui->comboBoxNumSamples->addItem("1000000", 1000000);
402
403         ui->comboBoxNumSamples->setEditable(true);
404
405         if (getCurrentLA() >= 0)
406                 setupDockWidgets();
407
408         /* Enable all relevant fields now (i.e. make them non-gray). */
409         ui->comboBoxNumSamples->setEnabled(true);
410         ui->labelChannels->setEnabled(true);
411         ui->action_Get_samples->setEnabled(true);
412 }
413
414 void MainWindow::on_action_Open_triggered()
415 {
416         QString s;
417         QString fileName = QFileDialog::getOpenFileName(this,
418                 tr("Open sample file"), ".",
419                 tr("Raw sample files (*.raw *.bin);;"
420                    "Gnuplot data files (*.dat);;"
421                    "VCD files (*.vcd);;"
422                    "All files (*)"));
423
424         if (fileName == NULL)
425                 return;
426
427         QFile file(fileName);
428         file.open(QIODevice::ReadOnly);
429         QDataStream in(&file);
430
431         /* TODO: Implement support for loading different input formats. */
432
433         sample_buffer = (uint8_t *)malloc(file.size());
434         if (sample_buffer == NULL) {
435                 /* TODO: Error handling. */
436         }
437
438         in.readRawData((char *)sample_buffer, file.size());
439
440         setNumSamples(file.size());
441         setNumChannels(8); /* FIXME */
442
443         file.close();
444
445         setupDockWidgets();
446
447         ui->comboBoxLA->clear();
448         ui->comboBoxLA->addItem(tr("File"));
449
450         /* FIXME: Store number of channels in the file or allow user config. */
451         s = QString(tr("Channels: %1")).arg(getNumChannels());
452         ui->labelChannels->setText(s);
453         ui->labelChannels->setEnabled(false);
454
455         ui->comboBoxSampleRate->clear();
456         ui->comboBoxSampleRate->setEnabled(false); /* FIXME */
457
458         ui->comboBoxNumSamples->clear();
459         ui->comboBoxNumSamples->addItem(QString::number(getNumSamples()),
460                                         QVariant::fromValue(getNumSamples()));
461         ui->comboBoxNumSamples->setEnabled(true);
462
463         ui->labelSampleStart->setText(tr("Start sample: "));
464         ui->labelSampleStart->setEnabled(true);
465
466         ui->labelSampleEnd->setText(tr("End sample: "));
467         ui->labelSampleEnd->setEnabled(true);
468
469         ui->labelScaleFactor->setText(tr("Scale factor: "));
470         ui->labelScaleFactor->setEnabled(true);
471
472         ui->action_Save_as->setEnabled(true);
473         ui->action_Get_samples->setEnabled(false);
474
475         for (int i = 0; i < getNumChannels(); ++i) {
476                 channelForms[i]->setNumSamples(file.size());
477
478                 channelForms[i]->update();
479         }
480 }
481
482 void MainWindow::on_action_Save_as_triggered()
483 {
484         QString fileName = QFileDialog::getSaveFileName(this,
485                 tr("Save sample file"), ".",
486                 tr("Raw sample files (*.raw *.bin);;All files (*)"));
487
488         if (fileName == NULL)
489                 return;
490
491         QFile file(fileName);
492         file.open(QIODevice::WriteOnly);
493         QDataStream out(&file);
494
495         /* TODO: Implement support for saving to different output formats. */
496
497         out.writeRawData((const char *)sample_buffer,
498                          getNumSamples() * (getNumChannels() / 8));
499         file.close();
500 }
501
502 void datafeed_in(const struct sr_dev_inst *sdi,
503                 struct sr_datafeed_packet *packet)
504 {
505         static int num_probes = 0;
506         static int logic_probelist[SR_MAX_NUM_PROBES + 1] = { -1 };
507         static uint64_t received_samples = 0;
508         static int triggered = 0;
509         static int unitsize = 0;
510         struct sr_probe *probe;
511         static struct sr_datafeed_header *header;
512         struct sr_datafeed_meta_logic *meta_logic;
513         struct sr_datafeed_logic *logic;
514         int num_enabled_probes, sample_size, ret;
515         uint64_t sample;
516         uint64_t filter_out_len;
517         uint8_t *filter_out;
518
519         /* If the first packet to come in isn't a header, don't even try. */
520         // if (packet->type != SR_DF_HEADER && o == NULL)
521         //      return;
522
523         /* TODO: Also check elsewhere? */
524         /* TODO: Abort acquisition too, if user pressed cancel. */
525         if (progress && progress->wasCanceled())
526                 return;
527
528         sample_size = -1;
529
530         switch (packet->type) {
531         case SR_DF_HEADER:
532                 qDebug("SR_DF_HEADER");
533                 header = (struct sr_datafeed_header *)packet->payload;
534         case SR_DF_END:
535                 qDebug("SR_DF_END");
536                 /* TODO: o */
537                 // progress->setValue(received_samples); /* FIXME */
538                 break;
539         case SR_DF_TRIGGER:
540                 qDebug("SR_DF_TRIGGER");
541                 /* TODO */
542                 triggered = 1;
543                 break;
544         case SR_DF_META_LOGIC:
545                 qDebug("SR_DF_META_LOGIC");
546                 meta_logic = (struct sr_datafeed_meta_logic *)packet->payload;
547                 num_probes = meta_logic->num_probes;
548                 num_enabled_probes = 0;
549                 for (int i = 0; i < meta_logic->num_probes; ++i) {
550                         probe = (struct sr_probe *)g_slist_nth_data(sdi->probes, i);
551                         if (probe->enabled)
552                                 logic_probelist[num_enabled_probes++] = probe->index;
553                 }
554                 logic_probelist[num_enabled_probes] = -1;
555
556                 qDebug() << "Acquisition with" << num_enabled_probes << "/"
557                          << num_probes << "probes at"
558                          << sr_samplerate_string(meta_logic->samplerate)
559                          << "starting at" << ctime(&header->starttime.tv_sec)
560                          << "(" << limit_samples << "samples)";
561
562                 /* TODO: realloc() */
563                 break;
564         case SR_DF_LOGIC:
565                 logic = (sr_datafeed_logic *)packet->payload;
566                 qDebug() << "SR_DF_LOGIC (length =" << logic->length
567                          << ", unitsize = " << logic->unitsize << ")";
568                 sample_size = logic->unitsize;
569
570                 if (sample_size == -1)
571                         break;
572
573                 /* Don't store any samples until triggered. */
574                 // if (opt_wait_trigger && !triggered)
575                 //      return;
576
577                 if (received_samples >= limit_samples)
578                         break;
579
580                 /* TODO */
581                 ret = sr_filter_probes(sample_size, 1 /* unitsize */, logic_probelist,
582                                        (uint8_t *)logic->data, logic->length,
583                                        &filter_out, &filter_out_len);
584                 if (ret != SR_OK)
585                         break;
586
587                 for (uint64_t i = 0; i < filter_out_len; ++i) {
588                         sample = filter_out[i];
589                         sample_buffer[i] = (uint8_t)(sample & 0xff); /* FIXME */
590                         // qDebug("Sample %" PRIu64 ": 0x%x", i, sample);
591                 }
592                 received_samples += logic->length / sample_size;
593
594                 progress->setValue(received_samples);
595                 break;
596         default:
597                 qDebug("SR_DF_XXXX, not yet handled");
598                 break;
599         }
600 }
601
602 void MainWindow::on_action_Get_samples_triggered()
603 {
604         uint64_t samplerate;
605         QString s;
606         int opt_dev;
607         struct sr_dev_inst *sdi;
608         QComboBox *n = ui->comboBoxNumSamples;
609
610         opt_dev = 0; /* FIXME */
611
612         /*
613          * The number of samples to get is a drop-down list, but you can also
614          * manually enter a value. If the latter, we have to get the value from
615          * the lineEdit object, otherwise via itemData() and the list index.
616          */
617         if (n->lineEdit() != NULL) {
618                 limit_samples = n->lineEdit()->text().toLongLong();
619         } else {
620                 limit_samples = n->itemData(n->currentIndex()).toLongLong();
621         }
622
623         samplerate = ui->comboBoxSampleRate->itemData(
624                 ui->comboBoxSampleRate->currentIndex()).toLongLong();
625
626         /* TODO: Sanity checks. */
627
628         /* TODO: Assumes unitsize == 1. */
629         if (!(sample_buffer = (uint8_t *)malloc(limit_samples))) {
630                 /* TODO: Error handling. */
631                 return;
632         }
633
634         sr_session_new();
635         sr_session_datafeed_callback_add(datafeed_in);
636
637         sdi = (struct sr_dev_inst *)g_slist_nth_data(devices, opt_dev);
638
639         /* Set the number of samples we want to get from the device. */
640         if (sr_dev_config_set(sdi, SR_HWCAP_LIMIT_SAMPLES,
641                         &limit_samples) != SR_OK) {
642                 qDebug("Failed to set sample limit.");
643                 sr_session_destroy();
644                 return;
645         }
646
647         if (sr_session_dev_add(sdi) != SR_OK) {
648                 qDebug("Failed to use device.");
649                 sr_session_destroy();
650                 return;
651         }
652
653         /* Set the samplerate. */
654         if (sr_dev_config_set(sdi, SR_HWCAP_SAMPLERATE, &samplerate) != SR_OK) {
655                 qDebug("Failed to set samplerate.");
656                 sr_session_destroy();
657                 return;
658         };
659
660         if (sr_session_start() != SR_OK) {
661                 qDebug("Failed to start session.");
662                 sr_session_destroy();
663                 return;
664         }
665
666         progress = new QProgressDialog("Getting samples from logic analyzer...",
667                                        "Abort", 0, limit_samples, this);
668         progress->setWindowModality(Qt::WindowModal);
669         progress->setMinimumDuration(100);
670
671         sr_session_run();
672         sr_session_destroy();
673
674         for (int i = 0; i < getNumChannels(); ++i) {
675                 channelForms[i]->setNumSamples(limit_samples);
676                 // channelForms[i]->setSampleStart(0);
677                 // channelForms[i]->setSampleEnd(limit_samples);
678
679                 /* If any of the scale factors change, update all of them.. */
680                 connect(channelForms[i], SIGNAL(scaleFactorChanged(float)),
681                         w, SLOT(updateScaleFactors(float)));
682
683                 channelForms[i]->generatePainterPath();
684                 // channelForms[i]->update();
685         }
686
687         setNumSamples(limit_samples);
688
689         /* Enable the relevant labels/buttons. */
690         ui->labelSampleStart->setEnabled(true);
691         ui->labelSampleEnd->setEnabled(true);
692         ui->labelScaleFactor->setEnabled(true);
693         ui->action_Save_as->setEnabled(true);
694
695         // sr_hw_get_samples_shutdown(&ctx, 1000);
696 }
697
698 void MainWindow::setSampleRate(uint64_t s)
699 {
700         sampleRate = s;
701 }
702
703 uint64_t MainWindow::getSampleRate(void)
704 {
705         return sampleRate;
706 }
707
708 void MainWindow::setNumSamples(uint64_t s)
709 {
710         numSamples = s;
711         updateScrollBar();
712 }
713
714 void MainWindow::updateScrollBar(void)
715 {
716         int stepSize = channelForms[0]->getStepSize();
717         float scaleFactor = channelForms[0]->getScaleFactor();
718
719         uint64_t viewport = channelForms[0]->getNumSamplesVisible() * stepSize / scaleFactor;
720         uint64_t length = numSamples * stepSize / scaleFactor;
721
722         horizontalScrollBar->setMinimum(0);
723         horizontalScrollBar->setPageStep(viewport);
724         if (viewport < length)
725         {
726                 horizontalScrollBar->setMaximum(length - viewport / 2);
727         } else {
728                 horizontalScrollBar->setMaximum(0);
729         }
730
731 }
732
733 uint64_t MainWindow::getNumSamples(void)
734 {
735         return numSamples;
736 }
737
738 void MainWindow::on_action_New_triggered()
739 {
740         for (int i = 0; i < NUMCHANNELS; ++i) {
741                 if (dockWidgets[i]) {
742                         /* TODO: Check if all childs are also killed. */
743                         delete dockWidgets[i];
744                         dockWidgets[i] = NULL;
745                 }
746         }
747
748         ui->comboBoxLA->clear();
749         ui->comboBoxLA->addItem(tr("No LA detected"));
750
751         ui->labelChannels->setText(tr("Channels: "));
752         ui->labelChannels->setEnabled(false);
753
754         ui->comboBoxSampleRate->clear();
755         ui->comboBoxSampleRate->setEnabled(false);
756
757         ui->comboBoxNumSamples->clear();
758         ui->comboBoxNumSamples->setEnabled(false);
759
760         ui->labelSampleStart->setText(tr("Start sample: "));
761         ui->labelSampleStart->setEnabled(false);
762
763         ui->labelSampleEnd->setText(tr("End sample: "));
764         ui->labelSampleEnd->setEnabled(false);
765
766         ui->labelScaleFactor->setText(tr("Scale factor: "));
767         ui->labelScaleFactor->setEnabled(false);
768
769         ui->action_Save_as->setEnabled(false);
770         ui->action_Get_samples->setEnabled(false);
771
772         setNumChannels(0);
773
774         /* TODO: More cleanups. */
775         /* TODO: Free sample buffer(s). */
776 }
777
778 void MainWindow::configChannelTitleBarLayoutChanged(int index)
779 {
780         QDockWidget::DockWidgetFeatures f =
781                 QDockWidget::DockWidgetClosable |
782                 QDockWidget::DockWidgetMovable |
783                 QDockWidget::DockWidgetFloatable;
784
785         configChannelTitleBarLayout = index;
786
787         if (configChannelTitleBarLayout == DOCK_VERTICAL)
788                 f |= QDockWidget::DockWidgetVerticalTitleBar;
789
790         for (int i = 0; i < getNumChannels(); ++i)
791                 dockWidgets[i]->setFeatures(f);
792 }
793
794
795 void MainWindow::updateScaleFactors(float value)
796 {
797         static int lock = 0;
798
799         /* TODO: There must be a better way to do this. */
800         if (lock == 1)
801                 return;
802
803         lock = 1;
804         for (int i = 0; i < getNumChannels(); ++i) {
805                 // qDebug("updating scaleFactor %d", i);
806                 channelForms[i]->setScaleFactor(value);
807                 // qDebug("updating scaleFactor %d (DONE)", i);
808         }
809         updateScrollBar();
810         lock = 0;
811 }
812
813 void MainWindow::on_actionConfigure_triggered()
814 {
815         DecodersForm *form = new DecodersForm();
816         form->show();
817 }
818
819 void MainWindow::on_actionProtocol_decoder_stacks_triggered()
820 {
821         DecoderStackForm *form = new DecoderStackForm();
822         form->show();
823 }
824
825 extern "C" void show_pd_annotation(struct srd_proto_data *pdata, void *cb_data)
826 {
827         char **annotations;
828
829         annotations = (char **)pdata->data;
830
831         MainWindow *mw = (MainWindow *)cb_data;
832
833         mw->ui->plainTextEdit->appendPlainText(
834                 QString("%1-%2: %3: %4").arg(pdata->start_sample)
835                         .arg(pdata->end_sample).arg(pdata->pdo->proto_id)
836                         .arg((char *)annotations[0]));
837 }
838
839 void MainWindow::on_actionQUICK_HACK_PD_TEST_triggered()
840 {
841 #define N 500000
842
843         struct srd_decoder_inst *di;
844         GHashTable *pd_opthash;
845         uint8_t *buf = (uint8_t *)malloc(N + 1);
846
847         pd_opthash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
848                                            g_free);
849
850         /* Hardcode a specific I2C probe mapping. */
851         g_hash_table_insert(pd_opthash, g_strdup("scl"), g_strdup("5"));
852         g_hash_table_insert(pd_opthash, g_strdup("sda"), g_strdup("7"));
853
854         /*
855          * Get data from a hardcoded binary file.
856          * (converted to binary from melexis_mlx90614_5s_24deg.sr.
857          */
858         QFile file("foo.bin");
859         int ret = file.open(QIODevice::ReadOnly);
860         ret = file.read((char *)buf, N);
861
862         // sr_log_loglevel_set(SR_LOG_NONE);
863         // srd_log_loglevel_set(SRD_LOG_NONE);
864
865         if (!(di = srd_inst_new("i2c", pd_opthash))) {
866                 ui->plainTextEdit->appendPlainText("ERROR: srd_inst_new");
867                 return;
868         }
869
870         if (srd_inst_probe_set_all(di, pd_opthash) != SRD_OK) {
871                 ui->plainTextEdit->appendPlainText("ERROR: srd_inst_set_probes");
872                 return;
873         }
874
875         if (srd_pd_output_callback_add(SRD_OUTPUT_ANN,
876             (srd_pd_output_callback_t)show_pd_annotation, (void *)this) != SRD_OK) {
877                 ui->plainTextEdit->appendPlainText("ERROR: srd_pd_output_callback_add");
878                 return;
879         }
880
881         if (srd_session_start(8, 1, 1000000) != SRD_OK) {
882                 ui->plainTextEdit->appendPlainText("ERROR: srd_session_start");
883                 return;
884         }
885
886         if (srd_session_send(0, buf, N) != SRD_OK) {
887                 ui->plainTextEdit->appendPlainText("ERROR: srd_session_send");
888                 return;
889         }
890 }