]> sigrok.org Git - pulseview.git/blob - pv/views/tabular_decoder/model.cpp
TabularDecView: Fix invalid assert()
[pulseview.git] / pv / views / tabular_decoder / model.cpp
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <QDebug>
21 #include <QString>
22
23 #include "pv/views/tabular_decoder/view.hpp"
24
25 #include "view.hpp"
26
27 #include "pv/util.hpp"
28
29 using std::make_shared;
30
31 using pv::util::Timestamp;
32 using pv::util::format_time_si;
33 using pv::util::format_time_minutes;
34 using pv::util::SIPrefix;
35
36 namespace pv {
37 namespace views {
38 namespace tabular_decoder {
39
40 AnnotationCollectionModel::AnnotationCollectionModel(QObject* parent) :
41         QAbstractTableModel(parent),
42         all_annotations_(nullptr),
43         dataset_(nullptr),
44         signal_(nullptr),
45         prev_segment_(0),
46         prev_last_row_(0),
47         hide_hidden_(false)
48 {
49         GlobalSettings::add_change_handler(this);
50         theme_is_dark_ = GlobalSettings::current_theme_is_dark();
51
52         // TBD Maybe use empty columns as indentation levels to indicate stacked decoders
53         header_data_.emplace_back(tr("Sample"));     // Column #0
54         header_data_.emplace_back(tr("Time"));       // Column #1
55         header_data_.emplace_back(tr("Decoder"));    // Column #2
56         header_data_.emplace_back(tr("Ann Row"));    // Column #3
57         header_data_.emplace_back(tr("Ann Class"));  // Column #4
58         header_data_.emplace_back(tr("Value"));      // Column #5
59 }
60
61 QVariant AnnotationCollectionModel::data_from_ann(const Annotation* ann, int index) const
62 {
63         switch (index) {
64         case 0: return QVariant((qulonglong)ann->start_sample());  // Column #0, Start Sample
65         case 1: {                                                  // Column #1, Start Time
66                         Timestamp t = ann->start_sample() / signal_->get_samplerate();
67                         QString unit = signal_->get_samplerate() ? tr("s") : tr("sa");
68                         QString s;
69                         if ((t < 60) || (signal_->get_samplerate() == 0))  // i.e. if unit is sa
70                                 s = format_time_si(t, SIPrefix::unspecified, 3, unit, false);
71                         else
72                                 s = format_time_minutes(t, 3, false);
73                         return QVariant(s);
74                 }
75         case 2: return QVariant(ann->row()->decoder()->name());    // Column #2, Decoder
76         case 3: return QVariant(ann->row()->description());        // Column #3, Ann Row
77         case 4: return QVariant(ann->ann_class_description());     // Column #4, Ann Class
78         case 5: return QVariant(ann->longest_annotation());        // Column #5, Value
79         default: return QVariant();
80         }
81 }
82
83 QVariant AnnotationCollectionModel::data(const QModelIndex& index, int role) const
84 {
85         if (!signal_ || !index.isValid() || !index.internalPointer())
86                 return QVariant();
87
88         const Annotation* ann =
89                 static_cast<const Annotation*>(index.internalPointer());
90
91         if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole))
92                 return data_from_ann(ann, index.column());
93
94         if (role == Qt::BackgroundRole) {
95                 int level = 0;
96
97                 const unsigned int ann_stack_level = ann->row_data()->row()->decoder()->get_stack_level();
98                 level = (signal_->decoder_stack().size() - 1 - ann_stack_level);
99
100                 // Only use custom cell background color if column index reached the hierarchy level
101                 if (index.column() >= level) {
102                         if (theme_is_dark_)
103                                 return QBrush(ann->dark_color());
104                         else
105                                 return QBrush(ann->bright_color());
106                 }
107         }
108
109         return QVariant();
110 }
111
112 Qt::ItemFlags AnnotationCollectionModel::flags(const QModelIndex& index) const
113 {
114         if (!index.isValid())
115                 return Qt::NoItemFlags;
116
117         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
118 }
119
120 QVariant AnnotationCollectionModel::headerData(int section, Qt::Orientation orientation,
121         int role) const
122 {
123         if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole))
124                 return header_data_.at(section);
125
126         return QVariant();
127 }
128
129 QModelIndex AnnotationCollectionModel::index(int row, int column,
130         const QModelIndex& parent_idx) const
131 {
132         (void)parent_idx;
133         assert(column >= 0);
134
135         if (!dataset_ || (row < 0))
136                 return QModelIndex();
137
138         QModelIndex idx;
139
140         if ((size_t)row < dataset_->size())
141                 idx = createIndex(row, column, (void*)dataset_->at(row));
142
143         return idx;
144 }
145
146 QModelIndex AnnotationCollectionModel::parent(const QModelIndex& index) const
147 {
148         (void)index;
149
150         return QModelIndex();
151 }
152
153 int AnnotationCollectionModel::rowCount(const QModelIndex& parent_idx) const
154 {
155         (void)parent_idx;
156
157         if (!dataset_)
158                 return 0;
159
160         return dataset_->size();
161 }
162
163 int AnnotationCollectionModel::columnCount(const QModelIndex& parent_idx) const
164 {
165         (void)parent_idx;
166
167         return header_data_.size();
168 }
169
170 void AnnotationCollectionModel::set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment)
171 {
172         if (!signal) {
173                 all_annotations_ = nullptr;
174                 dataset_ = nullptr;
175                 signal_ = nullptr;
176
177                 dataChanged(QModelIndex(), QModelIndex());
178                 layoutChanged();
179                 return;
180         }
181
182         all_annotations_ = signal->get_all_annotations_by_segment(current_segment);
183         signal_ = signal;
184
185         if (hide_hidden_)
186                 update_annotations_without_hidden();
187         else
188                 dataset_ = all_annotations_;
189
190         if (!dataset_ || dataset_->empty()) {
191                 prev_segment_ = current_segment;
192                 return;
193         }
194
195         const size_t new_row_count = dataset_->size() - 1;
196
197         // Force the view associated with this model to update when the segment changes
198         if (prev_segment_ != current_segment) {
199                 dataChanged(QModelIndex(), QModelIndex());
200                 layoutChanged();
201         } else {
202                 // Force the view associated with this model to update when we have more annotations
203                 if (prev_last_row_ < new_row_count) {
204                         dataChanged(index(prev_last_row_, 0), index(new_row_count, 0));
205                         layoutChanged();
206                 }
207         }
208
209         prev_segment_ = current_segment;
210         prev_last_row_ = new_row_count;
211 }
212
213 void AnnotationCollectionModel::set_hide_hidden(bool hide_hidden)
214 {
215         hide_hidden_ = hide_hidden;
216
217         if (hide_hidden_) {
218                 dataset_ = &all_annotations_without_hidden_;
219                 update_annotations_without_hidden();
220         } else {
221                 dataset_ = all_annotations_;
222                 all_annotations_without_hidden_.clear();  // To conserve memory
223         }
224 }
225
226 void AnnotationCollectionModel::update_annotations_without_hidden()
227 {
228         uint64_t count = 0;
229
230         if (!all_annotations_ || all_annotations_->empty()) {
231                 all_annotations_without_hidden_.clear();
232                 return;
233         }
234
235         for (const Annotation* ann : *all_annotations_) {
236                 if (!ann->visible())
237                         continue;
238
239                 if (all_annotations_without_hidden_.size() < (count + 100))
240                         all_annotations_without_hidden_.resize(count + 100);
241
242                 all_annotations_without_hidden_[count++] = ann;
243         }
244
245         all_annotations_without_hidden_.resize(count);
246
247         dataChanged(index(0, 0), index(count, 0));
248         layoutChanged();
249 }
250
251 void AnnotationCollectionModel::on_setting_changed(const QString &key, const QVariant &value)
252 {
253         (void)key;
254         (void)value;
255
256         // We don't really care about the actual setting, we just update the
257         // flag that indicates whether we are using a bright or dark color theme
258         theme_is_dark_ = GlobalSettings::current_theme_is_dark();
259 }
260
261 } // namespace tabular_decoder
262 } // namespace views
263 } // namespace pv