source: ntrip/trunk/BNC/bncwindow.cpp@ 945

Last change on this file since 945 was 938, checked in by mervart, 16 years ago

* empty log message *

File size: 43.1 KB
Line 
1// Part of BNC, a utility for retrieving decoding and
2// converting GNSS data streams from NTRIP broadcasters.
3//
4// Copyright (C) 2007
5// German Federal Agency for Cartography and Geodesy (BKG)
6// http://www.bkg.bund.de
7// Czech Technical University Prague, Department of Geodesy
8// http://www.fsv.cvut.cz
9//
10// Email: euref-ip@bkg.bund.de
11//
12// This program is free software; you can redistribute it and/or
13// modify it under the terms of the GNU General Public License
14// as published by the Free Software Foundation, version 2.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25/* -------------------------------------------------------------------------
26 * BKG NTRIP Client
27 * -------------------------------------------------------------------------
28 *
29 * Class: bncWindow
30 *
31 * Purpose: This class implements the main application window.
32 *
33 * Author: L. Mervart
34 *
35 * Created: 24-Dec-2005
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <unistd.h>
42#include "bncwindow.h"
43#include "bncapp.h"
44#include "bncgetthread.h"
45#include "bnctabledlg.h"
46#include "bnchlpdlg.h"
47#include "bnchtml.h"
48#include "bnctableitem.h"
49
50using namespace std;
51
52// Constructor
53////////////////////////////////////////////////////////////////////////////
54bncWindow::bncWindow() {
55
56 _caster = 0;
57
58 int ww = QFontMetrics(this->font()).width('w');
59
60 static const QStringList labels = QString("account,mountpoint,decoder,lat,long,nmea,bytes").split(",");
61
62 setMinimumSize(77*ww, 65*ww);
63
64 setWindowTitle(tr("BKG Ntrip Client (BNC) Version 1.6"));
65
66 // Create Actions
67 // --------------
68 _actHelp = new QAction(tr("&Help Contents"),this);
69 connect(_actHelp, SIGNAL(triggered()), SLOT(slotHelp()));
70
71 _actAbout = new QAction(tr("&About BNC"),this);
72 connect(_actAbout, SIGNAL(triggered()), SLOT(slotAbout()));
73
74 _actFontSel = new QAction(tr("Select &Font"),this);
75 connect(_actFontSel, SIGNAL(triggered()), SLOT(slotFontSel()));
76
77 _actSaveOpt = new QAction(tr("&Save Options"),this);
78 connect(_actSaveOpt, SIGNAL(triggered()), SLOT(slotSaveOptions()));
79
80 _actQuit = new QAction(tr("&Quit"),this);
81 connect(_actQuit, SIGNAL(triggered()), SLOT(close()));
82
83 _actAddMountPoints = new QAction(tr("Add &Mountpoints"),this);
84 connect(_actAddMountPoints, SIGNAL(triggered()), SLOT(slotAddMountPoints()));
85
86 _actDeleteMountPoints = new QAction(tr("&Delete Mountpoints"),this);
87 connect(_actDeleteMountPoints, SIGNAL(triggered()), SLOT(slotDeleteMountPoints()));
88 _actDeleteMountPoints->setEnabled(false);
89
90 _actGetData = new QAction(tr("Sta&rt"),this);
91 connect(_actGetData, SIGNAL(triggered()), SLOT(slotGetData()));
92
93 _actStop = new QAction(tr("Sto&p"),this);
94 connect(_actStop, SIGNAL(triggered()), SLOT(slotStop()));
95 _actStop->setEnabled(false);
96
97 _actwhatsthis= new QAction(tr("Help=Shift+F1"),this);
98 connect(_actwhatsthis, SIGNAL(triggered()), SLOT(slotWhatsThis()));
99
100 CreateMenu();
101 AddToolbar();
102
103 QSettings settings;
104 _proxyHostLineEdit = new QLineEdit(settings.value("proxyHost").toString());
105 _proxyPortLineEdit = new QLineEdit(settings.value("proxyPort").toString());
106 _proxyPortLineEdit->setMaximumWidth(9*ww);
107 _waitTimeSpinBox = new QSpinBox();
108 _waitTimeSpinBox->setMinimum(1);
109 _waitTimeSpinBox->setMaximum(30);
110 _waitTimeSpinBox->setSingleStep(1);
111 _waitTimeSpinBox->setSuffix(" sec");
112 _waitTimeSpinBox->setMaximumWidth(9*ww);
113 _waitTimeSpinBox->setValue(settings.value("waitTime").toInt());
114 _outFileLineEdit = new QLineEdit(settings.value("outFile").toString());
115 _outPortLineEdit = new QLineEdit(settings.value("outPort").toString());
116 _outPortLineEdit->setMaximumWidth(9*ww);
117 _outEphPortLineEdit = new QLineEdit(settings.value("outEphPort").toString());
118 _outEphPortLineEdit->setMaximumWidth(9*ww);
119 _corrPortLineEdit = new QLineEdit(settings.value("corrPort").toString());
120 _corrPortLineEdit->setMaximumWidth(9*ww);
121 _rnxPathLineEdit = new QLineEdit(settings.value("rnxPath").toString());
122 _ephPathLineEdit = new QLineEdit(settings.value("ephPath").toString());
123 _corrPathLineEdit = new QLineEdit(settings.value("corrPath").toString());
124
125 _rnxV3CheckBox = new QCheckBox();
126 _rnxV3CheckBox->setCheckState(Qt::CheckState(settings.value("rnxV3").toInt()));
127 _ephV3CheckBox = new QCheckBox();
128 _ephV3CheckBox->setCheckState(Qt::CheckState(settings.value("ephV3").toInt()));
129 _rnxScrpLineEdit = new QLineEdit(settings.value("rnxScript").toString());
130 _rnxSkelLineEdit = new QLineEdit(settings.value("rnxSkel").toString());
131 _rnxSkelLineEdit->setMaximumWidth(5*ww);
132 _rnxAppendCheckBox = new QCheckBox();
133 _rnxAppendCheckBox->setCheckState(Qt::CheckState(
134 settings.value("rnxAppend").toInt()));
135 _rnxIntrComboBox = new QComboBox();
136 _rnxIntrComboBox->setMaximumWidth(9*ww);
137 _rnxIntrComboBox->setEditable(false);
138 _rnxIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
139 int ii = _rnxIntrComboBox->findText(settings.value("rnxIntr").toString());
140 if (ii != -1) {
141 _rnxIntrComboBox->setCurrentIndex(ii);
142 }
143 _ephIntrComboBox = new QComboBox();
144 _ephIntrComboBox->setMaximumWidth(9*ww);
145 _ephIntrComboBox->setEditable(false);
146 _ephIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
147 int jj = _ephIntrComboBox->findText(settings.value("ephIntr").toString());
148 if (jj != -1) {
149 _ephIntrComboBox->setCurrentIndex(jj);
150 }
151 _corrIntrComboBox = new QComboBox();
152 _corrIntrComboBox->setMaximumWidth(9*ww);
153 _corrIntrComboBox->setEditable(false);
154 _corrIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
155 int mm = _corrIntrComboBox->findText(settings.value("corrIntr").toString());
156 if (mm != -1) {
157 _corrIntrComboBox->setCurrentIndex(mm);
158 }
159 _rnxSamplSpinBox = new QSpinBox();
160 _rnxSamplSpinBox->setMinimum(0);
161 _rnxSamplSpinBox->setMaximum(60);
162 _rnxSamplSpinBox->setSingleStep(5);
163 _rnxSamplSpinBox->setMaximumWidth(9*ww);
164 _rnxSamplSpinBox->setValue(settings.value("rnxSampl").toInt());
165 _rnxSamplSpinBox->setSuffix(" sec");
166
167 _binSamplSpinBox = new QSpinBox();
168 _binSamplSpinBox->setMinimum(0);
169 _binSamplSpinBox->setMaximum(60);
170 _binSamplSpinBox->setSingleStep(5);
171 _binSamplSpinBox->setMaximumWidth(9*ww);
172 _binSamplSpinBox->setValue(settings.value("binSampl").toInt());
173 _binSamplSpinBox->setSuffix(" sec");
174
175 _obsRateComboBox = new QComboBox();
176 _obsRateComboBox->setMaximumWidth(9*ww);
177 _obsRateComboBox->setEditable(false);
178 _obsRateComboBox->addItems(QString(",0.1 Hz,0.2 Hz,0.5 Hz,1 Hz,5 Hz").split(","));
179 int kk = _obsRateComboBox->findText(settings.value("obsRate").toString());
180 if (kk != -1) {
181 _obsRateComboBox->setCurrentIndex(kk);
182 }
183 _makePauseCheckBox = new QCheckBox();
184 _makePauseCheckBox->setCheckState(Qt::CheckState(
185 settings.value("makePause").toInt()));
186 _adviseRecoSpinBox = new QSpinBox();
187 _adviseRecoSpinBox->setMinimum(0);
188 _adviseRecoSpinBox->setMaximum(60);
189 _adviseRecoSpinBox->setSingleStep(1);
190 _adviseRecoSpinBox->setSuffix(" min");
191 _adviseRecoSpinBox->setMaximumWidth(9*ww);
192 _adviseRecoSpinBox->setValue(settings.value("adviseReco").toInt());
193 _adviseFailSpinBox = new QSpinBox();
194 _adviseFailSpinBox->setMinimum(0);
195 _adviseFailSpinBox->setMaximum(60);
196 _adviseFailSpinBox->setSingleStep(1);
197 _adviseFailSpinBox->setSuffix(" min");
198 _adviseFailSpinBox->setMaximumWidth(9*ww);
199 _adviseFailSpinBox->setValue(settings.value("adviseFail").toInt());
200 _logFileLineEdit = new QLineEdit(settings.value("logFile").toString());
201 _adviseScriptLineEdit = new QLineEdit(settings.value("adviseScript").toString());
202
203 _perfIntrComboBox = new QComboBox();
204 _perfIntrComboBox->setMaximumWidth(9*ww);
205 _perfIntrComboBox->setEditable(false);
206 _perfIntrComboBox->addItems(QString(",1 min,5 min,15 min,1 hour,6 hours,1 day").split(","));
207 int ll = _perfIntrComboBox->findText(settings.value("perfIntr").toString());
208 if (ll != -1) {
209 _perfIntrComboBox->setCurrentIndex(ll);
210 }
211
212 _mountPointsTable = new QTableWidget(0,7);
213
214 _mountPointsTable->horizontalHeader()->resizeSection(1,34*ww);
215 _mountPointsTable->horizontalHeader()->resizeSection(2,9*ww);
216 _mountPointsTable->horizontalHeader()->resizeSection(3,7*ww);
217 _mountPointsTable->horizontalHeader()->resizeSection(4,7*ww);
218 _mountPointsTable->horizontalHeader()->resizeSection(5,5*ww);
219 _mountPointsTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
220 _mountPointsTable->horizontalHeader()->setStretchLastSection(true);
221 _mountPointsTable->setHorizontalHeaderLabels(labels);
222 _mountPointsTable->setGridStyle(Qt::NoPen);
223 _mountPointsTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
224 _mountPointsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
225 _mountPointsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
226 QListIterator<QString> it(settings.value("mountPoints").toStringList());
227 if (!it.hasNext()) {
228 _actGetData->setEnabled(false);
229 }
230 int iRow = 0;
231 while (it.hasNext()) {
232 QStringList hlp = it.next().split(" ");
233 if (hlp.size() < 5) continue;
234 _mountPointsTable->insertRow(iRow);
235
236 QUrl url(hlp[0]);
237
238 QString fullPath = url.host() + QString(":%1").arg(url.port()) + url.path();
239 QString format(hlp[1]); QString latitude(hlp[2]); QString longitude(hlp[3]);
240 QString nmea(hlp[4]);
241
242 QTableWidgetItem* it;
243 it = new QTableWidgetItem(url.userInfo());
244 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
245 _mountPointsTable->setItem(iRow, 0, it);
246
247 it = new QTableWidgetItem(fullPath);
248 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
249 _mountPointsTable->setItem(iRow, 1, it);
250
251 it = new QTableWidgetItem(format);
252 _mountPointsTable->setItem(iRow, 2, it);
253
254 if (nmea == "yes") {
255 it = new QTableWidgetItem(latitude);
256 _mountPointsTable->setItem(iRow, 3, it);
257 it = new QTableWidgetItem(longitude);
258 _mountPointsTable->setItem(iRow, 4, it);
259 } else {
260 it = new QTableWidgetItem(latitude);
261 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
262 _mountPointsTable->setItem(iRow, 3, it);
263 it = new QTableWidgetItem(longitude);
264 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
265 _mountPointsTable->setItem(iRow, 4, it);
266 }
267
268 it = new QTableWidgetItem(nmea);
269 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
270 _mountPointsTable->setItem(iRow, 5, it);
271
272 bncTableItem* bncIt = new bncTableItem();
273 bncIt->setFlags(bncIt->flags() & ~Qt::ItemIsEditable);
274 _mountPointsTable->setItem(iRow, 6, bncIt);
275
276 iRow++;
277 }
278 _mountPointsTable->hideColumn(0);
279 _mountPointsTable->sortItems(1);
280
281 connect(_mountPointsTable, SIGNAL(itemSelectionChanged()),
282 SLOT(slotSelectionChanged()));
283
284 _log = new QTextBrowser();
285 _log->setReadOnly(true);
286
287 // WhatsThis
288 // ---------
289 _proxyHostLineEdit->setWhatsThis(tr("<p>If you are running BNC within a protected Local Area Network (LAN), you might need to use a proxy server to access the Internet. Enter your proxy server IP and port number in case one is operated in front of BNC. If you do not know the IP and port of your proxy server, check the proxy server settings in your Internet browser or ask your network administrator.</p><p>Note that IP streaming is sometimes not allowed in a LAN. In this case you need to ask your network administrator for an appropriate modification of the local security policy or for the installation of a TCP relay to the NTRIP broadcasters. If these are not possible, you might need to run BNC outside your LAN on a network that has unobstructed connection to the Internet.</p>"));
290 _proxyPortLineEdit->setWhatsThis(tr("<p>If you are running BNC within a protected Local Area Network (LAN), you might need to use a proxy server to access the Internet. Enter your proxy server IP and port number in case one is operated in front of BNC. If you do not know the IP and port of your proxy server, check the proxy server settings in your Internet browser or ask your network administrator.</p><p>Note that IP streaming is sometimes not allowed in a LAN. In this case you need to ask your network administrator for an appropriate modification of the local security policy or for the installation of a TCP relay to the NTRIP broadcasters. If these are not possible, you might need to run BNC outside your LAN on a network that has unobstructed connection to the Internet.</p>"));
291 _waitTimeSpinBox->setWhatsThis(tr("<p>When feeding a real-time GNSS engine waiting for input epoch by epoch, BNC drops whatever is received later than 'Wait for full epoch' seconds. A value of 3 to 5 seconds is recommended, depending on the latency of the incoming streams and the delay acceptable to your real-time GNSS engine or products.</p><p>Note that 'Wait for full epoch' does not effect the RINEX Observation file content. Observations received later than 'Wait for full epoch' seconds will still be included in the RINEX Observation files.</p>"));
292 _outFileLineEdit->setWhatsThis(tr("Specify the full path to a file where synchronized observations are saved in plain ASCII format. Beware that the size of this file can rapidly increase depending on the number of incoming streams."));
293 _outPortLineEdit->setWhatsThis(tr("BNC can produce synchronized observations in binary format on your local host through an IP port. Specify a port number here to activate this function."));
294 _outEphPortLineEdit->setWhatsThis(tr("BNC can produce ephemeris data in RINEX ASCII format on your local host through an IP port. Specify a port number here to activate this function."));
295 _corrPortLineEdit->setWhatsThis(tr("BNC can produce ephemeris corrections on your local host through an IP port. Specify a port number here to activate this function."));
296 _rnxPathLineEdit->setWhatsThis(tr("Here you specify the path to where the RINEX Observation files will be stored. If the specified directory does not exist, BNC will not create RINEX Observation files."));
297 _ephPathLineEdit->setWhatsThis(tr("Specify the path for saving Broadcast Ephemeris data as RINEX Navigation files. If the specified directory does not exist, BNC will not create RINEX Navigation files."));
298 _corrPathLineEdit->setWhatsThis(tr("Specify the path for saving Broadcast Ephemeris corrections. If the specified directory does not exist, BNC will not create the files."));
299 _rnxScrpLineEdit->setWhatsThis(tr("<p>Whenever a RINEX Observation file is saved, you might want to compress, copy or upload it immediately via FTP. BNC allows you to execute a script/batch file to carry out these operations. To do that specify the full path of the script/batch file here. BNC will pass the full RINEX Observation file path to the script as a command line parameter (%1 on Windows systems, $1 onUnix/Linux systems).</p><p>The triggering event for calling the script or batch file is the end of a RINEX Observation file 'Interval'. If that is overridden by a stream outage, the triggering event is the stream reconnection.</p>"));
300 _rnxSkelLineEdit->setWhatsThis(tr("<p>Whenever BNC starts generating RINEX Observation files (and then once every day at midnight), it first tries to retrieve information needed for RINEX headers from so-called public RINEX header skeleton files which are derived from sitelogs. However, sometimes public RINEX header skeleton files are not available, its contents is not up to date, or you need to put additional/optional records in the RINEX header.</p><p>For that BNC allows using personal skeleton files that contain the header records you would like to include. You can derive a personal RINEX header skeleton file from the information given in an up to date sitelog. A file in the RINEX 'Directory' with the RINEX 'Skeleton extension' is interpreted by BNC as a personal RINEX header skeleton file for the corresponding stream.</p>"));
301 _rnxAppendCheckBox->setWhatsThis(tr("<p>When BNC is started, new files are created by default and any existing files with the same name will be overwritten. However, users might want to append already existing files following a restart of BNC, a system crash or when BNC crashed. Tick 'Append files' to continue with existing files and keep what has been recorded so far.</p>"));
302 _rnxIntrComboBox->setWhatsThis(tr("<p>Select the length of the RINEX Observation file.</p>"));
303 _ephIntrComboBox->setWhatsThis(tr("<p>Select the length of the RINEX Navigation file.</p>"));
304 _corrIntrComboBox->setWhatsThis(tr("<p>Select the length of the correction file.</p>"));
305 _rnxSamplSpinBox->setWhatsThis(tr("<p>Select the RINEX Observation sampling interval in seconds. A value of zero '0' tells BNC to store all received epochs into RINEX.</p>"));
306 _binSamplSpinBox->setWhatsThis(tr("<p>Select the Observation sampling interval in seconds. A value of zero '0' tells BNC to send/store all received epochs.</p>"));
307 _obsRateComboBox->setWhatsThis(tr("<p>BNC can collect all returns (success or failure) coming from a decoder within a certain short time span to then decide whether a stream has an outage or its content is corrupted. The procedure needs a rough estimate of the expected 'Observation rate' of the incoming streams. When a continuous problem is detected, BNC can inform its operator about this event through an advisory note.</p><p>An empty option field (default) means that you don't want an explicit information from BNC about stream outages and incoming streams that can not be decoded and that the special procedure for handling of corrupted streams is bypassed.</p>"));
308 _adviseRecoSpinBox->setWhatsThis(tr("<p>Following a stream outage or a longer series of bad observations, an advisory note is generated when valid observations are received again throughout the 'Recovery threshold' time span. A value of about 5min (default) is recommended.</p><p>A value of zero '0' means that for any stream recovery, however short, BNC immediately generates an advisory note.</p><p>Note that for using this function you need to specify the 'Observation rate'.</p>"));
309 _adviseFailSpinBox->setWhatsThis(tr("<p>An advisory note is generated when no (or only corrupted) observations are seen throughout the 'Failure threshold' time span. A value of 15 min (default) is recommended.</p><p>A value of zero '0' means that for any stream failure, however short, BNC immediately generates an advisory note.</p><p>Note that for using this function you need to specify the 'Observation rate'.</p>"));
310 _makePauseCheckBox->setWhatsThis(tr("<p>In case of a continuously corrupted stream, the decoding process can be paused and decodings are then attempted again at decreasing rate till the stream hopefully recovers. Tick 'Pause' to activate this function.</p><p>Do not tick 'Pause' (default) in order to prevent BNC from making any decoding pause. Be aware that this may incur an unnecessary workload.</p><p>Note that this function is only effective if an 'Observation rate' is specified.</p>"));
311 _logFileLineEdit->setWhatsThis(tr("Records of BNC's activities are shown in the Log section on the bottom of this window. They can be saved into a file when a valid path is specified in the 'Logfile (full path)' field."));
312 _adviseScriptLineEdit->setWhatsThis(tr("<p>Specify the full path to a script or batch file to handle advisory notes generated in the event of corrupted streams or stream outages. The affected mountpoint and one of the comments 'Begin_Outage', 'End_Outage', 'Begin_Corrupted', or 'End_Corrupted' are passed on to the script as command line parameters.</p><p>The script can be configured to send an email to BNC's operator and/or to the affected stream provider. An empty option field (default) or invalid path means that you don't want to use this option.</p><p> Note that for using this function you need to specify the 'Observation rate'.</p>"));
313 _perfIntrComboBox->setWhatsThis(tr("<p>BNC can average all latencies per stream over a certain period of GPS time. The resulting mean latencies are recorded in the Log file/section at the end of each 'Performance log' interval together with results of a statistical evaluation (approximate number of covered epochs, data gaps).</p><p>Select a 'Performance log' interval or select the empty option field if you do not want BNC to log latencies and statistical information.</p>"));
314 _mountPointsTable->setWhatsThis(tr("<p>Streams selected for retrieval are listed in the 'Mountpoints' section. Clicking on 'Add Mountpoints' button will open a window that allows the user to select data streams from an NTRIP broadcaster according to their mountpoints. To remove a stream from the 'Mountpoints' list, highlight it by clicking on it and hit the 'Delete Mountpoints' button. You can also remove multiple mountpoints by highlighting them using +Shift and +Ctrl.</p><p>BNC automatically allocates one of its internal decoders to a stream based on the stream's 'format' and 'format-details' as given in the sourcetable. However, there might be cases where you need to override the automatic selection due to incorrect sourcetable for example. BNC allows users to manually select the required decoder by editing the decoder string. Double click on the 'decoder' field, enter your preferred decoder and then hit Enter. The accepted decoder strings are 'RTCM_2.x', 'RTCM_3.x', and 'RTIGS'.</p><p>In case you need to log the raw data as is, BNC allows users to by-pass its decoders and and directly save the input in daily log files. To do this specify the decoder string as 'ZERO'.</p><p>BNC can also retrieve streams from virtual reference stations (VRS). To initiate these streams, an approximate rover position needs to be sent in NMEA GGA message to the NTRIP broadcaster. In return, a user-specific data stream is generated, typically by a Network-RTK software. This stream is customized to the exact latitude and longitude as shown in the 'lat' and 'long' columns under 'Mountpoints'. These VRS streams are indicated by a 'yes' in the 'nmea' column under 'Mountpoints' as well as in the sourcetable. The default 'lat' and 'long' values are taken from the sourcetable. However, in most cases you would probably want to change this according to your requirement. Double click on 'lat' and 'long' fields, enter the values you wish to send and then hit Enter. The format is in positive north latitude degrees (e.g. for northern hemisphere: 52.436, for southern hemisphere: -24.567) and eastern longitude degrees (e.g.: 358.872 or -1.128). Only mountpoints with a 'yes' in its 'nmea' column can be edited. The position should preferably be a point within the coverage of the network.</p>"));
315 _log->setWhatsThis(tr("Records of BNC's activities are shown in the Log section. The message log covers the communication status between BNC and the NTRIP broadcaster as well as any problems that occur in the communication link, stream availability, stream delay, stream conversion etc."));
316 _ephV3CheckBox->setWhatsThis(tr("The default format for RINEX Navigation files containing Broadcast Ephemeris is RINEX Version 2.11. Select 'Version 3' if you want to save the ephemeris in RINEX Version 3 format."));
317 _rnxV3CheckBox->setWhatsThis(tr("The default format for RINEX Observation files is RINEX Version 2.11. Select 'Version 3' if you want to save the observations in RINEX Version 3 format."));
318
319 // Canvas with Editable Fields
320 // ---------------------------
321 _canvas = new QWidget;
322 setCentralWidget(_canvas);
323
324 QTabWidget* aogroup = new QTabWidget();
325 QWidget* pgroup = new QWidget();
326 QWidget* ggroup = new QWidget();
327 QWidget* sgroup = new QWidget();
328 QWidget* egroup = new QWidget();
329 QWidget* agroup = new QWidget();
330 QWidget* cgroup = new QWidget();
331 QWidget* ogroup = new QWidget();
332 aogroup->addTab(pgroup,tr("Proxy"));
333 aogroup->addTab(ggroup,tr("General"));
334 aogroup->addTab(ogroup,tr("RINEX Observations"));
335 aogroup->addTab(egroup,tr("RINEX Ephemeris"));
336 aogroup->addTab(sgroup,tr("Synchronized Observations"));
337 aogroup->addTab(cgroup,tr("Ephemeris Corrections"));
338 aogroup->addTab(agroup,tr("Monitor"));
339
340 QGridLayout* pLayout = new QGridLayout;
341 pLayout->setColumnMinimumWidth(0,12*ww);
342 pLayout->addWidget(new QLabel("Proxy host"),0,0, Qt::AlignLeft);
343 pLayout->addWidget(_proxyHostLineEdit,0, 1);
344 pLayout->addWidget(new QLabel("Proxy port"),1,0, Qt::AlignLeft);
345 pLayout->addWidget(_proxyPortLineEdit,1,1);
346 pLayout->addWidget(new QLabel("Settings for the proxy in protected networks, leave the boxes blank if none."),2, 0, 1, 2, Qt::AlignLeft);
347 pLayout->addWidget(new QLabel(" "),3,0);
348 pLayout->addWidget(new QLabel(" "),4,0);
349 pLayout->addWidget(new QLabel(" "),5,0);
350 pgroup->setLayout(pLayout);
351
352 QGridLayout* gLayout = new QGridLayout;
353 gLayout->setColumnMinimumWidth(0,12*ww);
354 gLayout->addWidget(new QLabel("Logfile (full path)"), 0,0);
355 gLayout->addWidget(_logFileLineEdit, 0,1);
356 gLayout->addWidget(new QLabel("Append files") ,1,0 );
357 gLayout->addWidget(_rnxAppendCheckBox, 1,1 );
358 gLayout->addWidget(new QLabel("General settings for logfile and file handling."),2, 0, 1, 2, Qt::AlignLeft);
359 gLayout->addWidget(new QLabel(" "),3,0);
360 gLayout->addWidget(new QLabel(" "),4,0);
361 gLayout->addWidget(new QLabel(" "),5,0);
362 ggroup->setLayout(gLayout);
363
364 QGridLayout* sLayout = new QGridLayout;
365 sLayout->setColumnMinimumWidth(0,12*ww);
366 sLayout->addWidget(new QLabel("Port"), 0, 0);
367 sLayout->addWidget(_outPortLineEdit, 0, 1);
368 sLayout->addWidget(new QLabel("Wait for full epoch"), 1, 0);
369 sLayout->addWidget(_waitTimeSpinBox, 1, 1);
370 sLayout->addWidget(new QLabel("File (full path)"), 2, 0);
371 sLayout->addWidget(_outFileLineEdit, 2, 1);
372 sLayout->addWidget(new QLabel("Sampling"), 3, 0, Qt::AlignLeft);
373 sLayout->addWidget(_binSamplSpinBox, 3, 1, Qt::AlignLeft);
374 sLayout->addWidget(new QLabel("Output synchronized observations epoch by epoch."),4,0,1,2,Qt::AlignLeft);
375 sLayout->addWidget(new QLabel(" "),5,0);
376 sLayout->addWidget(new QLabel(" "),6,0);
377 sgroup->setLayout(sLayout);
378
379 QGridLayout* eLayout = new QGridLayout;
380 eLayout->setColumnMinimumWidth(0,12*ww);
381 eLayout->addWidget(new QLabel("Directory"), 0, 0);
382 eLayout->addWidget(_ephPathLineEdit, 0, 1);
383 eLayout->addWidget(new QLabel("Interval"), 1, 0);
384 eLayout->addWidget(_ephIntrComboBox, 1, 1);
385 eLayout->addWidget(new QLabel("Port"), 2, 0);
386 eLayout->addWidget(_outEphPortLineEdit, 2, 1);
387 eLayout->addWidget(new QLabel("Version 3"), 3, 0);
388 eLayout->addWidget(_ephV3CheckBox, 3, 1);
389 eLayout->addWidget(new QLabel("Saving RINEX ephemeris files and ephemeris output through IP port."),4,0,1,2,Qt::AlignLeft);
390 eLayout->addWidget(new QLabel(" "),5,0);
391 egroup->setLayout(eLayout);
392
393 QGridLayout* aLayout = new QGridLayout;
394 aLayout->setColumnMinimumWidth(0,12*ww);
395 aLayout->setColumnMinimumWidth(1,8*ww);
396 aLayout->setColumnMinimumWidth(2,12*ww);
397 aLayout->setColumnMinimumWidth(3,40*ww);
398 aLayout->addWidget(new QLabel("Observation rate"), 0, 0);
399 aLayout->addWidget(_obsRateComboBox, 0, 1);
400 aLayout->addWidget(new QLabel("Failure threshold"), 1, 0);
401 aLayout->addWidget(_adviseFailSpinBox, 1, 1);
402 aLayout->addWidget(new QLabel("Recovery threshold"), 2, 0);
403 aLayout->addWidget(_adviseRecoSpinBox, 2, 1);
404 aLayout->addWidget(new QLabel("Pause"), 2, 2, Qt::AlignRight);
405 aLayout->addWidget(_makePauseCheckBox, 2, 3, Qt::AlignLeft);
406 aLayout->addWidget(new QLabel("Script (full path)"), 3, 0);
407 aLayout->addWidget(_adviseScriptLineEdit, 3, 1,1,3);
408 aLayout->addWidget(new QLabel("Performance log"), 4, 0);
409 aLayout->addWidget(_perfIntrComboBox, 4, 1);
410 aLayout->addWidget(new QLabel("Network monitoring, outages, handling of corrupted streams, latencies, statistics."),5,0,1,4,Qt::AlignLeft);
411 agroup->setLayout(aLayout);
412
413 QGridLayout* oLayout = new QGridLayout;
414 oLayout->setColumnMinimumWidth(0,12*ww);
415 oLayout->setColumnMinimumWidth(1,8*ww);
416 oLayout->setColumnMinimumWidth(2,12*ww);
417 oLayout->setColumnMinimumWidth(3,40*ww);
418 oLayout->addWidget(new QLabel("Directory"), 0, 0);
419 oLayout->addWidget(_rnxPathLineEdit, 0, 1,1,3);
420 oLayout->addWidget(new QLabel("Interval"), 1, 0);
421 oLayout->addWidget(_rnxIntrComboBox, 1, 1);
422 oLayout->addWidget(new QLabel("Sampling"), 1, 2, Qt::AlignRight);
423 oLayout->addWidget(_rnxSamplSpinBox, 1, 3, Qt::AlignLeft);
424 oLayout->addWidget(new QLabel("Skeleton extension"), 2, 0);
425 oLayout->addWidget(_rnxSkelLineEdit, 2, 1);
426 oLayout->addWidget(new QLabel("Script (full path)"), 3, 0);
427 oLayout->addWidget(_rnxScrpLineEdit, 3, 1, 1, 3);
428 oLayout->addWidget(new QLabel("Version 3"), 4, 0);
429 oLayout->addWidget(_rnxV3CheckBox, 4, 1);
430 oLayout->addWidget(new QLabel("Saving RINEX observation files."),5,0,1,4, Qt::AlignLeft);
431 ogroup->setLayout(oLayout);
432
433 QGridLayout* cLayout = new QGridLayout;
434 cLayout->setColumnMinimumWidth(0,12*ww);
435 cLayout->addWidget(new QLabel("Directory"), 0, 0);
436 cLayout->addWidget(_corrPathLineEdit, 0, 1);
437 cLayout->addWidget(new QLabel("Interval"), 1, 0);
438 cLayout->addWidget(_corrIntrComboBox, 1, 1);
439 cLayout->addWidget(new QLabel("Port"), 2, 0);
440 cLayout->addWidget(_corrPortLineEdit, 2, 1);
441 cLayout->addWidget(new QLabel("Saving ephemeris correction files and correction output through IP port."),3,0,1,2,Qt::AlignLeft);
442 cLayout->addWidget(new QLabel(" "),4,0);
443 cgroup->setLayout(cLayout);
444
445 QVBoxLayout* mLayout = new QVBoxLayout;
446 mLayout->addWidget(aogroup);
447 mLayout->addWidget(_mountPointsTable);
448 mLayout->addWidget(_log);
449
450 _canvas->setLayout(mLayout);
451}
452
453// Destructor
454////////////////////////////////////////////////////////////////////////////
455bncWindow::~bncWindow() {
456 delete _caster;
457}
458
459// Retrieve Table
460////////////////////////////////////////////////////////////////////////////
461void bncWindow::slotAddMountPoints() {
462
463 QSettings settings;
464 QString proxyHost = settings.value("proxyHost").toString();
465 int proxyPort = settings.value("proxyPort").toInt();
466 if (proxyHost != _proxyHostLineEdit->text() ||
467 proxyPort != _proxyPortLineEdit->text().toInt()) {
468 int iRet = QMessageBox::question(this, "Question", "Proxy options "
469 "changed. Use the new ones?",
470 QMessageBox::Yes, QMessageBox::No,
471 QMessageBox::NoButton);
472 if (iRet == QMessageBox::Yes) {
473 settings.setValue("proxyHost", _proxyHostLineEdit->text());
474 settings.setValue("proxyPort", _proxyPortLineEdit->text());
475 }
476 }
477
478 bncTableDlg* dlg = new bncTableDlg(this);
479 dlg->move(this->pos().x()+50, this->pos().y()+50);
480 connect(dlg, SIGNAL(newMountPoints(QStringList*)),
481 this, SLOT(slotNewMountPoints(QStringList*)));
482 dlg->exec();
483 delete dlg;
484
485}
486
487// Delete Selected Mount Points
488////////////////////////////////////////////////////////////////////////////
489void bncWindow::slotDeleteMountPoints() {
490
491 int nRows = _mountPointsTable->rowCount();
492 bool flg[nRows];
493 for (int iRow = 0; iRow < nRows; iRow++) {
494 if (_mountPointsTable->isItemSelected(_mountPointsTable->item(iRow,1))) {
495 flg[iRow] = true;
496 }
497 else {
498 flg[iRow] = false;
499 }
500 }
501 for (int iRow = nRows-1; iRow >= 0; iRow--) {
502 if (flg[iRow]) {
503 _mountPointsTable->removeRow(iRow);
504 }
505 }
506 _actDeleteMountPoints->setEnabled(false);
507
508 if (_mountPointsTable->rowCount() == 0) {
509 _actGetData->setEnabled(false);
510 }
511}
512
513// New Mount Points Selected
514////////////////////////////////////////////////////////////////////////////
515void bncWindow::slotNewMountPoints(QStringList* mountPoints) {
516 int iRow = 0;
517 QListIterator<QString> it(*mountPoints);
518 while (it.hasNext()) {
519 QStringList hlp = it.next().split(" ");
520 QUrl url(hlp[0]);
521 QString fullPath = url.host() + QString(":%1").arg(url.port()) + url.path();
522 QString format(hlp[1]); QString latitude(hlp[2]); QString longitude(hlp[3]);
523 QString nmea(hlp[4]);
524
525 _mountPointsTable->insertRow(iRow);
526
527 QTableWidgetItem* it;
528 it = new QTableWidgetItem(url.userInfo());
529 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
530 _mountPointsTable->setItem(iRow, 0, it);
531
532 it = new QTableWidgetItem(fullPath);
533 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
534 _mountPointsTable->setItem(iRow, 1, it);
535
536 it = new QTableWidgetItem(format);
537 _mountPointsTable->setItem(iRow, 2, it);
538
539 if (nmea == "yes") {
540 it = new QTableWidgetItem(latitude);
541 _mountPointsTable->setItem(iRow, 3, it);
542 it = new QTableWidgetItem(longitude);
543 _mountPointsTable->setItem(iRow, 4, it);
544 } else {
545 it = new QTableWidgetItem(latitude);
546 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
547 _mountPointsTable->setItem(iRow, 3, it);
548 it = new QTableWidgetItem(longitude);
549 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
550 _mountPointsTable->setItem(iRow, 4, it);
551 }
552
553 it = new QTableWidgetItem(nmea);
554 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
555 _mountPointsTable->setItem(iRow, 5, it);
556
557 bncTableItem* bncIt = new bncTableItem();
558 _mountPointsTable->setItem(iRow, 6, bncIt);
559
560 iRow++;
561 }
562 _mountPointsTable->hideColumn(0);
563 _mountPointsTable->sortItems(1);
564 if (mountPoints->count() > 0) {
565 _actGetData->setEnabled(true);
566 }
567 delete mountPoints;
568}
569
570// Save Options
571////////////////////////////////////////////////////////////////////////////
572void bncWindow::slotSaveOptions() {
573 QSettings settings;
574 settings.setValue("proxyHost", _proxyHostLineEdit->text());
575 settings.setValue("proxyPort", _proxyPortLineEdit->text());
576 settings.setValue("waitTime", _waitTimeSpinBox->value());
577 settings.setValue("obsRate", _obsRateComboBox->currentText());
578 settings.setValue("adviseFail", _adviseFailSpinBox->value());
579 settings.setValue("adviseReco", _adviseRecoSpinBox->value());
580 settings.setValue("makePause", _makePauseCheckBox->checkState());
581 settings.setValue("outFile", _outFileLineEdit->text());
582 settings.setValue("perfIntr", _perfIntrComboBox->currentText());
583 settings.setValue("outPort", _outPortLineEdit->text());
584 settings.setValue("outEphPort", _outEphPortLineEdit->text());
585 settings.setValue("corrPort", _corrPortLineEdit->text());
586 settings.setValue("rnxPath", _rnxPathLineEdit->text());
587 settings.setValue("ephPath", _ephPathLineEdit->text());
588 settings.setValue("corrPath", _corrPathLineEdit->text());
589 settings.setValue("rnxScript", _rnxScrpLineEdit->text());
590 settings.setValue("rnxIntr", _rnxIntrComboBox->currentText());
591 settings.setValue("ephIntr", _ephIntrComboBox->currentText());
592 settings.setValue("corrIntr", _corrIntrComboBox->currentText());
593 settings.setValue("rnxSampl", _rnxSamplSpinBox->value());
594 settings.setValue("binSampl", _binSamplSpinBox->value());
595 settings.setValue("rnxSkel", _rnxSkelLineEdit->text());
596 settings.setValue("rnxAppend", _rnxAppendCheckBox->checkState());
597 settings.setValue("rnxV3", _rnxV3CheckBox->checkState());
598 settings.setValue("ephV3", _ephV3CheckBox->checkState());
599 settings.setValue("logFile", _logFileLineEdit->text());
600 settings.setValue("adviseScript",_adviseScriptLineEdit->text());
601
602QStringList mountPoints;
603
604 for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
605 QUrl url( "//" + _mountPointsTable->item(iRow, 0)->text() +
606 "@" + _mountPointsTable->item(iRow, 1)->text() );
607
608 mountPoints.append(url.toString() + " " +
609 _mountPointsTable->item(iRow, 2)->text()
610 + " " + _mountPointsTable->item(iRow, 3)->text()
611 + " " + _mountPointsTable->item(iRow, 4)->text()
612 + " " + _mountPointsTable->item(iRow, 5)->text());
613 }
614 settings.setValue("mountPoints", mountPoints);
615}
616
617// All get slots terminated
618////////////////////////////////////////////////////////////////////////////
619void bncWindow::slotGetThreadErrors() {
620 slotMessage("All Get Threads Terminated");
621 ((bncApp*)qApp)->slotMessage("All Get Threads Terminated");
622 _actAddMountPoints->setEnabled(true);
623 _actGetData->setEnabled(true);
624}
625
626// Retrieve Data
627////////////////////////////////////////////////////////////////////////////
628void bncWindow::slotGetData() {
629 slotSaveOptions();
630
631 _actAddMountPoints->setEnabled(false);
632 _actDeleteMountPoints->setEnabled(false);
633 _actGetData->setEnabled(false);
634 _actStop->setEnabled(true);
635
636 _caster = new bncCaster(_outFileLineEdit->text(),
637 _outPortLineEdit->text().toInt());
638
639 ((bncApp*)qApp)->setPort(_outEphPortLineEdit->text().toInt());
640 ((bncApp*)qApp)->setPortCorr(_corrPortLineEdit->text().toInt());
641
642 connect(_caster, SIGNAL(getThreadErrors()),
643 this, SLOT(slotGetThreadErrors()));
644
645 connect(_caster, SIGNAL(newMessage(QByteArray)),
646 this, SLOT(slotMessage(QByteArray)));
647 connect(_caster, SIGNAL(newMessage(QByteArray)),
648 (bncApp*)qApp, SLOT(slotMessage(QByteArray)));
649
650 slotMessage("============ Start BNC ============");
651 ((bncApp*)qApp)->slotMessage("============ Start BNC ============");
652
653 for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
654 QUrl url( "//" + _mountPointsTable->item(iRow, 0)->text() +
655 "@" + _mountPointsTable->item(iRow, 1)->text() );
656
657 QByteArray format = _mountPointsTable->item(iRow, 2)->text().toAscii();
658
659 QByteArray latitude = _mountPointsTable->item(iRow, 3)->text().toAscii();
660 QByteArray longitude = _mountPointsTable->item(iRow, 4)->text().toAscii();
661 QByteArray nmea = _mountPointsTable->item(iRow, 5)->text().toAscii();
662
663 bncGetThread* getThread = new bncGetThread(url, format, latitude, longitude, nmea, iRow);
664
665 connect(getThread, SIGNAL(newMessage(QByteArray)),
666 this, SLOT(slotMessage(QByteArray)));
667 connect(getThread, SIGNAL(newMessage(QByteArray)),
668 (bncApp*)qApp, SLOT(slotMessage(QByteArray)));
669
670 connect(getThread, SIGNAL(newBytes(QByteArray, double)),
671 (bncTableItem*) _mountPointsTable->item(iRow, 6),
672 SLOT(slotNewBytes(QByteArray, double)));
673
674 _caster->addGetThread(getThread);
675
676 getThread->start();
677 }
678}
679
680// Retrieve Data
681////////////////////////////////////////////////////////////////////////////
682void bncWindow::slotStop() {
683 int iRet = QMessageBox::question(this, "Stop", "Stop retrieving data?",
684 QMessageBox::Yes, QMessageBox::No,
685 QMessageBox::NoButton);
686 if (iRet == QMessageBox::Yes) {
687 delete _caster; _caster = 0;
688 _actGetData->setEnabled(true);
689 _actStop->setEnabled(false);
690 _actAddMountPoints->setEnabled(true);
691 }
692}
693
694// Close Application gracefully
695////////////////////////////////////////////////////////////////////////////
696void bncWindow::closeEvent(QCloseEvent* event) {
697
698 int iRet = QMessageBox::question(this, "Close", "Save Options?",
699 QMessageBox::Yes, QMessageBox::No,
700 QMessageBox::Cancel);
701
702 if (iRet == QMessageBox::Cancel) {
703 event->ignore();
704 return;
705 }
706 else if (iRet == QMessageBox::Yes) {
707 slotSaveOptions();
708 }
709
710 QMainWindow::closeEvent(event);
711}
712
713// User changed the selection of mountPoints
714////////////////////////////////////////////////////////////////////////////
715void bncWindow::slotSelectionChanged() {
716 if (_mountPointsTable->selectedItems().isEmpty()) {
717 _actDeleteMountPoints->setEnabled(false);
718 }
719 else {
720 _actDeleteMountPoints->setEnabled(true);
721 }
722}
723
724// Display Program Messages
725////////////////////////////////////////////////////////////////////////////
726void bncWindow::slotMessage(const QByteArray msg) {
727
728 const int maxBufferSize = 10000;
729
730 QString txt = _log->toPlainText() + "\n" +
731 QDateTime::currentDateTime().toUTC().toString("yy-MM-dd hh:mm:ss ") + msg;
732 _log->clear();
733 _log->append(txt.right(maxBufferSize));
734}
735
736// About Message
737////////////////////////////////////////////////////////////////////////////
738void bncWindow::slotAbout() {
739 new bncAboutDlg(0);
740}
741
742// Help Window
743////////////////////////////////////////////////////////////////////////////
744void bncWindow::slotHelp() {
745 QUrl url;
746 url.setPath(":bnchelp.html");
747 new bncHlpDlg(0, url);
748}
749
750// Select Fonts
751////////////////////////////////////////////////////////////////////////////
752void bncWindow::slotFontSel() {
753 bool ok;
754 QFont newFont = QFontDialog::getFont(&ok, this->font(), this);
755 if (ok) {
756 QSettings settings;
757 settings.setValue("font", newFont.toString());
758 QApplication::setFont(newFont);
759 int ww = QFontMetrics(newFont).width('w');
760 setMinimumSize(60*ww, 80*ww);
761 resize(60*ww, 80*ww);
762 }
763}
764
765// Whats This Help
766void bncWindow::slotWhatsThis() {
767QWhatsThis::enterWhatsThisMode();
768}
769
770void bncWindow::CreateMenu() {
771 // Create Menus
772 // ------------
773 _menuFile = menuBar()->addMenu(tr("&File"));
774 _menuFile->addAction(_actFontSel);
775 _menuFile->addSeparator();
776 _menuFile->addAction(_actSaveOpt);
777 _menuFile->addSeparator();
778 _menuFile->addAction(_actQuit);
779
780 _menuHlp = menuBar()->addMenu(tr("&Help"));
781 _menuHlp->addAction(_actHelp);
782 _menuHlp->addAction(_actAbout);
783
784}
785
786void bncWindow::AddToolbar() {
787 // Tool (Command) Bar
788 // ------------------
789 QToolBar* toolBar = new QToolBar;
790 addToolBar(Qt::BottomToolBarArea, toolBar);
791 toolBar->setMovable(false);
792 toolBar->addAction(_actAddMountPoints);
793 toolBar->addAction(_actDeleteMountPoints);
794 toolBar->addAction(_actGetData);
795 toolBar->addAction(_actStop);
796 toolBar->addWidget(new QLabel(" "));
797 toolBar->addAction(_actwhatsthis);
798}
799
800bncAboutDlg::bncAboutDlg(QWidget* parent) :
801 QDialog(parent) {
802
803 QTextBrowser* tb = new QTextBrowser;
804 QUrl url; url.setPath(":bncabout.html");
805 tb->setSource(url);
806 tb->setReadOnly(true);
807
808 int ww = QFontMetrics(font()).width('w');
809 QPushButton* _closeButton = new QPushButton("Close");
810 _closeButton->setMaximumWidth(10*ww);
811 connect(_closeButton, SIGNAL(clicked()), this, SLOT(close()));
812
813 QGridLayout* dlgLayout = new QGridLayout();
814 QLabel* img = new QLabel();
815 img->setPixmap(QPixmap(":ntrip-logo.png"));
816 dlgLayout->addWidget(img, 0,0);
817 dlgLayout->addWidget(new QLabel("BKG NTRIP Client (BNC) Version 1.6"), 0,1);
818 dlgLayout->addWidget(tb,1,0,1,2);
819 dlgLayout->addWidget(_closeButton,2,1,Qt::AlignRight);
820
821 setLayout(dlgLayout);
822 resize(60*ww, 60*ww);
823 show();
824}
825
826bncAboutDlg::~bncAboutDlg() {
827};
828
Note: See TracBrowser for help on using the repository browser.