]> sigrok.org Git - pulseview.git/blame_incremental - pv/view/decodetrace.cpp
Do not attempt to draw text for small annotations
[pulseview.git] / pv / view / decodetrace.cpp
... / ...
CommitLineData
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
21extern "C" {
22#include <libsigrokdecode/libsigrokdecode.h>
23}
24
25#include <extdef.h>
26
27#include <boost/foreach.hpp>
28#include <boost/functional/hash.hpp>
29
30#include <QAction>
31#include <QApplication>
32#include <QComboBox>
33#include <QFormLayout>
34#include <QLabel>
35#include <QMenu>
36#include <QPushButton>
37
38#include "decodetrace.h"
39
40#include <pv/sigsession.h>
41#include <pv/data/decoderstack.h>
42#include <pv/data/decode/decoder.h>
43#include <pv/data/logic.h>
44#include <pv/data/logicsnapshot.h>
45#include <pv/data/decode/annotation.h>
46#include <pv/view/logicsignal.h>
47#include <pv/view/view.h>
48#include <pv/widgets/decodergroupbox.h>
49#include <pv/widgets/decodermenu.h>
50
51using boost::dynamic_pointer_cast;
52using boost::shared_ptr;
53using std::list;
54using std::max;
55using std::map;
56using std::min;
57using std::vector;
58
59namespace pv {
60namespace view {
61
62const QColor DecodeTrace::DecodeColours[4] = {
63 QColor(0xEF, 0x29, 0x29), // Red
64 QColor(0xFC, 0xE9, 0x4F), // Yellow
65 QColor(0x8A, 0xE2, 0x34), // Green
66 QColor(0x72, 0x9F, 0xCF) // Blue
67};
68
69const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29);
70const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85);
71
72const int DecodeTrace::ArrowSize = 4;
73const double DecodeTrace::EndCapWidth = 5;
74const int DecodeTrace::DrawPadding = 100;
75
76const QColor DecodeTrace::Colours[16] = {
77 QColor(0xEF, 0x29, 0x29),
78 QColor(0xF6, 0x6A, 0x32),
79 QColor(0xFC, 0xAE, 0x3E),
80 QColor(0xFB, 0xCA, 0x47),
81 QColor(0xFC, 0xE9, 0x4F),
82 QColor(0xCD, 0xF0, 0x40),
83 QColor(0x8A, 0xE2, 0x34),
84 QColor(0x4E, 0xDC, 0x44),
85 QColor(0x55, 0xD7, 0x95),
86 QColor(0x64, 0xD1, 0xD2),
87 QColor(0x72, 0x9F, 0xCF),
88 QColor(0xD4, 0x76, 0xC4),
89 QColor(0x9D, 0x79, 0xB9),
90 QColor(0xAD, 0x7F, 0xA8),
91 QColor(0xC2, 0x62, 0x9B),
92 QColor(0xD7, 0x47, 0x6F)
93};
94
95const QColor DecodeTrace::OutlineColours[16] = {
96 QColor(0x77, 0x14, 0x14),
97 QColor(0x7B, 0x35, 0x19),
98 QColor(0x7E, 0x57, 0x1F),
99 QColor(0x7D, 0x65, 0x23),
100 QColor(0x7E, 0x74, 0x27),
101 QColor(0x66, 0x78, 0x20),
102 QColor(0x45, 0x71, 0x1A),
103 QColor(0x27, 0x6E, 0x22),
104 QColor(0x2A, 0x6B, 0x4A),
105 QColor(0x32, 0x68, 0x69),
106 QColor(0x39, 0x4F, 0x67),
107 QColor(0x6A, 0x3B, 0x62),
108 QColor(0x4E, 0x3C, 0x5C),
109 QColor(0x56, 0x3F, 0x54),
110 QColor(0x61, 0x31, 0x4D),
111 QColor(0x6B, 0x23, 0x37)
112};
113
114DecodeTrace::DecodeTrace(pv::SigSession &session,
115 boost::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
116 Trace(QString::fromUtf8(
117 decoder_stack->stack().front()->decoder()->name)),
118 _session(session),
119 _decoder_stack(decoder_stack),
120 _delete_mapper(this),
121 _show_hide_mapper(this)
122{
123 assert(_decoder_stack);
124
125 _colour = DecodeColours[index % countof(DecodeColours)];
126
127 connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
128 this, SLOT(on_new_decode_data()));
129 connect(&_delete_mapper, SIGNAL(mapped(int)),
130 this, SLOT(on_delete_decoder(int)));
131 connect(&_show_hide_mapper, SIGNAL(mapped(int)),
132 this, SLOT(on_show_hide_decoder(int)));
133}
134
135bool DecodeTrace::enabled() const
136{
137 return true;
138}
139
140const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
141{
142 return _decoder_stack;
143}
144
145void DecodeTrace::set_view(pv::view::View *view)
146{
147 assert(view);
148 Trace::set_view(view);
149}
150
151void DecodeTrace::paint_back(QPainter &p, int left, int right)
152{
153 Trace::paint_back(p, left, right);
154 paint_axis(p, get_y(), left, right);
155}
156
157void DecodeTrace::paint_mid(QPainter &p, int left, int right)
158{
159 using namespace pv::data::decode;
160
161 const double scale = _view->scale();
162 assert(scale > 0);
163
164 double samplerate = _decoder_stack->samplerate();
165
166 _cur_row_headings.clear();
167
168 // Show sample rate as 1Hz when it is unknown
169 if (samplerate == 0.0)
170 samplerate = 1.0;
171
172 const double pixels_offset = (_view->offset() -
173 _decoder_stack->get_start_time()) / scale;
174 const double samples_per_pixel = samplerate * scale;
175
176 const uint64_t start_sample = (uint64_t)max((left + pixels_offset) *
177 samples_per_pixel, 0.0);
178 const uint64_t end_sample = (uint64_t)max((right + pixels_offset) *
179 samples_per_pixel, 0.0);
180
181 QFontMetrics m(QApplication::font());
182 const int text_height = m.boundingRect(QRect(), 0, "Tg").height();
183 const int annotation_height = (text_height * 5) / 4;
184 const int row_height = (text_height * 6) / 4;
185
186 assert(_decoder_stack);
187 const QString err = _decoder_stack->error_message();
188 if (!err.isEmpty())
189 {
190 draw_unresolved_period(p, annotation_height, left, right,
191 samples_per_pixel, pixels_offset);
192 draw_error(p, err, left, right);
193 return;
194 }
195
196 // Iterate through the rows
197 assert(_view);
198 int y = get_y();
199
200 assert(_decoder_stack);
201
202 const vector<Row> rows(_decoder_stack->get_visible_rows());
203 for (size_t i = 0; i < rows.size(); i++)
204 {
205 const Row &row = rows[i];
206
207 size_t base_colour = 0x13579BDF;
208 boost::hash_combine(base_colour, this);
209 boost::hash_combine(base_colour, row.decoder());
210 boost::hash_combine(base_colour, row.row());
211 base_colour >>= 16;
212
213 vector<Annotation> annotations;
214 _decoder_stack->get_annotation_subset(annotations, row,
215 start_sample, end_sample);
216 if (!annotations.empty()) {
217 BOOST_FOREACH(const Annotation &a, annotations)
218 draw_annotation(a, p, get_text_colour(),
219 annotation_height, left, right,
220 samples_per_pixel, pixels_offset, y,
221 base_colour);
222 y += row_height;
223
224 _cur_row_headings.push_back(row.title());
225 }
226 }
227
228 // Draw the hatching
229 draw_unresolved_period(p, annotation_height, left, right,
230 samples_per_pixel, pixels_offset);
231}
232
233void DecodeTrace::paint_fore(QPainter &p, int left, int right)
234{
235 using namespace pv::data::decode;
236
237 (void)right;
238
239 QFontMetrics m(QApplication::font());
240 const int text_height = m.boundingRect(QRect(), 0, "Tg").height();
241 const int row_height = (text_height * 6) / 4;
242
243 for (size_t i = 0; i < _cur_row_headings.size(); i++)
244 {
245 const int y = i * row_height + get_y();
246
247 p.setPen(QPen(Qt::NoPen));
248 p.setBrush(QApplication::palette().brush(QPalette::WindowText));
249
250 if (i != 0)
251 {
252 const QPointF points[] = {
253 QPointF(left, y - ArrowSize),
254 QPointF(left + ArrowSize, y),
255 QPointF(left, y + ArrowSize)
256 };
257 p.drawPolygon(points, countof(points));
258 }
259
260 const QRect r(left + ArrowSize * 2, y - row_height / 2,
261 right - left, row_height);
262 const QString h(_cur_row_headings[i]);
263 const int f = Qt::AlignLeft | Qt::AlignVCenter |
264 Qt::TextDontClip;
265
266 // Draw the outline
267 p.setPen(QApplication::palette().color(QPalette::Base));
268 for (int dx = -1; dx <= 1; dx++)
269 for (int dy = -1; dy <= 1; dy++)
270 if (dx != 0 && dy != 0)
271 p.drawText(r.translated(dx, dy), f, h);
272
273 // Draw the text
274 p.setPen(QApplication::palette().color(QPalette::WindowText));
275 p.drawText(r, f, h);
276 }
277}
278
279void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
280{
281 using pv::data::decode::Decoder;
282
283 assert(form);
284 assert(parent);
285 assert(_decoder_stack);
286
287 // Add the standard options
288 Trace::populate_popup_form(parent, form);
289
290 // Add the decoder options
291 _bindings.clear();
292 _probe_selectors.clear();
293 _decoder_forms.clear();
294
295 const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
296
297 if (stack.empty())
298 {
299 QLabel *const l = new QLabel(
300 tr("<p><i>No decoders in the stack</i></p>"));
301 l->setAlignment(Qt::AlignCenter);
302 form->addRow(l);
303 }
304 else
305 {
306 list< shared_ptr<Decoder> >::const_iterator iter =
307 stack.begin();
308 for (int i = 0; i < (int)stack.size(); i++, iter++) {
309 shared_ptr<Decoder> dec(*iter);
310 create_decoder_form(i, dec, parent, form);
311 }
312
313 form->addRow(new QLabel(
314 tr("<i>* Required Probes</i>"), parent));
315 }
316
317 // Add stacking button
318 pv::widgets::DecoderMenu *const decoder_menu =
319 new pv::widgets::DecoderMenu(parent);
320 connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)),
321 this, SLOT(on_stack_decoder(srd_decoder*)));
322
323 QPushButton *const stack_button =
324 new QPushButton(tr("Stack Decoder"), parent);
325 stack_button->setMenu(decoder_menu);
326
327 QHBoxLayout *stack_button_box = new QHBoxLayout;
328 stack_button_box->addWidget(stack_button, 0, Qt::AlignRight);
329 form->addRow(stack_button_box);
330}
331
332QMenu* DecodeTrace::create_context_menu(QWidget *parent)
333{
334 QMenu *const menu = Trace::create_context_menu(parent);
335
336 menu->addSeparator();
337
338 QAction *const del = new QAction(tr("Delete"), this);
339 del->setShortcuts(QKeySequence::Delete);
340 connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
341 menu->addAction(del);
342
343 return menu;
344}
345
346void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
347 QPainter &p, QColor text_color, int h, int left, int right,
348 double samples_per_pixel, double pixels_offset, int y,
349 size_t base_colour) const
350{
351 const double start = a.start_sample() / samples_per_pixel -
352 pixels_offset;
353 const double end = a.end_sample() / samples_per_pixel -
354 pixels_offset;
355
356 const size_t colour = (base_colour + a.format()) % countof(Colours);
357 const QColor &fill = Colours[colour];
358 const QColor &outline = OutlineColours[colour];
359
360 if (start > right + DrawPadding || end < left - DrawPadding)
361 return;
362
363 if (a.start_sample() == a.end_sample())
364 draw_instant(a, p, fill, outline, text_color, h,
365 start, y);
366 else
367 draw_range(a, p, fill, outline, text_color, h,
368 start, end, y);
369}
370
371void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
372 QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
373{
374 const QString text = a.annotations().empty() ?
375 QString() : a.annotations().back();
376 const double w = min(p.boundingRect(QRectF(), 0, text).width(),
377 0.0) + h;
378 const QRectF rect(x - w / 2, y - h / 2, w, h);
379
380 p.setPen(outline);
381 p.setBrush(fill);
382 p.drawRoundedRect(rect, h / 2, h / 2);
383
384 p.setPen(text_color);
385 p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
386}
387
388void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
389 QColor fill, QColor outline, QColor text_color, int h, double start,
390 double end, int y) const
391{
392 const double top = y + .5 - h / 2;
393 const double bottom = y + .5 + h / 2;
394 const vector<QString> annotations = a.annotations();
395
396 p.setPen(outline);
397 p.setBrush(fill);
398
399 // If the two ends are within 1 pixel, draw a vertical line
400 if (start + 1.0 > end)
401 {
402 p.drawLine(QPointF(start, top), QPointF(start, bottom));
403 return;
404 }
405
406 const double cap_width = min((end - start) / 4, EndCapWidth);
407
408 QPointF pts[] = {
409 QPointF(start, y + .5f),
410 QPointF(start + cap_width, top),
411 QPointF(end - cap_width, top),
412 QPointF(end, y + .5f),
413 QPointF(end - cap_width, bottom),
414 QPointF(start + cap_width, bottom)
415 };
416
417 p.drawConvexPolygon(pts, countof(pts));
418
419 if (annotations.empty())
420 return;
421
422 QRectF rect(start + cap_width, y - h / 2,
423 end - start - cap_width * 2, h);
424 if (rect.width() <= 4)
425 return;
426
427 p.setPen(text_color);
428
429 // Try to find an annotation that will fit
430 QString best_annotation;
431 int best_width = 0;
432
433 BOOST_FOREACH(const QString &a, annotations) {
434 const int w = p.boundingRect(QRectF(), 0, a).width();
435 if (w <= rect.width() && w > best_width)
436 best_annotation = a, best_width = w;
437 }
438
439 if (best_annotation.isEmpty())
440 best_annotation = annotations.back();
441
442 // If not ellide the last in the list
443 p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText(
444 best_annotation, Qt::ElideRight, rect.width()));
445}
446
447void DecodeTrace::draw_error(QPainter &p, const QString &message,
448 int left, int right)
449{
450 const int y = get_y();
451
452 p.setPen(ErrorBgColour.darker());
453 p.setBrush(ErrorBgColour);
454
455 const QRectF bounding_rect =
456 QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
457 const QRectF text_rect = p.boundingRect(bounding_rect,
458 Qt::AlignCenter, message);
459 const float r = text_rect.height() / 4;
460
461 p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
462 Qt::AbsoluteSize);
463
464 p.setPen(get_text_colour());
465 p.drawText(text_rect, message);
466}
467
468void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
469 int right, double samples_per_pixel, double pixels_offset)
470{
471 using namespace pv::data;
472 using pv::data::decode::Decoder;
473
474 assert(_decoder_stack);
475
476 shared_ptr<Logic> data;
477 shared_ptr<LogicSignal> logic_signal;
478
479 const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
480
481 // We get the logic data of the first probe in the list.
482 // This works because we are currently assuming all
483 // LogicSignals have the same data/snapshot
484 BOOST_FOREACH (const shared_ptr<Decoder> &dec, stack)
485 if (dec && !dec->probes().empty() &&
486 ((logic_signal = (*dec->probes().begin()).second)) &&
487 ((data = logic_signal->logic_data())))
488 break;
489
490 if (!data || data->get_snapshots().empty())
491 return;
492
493 const shared_ptr<LogicSnapshot> snapshot =
494 data->get_snapshots().front();
495 assert(snapshot);
496 const int64_t sample_count = (int64_t)snapshot->get_sample_count();
497 if (sample_count == 0)
498 return;
499
500 const int64_t samples_decoded = _decoder_stack->samples_decoded();
501 if (sample_count == samples_decoded)
502 return;
503
504 const int y = get_y();
505 const double start = max(samples_decoded /
506 samples_per_pixel - pixels_offset, left - 1.0);
507 const double end = min(sample_count / samples_per_pixel -
508 pixels_offset, right + 1.0);
509 const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h);
510
511 p.setPen(QPen(Qt::NoPen));
512 p.setBrush(Qt::white);
513 p.drawRect(no_decode_rect);
514
515 p.setPen(NoDecodeColour);
516 p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern));
517 p.drawRect(no_decode_rect);
518}
519
520void DecodeTrace::create_decoder_form(int index,
521 shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
522 QFormLayout *form)
523{
524 const GSList *probe;
525
526 assert(dec);
527 const srd_decoder *const decoder = dec->decoder();
528 assert(decoder);
529
530 pv::widgets::DecoderGroupBox *const group =
531 new pv::widgets::DecoderGroupBox(
532 QString::fromUtf8(decoder->name));
533 group->set_decoder_visible(dec->shown());
534
535 _delete_mapper.setMapping(group, index);
536 connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
537
538 _show_hide_mapper.setMapping(group, index);
539 connect(group, SIGNAL(show_hide_decoder()),
540 &_show_hide_mapper, SLOT(map()));
541
542 QFormLayout *const decoder_form = new QFormLayout;
543 group->add_layout(decoder_form);
544
545 // Add the mandatory probes
546 for(probe = decoder->probes; probe; probe = probe->next) {
547 const struct srd_probe *const p =
548 (struct srd_probe *)probe->data;
549 QComboBox *const combo = create_probe_selector(parent, dec, p);
550 connect(combo, SIGNAL(currentIndexChanged(int)),
551 this, SLOT(on_probe_selected(int)));
552 decoder_form->addRow(tr("<b>%1</b> (%2) *")
553 .arg(QString::fromUtf8(p->name))
554 .arg(QString::fromUtf8(p->desc)), combo);
555
556 const ProbeSelector s = {combo, dec, p};
557 _probe_selectors.push_back(s);
558 }
559
560 // Add the optional probes
561 for(probe = decoder->opt_probes; probe; probe = probe->next) {
562 const struct srd_probe *const p =
563 (struct srd_probe *)probe->data;
564 QComboBox *const combo = create_probe_selector(parent, dec, p);
565 connect(combo, SIGNAL(currentIndexChanged(int)),
566 this, SLOT(on_probe_selected(int)));
567 decoder_form->addRow(tr("<b>%1</b> (%2)")
568 .arg(QString::fromUtf8(p->name))
569 .arg(QString::fromUtf8(p->desc)), combo);
570
571 const ProbeSelector s = {combo, dec, p};
572 _probe_selectors.push_back(s);
573 }
574
575 // Add the options
576 shared_ptr<prop::binding::DecoderOptions> binding(
577 new prop::binding::DecoderOptions(_decoder_stack, dec));
578 binding->add_properties_to_form(decoder_form, true);
579
580 _bindings.push_back(binding);
581
582 form->addRow(group);
583 _decoder_forms.push_back(group);
584}
585
586QComboBox* DecodeTrace::create_probe_selector(
587 QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
588 const srd_probe *const probe)
589{
590 assert(dec);
591
592 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
593
594 assert(_decoder_stack);
595 const map<const srd_probe*,
596 shared_ptr<LogicSignal> >::const_iterator probe_iter =
597 dec->probes().find(probe);
598
599 QComboBox *selector = new QComboBox(parent);
600
601 selector->addItem("-", qVariantFromValue((void*)NULL));
602
603 if (probe_iter == dec->probes().end())
604 selector->setCurrentIndex(0);
605
606 for(size_t i = 0; i < sigs.size(); i++) {
607 const shared_ptr<view::Signal> s(sigs[i]);
608 assert(s);
609
610 if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
611 {
612 selector->addItem(s->get_name(),
613 qVariantFromValue((void*)s.get()));
614 if ((*probe_iter).second == s)
615 selector->setCurrentIndex(i + 1);
616 }
617 }
618
619 return selector;
620}
621
622void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
623{
624 assert(dec);
625
626 map<const srd_probe*, shared_ptr<LogicSignal> > probe_map;
627 const vector< shared_ptr<Signal> > sigs = _session.get_signals();
628
629 BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
630 {
631 if(s._decoder != dec)
632 break;
633
634 const LogicSignal *const selection =
635 (LogicSignal*)s._combo->itemData(
636 s._combo->currentIndex()).value<void*>();
637
638 BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
639 if(sig.get() == selection) {
640 probe_map[s._probe] =
641 dynamic_pointer_cast<LogicSignal>(sig);
642 break;
643 }
644 }
645
646 dec->set_probes(probe_map);
647}
648
649void DecodeTrace::commit_probes()
650{
651 assert(_decoder_stack);
652 BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
653 _decoder_stack->stack())
654 commit_decoder_probes(dec);
655
656 _decoder_stack->begin_decode();
657}
658
659void DecodeTrace::on_new_decode_data()
660{
661 if (_view)
662 _view->update_viewport();
663}
664
665void DecodeTrace::delete_pressed()
666{
667 on_delete();
668}
669
670void DecodeTrace::on_delete()
671{
672 _session.remove_decode_signal(this);
673}
674
675void DecodeTrace::on_probe_selected(int)
676{
677 commit_probes();
678}
679
680void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
681{
682 assert(decoder);
683 assert(_decoder_stack);
684 _decoder_stack->push(shared_ptr<data::decode::Decoder>(
685 new data::decode::Decoder(decoder)));
686 _decoder_stack->begin_decode();
687
688 create_popup_form();
689}
690
691void DecodeTrace::on_delete_decoder(int index)
692{
693 _decoder_stack->remove(index);
694
695 // Update the popup
696 create_popup_form();
697
698 _decoder_stack->begin_decode();
699}
700
701void DecodeTrace::on_show_hide_decoder(int index)
702{
703 using pv::data::decode::Decoder;
704
705 const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
706
707 // Find the decoder in the stack
708 list< shared_ptr<Decoder> >::const_iterator iter = stack.begin();
709 for(int i = 0; i < index; i++, iter++)
710 assert(iter != stack.end());
711
712 shared_ptr<Decoder> dec = *iter;
713 assert(dec);
714
715 const bool show = !dec->shown();
716 dec->show(show);
717
718 assert(index < (int)_decoder_forms.size());
719 _decoder_forms[index]->set_decoder_visible(show);
720
721 _view->update_viewport();
722}
723
724} // namespace view
725} // namespace pv