1 // Copyright (c) 2016 The SigViewer Development Team
2 // Licensed under the GNU General Public License (GPL)
3 // https://www.gnu.org/licenses/gpl
4 
5 
6 #include "basic_header_info_dialog.h"
7 #include "file_handling/basic_header.h"
8 #include "file_handling_impl/xdf_reader.h"
9 
10 #include <cmath>
11 #include <algorithm>
12 
13 #include <QPushButton>
14 #include <QVBoxLayout>
15 #include <QHBoxLayout>
16 #include <QHeaderView>
17 #include <QSettings>
18 #include <QFileInfo>
19 #include <QtXml>
20 
21 namespace sigviewer
22 {
23 
24 // constructor
BasicHeaderInfoDialog(QSharedPointer<BasicHeader> header,QWidget * parent)25 BasicHeaderInfoDialog::BasicHeaderInfoDialog(QSharedPointer<BasicHeader> header,
26                                              QWidget* parent)
27     : QDialog(parent),
28       basic_header_(header)
29 {
30     setWindowTitle(tr("Basic Header Info"));
31     QVBoxLayout* top_layout = new QVBoxLayout(this);
32     top_layout->setMargin(10);
33     top_layout->setSpacing(10);
34     info_tree_widget_ = new QTreeWidget(this);
35     top_layout->addWidget(info_tree_widget_);
36     QHBoxLayout* button_layout = new QHBoxLayout;
37     button_layout->setMargin(0);
38     top_layout->addLayout(button_layout);
39     button_layout->addStretch(1);
40     close_button_ = new QPushButton(tr("Close"), this);
41     toggle_button_ = new QPushButton(tr("Collapse All"), this);
42     button_layout->addWidget(toggle_button_);
43     button_layout->addWidget(close_button_);
44     button_layout->addStretch(1);
45     buildTree();
46     resize(850, 850);
47     top_layout->activate();
48     readSettings();
49     connect(close_button_, SIGNAL(clicked()), this, SLOT(closeInfoDialog()));
50     connect(this, SIGNAL(finished(int)), this, SLOT(closeInfoDialog()));
51     connect(toggle_button_, SIGNAL(clicked()), this, SLOT(toggleCollapseExpand()));
52 //    connect(info_tree_widget_, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(showStreamName(QTreeWidgetItem*)));
53 //    connect(info_tree_widget_, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(hideStreamName(QTreeWidgetItem*)));
54 }
55 
toggleCollapseExpand()56 void BasicHeaderInfoDialog::toggleCollapseExpand()
57 {
58     if (toggle_button_->text().compare("Collapse All") == 0)
59     {
60         info_tree_widget_->collapseAll();
61         toggle_button_->setText("Expand All");
62     }
63     else if (toggle_button_->text().compare("Expand All") == 0)
64     {
65         info_tree_widget_->expandAll();
66         toggle_button_->setText("Collapse All");
67     }
68 }
69 
showStreamName(QTreeWidgetItem * item)70 void BasicHeaderInfoDialog::showStreamName(QTreeWidgetItem *item)
71 {
72     if (item->text(0).startsWith("Stream", Qt::CaseInsensitive))
73     {
74         QRegExp rx("(\\d+)");
75         int pos = rx.indexIn(item->text(0));
76         QString str;
77         if (pos > -1)
78         {
79             str = rx.cap(1);
80         }
81         int streamNumber = str.toInt() - 1; //-1 to switch back to 0-based indexing
82 
83 //        int streamNumber = item->text(0).remove("Stream ").toInt() - 1;//-1 to switch back to 0 index
84         item->setText(1, QString::fromStdString(XDFdata->streams[streamNumber].info.name));
85     }
86 }
87 
hideStreamName(QTreeWidgetItem * item)88 void BasicHeaderInfoDialog::hideStreamName(QTreeWidgetItem *item)
89 {
90     if (item->text(0).startsWith("Stream", Qt::CaseInsensitive))
91     {
92         item->setText(1, "");
93     }
94 }
95 
closeInfoDialog()96 void BasicHeaderInfoDialog::closeInfoDialog()
97 {
98     QSettings settings;
99     settings.beginGroup("BasicHeaderInfoDialog");
100     settings.setValue("geometry", saveGeometry());
101     settings.endGroup();
102 
103     close();
104 }
105 
readSettings()106 void BasicHeaderInfoDialog::readSettings()
107 {
108     QSettings settings;
109     settings.beginGroup("BasicHeaderInfoDialog");
110     restoreGeometry(settings.value("geometry").toByteArray());
111     settings.endGroup();
112 }
113 
114 // build tree
buildTree()115 void BasicHeaderInfoDialog::buildTree()
116 {
117     info_tree_widget_->setIconSize(QSize(16, 16));
118     info_tree_widget_->setRootIsDecorated(true);
119     QStringList header_labels;
120     header_labels << tr("Property") << tr("Value");
121     info_tree_widget_->setHeaderLabels(header_labels);
122 
123     info_tree_widget_->header()->setSectionResizeMode(QHeaderView::Interactive);
124     info_tree_widget_->setColumnWidth(0, width() * 0.65);
125     info_tree_widget_->setAnimated(true);
126 
127     QTreeWidgetItem* root_item;
128     QTreeWidgetItem* tmp_item;
129 
130     // basic
131     root_item = new QTreeWidgetItem(info_tree_widget_);
132     root_item->setText(0, tr("Basic"));
133 //    root_item->setIcon(0, QIcon(":/images/ic_info_outline_black_24dp.png"));
134 
135     tmp_item = new QTreeWidgetItem(root_item);
136     tmp_item->setText(0, tr("File Type"));
137     tmp_item->setText(1, basic_header_->getFileTypeString());
138 
139     QMap<QString, QString> recording_info = basic_header_->getRecordingInfo();
140     foreach (QString key, recording_info.keys())
141     {
142         tmp_item = new QTreeWidgetItem(root_item);
143         tmp_item->setText(0, key);
144         tmp_item->setText(1, recording_info[key]);
145     }
146 
147 
148     //    tmp_item = new QTreeWidgetItem(root_item);
149     //    tmp_item->setText(0, tr("Recording Time"));
150     //    tmp_item->setText(1, basic_header_->getRecordingTime().toString("dd.MM.yyyy hh:mm:ss"));
151     //    tmp_item = new QTreeWidgetItem(root_item);
152     //    // tmp_item ->setTextAlignment(1, Qt::AlignRight);
153     //    tmp_item->setText(0, tr("Triggered"));
154     //    tmp_item->setText(1, basic_header_->isTriggered() ? tr("yes") : tr("no"));
155     //    tmp_item = new QTreeWidgetItem(root_item);
156     //    // tmp_item ->setTextAlignment(1, Qt::AlignRight);
157     //    tmp_item->setText(0, tr("Recording"));
158     //    tmp_item->setText(1, QString::number(basic_header_->getRecordDuration() *
159     //                                      basic_header_->getNumberRecords()));
160     //    tmp_item->setText(2, tr("seconds"));
161 
162     // file
163     root_item = new QTreeWidgetItem(info_tree_widget_);
164     root_item->setText(0, tr("File"));
165 //    root_item->setIcon(0, QIcon(":/images/ic_folder_open_black_24dp.png"));
166     tmp_item = new QTreeWidgetItem(root_item);
167     tmp_item->setText(0, tr("Size"));
168     QFileInfo file_info (basic_header_->getFilePath());
169     tmp_item->setText(1, QString::number(file_info.size() / 1024).append(tr(" KB")));
170     tmp_item->setText(0, tr("File Size"));
171 
172     // events
173     root_item = new QTreeWidgetItem(info_tree_widget_);
174     root_item->setText(0, tr("Events"));
175 //    root_item->setIcon(0, QIcon(":/images/ic_font_download_black_24dp.png"));
176     tmp_item = new QTreeWidgetItem(root_item);
177     // tmp_item ->setTextAlignment(1, Qt::AlignRight);
178     tmp_item->setText(0, tr("Number"));
179     tmp_item->setText(1, QString::number(basic_header_->getNumberEvents()).append(tr(" events")));
180     tmp_item = new QTreeWidgetItem(root_item);
181     // tmp_item ->setTextAlignment(1, Qt::AlignRight);
182     tmp_item->setText(0, tr("Sample Rate"));
183     tmp_item->setText(1, QString::number(basic_header_->getEventSamplerate()).append(tr(" Hz")));
184 
185     //exclusively for XDF
186     if (basic_header_->getFileTypeString().startsWith("XDF", Qt::CaseInsensitive))
187     {
188         for (size_t i =0; i < XDFdata->streams.size(); i++)
189         {
190             // basic
191             root_item = new QTreeWidgetItem(info_tree_widget_);
192 //            root_item->setText(0, "Stream "+QString::number(i + 1));//+1 for user's convenience (1 based instead 0 based)
193             root_item->setText(0, QString("Stream %1 (%2)").arg(QString::number(i+1)).        //+1 for 1-based indexing
194                                    arg(QString::fromStdString(XDFdata->streams[i].info.name)));
195 
196 //            root_item->setIcon(0, QIcon(":/images/ic_flag_black_24dp.png"));
197 
198             QDomDocument streamHeader;
199             streamHeader.setContent(QString::fromStdString(XDFdata->streams[i].streamHeader));
200             QDomElement rootElement = streamHeader.firstChildElement();
201 
202             for (QDomNode n = rootElement.firstChild(); !n.isNull();)
203             {
204                 tmp_item = new QTreeWidgetItem(root_item);
205                 tmp_item->setText(0, n.nodeName());
206                 if (!n.firstChild().isElement())
207                 {
208                     tmp_item->setText(1, n.toElement().text());
209                     while (n.nextSibling().isNull() &&
210                            n.parentNode() != rootElement)
211                     {
212                         n = n.parentNode();
213                         tmp_item = tmp_item->parent();
214                         root_item = tmp_item->parent();
215                     }
216                     n = n.nextSibling();
217                 }
218                 else
219                 {
220                     n = n.firstChild();
221                     root_item = tmp_item;
222                 }
223             }
224 
225             QDomDocument streamFooter;
226             streamFooter.setContent(QString::fromStdString(XDFdata->streams[i].streamFooter));
227             rootElement = streamFooter.firstChildElement();
228             for (QDomNode n = rootElement.firstChild(); !n.isNull();)
229             {
230                 tmp_item = new QTreeWidgetItem(root_item);
231                 tmp_item->setText(0, n.nodeName());
232                 if (!n.firstChild().isElement())
233                 {
234                     tmp_item->setText(1, n.toElement().text());
235                     while (n.nextSibling().isNull() &&
236                            n.parentNode() != rootElement)
237                     {
238                         n = n.parentNode();
239                         tmp_item = tmp_item->parent();
240                         root_item = tmp_item->parent();
241                     }
242                     n = n.nextSibling();
243                 }
244                 else
245                 {
246                     n = n.firstChild();
247                     root_item = tmp_item;
248                 }
249             }
250 
251         }
252     }   //XDF ends here
253     else
254     {
255         // patient
256         root_item = new QTreeWidgetItem(info_tree_widget_);
257         root_item->setText(0, tr("Patient"));
258 //        root_item->setIcon(0, QIcon(":/images/patient_16x16.png"));
259         QMap<QString, QString> patient_info = basic_header_->getPatientInfo();
260         foreach (QString key, patient_info.keys())
261         {
262             tmp_item = new QTreeWidgetItem(root_item);
263             tmp_item->setText(0, key);
264             tmp_item->setText(1, patient_info[key]);
265         }
266 
267         // channels
268         root_item = new QTreeWidgetItem(info_tree_widget_);
269         root_item->setText(0, tr("Channels"));
270 //        root_item->setIcon(0, QIcon(":/images/channels_22x22.png"));
271 
272         for (uint32 channel_nr = 0;
273              channel_nr < basic_header_->getNumberChannels();
274              channel_nr++)
275         {
276             QTreeWidgetItem* channel_item;
277             QSharedPointer<SignalChannel const> channel = basic_header_->getChannel (channel_nr);
278             channel_item = new QTreeWidgetItem(root_item);
279 
280 //            QRegExp rx("(^channel\\s*\\d+)");
281 //            rx.setCaseSensitivity(Qt::CaseInsensitive);
282 
283 //            if (channel->getLabel().contains(rx))
284 //                channel_item->setText(0, channel->getLabel());
285 //            else
286                 channel_item->setText(0, QString("Channel %1 (%2)").arg(channel_nr + 1) // +1 for 1-based indexing
287                                   .arg(channel->getLabel()));
288 
289             // channel basic
290             tmp_item = new QTreeWidgetItem(channel_item);
291             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
292             tmp_item->setText(0, tr("Label"));
293             tmp_item->setText(1, channel->getLabel());
294             tmp_item = new QTreeWidgetItem(channel_item);
295 
296             tmp_item->setText(0, tr("Sample Rate"));
297             float64 fs = channel->getSampleRate();
298             if (fs < 0.0)
299                 fs = basic_header_->getSampleRate();
300             tmp_item->setText(1, QString::number(fs).append(tr(" Hz")));
301 
302             tmp_item = new QTreeWidgetItem(channel_item);
303             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
304             tmp_item->setText(0, tr("Physical Dimension"));
305             tmp_item->setText(1, channel->getPhysicalDim());
306             tmp_item = new QTreeWidgetItem(channel_item);
307             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
308             tmp_item->setText(0, tr("Physical Maximum"));
309             tmp_item->setText(1, QString::number(channel->getPhysicalMaximum()));
310             tmp_item = new QTreeWidgetItem(channel_item);
311             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
312             tmp_item->setText(0, tr("Physical Minimum"));
313             tmp_item->setText(1, QString::number(channel->getPhysicalMinimum()));
314             tmp_item = new QTreeWidgetItem(channel_item);
315             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
316             tmp_item->setText(0, tr("Digital Maximum"));
317             tmp_item->setText(1, QString::number(channel->getDigitalMaximum()));
318             tmp_item = new QTreeWidgetItem(channel_item);
319             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
320             tmp_item->setText(0, tr("Digital Minimum"));
321             tmp_item->setText(1, QString::number(channel->getDigitalMinimum()));
322             tmp_item = new QTreeWidgetItem(channel_item);
323             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
324             tmp_item->setText(0, tr("Data Type"));
325             tmp_item->setText(1, channel->typeString());
326 
327             // filter
328             QTreeWidgetItem* filter_item;
329             filter_item = new QTreeWidgetItem(channel_item);
330             filter_item->setText(0, tr("Filter"));
331             tmp_item = new QTreeWidgetItem(filter_item);
332             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
333             tmp_item->setText(0, tr("Highpass"));
334             //tmp_item->setText(1, isnan(channel->getHighpass()) ? tr("unknown") :
335             //    (channel->getHighpass() < 0 ? "" :
336             //                       QString::number(channel->getHighpass())));
337             tmp_item->setText(2, tr("Hz"));
338             tmp_item = new QTreeWidgetItem(filter_item);
339             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
340             tmp_item->setText(0, tr("Lowpass"));
341             // tmp_item->setText(1, channel->getLowpass() ? tr("unknown") :
342             //                (channel->getLowpass() < 0 ? "" :
343             //                           QString::number(channel->getLowpass())));
344             tmp_item->setText(2, tr("Hz"));
345             tmp_item = new QTreeWidgetItem(filter_item);
346             // tmp_item ->setTextAlignment(1, Qt::AlignRight);
347             tmp_item->setText(0, tr("Notch"));
348             //tmp_item->setText(1, isnan(channel->getNotch()) ? tr("unknown") : (channel->getNotch() ? tr("yes") : tr("no")));
349         }
350     }
351     info_tree_widget_->expandAll();
352 }
353 
354 }
355