]> sigrok.org Git - pulseview.git/blob - pv/view/decodesignal.cpp
Moved decoder config into the popup
[pulseview.git] / pv / view / decodesignal.cpp
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
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 <libsigrokdecode/libsigrokdecode.h>
23 }
24
25 #include <extdef.h>
26
27 #include <boost/foreach.hpp>
28
29 #include <QAction>
30 #include <QComboBox>
31 #include <QFormLayout>
32 #include <QLabel>
33 #include <QMenu>
34
35 #include "decodesignal.h"
36
37 #include <pv/sigsession.h>
38 #include <pv/data/decoder.h>
39 #include <pv/view/logicsignal.h>
40 #include <pv/view/view.h>
41 #include <pv/view/decode/annotation.h>
42
43 using namespace boost;
44 using namespace std;
45
46 namespace pv {
47 namespace view {
48
49 const QColor DecodeSignal::DecodeColours[4] = {
50         QColor(0xEF, 0x29, 0x29),       // Red
51         QColor(0xFC, 0xE9, 0x4F),       // Yellow
52         QColor(0x8A, 0xE2, 0x34),       // Green
53         QColor(0x72, 0x9F, 0xCF)        // Blue
54 };
55
56 const QColor DecodeSignal::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
57
58 DecodeSignal::DecodeSignal(pv::SigSession &session,
59         boost::shared_ptr<pv::data::Decoder> decoder, int index) :
60         Trace(session, QString(decoder->decoder()->name)),
61         _decoder(decoder),
62         _binding(decoder)
63 {
64         assert(_decoder);
65
66         _colour = DecodeColours[index % countof(DecodeColours)];
67
68         connect(_decoder.get(), SIGNAL(new_decode_data()),
69                 this, SLOT(on_new_decode_data()));
70 }
71
72 bool DecodeSignal::enabled() const
73 {
74         return true;
75 }
76
77 const boost::shared_ptr<pv::data::Decoder>& DecodeSignal::decoder() const
78 {
79         return _decoder;
80 }
81
82 void DecodeSignal::set_view(pv::view::View *view)
83 {
84         assert(view);
85         Trace::set_view(view);
86 }
87
88 void DecodeSignal::paint_back(QPainter &p, int left, int right)
89 {
90         paint_axis(p, get_y(), left, right);
91 }
92
93 void DecodeSignal::paint_mid(QPainter &p, int left, int right)
94 {
95         using namespace pv::view::decode;
96
97         assert(_decoder);
98         const QString err = _decoder->error_message();
99         if (!err.isEmpty()) {
100                 draw_error(p, err, left, right);
101                 return;
102         }
103
104         assert(_view);
105         const int y = get_y();
106
107         const double scale = _view->scale();
108         assert(scale > 0);
109
110         double samplerate = _decoder->get_samplerate();
111
112         // Show sample rate as 1Hz when it is unknown
113         if (samplerate == 0.0)
114                 samplerate = 1.0;
115
116         const double pixels_offset = (_view->offset() -
117                 _decoder->get_start_time()) / scale;
118         const double samples_per_pixel = samplerate * scale;
119
120         assert(_decoder);
121         vector< shared_ptr<Annotation> > annotations(_decoder->annotations());
122         BOOST_FOREACH(shared_ptr<Annotation> a, annotations) {
123                 assert(a);
124                 a->paint(p, get_text_colour(), _text_size.height(),
125                         left, right, samples_per_pixel, pixels_offset, y);
126         }
127 }
128
129 void DecodeSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
130 {
131         const GSList *probe;
132
133         assert(form);
134         assert(parent);
135         assert(_decoder);
136
137         const srd_decoder *const decoder = _decoder->decoder();
138
139         assert(decoder);
140
141         Trace::populate_popup_form(parent, form);
142
143         form->addRow(new QLabel(tr("<h3>Probes</h3>"), parent));
144
145         _probe_selector_map.clear();
146
147         // Add the mandatory probes
148         for(probe = decoder->probes; probe; probe = probe->next) {
149                 const struct srd_probe *const p =
150                         (struct srd_probe *)probe->data;
151                 QComboBox *const combo = create_probe_selector(parent, p);
152                 connect(combo, SIGNAL(currentIndexChanged(int)),
153                         this, SLOT(on_probe_selected(int)));
154                 form->addRow(tr("<b>%1</b> (%2) *")
155                         .arg(p->name).arg(p->desc), combo);
156
157                 _probe_selector_map[p] = combo;
158         }
159
160         // Add the optional probes
161         for(probe = decoder->opt_probes; probe; probe = probe->next) {
162                 const struct srd_probe *const p =
163                         (struct srd_probe *)probe->data;
164                 QComboBox *const combo = create_probe_selector(parent, p);
165                 connect(combo, SIGNAL(currentIndexChanged(int)),
166                         this, SLOT(on_probe_selected(int)));
167                 form->addRow(tr("<b>%1</b> (%2)")
168                         .arg(p->name).arg(p->desc), combo);
169
170                 _probe_selector_map[p] = combo;
171         }
172
173         form->addRow(new QLabel(
174                 tr("<i>* Required Probes</i>"), parent));
175
176         // Add the options
177         if (!_binding.properties().empty()) {
178                 form->addRow(new QLabel(tr("<h3>Options</h3>"),
179                         parent));
180                 _binding.add_properties_to_form(form, true);
181         }
182 }
183
184 QMenu* DecodeSignal::create_context_menu(QWidget *parent)
185 {
186         QMenu *const menu = Trace::create_context_menu(parent);
187
188         menu->addSeparator();
189
190         QAction *const del = new QAction(tr("Delete"), this);
191         del->setShortcuts(QKeySequence::Delete);
192         connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
193         menu->addAction(del);
194
195         return menu;
196 }
197
198 void DecodeSignal::draw_error(QPainter &p, const QString &message,
199         int left, int right)
200 {
201         const int y = get_y();
202
203         p.setPen(ErrorBgColour.darker());
204         p.setBrush(ErrorBgColour);
205
206         const QRectF bounding_rect =
207                 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
208         const QRectF text_rect = p.boundingRect(bounding_rect,
209                 Qt::AlignCenter, message);
210         const float r = text_rect.height() / 4;
211
212         p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
213                 Qt::AbsoluteSize);
214
215         p.setPen(get_text_colour());
216         p.drawText(text_rect, message);
217 }
218
219 QComboBox* DecodeSignal::create_probe_selector(
220         QWidget *parent, const srd_probe *const probe)
221 {
222         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
223
224         assert(_decoder);
225         const map<const srd_probe*,
226                 shared_ptr<LogicSignal> >::const_iterator probe_iter =
227                 _decoder->probes().find(probe);
228
229         QComboBox *selector = new QComboBox(parent);
230
231         selector->addItem("-", qVariantFromValue((void*)NULL));
232
233         if (probe_iter == _decoder->probes().end())
234                 selector->setCurrentIndex(0);
235
236         for(size_t i = 0; i < sigs.size(); i++) {
237                 const shared_ptr<view::Signal> s(sigs[i]);
238                 assert(s);
239
240                 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
241                 {
242                         selector->addItem(s->get_name(),
243                                 qVariantFromValue((void*)s.get()));
244                         if ((*probe_iter).second == s)
245                                 selector->setCurrentIndex(i + 1);
246                 }
247         }
248
249         return selector;
250 }
251
252 void DecodeSignal::commit_probes()
253 {
254         assert(_decoder);
255
256         map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
257         const vector< shared_ptr<Signal> > sigs = _session.get_signals();
258
259         for(map<const srd_probe*, QComboBox*>::const_iterator i =
260                 _probe_selector_map.begin();
261                 i != _probe_selector_map.end(); i++)
262         {
263                 const QComboBox *const combo = (*i).second;
264                 const LogicSignal *const selection =
265                         (LogicSignal*)combo->itemData(combo->currentIndex()).
266                         value<void*>();
267
268                 BOOST_FOREACH(shared_ptr<Signal> s, sigs)
269                         if(s.get() == selection) {
270                                 probe_map[(*i).first] =
271                                         dynamic_pointer_cast<LogicSignal>(s);
272                                 break;
273                         }
274         }
275
276         _decoder->set_probes(probe_map);
277 }
278
279 void DecodeSignal::on_new_decode_data()
280 {
281         if (_view)
282                 _view->update_viewport();
283 }
284
285 void DecodeSignal::delete_pressed()
286 {
287         on_delete();
288 }
289
290 void DecodeSignal::on_delete()
291 {
292         _session.remove_decode_signal(this);
293 }
294
295 void DecodeSignal::on_probe_selected(int)
296 {
297         commit_probes();
298 }
299
300 } // namespace view
301 } // namespace pv