Switch to unified view

a b/cfgui/confgui.cpp
1
/* Copyright (C) 2005-2016 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
18
#include "confgui.h"
19
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <vector>
23
#include <iostream>
24
#include <algorithm>
25
26
#include <qglobal.h>
27
#include <QHBoxLayout>
28
#include <QVBoxLayout>
29
#include <QTabWidget>
30
#include <QDialogButtonBox>
31
#include <QFrame>
32
#include <QListWidget>
33
#include <QFileDialog>
34
#include <QDebug>
35
#include <QDir>
36
#include <qobject.h>
37
#include <qlayout.h>
38
#include <qsize.h>
39
#include <qsizepolicy.h>
40
#include <qlabel.h>
41
#include <qspinbox.h>
42
#include <qtooltip.h>
43
#include <qlineedit.h>
44
#include <qcheckbox.h>
45
#include <qinputdialog.h>
46
#include <qpushbutton.h>
47
#include <qstringlist.h>
48
#include <qcombobox.h>
49
50
#include "smallut.h"
51
52
#ifdef ENABLE_XMLCONF
53
#include "picoxml.h"
54
#endif
55
56
using namespace std;
57
58
namespace confgui {
59
60
static const int spacing = 3;
61
// left,top,right, bottom
62
static QMargins margin(4,3,4,3);
63
64
ConfTabsW::ConfTabsW(QWidget *parent, const QString& title,
65
                     ConfLinkFact *fact)
66
    : QDialog(parent), m_makelink(fact)
67
{
68
    setWindowTitle(title);
69
    tabWidget = new QTabWidget;
70
71
    buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
72
                                     | QDialogButtonBox::Cancel);
73
74
    QVBoxLayout *mainLayout = new QVBoxLayout;
75
    mainLayout->setSpacing(spacing);
76
    mainLayout->setContentsMargins(margin);
77
    mainLayout->addWidget(tabWidget);
78
    mainLayout->addWidget(buttonBox);
79
    setLayout(mainLayout);
80
81
    resize(QSize(500, 400).expandedTo(minimumSizeHint()));
82
83
    connect(buttonBox, SIGNAL(accepted()), this, SLOT(acceptChanges()));
84
    connect(buttonBox, SIGNAL(rejected()), this, SLOT(rejectChanges()));
85
}
86
87
void ConfTabsW::hideButtons()
88
{
89
    if (buttonBox)
90
        buttonBox->hide();
91
}
92
93
void ConfTabsW::acceptChanges()
94
{
95
    cerr << "ConfTabsW::acceptChanges()\n";
96
    for (auto& entry : m_panels) {
97
        entry->storeValues();
98
    }
99
    for (auto& entry : m_widgets) {
100
        entry->storeValues();
101
    }
102
    emit sig_prefsChanged();
103
    if (!buttonBox->isHidden())
104
        close();
105
}
106
107
void ConfTabsW::rejectChanges()
108
{
109
    cerr << "ConfTabsW::rejectChanges()\n";
110
    reloadPanels();
111
    if (!buttonBox->isHidden())
112
        close();
113
}
114
115
void ConfTabsW::reloadPanels()
116
{
117
    for (auto& entry : m_panels) {
118
        entry->loadValues();
119
    }
120
    for (auto& entry : m_widgets) {
121
        entry->loadValues();
122
    }
123
}
124
125
int ConfTabsW::addPanel(const QString& title)
126
{
127
    ConfPanelW *w = new ConfPanelW(this);
128
    m_panels.push_back(w);
129
    return tabWidget->addTab(w, title);
130
}
131
132
int ConfTabsW::addForeignPanel(ConfPanelWIF* w, const QString& title)
133
{
134
    m_widgets.push_back(w);
135
    QWidget *qw = dynamic_cast<QWidget *>(w);
136
    if (qw == 0) {
137
        qDebug() << "Can't cast panel to QWidget";
138
        abort();
139
    }
140
    return tabWidget->addTab(qw, title);
141
}
142
143
void ConfTabsW::setCurrentIndex(int idx)
144
{
145
    if (tabWidget) {
146
        tabWidget->setCurrentIndex(idx);
147
    }
148
}
149
150
ConfParamW *ConfTabsW::addParam(
151
    int tabindex, ParamType tp, const QString& varname,
152
    const QString& label, const QString& tooltip,
153
    int ival, int maxval, const QStringList* sl)
154
{
155
    ConfLink lnk = (*m_makelink)(varname);
156
157
    ConfPanelW *panel = (ConfPanelW*)tabWidget->widget(tabindex);
158
    if (panel == 0) {
159
        return 0;
160
    }
161
162
    ConfParamW *cp = 0;
163
    switch (tp) {
164
    case CFPT_BOOL:
165
        cp = new ConfParamBoolW(varname, this, lnk, label, tooltip, ival);
166
        break;
167
    case CFPT_INT: {
168
        size_t v = (size_t)sl;
169
        int v1 = (v & 0xffffffff);
170
        cp = new ConfParamIntW(varname, this, lnk, label, tooltip, ival,
171
                               maxval, v1);
172
        break;
173
    }
174
    case CFPT_STR:
175
        cp = new ConfParamStrW(varname, this, lnk, label, tooltip);
176
        break;
177
    case CFPT_CSTR:
178
        cp = new ConfParamCStrW(varname, this, lnk, label, tooltip, *sl);
179
        break;
180
    case CFPT_FN:
181
        cp = new ConfParamFNW(varname, this, lnk, label, tooltip, ival);
182
        break;
183
    case CFPT_STRL:
184
        cp = new ConfParamSLW(varname, this, lnk, label, tooltip);
185
    case CFPT_DNL:
186
        cp = new ConfParamDNLW(varname, this, lnk, label, tooltip);
187
        break;
188
    case CFPT_CSTRL:
189
        cp = new ConfParamCSLW(varname, this, lnk, label, tooltip, *sl);
190
        break;
191
192
    }
193
    panel->addWidget(cp);
194
    m_params.push_back(cp);
195
    return cp;
196
}
197
198
ConfParamW *ConfTabsW::findParamW(const QString& varname)
199
{
200
    for (vector<ConfParamW *>::iterator it = m_params.begin();
201
            it != m_params.end(); it++) {
202
        if (!varname.compare((*it)->getVarName())) {
203
            return *it;
204
        }
205
    }
206
    return 0;
207
}
208
void ConfTabsW::endOfList(int tabindex)
209
{
210
    ConfPanelW *panel = (ConfPanelW*)tabWidget->widget(tabindex);
211
    if (panel == 0) 
212
        return;
213
    panel->endOfList();
214
}
215
216
bool ConfTabsW::enableLink(ConfParamW* boolw, ConfParamW* otherw, bool revert)
217
{
218
    if (std::find(m_params.begin(), m_params.end(), boolw) == m_params.end() ||
219
            std::find(m_params.begin(), m_params.end(), otherw) ==
220
        m_params.end()) {
221
        cerr << "ConfTabsW::enableLink: param not found\n";
222
        return false;
223
    }
224
    ConfParamBoolW *bw = dynamic_cast<ConfParamBoolW*>(boolw);
225
    if (bw == 0) {
226
        cerr << "ConfTabsW::enableLink: not a boolw\n";
227
        return false;
228
    }
229
    otherw->setEnabled(revert ? !bw->m_cb->isChecked() : bw->m_cb->isChecked());
230
    if (revert) {
231
        connect(bw->m_cb, SIGNAL(toggled(bool)),
232
                otherw, SLOT(setDisabled(bool)));
233
    } else {
234
        connect(bw->m_cb, SIGNAL(toggled(bool)),
235
                otherw, SLOT(setEnabled(bool)));
236
    }
237
    return true;
238
}
239
240
ConfPanelW::ConfPanelW(QWidget *parent)
241
    : QWidget(parent)
242
{
243
    m_vboxlayout = new QVBoxLayout(this);
244
    m_vboxlayout->setSpacing(spacing);
245
    m_vboxlayout->setAlignment(Qt::AlignTop);
246
    m_vboxlayout->setContentsMargins(margin);
247
}
248
249
void ConfPanelW::addWidget(QWidget *w)
250
{
251
    m_vboxlayout->addWidget(w);
252
    m_widgets.push_back(w);
253
}
254
255
void ConfPanelW::endOfList()
256
{
257
    m_vboxlayout->addStretch(2);
258
}
259
260
void ConfPanelW::storeValues()
261
{
262
    for (vector<QWidget *>::iterator it = m_widgets.begin();
263
            it != m_widgets.end(); it++) {
264
        ConfParamW *p = (ConfParamW*)*it;
265
        p->storeValue();
266
    }
267
}
268
269
void ConfPanelW::loadValues()
270
{
271
    for (vector<QWidget *>::iterator it = m_widgets.begin();
272
            it != m_widgets.end(); it++) {
273
        ConfParamW *p = (ConfParamW*)*it;
274
        p->loadValue();
275
    }
276
}
277
static QString myGetFileName(bool isdir, QString caption = QString(),
278
                             bool filenosave = false);
279
280
static QString myGetFileName(bool isdir, QString caption, bool filenosave)
281
{
282
    QFileDialog dialog(0, caption);
283
284
    if (isdir) {
285
        dialog.setFileMode(QFileDialog::Directory);
286
        dialog.setOptions(QFileDialog::ShowDirsOnly);
287
    } else {
288
        dialog.setFileMode(QFileDialog::AnyFile);
289
        if (filenosave) {
290
            dialog.setAcceptMode(QFileDialog::AcceptOpen);
291
        } else {
292
            dialog.setAcceptMode(QFileDialog::AcceptSave);
293
        }
294
    }
295
    dialog.setViewMode(QFileDialog::List);
296
    QFlags<QDir::Filter> flags = QDir::NoDotAndDotDot | QDir::Hidden;
297
    if (isdir) {
298
        flags |= QDir::Dirs;
299
    } else {
300
        flags |= QDir::Dirs | QDir::Files;
301
    }
302
    dialog.setFilter(flags);
303
304
    if (dialog.exec() == QDialog::Accepted) {
305
        return dialog.selectedFiles().value(0);
306
    }
307
    return QString();
308
}
309
310
void ConfParamW::setValue(const QString& value)
311
{
312
    if (m_fsencoding) {
313
        m_cflink->set(string((const char *)value.toLocal8Bit()));
314
    } else {
315
        m_cflink->set(string((const char *)value.toUtf8()));
316
    }
317
}
318
319
void ConfParamW::setValue(int value)
320
{
321
    char buf[30];
322
    sprintf(buf, "%d", value);
323
    m_cflink->set(string(buf));
324
}
325
326
void ConfParamW::setValue(bool value)
327
{
328
    char buf[30];
329
    sprintf(buf, "%d", value);
330
    m_cflink->set(string(buf));
331
}
332
333
extern void setSzPol(QWidget *w, QSizePolicy::Policy hpol,
334
                     QSizePolicy::Policy vpol,
335
                     int hstretch, int vstretch);
336
337
void setSzPol(QWidget *w, QSizePolicy::Policy hpol,
338
              QSizePolicy::Policy vpol,
339
              int hstretch, int vstretch)
340
{
341
    QSizePolicy policy(hpol, vpol);
342
    policy.setHorizontalStretch(hstretch);
343
    policy.setVerticalStretch(vstretch);
344
    policy.setHeightForWidth(w->sizePolicy().hasHeightForWidth());
345
    w->setSizePolicy(policy);
346
}
347
348
bool ConfParamW::createCommon(const QString& lbltxt, const QString& tltptxt)
349
{
350
    m_hl = new QHBoxLayout(this);
351
    m_hl->setSpacing(spacing);
352
    m_hl->setContentsMargins(margin);
353
354
    QLabel *tl = new QLabel(this);
355
    setSzPol(tl, QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 0);
356
    tl->setText(lbltxt);
357
    tl->setToolTip(tltptxt);
358
359
    m_hl->addWidget(tl);
360
361
    return true;
362
}
363
364
ConfParamIntW::ConfParamIntW(
365
    const QString& varnm, QWidget *parent, ConfLink cflink,
366
    const QString& lbltxt, const QString& tltptxt,
367
    int minvalue, int maxvalue, int defaultvalue)
368
    : ConfParamW(varnm, parent, cflink), m_defaultvalue(defaultvalue)
369
{
370
    if (!createCommon(lbltxt, tltptxt)) {
371
        return;
372
    }
373
374
    m_sb = new QSpinBox(this);
375
    m_sb->setMinimum(minvalue);
376
    m_sb->setMaximum(maxvalue);
377
    setSzPol(m_sb, QSizePolicy::Fixed, QSizePolicy::Fixed, 0, 0);
378
    m_hl->addWidget(m_sb);
379
380
    QFrame *fr = new QFrame(this);
381
    setSzPol(fr, QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 0);
382
    m_hl->addWidget(fr);
383
384
    loadValue();
385
}
386
387
void ConfParamIntW::storeValue()
388
{
389
    if (m_origvalue != m_sb->value()) {
390
        setValue(m_sb->value());
391
    }
392
}
393
394
void ConfParamIntW::loadValue()
395
{
396
    string s;
397
    if (m_cflink->get(s)) {
398
        m_sb->setValue(m_origvalue = atoi(s.c_str()));
399
    } else {
400
        m_sb->setValue(m_origvalue = m_defaultvalue);
401
    }
402
}
403
404
ConfParamStrW::ConfParamStrW(
405
    const QString& varnm, QWidget *parent, ConfLink cflink,
406
    const QString& lbltxt, const QString& tltptxt)
407
    : ConfParamW(varnm, parent, cflink)
408
{
409
    if (!createCommon(lbltxt, tltptxt)) {
410
        return;
411
    }
412
413
    m_le = new QLineEdit(this);
414
    setSzPol(m_le, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0);
415
416
    m_hl->addWidget(m_le);
417
418
    loadValue();
419
}
420
421
void ConfParamStrW::storeValue()
422
{
423
    if (m_origvalue.compare(m_le->text())) {
424
        setValue(m_le->text());
425
    }
426
}
427
428
void ConfParamStrW::loadValue()
429
{
430
    string s;
431
    m_cflink->get(s);
432
    if (m_fsencoding) {
433
        m_le->setText(m_origvalue = QString::fromLocal8Bit(s.c_str()));
434
    } else {
435
        m_le->setText(m_origvalue = QString::fromUtf8(s.c_str()));
436
    }
437
}
438
439
ConfParamCStrW::ConfParamCStrW(
440
    const QString& varnm, QWidget *parent, ConfLink cflink,
441
    const QString& lbltxt, const QString& tltptxt, const QStringList& sl)
442
    : ConfParamW(varnm, parent, cflink)
443
{
444
    if (!createCommon(lbltxt, tltptxt)) {
445
        return;
446
    }
447
    m_cmb = new QComboBox(this);
448
    m_cmb->setEditable(false);
449
    m_cmb->insertItems(0, sl);
450
451
    setSzPol(m_cmb, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0);
452
453
    m_hl->addWidget(m_cmb);
454
455
    loadValue();
456
}
457
458
void ConfParamCStrW::setList(const QStringList& sl)
459
{
460
    m_cmb->clear();
461
    m_cmb->insertItems(0, sl);
462
    loadValue();
463
}
464
465
void ConfParamCStrW::storeValue()
466
{
467
    if (m_origvalue.compare(m_cmb->currentText())) {
468
        setValue(m_cmb->currentText());
469
    }
470
}
471
472
void ConfParamCStrW::loadValue()
473
{
474
    string s;
475
    m_cflink->get(s);
476
    QString cs;
477
    if (m_fsencoding) {
478
        cs = QString::fromLocal8Bit(s.c_str());
479
    } else {
480
        cs = QString::fromUtf8(s.c_str());
481
    }
482
483
    for (int i = 0; i < m_cmb->count(); i++) {
484
        if (!cs.compare(m_cmb->itemText(i))) {
485
            m_cmb->setCurrentIndex(i);
486
            break;
487
        }
488
    }
489
    m_origvalue = m_cmb->currentText();
490
}
491
492
ConfParamBoolW::ConfParamBoolW(
493
    const QString& varnm, QWidget *parent, ConfLink cflink,
494
    const QString& lbltxt, const QString& tltptxt, bool deflt)
495
    : ConfParamW(varnm, parent, cflink), m_dflt(deflt)
496
{
497
    // No createCommon because the checkbox has a label
498
    m_hl = new QHBoxLayout(this);
499
    m_hl->setSpacing(spacing);
500
    m_hl->setContentsMargins(margin);
501
502
    m_cb = new QCheckBox(lbltxt, this);
503
    setSzPol(m_cb, QSizePolicy::Fixed, QSizePolicy::Fixed, 0, 0);
504
    m_cb->setToolTip(tltptxt);
505
    m_hl->addWidget(m_cb);
506
507
    QFrame *fr = new QFrame(this);
508
    setSzPol(fr, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0);
509
    m_hl->addWidget(fr);
510
511
    loadValue();
512
}
513
514
void ConfParamBoolW::storeValue()
515
{
516
    if (m_origvalue != m_cb->isChecked()) {
517
        setValue(m_cb->isChecked());
518
    }
519
}
520
521
void ConfParamBoolW::loadValue()
522
{
523
    string s;
524
    if (!m_cflink->get(s)) {
525
        m_origvalue = m_dflt;
526
    } else {
527
        m_origvalue = stringToBool(s);
528
    }
529
    m_cb->setChecked(m_origvalue);
530
}
531
532
ConfParamFNW::ConfParamFNW(
533
    const QString& varnm, QWidget *parent, ConfLink cflink,
534
    const QString& lbltxt, const QString& tltptxt, bool isdir)
535
    : ConfParamW(varnm, parent, cflink), m_isdir(isdir)
536
{
537
    if (!createCommon(lbltxt, tltptxt)) {
538
        return;
539
    }
540
541
    m_fsencoding = true;
542
543
    m_le = new QLineEdit(this);
544
    m_le->setMinimumSize(QSize(150, 0));
545
    setSzPol(m_le, QSizePolicy::Preferred, QSizePolicy::Fixed, 1, 0);
546
    m_hl->addWidget(m_le);
547
548
    m_pb = new QPushButton(this);
549
550
    QString text = tr("Choose");
551
    m_pb->setText(text);
552
    int width = m_pb->fontMetrics().boundingRect(text).width() + 15;
553
    m_pb->setMaximumWidth(width);
554
    setSzPol(m_pb, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0);
555
    m_hl->addWidget(m_pb);
556
557
    loadValue();
558
    QObject::connect(m_pb, SIGNAL(clicked()), this, SLOT(showBrowserDialog()));
559
}
560
561
void ConfParamFNW::storeValue()
562
{
563
    if (m_origvalue.compare(m_le->text())) {
564
        setValue(m_le->text());
565
    }
566
}
567
568
void ConfParamFNW::loadValue()
569
{
570
    string s;
571
    m_cflink->get(s);
572
    m_le->setText(m_origvalue = QString::fromLocal8Bit(s.c_str()));
573
}
574
575
void ConfParamFNW::showBrowserDialog()
576
{
577
    QString s = myGetFileName(m_isdir);
578
    if (!s.isEmpty()) {
579
        m_le->setText(s);
580
    }
581
}
582
583
class SmallerListWidget: public QListWidget {
584
public:
585
    SmallerListWidget(QWidget *parent)
586
        : QListWidget(parent) {}
587
    virtual QSize sizeHint() const {
588
        return QSize(150, 40);
589
    }
590
};
591
592
ConfParamSLW::ConfParamSLW(
593
    const QString& varnm, QWidget *parent, ConfLink cflink,
594
    const QString& lbltxt, const QString& tltptxt)
595
    : ConfParamW(varnm, parent, cflink)
596
{
597
    // Can't use createCommon here cause we want the buttons below the label
598
    m_hl = new QHBoxLayout(this);
599
    m_hl->setSpacing(spacing);
600
    m_hl->setContentsMargins(margin);
601
602
    QVBoxLayout *vl1 = new QVBoxLayout();
603
    vl1->setSpacing(spacing);
604
    vl1->setContentsMargins(margin);
605
    QHBoxLayout *hl1 = new QHBoxLayout();
606
    hl1->setSpacing(spacing);
607
    hl1->setContentsMargins(margin);
608
609
    QLabel *tl = new QLabel(this);
610
    setSzPol(tl, QSizePolicy::Preferred, QSizePolicy::Fixed, 0, 0);
611
    tl->setText(lbltxt);
612
    tl->setToolTip(tltptxt);
613
    vl1->addWidget(tl);
614
615
    QPushButton *pbA = new QPushButton(this);
616
    QString text = tr("+");
617
    pbA->setText(text);
618
    int width = pbA->fontMetrics().boundingRect(text).width() + 15;
619
    pbA->setMaximumWidth(width);
620
    setSzPol(pbA, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0);
621
    hl1->addWidget(pbA);
622
623
    QPushButton *pbD = new QPushButton(this);
624
    text = tr("-");
625
    pbD->setText(text);
626
    width = pbD->fontMetrics().boundingRect(text).width() + 15;
627
    pbD->setMaximumWidth(width);
628
    setSzPol(pbD, QSizePolicy::Minimum, QSizePolicy::Fixed, 0, 0);
629
    hl1->addWidget(pbD);
630
631
    vl1->addLayout(hl1);
632
    m_hl->addLayout(vl1);
633
634
    m_lb = new SmallerListWidget(this);
635
    m_lb->setSelectionMode(QAbstractItemView::ExtendedSelection);
636
    setSzPol(m_lb, QSizePolicy::Preferred, QSizePolicy::Preferred, 1, 1);
637
    m_hl->addWidget(m_lb);
638
639
    setSzPol(this, QSizePolicy::Preferred, QSizePolicy::Preferred, 1, 1);
640
    loadValue();
641
    QObject::connect(pbA, SIGNAL(clicked()), this, SLOT(showInputDialog()));
642
    QObject::connect(pbD, SIGNAL(clicked()), this, SLOT(deleteSelected()));
643
}
644
645
void ConfParamSLW::storeValue()
646
{
647
    vector<string> ls;
648
    for (int i = 0; i < m_lb->count(); i++) {
649
        // General parameters are encoded as utf-8. File names as
650
        // local8bit There is no hope for 8bit file names anyway
651
        // except for luck: the original encoding is unknown.
652
        QString text = m_lb->item(i)->text();
653
        if (m_fsencoding) {
654
            ls.push_back((const char *)(text.toLocal8Bit()));
655
        } else {
656
            ls.push_back((const char *)(text.toUtf8()));
657
        }
658
    }
659
    string s;
660
    stringsToString(ls, s);
661
    if (s.compare(m_origvalue)) {
662
        m_cflink->set(s);
663
    }
664
}
665
666
void ConfParamSLW::loadValue()
667
{
668
    m_origvalue.clear();
669
    m_cflink->get(m_origvalue);
670
    vector<string> ls;
671
    stringToStrings(m_origvalue, ls);
672
    QStringList qls;
673
    for (vector<string>::const_iterator it = ls.begin(); it != ls.end(); it++) {
674
        if (m_fsencoding) {
675
            qls.push_back(QString::fromLocal8Bit(it->c_str()));
676
        } else {
677
            qls.push_back(QString::fromUtf8(it->c_str()));
678
        }
679
    }
680
    m_lb->clear();
681
    m_lb->insertItems(0, qls);
682
}
683
684
void ConfParamSLW::showInputDialog()
685
{
686
    bool ok;
687
    QString s = QInputDialog::getText(this,
688
                                      "", // title
689
                                      "", // label,
690
                                      QLineEdit::Normal, // EchoMode mode
691
                                      "", // const QString & text
692
                                      &ok);
693
694
    if (ok && !s.isEmpty()) {
695
        QList<QListWidgetItem *>items =
696
            m_lb->findItems(s, Qt::MatchFixedString | Qt::MatchCaseSensitive);
697
        if (items.empty()) {
698
            m_lb->insertItem(0, s);
699
            m_lb->sortItems();
700
        }
701
    }
702
}
703
704
void ConfParamSLW::deleteSelected()
705
{
706
    // We used to repeatedly go through the list and delete the first
707
    // found selected item (then restart from the beginning). But it
708
    // seems (probably depends on the qt version), that, when deleting
709
    // a selected item, qt will keep the selection active at the same
710
    // index (now containing the next item), so that we'd end up
711
    // deleting the whole list.
712
    //
713
    // Instead, we now build a list of indices, and delete it starting
714
    // from the top so as not to invalidate lower indices
715
716
    vector<int> idxes;
717
    for (int i = 0; i < m_lb->count(); i++) {
718
        if (m_lb->item(i)->isSelected()) {
719
            idxes.push_back(i);
720
        }
721
    }
722
    for (vector<int>::reverse_iterator it = idxes.rbegin();
723
            it != idxes.rend(); it++) {
724
        QListWidgetItem *item = m_lb->takeItem(*it);
725
        emit entryDeleted(item->text());
726
        delete item;
727
    }
728
}
729
730
// "Add entry" dialog for a file name list
731
void ConfParamDNLW::showInputDialog()
732
{
733
    QString s = myGetFileName(true);
734
    if (!s.isEmpty()) {
735
        QList<QListWidgetItem *>items =
736
            m_lb->findItems(s, Qt::MatchFixedString | Qt::MatchCaseSensitive);
737
        if (items.empty()) {
738
            m_lb->insertItem(0, s);
739
            m_lb->sortItems();
740
            QList<QListWidgetItem *>items =
741
                m_lb->findItems(s, Qt::MatchFixedString | Qt::MatchCaseSensitive);
742
            if (m_lb->selectionMode() == QAbstractItemView::SingleSelection &&
743
                    !items.empty()) {
744
                m_lb->setCurrentItem(*items.begin());
745
            }
746
        }
747
    }
748
}
749
750
// "Add entry" dialog for a constrained string list
751
void ConfParamCSLW::showInputDialog()
752
{
753
    bool ok;
754
    QString s = QInputDialog::getItem(this,  // parent
755
                                      "", // title
756
                                      "", // label,
757
                                      m_sl, // items,
758
                                      0, // current = 0
759
                                      false, // editable = true,
760
                                      &ok);
761
762
    if (ok && !s.isEmpty()) {
763
        QList<QListWidgetItem *>items =
764
            m_lb->findItems(s, Qt::MatchFixedString | Qt::MatchCaseSensitive);
765
        if (items.empty()) {
766
            m_lb->insertItem(0, s);
767
            m_lb->sortItems();
768
        }
769
    }
770
}
771
772
773
774
#ifdef ENABLE_XMLCONF
775
776
static QString u8s2qs(const std::string us)
777
{
778
    return QString::fromUtf8(us.c_str());
779
}
780
781
static const string& mapfind(const string& nm, const map<string, string>& mp)
782
{
783
    static string strnull;
784
    map<string, string>::const_iterator it;
785
    it = mp.find(nm);
786
    if (it == mp.end()) {
787
        return strnull;
788
    }
789
    return it->second;
790
}
791
792
static string looksLikeAssign(const string& data)
793
{
794
    //LOGDEB("looksLikeAssign. data: [" << data << "]");
795
    vector<string> toks;
796
    stringToTokens(data, toks, "\n\r\t ");
797
    if (toks.size() >= 2 && !toks[1].compare("=")) {
798
        return toks[0];
799
    }
800
    return string();
801
}
802
803
ConfTabsW *xmlToConfGUI(const string& xml, string& toptext,
804
                        ConfLinkFact* lnkf, QWidget *parent)
805
{
806
    //LOGDEB("xmlToConfGUI: [" << xml << "]");
807
808
    class XMLToConfGUI : public PicoXMLParser {
809
    public:
810
        XMLToConfGUI(const string& x, ConfLinkFact *lnkf, QWidget *parent)
811
            : PicoXMLParser(x), m_lnkfact(lnkf), m_parent(parent),
812
              m_idx(0), m_hadTitle(false), m_hadGroup(false) {
813
        }
814
        virtual ~XMLToConfGUI() {}
815
816
        virtual void startElement(const string& tagname,
817
                                  const map<string, string>& attrs) {
818
            if (!tagname.compare("var")) {
819
                m_curvar = mapfind("name", attrs);
820
                m_curvartp = mapfind("type", attrs);
821
                m_curvarvals = mapfind("values", attrs);
822
                //LOGDEB("Curvar: " << m_curvar);
823
                if (m_curvar.empty() || m_curvartp.empty()) {
824
                    throw std::runtime_error(
825
                        "<var> with no name attribute or no type ! nm [" +
826
                        m_curvar + "] tp [" + m_curvartp + "]");
827
                } else {
828
                    m_brief.clear();
829
                    m_descr.clear();
830
                }
831
            } else if (!tagname.compare("filetitle") ||
832
                       !tagname.compare("grouptitle")) {
833
                m_other.clear();
834
            }
835
        }
836
837
        virtual void endElement(const string& tagname) {
838
            if (!tagname.compare("var")) {
839
                if (!m_hadTitle) {
840
                    m_w = new ConfTabsW(m_parent, "Teh title", m_lnkfact);
841
                    m_hadTitle = true;
842
                }
843
                if (!m_hadGroup) {
844
                    m_idx = m_w->addPanel("Group title");
845
                    m_hadGroup = true;
846
                }
847
                ConfTabsW::ParamType paramtype;
848
                if (!m_curvartp.compare("bool")) {
849
                    paramtype = ConfTabsW::CFPT_BOOL;
850
                } else if (!m_curvartp.compare("int")) {
851
                    paramtype = ConfTabsW::CFPT_INT;
852
                } else if (!m_curvartp.compare("string")) {
853
                    paramtype = ConfTabsW::CFPT_STR;
854
                } else if (!m_curvartp.compare("cstr")) {
855
                    paramtype = ConfTabsW::CFPT_CSTR;
856
                } else if (!m_curvartp.compare("cstrl")) {
857
                    paramtype = ConfTabsW::CFPT_CSTRL;
858
                } else if (!m_curvartp.compare("fn")) {
859
                    paramtype = ConfTabsW::CFPT_FN;
860
                } else if (!m_curvartp.compare("dfn")) {
861
                    paramtype = ConfTabsW::CFPT_FN;
862
                } else if (!m_curvartp.compare("strl")) {
863
                    paramtype = ConfTabsW::CFPT_STRL;
864
                } else if (!m_curvartp.compare("dnl")) {
865
                    paramtype = ConfTabsW::CFPT_DNL;
866
                } else {
867
                    throw std::runtime_error("Bad type " + m_curvartp +
868
                                             " for " + m_curvar);
869
                }
870
                rtrimstring(m_brief, " .");
871
                switch (paramtype) {
872
                case ConfTabsW::CFPT_BOOL: {
873
                    int def = atoi(m_curvarvals.c_str());
874
                    m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar),
875
                                  u8s2qs(m_brief), u8s2qs(m_descr), def);
876
                    break;
877
                }
878
                case ConfTabsW::CFPT_INT: {
879
                    vector<string> vals;
880
                    stringToTokens(m_curvarvals, vals);
881
                    int min = 0, max = 0, def = 0;
882
                    if (vals.size() >= 3) {
883
                        min = atoi(vals[0].c_str());
884
                        max = atoi(vals[1].c_str());
885
                        def = atoi(vals[2].c_str());
886
                    }
887
                    QStringList *sldef = 0;
888
                    sldef = (QStringList*)(((char*)sldef) + def);
889
                    m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar),
890
                                  u8s2qs(m_brief), u8s2qs(m_descr),
891
                                  min, max, sldef);
892
                    break;
893
                }
894
                case  ConfTabsW::CFPT_CSTR:
895
                case ConfTabsW::CFPT_CSTRL: {
896
                    vector<string> cstrl;
897
                    stringToTokens(neutchars(m_curvarvals, "\n\r"), cstrl);
898
                    QStringList qstrl;
899
                    for (unsigned int i = 0; i < cstrl.size(); i++) {
900
                        qstrl.push_back(u8s2qs(cstrl[i]));
901
                    }
902
                    m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar),
903
                                  u8s2qs(m_brief), u8s2qs(m_descr),
904
                                  0, 0, &qstrl);
905
                    break;
906
                }
907
                default:
908
                    m_w->addParam(m_idx, paramtype, u8s2qs(m_curvar),
909
                                  u8s2qs(m_brief), u8s2qs(m_descr));
910
                }
911
            } else if (!tagname.compare("filetitle")) {
912
                m_w = new ConfTabsW(m_parent, u8s2qs(m_other), m_lnkfact);
913
                m_hadTitle = true;
914
                m_other.clear();
915
            } else if (!tagname.compare("grouptitle")) {
916
                if (!m_hadTitle) {
917
                    m_w = new ConfTabsW(m_parent, "Teh title", m_lnkfact);
918
                    m_hadTitle = true;
919
                }
920
                // Get rid of "parameters" in the title, it's not interesting
921
                // and this makes our tab headers smaller.
922
                string ps{"parameters"};
923
                string::size_type pos = m_other.find(ps);
924
                if (pos != string::npos) {
925
                    m_other = m_other.replace(pos, ps.size(), "");
926
                }
927
                m_idx = m_w->addPanel(u8s2qs(m_other));
928
                m_hadGroup = true;
929
                m_other.clear();
930
            } else if (!tagname.compare("descr")) {
931
            } else if (!tagname.compare("brief")) {
932
                m_brief = neutchars(m_brief, "\n\r");
933
            }
934
        }
935
936
        virtual void characterData(const string& data) {
937
            if (!tagStack().back().compare("brief")) {
938
                m_brief += data;
939
            } else if (!tagStack().back().compare("descr")) {
940
                m_descr += data;
941
            } else if (!tagStack().back().compare("filetitle") ||
942
                       !tagStack().back().compare("grouptitle")) {
943
                // We don't want \n in there
944
                m_other += neutchars(data, "\n\r");
945
                m_other += " ";
946
            } else if (!tagStack().back().compare("confcomments")) {
947
                string nvarname = looksLikeAssign(data);
948
                if (!nvarname.empty() && nvarname.compare(m_curvar)) {
949
                    cerr << "Var assigned [" << nvarname << "] mismatch "
950
                         "with current variable [" << m_curvar << "]\n";
951
                }
952
                m_toptext += data;
953
            }
954
        }
955
956
        ConfTabsW *m_w;
957
958
        ConfLinkFact *m_lnkfact;
959
        QWidget *m_parent;
960
        int m_idx;
961
        string m_curvar;
962
        string m_curvartp;
963
        string m_curvarvals;
964
        string m_brief;
965
        string m_descr;
966
        string m_other;
967
        string m_toptext;
968
        bool m_hadTitle;
969
        bool m_hadGroup;
970
    };
971
972
    XMLToConfGUI parser(xml, lnkf, parent);
973
    try {
974
        if (!parser.parse()) {
975
            cerr << "Parse failed: " << parser.getReason() << endl;
976
            return 0;
977
        }
978
    } catch (const std::runtime_error& e) {
979
        cerr << e.what() << endl;
980
        return 0;
981
    }
982
    toptext = parser.m_toptext;
983
    return parser.m_w;
984
}
985
986
#endif /* ENABLE_XMLCONF */
987
988
} // Namespace confgui