Subversion Repositories svn1

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
95 david 1
#include "qmconfclass.h"
174 - 2
#include "qmconfig.h"
95 david 3
#include "ui_qmconfclass.h"
99 david 4
#include    "consts.h"
5
#include    "structs.h"
6
#include    "proto.h"
180 - 7
#include "mainwindow.h"
310 david 8
#include "qmTableWidgetItem.h"
95 david 9
 
286 david 10
#include <QDialogButtonBox>
99 david 11
#include "QTableWidgetItem"
107 david 12
#include "spinboxdelegate.h"
13
#include "timedelegate.h"
111 david 14
#include "textdelegate.h"
180 - 15
#include <QMenu>
99 david 16
 
227 - 17
#define COL_ABR 0
18
#define COL_CLASS 1
19
#define COL_TIME 2
20
#define COL_WINNERS 3
21
#define COL_NE 4
22
#define COL_NE_WINNERS 5
23
#define COL_ST_TOTAL 6
228 - 24
#define COL_ST_VALID 7
25
#define COL_ST_DISQ 8
26
#define COL_ST_NONEQ 9
27
#define COL_ST_VET 10
284 david 28
#define COL_ST_CEV 11
29
#define COL_ST_CNE 12
311 david 30
#define COL_INDEX 13
227 - 31
 
311 david 32
#define COL_COUNT 14
227 - 33
 
311 david 34
 
104 - 35
QmConfClass::QmConfClass(QWidget *parent) :
286 david 36
    QWidget(parent)
95 david 37
{
181 - 38
    populating = false;
312 david 39
    dirty = false;
99 david 40
 
286 david 41
    QVBoxLayout *verticalLayout = new QVBoxLayout(this);
42
    verticalLayout->setContentsMargins(0, 0, 0, 0);
43
 
44
    QGroupBox *groupBox = new QGroupBox("Class");
45
    verticalLayout->addWidget(groupBox);
46
    QVBoxLayout *verticalLayout2 = new QVBoxLayout(groupBox);
47
    tableWidget = new QTableWidget(groupBox);
48
    tableWidget->setAlternatingRowColors(true);
49
    tableWidget->setRowCount(config.num_class + 1);
50
    tableWidget->setColumnCount(COL_COUNT);
51
    tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
52
    tableWidget->horizontalHeader()->setVisible(true);
53
    tableWidget->horizontalHeader()->setDefaultSectionSize(70);
54
    tableWidget->horizontalHeader()->setHighlightSections(true);
290 david 55
    //tableWidget->horizontalHeader()->setStretchLastSection(true);
286 david 56
    tableWidget->verticalHeader()->setVisible(true);
57
    tableWidget->verticalHeader()->setDefaultSectionSize(20);
58
    //tableWidget->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding );
59
    //tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
60
    verticalLayout2->addWidget(tableWidget);
61
 
62
    //QSpacerItem *verticalSpacer1;
63
    //verticalSpacer1 = new QSpacerItem(0, 10, QSizePolicy::Expanding, QSizePolicy::Expanding);
64
    //verticalLayout2->addItem(verticalSpacer1);
65
 
66
    QHBoxLayout *horizontalLayout;
67
    horizontalLayout = new QHBoxLayout();
68
    horizontalLayout->setContentsMargins(0, 0, 5, 5);
69
    verticalLayout->addLayout(horizontalLayout);
70
 
71
    QDialogButtonBox *buttonBox = new QDialogButtonBox();
72
    pushButtonRestore = buttonBox->addButton("Restore",QDialogButtonBox::ActionRole );
73
    pushButtonSave = buttonBox->addButton("Save",QDialogButtonBox::ActionRole );
74
    horizontalLayout->addWidget(buttonBox);
75
 
76
 
77
    connect(pushButtonSave, SIGNAL(clicked(bool)), this, SLOT(save()) );
78
    connect(pushButtonRestore, SIGNAL(clicked(bool)), this, SLOT(cancel()) );
79
 
109 david 80
    QStringList labels;
228 - 81
    labels << "Abr" << "Full Name" << "Start Time" << "Winners" << "NE" << "NE Winners";
109 david 82
 
311 david 83
    labels << "Total" << "Valid" <<"Disqual" << "NonEq" << "VetCheck" << "Comp Ev" << " Comp NE";
107 david 84
 
311 david 85
    labels << "Index";
86
 
87
    tableWidget->setHorizontalHeaderLabels(labels);
88
    tableWidget->resizeColumnsToContents();
89
 
286 david 90
    connect(tableWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(ctxMenu(const QPoint &)));
91
    connect(tableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int,int)));
180 - 92
 
107 david 93
    /*
94
    **  Setup delegated for specialised editing
95
    */
286 david 96
    tableWidget->setItemDelegateForColumn(COL_ABR, new textDelegate(2));
97
    tableWidget->setItemDelegateForColumn(COL_CLASS, new textDelegate(LEN_CLASS_NAME));
98
    tableWidget->setItemDelegateForColumn(COL_TIME, new timeDelegate());
99
    tableWidget->setItemDelegateForColumn(COL_WINNERS, new SpinBoxDelegate(0,50));
100
    tableWidget->setItemDelegateForColumn(COL_NE_WINNERS, new SpinBoxDelegate(0,50));
312 david 101
}
107 david 102
 
312 david 103
/*----------------------------------------------------------------------------
104
** FUNCTION           : showEvent 
105
**
106
** DESCRIPTION        : Called when the page is shown
107
**                      Used to refresh the contents of the display as well
108
**                      as show them for the first time
109
**
110
----------------------------------------------------------------------------*/
111
 
112
void QmConfClass::showEvent ( QShowEvent * event )
113
{
114
    //qDebug("populate::showEvent");
115
    if ( ! event->spontaneous() && !dirty )
116
    {
117
        populate();
118
    }
311 david 119
}
180 - 120
 
311 david 121
/*----------------------------------------------------------------------------
122
** FUNCTION           : populate
123
**
124
** DESCRIPTION        : Populate the display with information
125
**
126
----------------------------------------------------------------------------*/
180 - 127
 
104 - 128
void QmConfClass::populate(void)
99 david 129
{
311 david 130
    bool    reportDataLoaded = false;
131
    t_class_summary data;
132
 
181 - 133
    populating = true;
310 david 134
    tableWidget->clearContents();
135
    tableWidget->setSortingEnabled(false);
286 david 136
    tableWidget->setRowCount(config.num_class + 1);
311 david 137
 
138
    /*
139
    ** Load Report Data if we can
140
    */
141
    if( load_report_data() )
99 david 142
    {
311 david 143
        calc_class_summary( & data );
144
        reportDataLoaded = true;
145
    }
105 - 146
 
311 david 147
    /*
148
    **  Add all the information
149
    */
150
    int ii = 0;
151
    int entryIndex = 0;
152
    for ( ii = 0; ii < MAX_CLASS; ii++)
153
    {
154
        if ( config.team_class[ii].abr[0] )
111 david 155
        {
311 david 156
            // Keep the config item index entry
157
            //  Will need it when we write stiff out
158
            tableWidget->setItem(entryIndex, COL_INDEX, new qmTwiNumber(ii));
159
 
160
            tableWidget->setItem(entryIndex, COL_ABR, new QTableWidgetItem(config.team_class[ii].abr) );
161
            tableWidget->setItem(entryIndex, COL_CLASS, new QTableWidgetItem(config.team_class[ii].full_name ));
162
 
111 david 163
            QTableWidgetItem *item;
164
            item = new QTableWidgetItem();
165
            item->setData(Qt::EditRole,QTime().addSecs(config.team_class[ii].start) );
311 david 166
            tableWidget->setItem(entryIndex, COL_TIME, item);
107 david 167
 
310 david 168
            item = new qmTwiEditNumber(config.class_winners[ii]);
311 david 169
            tableWidget->setItem(entryIndex, COL_WINNERS, item);
181 - 170
 
171
            item = new QTableWidgetItem();
172
            item->setCheckState(config.nonequestrian_class == 1 + ii ? Qt::Checked : Qt::Unchecked);
311 david 173
            tableWidget->setItem(entryIndex, COL_NE, item);
181 - 174
 
310 david 175
            item = new qmTwiEditNumber(config.class_ne_winners[ii]);
311 david 176
            tableWidget->setItem(entryIndex, COL_NE_WINNERS, item);
177
 
178
            if (reportDataLoaded)
179
            {
180
                tableWidget->setItem(entryIndex,COL_ST_TOTAL, new qmTwiNumber(data.teamclass[ii+1].total) );
181
                tableWidget->setItem(entryIndex,COL_ST_VALID, new qmTwiNumber(data.teamclass[ii+1].valid) );
182
                tableWidget->setItem(entryIndex,COL_ST_DISQ,  new qmTwiNumber(data.teamclass[ii+1].disqualified));
183
                tableWidget->setItem(entryIndex,COL_ST_NONEQ, new qmTwiNumber(data.teamclass[ii+1].non_equestrian));
184
                tableWidget->setItem(entryIndex,COL_ST_VET,   new qmTwiNumber(data.teamclass[ii+1].vet_check));
185
                tableWidget->setItem(entryIndex,COL_ST_CEV,   new qmTwiNumber(data.teamclass[ii+1].valid_ev));
186
                tableWidget->setItem(entryIndex,COL_ST_CNE,   new qmTwiNumber(data.teamclass[ii+1].valid_ne));
187
            }
188
            entryIndex++;
111 david 189
        }
99 david 190
    }
311 david 191
 
192
    //  Insert Totals
193
    //      Flag that items on this this row is not to be sorted - or at least 
194
    //      sorted at the end
195
    //
196
    if (reportDataLoaded)
197
    {
198
        tableWidget->setItem(entryIndex,COL_ABR,      new qmTwiString("Totals", 1));
199
        tableWidget->setItem(entryIndex,COL_ST_TOTAL, new qmTwiNumber(data.total.total, 1));
200
        tableWidget->setItem(entryIndex,COL_ST_VALID, new qmTwiNumber(data.total.valid, 1));
201
        tableWidget->setItem(entryIndex,COL_ST_DISQ,  new qmTwiNumber(data.total.disqualified, 1));
202
        tableWidget->setItem(entryIndex,COL_ST_NONEQ, new qmTwiNumber(data.total.non_equestrian, 1));
203
        tableWidget->setItem(entryIndex,COL_ST_VET,   new qmTwiNumber(data.total.vet_check, 1));
204
        tableWidget->setItem(entryIndex,COL_ST_CEV,   new qmTwiNumber(data.total.valid_ev, 1));
205
        tableWidget->setItem(entryIndex,COL_ST_CNE,   new qmTwiNumber(data.total.valid_ne, 1));
206
    }
207
 
208
    tableWidget->resizeColumnsToContents();
209
    tableWidget->resizeRowsToContents();
210
    tableWidget->setSortingEnabled(true);
211
 
181 - 212
    updateChanged(false);
213
    populating = false;
99 david 214
}
215
 
104 - 216
void QmConfClass::save(void)
99 david 217
{
104 - 218
    /*
219
    **    Copy original data
220
    */
176 - 221
    QmConfig newcfg(config);
104 - 222
 
223
    /*
224
    **  Extract the data from the Widgets
312 david 225
    **  Trap: The Class info is cross ref by index into the table
226
    **        Thus we need to maintain the order in the config.
104 - 227
    */
311 david 228
    for (int entryIndex = 0; entryIndex < tableWidget->rowCount(); entryIndex++)
104 - 229
    {
310 david 230
 
231
        // Check that this is a real Entry or the 'Totals' header
311 david 232
        //      Real rows have a COL_INDEX entr
310 david 233
        //
311 david 234
        QTableWidgetItem *item = tableWidget->item ( entryIndex, COL_INDEX );
235
        if (! item)
310 david 236
        {
237
            qDebug("Ignore row:%d", entryIndex);
238
            continue;
239
        }
311 david 240
        int ii = item->text().toInt();
241
        qDebug("Processing: %d", ii);
310 david 242
 
312 david 243
        /*
244
        ** This is a new entry - allocate a new index
245
        */
246
        if (ii < 0)
311 david 247
        {
312 david 248
            ii = newcfg.num_class;
249
            newcfg.num_class++;
250
        }
251
 
252
        if (ii >= MAX_CLASS )
253
        {
254
            qDebug("Ignore row:%d. Class out of range", entryIndex);
311 david 255
            continue;
256
        }
257
 
258
        item = tableWidget->item ( entryIndex, COL_ABR );
111 david 259
        if ( item )
260
        {
261
            strncpy(newcfg.team_class[ii].abr, qPrintable(item->text()), sizeof(newcfg.team_class[ii].abr)-1);
262
        }
263
        else
264
        {
265
            *newcfg.team_class[ii].abr = 0;
266
        }
104 - 267
 
310 david 268
        item = tableWidget->item ( entryIndex, COL_CLASS );
111 david 269
        if ( item )
270
        {
271
            strncpy(newcfg.team_class[ii].full_name, qPrintable(item->text()), sizeof(newcfg.team_class[ii].full_name)-1);
272
        }
273
        else
274
        {
275
            *newcfg.team_class[ii].full_name = 0;
276
        }
108 david 277
 
310 david 278
        item = tableWidget->item( entryIndex, COL_TIME );
111 david 279
        if ( item )
280
        {
195 david 281
            QVariant data = item->data(Qt::EditRole);
282
            if (data.isValid())
283
            {
284
                newcfg.team_class[ii].start = QTime(0,0,0).secsTo(item->data(Qt::EditRole).toTime());
285
            }
286
            else
287
            {
288
                newcfg.team_class[ii].start = -1;
289
            }
111 david 290
        }
291
        else
292
        {
195 david 293
            newcfg.team_class[ii].start = -1;
111 david 294
        }
295
 
310 david 296
        item = tableWidget->item( entryIndex, COL_WINNERS );
111 david 297
        if ( item )
298
        {
299
            newcfg.class_winners[ii] = item->data(Qt::EditRole).toInt();
300
        }
301
        else
302
        {
303
            newcfg.class_winners[ii] = 0;
304
        }
227 - 305
 
310 david 306
        item = tableWidget->item( entryIndex, COL_NE );
181 - 307
        if ( item )
308
        {
309
            if ( item->checkState() == Qt::Checked)
310
            {
311
                newcfg.nonequestrian_class = ii;
312
                strncpy(newcfg.nonequestrian_class_abr, newcfg.team_class[ii].abr, sizeof(newcfg.team_class[ii].abr)-1);
313
            }
314
        }
227 - 315
 
310 david 316
        item = tableWidget->item( entryIndex, COL_NE_WINNERS );
227 - 317
        if ( item )
318
        {
319
            newcfg.class_ne_winners[ii] = item->data(Qt::EditRole).toInt();
320
        }
321
        else
322
        {
323
            newcfg.class_ne_winners[ii] = 0;
324
        }
104 - 325
    }
326
 
181 - 327
    // Validate the data
328
    try
104 - 329
    {
181 - 330
        MainWindow::showMessage( "Saving Config");
104 - 331
 
181 - 332
        /*
333
         **  Now do the Class definitions
334
         */
335
 
336
        for( int i = 0; i < MAX_CLASS; i++ )
104 - 337
        {
181 - 338
            compact( newcfg.team_class[i].abr );
339
            compact( newcfg.team_class[i].full_name );
104 - 340
        }
181 - 341
 
342
        for( int i = 0; i < MAX_CLASS; i++ )
104 - 343
        {
181 - 344
            if( ( newcfg.team_class[i].abr[0] == '\0' ) != ( newcfg.team_class[i].full_name[0] == '\0' ) )
345
            {
346
                throw( "Configuration error. Class without description" );
347
 
348
            }
349
            if( newcfg.team_class[i].abr[0] != '\0' && newcfg.team_class[i].start < 0L )
350
            {
351
                throw( "Configuration error. Bad start time on class" );
352
 
353
            }
104 - 354
        }
355
 
181 - 356
        newcfg.num_class = 0;
357
        for( int i = 0; i < MAX_CLASS; i++ )
358
            if( newcfg.team_class[i].full_name[0] )
359
                newcfg.num_class++;
104 - 360
 
181 - 361
        for( int i = newcfg.num_class; i < MAX_CLASS; i++ )
362
            if( newcfg.team_class[i].full_name[0] )
363
            {
364
                throw( "Configuration error: Missing Class name. Gaps not allowed" );
365
 
366
            }
367
 
368
        if( newcfg.num_class == 0 )
104 - 369
        {
181 - 370
            throw( "Error: No categories defined" );
371
 
104 - 372
        }
373
 
312 david 374
        /*
375
        ** Test for duplicate abbreviations 
376
        ** I know its a slow agorithm, but its only a small set 
377
        */
378
        for( int i = 0; i < MAX_CLASS; i++ )
379
        {
380
            if (newcfg.team_class[i].abr[0] != '\0')
381
            {
382
                for (int j = i+1; j < MAX_CLASS; j++)
383
                {
384
                    if (newcfg.team_class[j].abr[0] != '\0')
385
                    {
386
                        if (    newcfg.team_class[i].abr[0] == newcfg.team_class[j].abr[0]
387
                             && newcfg.team_class[i].abr[1] == newcfg.team_class[j].abr[1]
388
                            )
389
                        {
390
                            qDebug("Duplicate abr: %s", newcfg.team_class[i].abr);
391
                            throw( "Error: Duplicate abbreviations detected");
392
                        }
393
                    }
394
                }
395
            }
396
        }
397
 
398
        /*
399
        ** Sanity Test
400
        */
181 - 401
        newcfg.nonequestrian_class = newcfg.lookup_class( newcfg.nonequestrian_class_abr );
402
        if( newcfg.equestrian_leg && newcfg.nonequestrian_class == 0 )
403
            MainWindow::showMessage( "WARNING: Non-equestrian class not found" );
312 david 404
        qDebug("NE Index is: %d", newcfg.nonequestrian_class );
181 - 405
        /*
406
        **  Sanity test of the data
407
        */
408
        for( int i = 0; i < MAX_CLASS; i++ )
409
        {
410
            if( newcfg.team_class[i].abr[0] != '\0' && newcfg.class_winners[i] == 0 )
411
            {
312 david 412
                if (newcfg.nonequestrian_class != i+1)
413
                {
414
                    MainWindow::showMessage(QString("Warning: Class without winners:")+ newcfg.team_class[i].abr); 
415
                }
181 - 416
            }
111 david 417
        }
418
 
227 - 419
        /*
420
        ** Cannot mix winners for NE class and NE Winners for each class
421
        */
422
        if (newcfg.nonequestrian_class)
423
        {
424
            newcfg.class_ne_winners_by_class = false;
425
            for( int i = 0; i < MAX_CLASS; i++ )
426
            {
228 - 427
                if ( newcfg.class_ne_winners[i])
428
                {
429
                    newcfg.class_ne_winners_by_class = true;
430
                    break;
431
                }
227 - 432
            }
433
            if (newcfg.class_winners[newcfg.nonequestrian_class - 1] && newcfg.class_ne_winners_by_class )
434
            {
435
                MainWindow::showMessage( QString("Should not mix NE winners by each class and by NE Class"));
436
                //throw( "Error: Cannot mix NE winners by each class and by NE Class" );
437
            }
438
        }
439
 
440
 
104 - 441
        config = newcfg;
176 - 442
        config.write_config();
181 - 443
        updateChanged(false);
444
 
312 david 445
        // Force re-populate so that the index field is now correct
446
        populate();
447
 
448
 
104 - 449
    }
312 david 450
    catch (const QString str )
181 - 451
    {
452
        MainWindow::showMessage(str);
453
    }
104 - 454
 
99 david 455
}
456
 
104 - 457
void QmConfClass::cancel(void)
99 david 458
{
459
    populate();
460
}
461
 
104 - 462
QmConfClass::~QmConfClass()
95 david 463
{
286 david 464
 
95 david 465
}
466
 
180 - 467
void QmConfClass::ctxMenu(const QPoint & pos)
468
{
469
    qDebug("Context Menu");
470
    QMenu *menu = new QMenu;
312 david 471
    menu->addAction(tr("New Category"), this, SLOT(ctxMenuAddRow()));
472
 
473
    /*
474
    **      Determine if we can delete an item 
475
    **      Do NOT delete items, unless thay have been added in this session
476
    **      Once a class entry has been made it may be cross referenecd from
477
    **      team data.
478
    */
286 david 479
    QTableWidgetItem *item = tableWidget->itemAt(pos);
180 - 480
    if (item)
481
    {
312 david 482
        int row = item->row();
483
        qDebug("Item is at row:%d", row);
484
        QTableWidgetItem *indexItem = tableWidget->item ( row, COL_INDEX );
485
        if (indexItem)
486
        {
487
            int index = indexItem->text().toInt();
488
            qDebug("Item is at row:%d, Index: %d", row, index);
489
            if (index < 0)
490
            {
491
                menu->addAction(tr("Delete Category"), this, SLOT(ctxMenuDeleteRow())); 
492
            }
493
        }
180 - 494
    }
286 david 495
   menu->exec(tableWidget->mapToGlobal(pos));
180 - 496
 
497
}
498
 
499
void QmConfClass::ctxMenuDeleteRow(void)
500
{
312 david 501
    //
502
    //  Assume that if the menu was shown, then we can delete it
503
    // 
286 david 504
    //qDebug ("DELETE ROW: %d", tableWidget->currentRow () );
505
    tableWidget->removeRow(tableWidget->currentRow ());
189 - 506
    updateChanged(true);
180 - 507
}
508
 
509
void QmConfClass::ctxMenuAddRow(void)
510
{
286 david 511
    tableWidget->setRowCount( 1+ tableWidget->rowCount());
312 david 512
    tableWidget->setItem(tableWidget->rowCount() - 1, COL_INDEX, new qmTwiNumber(-1));
180 - 513
}
514
 
181 - 515
void QmConfClass::cellChanged(int row,int col)
516
{
517
    if ( populating )
518
        return;
312 david 519
    if (col == COL_INDEX)
520
        return;
521
 
522
    updateChanged(true); 
181 - 523
    populating = true;
524
    //qDebug("Cell changed: %d, %d", row, col);
227 - 525
    if (col == COL_NE)
181 - 526
    {
286 david 527
        for (int ii = 0; ii < tableWidget->rowCount(); ii++)
181 - 528
        {
286 david 529
            QTableWidgetItem *item = tableWidget->item(ii, COL_NE);
181 - 530
            if (item)
531
            {
532
                item->setCheckState(ii == row ? Qt::Checked : Qt::Unchecked);
533
            }
534
        }
535
    }
536
    populating = false;
537
}
538
 
539
void QmConfClass::updateChanged(bool newDirty)
540
{
541
    if (newDirty != dirty)
542
    {
543
        dirty = newDirty;
544
        if (dirty)
545
        {
286 david 546
            pushButtonSave->setEnabled(true);
547
            pushButtonSave->setStyleSheet("background-color: rgb(255, 0, 0);");
181 - 548
        }
549
        else
550
        {
286 david 551
            pushButtonSave->setEnabled(false);
552
            pushButtonSave->setStyleSheet("");
181 - 553
        }
554
    }
555
}
556
 
104 - 557
void QmConfClass::changeEvent(QEvent *e)
95 david 558
{
559
    QWidget::changeEvent(e);
560
    switch (e->type()) {
286 david 561
 
95 david 562
    default:
563
        break;
564
    }
565
}