Rev 335 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "qmconfclass.h"#include "qmconfig.h"#include "ui_qmconfclass.h"#include "consts.h"#include "structs.h"#include "proto.h"#include "mainwindow.h"#include "qmTableWidgetItem.h"#include <QDialogButtonBox>#include "QTableWidgetItem"#include "spinboxdelegate.h"#include "timedelegate.h"#include "textdelegate.h"#include <QMenu>#define COL_ABR 0#define COL_CLASS 1#define COL_TIME 2#define COL_WINNERS 3#define COL_NE 4#define COL_NE_WINNERS 5#define COL_ST_TOTAL 6#define COL_ST_VALID 7#define COL_ST_DISQ 8#define COL_ST_NONEQ 9#define COL_ST_VET 10#define COL_ST_CEV 11#define COL_ST_CNE 12#define COL_INDEX 13#define COL_COUNT 14QmConfClass::QmConfClass(QWidget *parent) :QWidget(parent){populating = false;dirty = false;QVBoxLayout *verticalLayout = new QVBoxLayout(this);verticalLayout->setContentsMargins(0, 0, 0, 0);QGroupBox *groupBox = new QGroupBox("Class");verticalLayout->addWidget(groupBox);QVBoxLayout *verticalLayout2 = new QVBoxLayout(groupBox);tableWidget = new QTableWidget(groupBox);tableWidget->setAlternatingRowColors(true);tableWidget->setRowCount(config.num_class + 1);tableWidget->setColumnCount(COL_COUNT);tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);tableWidget->horizontalHeader()->setVisible(true);tableWidget->horizontalHeader()->setDefaultSectionSize(70);tableWidget->horizontalHeader()->setHighlightSections(true);//tableWidget->horizontalHeader()->setStretchLastSection(true);tableWidget->verticalHeader()->setVisible(true);tableWidget->verticalHeader()->setDefaultSectionSize(20);//tableWidget->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding );//tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);verticalLayout2->addWidget(tableWidget);//QSpacerItem *verticalSpacer1;//verticalSpacer1 = new QSpacerItem(0, 10, QSizePolicy::Expanding, QSizePolicy::Expanding);//verticalLayout2->addItem(verticalSpacer1);QHBoxLayout *horizontalLayout;horizontalLayout = new QHBoxLayout();horizontalLayout->setContentsMargins(0, 0, 5, 5);verticalLayout->addLayout(horizontalLayout);QDialogButtonBox *buttonBox = new QDialogButtonBox();pushButtonRestore = buttonBox->addButton("Restore",QDialogButtonBox::ActionRole );pushButtonSave = buttonBox->addButton("Save",QDialogButtonBox::ActionRole );horizontalLayout->addWidget(buttonBox);connect(pushButtonSave, SIGNAL(clicked(bool)), this, SLOT(save()) );connect(pushButtonRestore, SIGNAL(clicked(bool)), this, SLOT(cancel()) );QStringList labels;labels << "Abr" << "Full Name" << "Start Time" << "Winners" << "NE" << "NE Winners";labels << "Total" << "Valid" <<"Disqual" << "NonEq" << "VetCheck" << "Comp Ev" << " Comp NE";labels << "Index";tableWidget->setHorizontalHeaderLabels(labels);connect(tableWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(ctxMenu(const QPoint &)));connect(tableWidget, SIGNAL(cellChanged(int,int)), this, SLOT(cellChanged(int,int)));/*** Setup delegated for specialised editing*/tableWidget->setItemDelegateForColumn(COL_ABR, new textDelegate(2));tableWidget->setItemDelegateForColumn(COL_CLASS, new textDelegate(LEN_CLASS_NAME));tableWidget->setItemDelegateForColumn(COL_TIME, new timeDelegate());tableWidget->setItemDelegateForColumn(COL_WINNERS, new SpinBoxDelegate(0,50));tableWidget->setItemDelegateForColumn(COL_NE_WINNERS, new SpinBoxDelegate(0,50));}/*----------------------------------------------------------------------------** FUNCTION : showEvent**** DESCRIPTION : Called when the page is shown** Used to refresh the contents of the display as well** as show them for the first time**----------------------------------------------------------------------------*/void QmConfClass::showEvent ( QShowEvent * event ){//qDebug("populate::showEvent");if ( ! event->spontaneous() && !dirty ){populate();}}/*----------------------------------------------------------------------------** FUNCTION : populate**** DESCRIPTION : Populate the display with information**----------------------------------------------------------------------------*/void QmConfClass::populate(void){bool reportDataLoaded = false;t_class_summary data;populating = true;tableWidget->clearContents();tableWidget->setRowCount(0);tableWidget->setSortingEnabled(false);/*** Load Report Data if we can*/if( load_report_data() ){calc_class_summary( & data );reportDataLoaded = true;}/*** Add all the information*/int ii = 0;int entryIndex = 0;for ( ii = 0; ii < MAX_CLASS; ii++){if ( config.team_class[ii].abr[0] ){// Keep the config item index entry// Will need it when we write stiff outtableWidget->setRowCount(tableWidget->rowCount() + 1);tableWidget->setItem(entryIndex, COL_INDEX, new qmTwiNumber(ii));tableWidget->setItem(entryIndex, COL_ABR, new qmTwiString(config.team_class[ii].abr) );tableWidget->setItem(entryIndex, COL_CLASS, new qmTwiString(config.team_class[ii].full_name ));QTableWidgetItem *item;item = new QTableWidgetItem();item->setData(Qt::EditRole,QTime().addSecs(config.team_class[ii].start) );tableWidget->setItem(entryIndex, COL_TIME, item);tableWidget->setItem(entryIndex, COL_WINNERS, new qmTwiEditNumber(config.class_winners[ii]));tableWidget->setItem(entryIndex, COL_NE, new qmTwiEditFlag("",config.nonequestrian_class == 1 + ii));tableWidget->setItem(entryIndex, COL_NE_WINNERS, new qmTwiEditNumber(config.class_ne_winners[ii]));if (reportDataLoaded){tableWidget->setItem(entryIndex,COL_ST_TOTAL, new qmTwiNumber(data.teamclass[ii+1].total) );tableWidget->setItem(entryIndex,COL_ST_VALID, new qmTwiNumber(data.teamclass[ii+1].valid) );tableWidget->setItem(entryIndex,COL_ST_DISQ, new qmTwiNumber(data.teamclass[ii+1].disqualified));tableWidget->setItem(entryIndex,COL_ST_NONEQ, new qmTwiNumber(data.teamclass[ii+1].non_equestrian));tableWidget->setItem(entryIndex,COL_ST_VET, new qmTwiNumber(data.teamclass[ii+1].vet_check));tableWidget->setItem(entryIndex,COL_ST_CEV, new qmTwiNumber(data.teamclass[ii+1].valid_ev));tableWidget->setItem(entryIndex,COL_ST_CNE, new qmTwiNumber(data.teamclass[ii+1].valid_ne));}entryIndex++;}}// Insert Totals// Flag that items on this this row is not to be sorted - or at least// sorted at the end//if (reportDataLoaded){tableWidget->setRowCount(tableWidget->rowCount() + 1);tableWidget->setItem(entryIndex,COL_ABR, new qmTwiString("Totals", 1));tableWidget->setItem(entryIndex, COL_CLASS, new qmTwiString(""));tableWidget->setItem(entryIndex, COL_TIME, new qmTwiString(""));tableWidget->setItem(entryIndex, COL_WINNERS, new qmTwiString(""));tableWidget->setItem(entryIndex, COL_NE, new qmTwiString(""));tableWidget->setItem(entryIndex, COL_NE_WINNERS, new qmTwiString(""));tableWidget->setItem(entryIndex, COL_INDEX, new qmTwiString(""));tableWidget->setItem(entryIndex,COL_ST_TOTAL, new qmTwiNumber(data.total.total, 1));tableWidget->setItem(entryIndex,COL_ST_VALID, new qmTwiNumber(data.total.valid, 1));tableWidget->setItem(entryIndex,COL_ST_DISQ, new qmTwiNumber(data.total.disqualified, 1));tableWidget->setItem(entryIndex,COL_ST_NONEQ, new qmTwiNumber(data.total.non_equestrian, 1));tableWidget->setItem(entryIndex,COL_ST_VET, new qmTwiNumber(data.total.vet_check, 1));tableWidget->setItem(entryIndex,COL_ST_CEV, new qmTwiNumber(data.total.valid_ev, 1));tableWidget->setItem(entryIndex,COL_ST_CNE, new qmTwiNumber(data.total.valid_ne, 1));}tableWidget->resizeColumnsToContents();tableWidget->resizeRowsToContents();tableWidget->sortByColumn ( COL_INDEX, Qt::AscendingOrder );tableWidget->setSortingEnabled(true);updateChanged(false);populating = false;}void QmConfClass::save(void){/*** Copy original data*/QmConfig newcfg(config);/*** Extract the data from the Widgets** Trap: The Class info is cross ref by index into the table** Thus we need to maintain the order in the config.*/for (int entryIndex = 0; entryIndex < tableWidget->rowCount(); entryIndex++){// Check that this is a real Entry or the 'Totals' header// Real rows have a COL_INDEX entry that is not empty//QTableWidgetItem *item = tableWidget->item ( entryIndex, COL_INDEX );if (! item){//qDebug("Ignore row:%d", entryIndex);continue;}if ( item->text().length() == 0){//qDebug("Ignore empty row:%d", entryIndex);continue;}int ii = item->text().toInt();//qDebug("Processing: %d", ii);/*** This is a new entry - allocate a new index** Scan the entire available space looking for the first empty slot*/if (ii < 0){for (ii = 0; ii < MAX_CLASS ; ii++){if (newcfg.team_class[ii].abr[0] == 0){break;}}}if (ii >= MAX_CLASS ){qDebug("Ignore row:%d. Class out of range", entryIndex);continue;}item = tableWidget->item ( entryIndex, COL_ABR );if ( item ){strncpy(newcfg.team_class[ii].abr, qPrintable(item->text()), sizeof(newcfg.team_class[ii].abr)-1);}else{*newcfg.team_class[ii].abr = 0;}item = tableWidget->item ( entryIndex, COL_CLASS );if ( item ){strncpy(newcfg.team_class[ii].full_name, qPrintable(item->text()), sizeof(newcfg.team_class[ii].full_name)-1);}else{*newcfg.team_class[ii].full_name = 0;}item = tableWidget->item( entryIndex, COL_TIME );if ( item ){QVariant data = item->data(Qt::EditRole);if (data.isValid()){newcfg.team_class[ii].start = QTime(0,0,0).secsTo(item->data(Qt::EditRole).toTime());}else{newcfg.team_class[ii].start = -1;}}else{newcfg.team_class[ii].start = -1;}item = tableWidget->item( entryIndex, COL_WINNERS );if ( item ){newcfg.class_winners[ii] = item->data(Qt::EditRole).toInt();}else{newcfg.class_winners[ii] = 0;}item = tableWidget->item( entryIndex, COL_NE );if ( item ){if ( item->checkState() == Qt::Checked){newcfg.nonequestrian_class = ii;strncpy(newcfg.nonequestrian_class_abr, newcfg.team_class[ii].abr, sizeof(newcfg.team_class[ii].abr)-1);}}item = tableWidget->item( entryIndex, COL_NE_WINNERS );if ( item ){newcfg.class_ne_winners[ii] = item->data(Qt::EditRole).toInt();}else{newcfg.class_ne_winners[ii] = 0;}}// Validate the datatry{MainWindow::showMessage( "Saving Config");/*** Now do the Class definitions*/for( int i = 0; i < MAX_CLASS; i++ ){compact( newcfg.team_class[i].abr );compact( newcfg.team_class[i].full_name );}for( int i = 0; i < MAX_CLASS; i++ ){if( ( newcfg.team_class[i].abr[0] == '\0' ) != ( newcfg.team_class[i].full_name[0] == '\0' ) ){throw( "Configuration error. Class without description" );}if( newcfg.team_class[i].abr[0] != '\0' && newcfg.team_class[i].start < 0L ){throw( "Configuration error. Bad start time on class" );}}// Calculate new max number of classes// Scan from the end looking for the last one used//for (int i = MAX_CLASS-1; i >= 0; i--){if (newcfg.team_class[i].abr[0]){//qDebug("num_class:%d -> %d", newcfg.num_class, 1+i);newcfg.num_class = 1 + i;break;}}for( int i = newcfg.num_class; i < MAX_CLASS; i++ )if( newcfg.team_class[i].full_name[0] ){qDebug( "Configuration error: Missing Class name. Gaps not allowed" );}if( newcfg.num_class == 0 ){throw( "Error: No categories defined" );}/*** Test for duplicate abbreviations** I know its a slow agorithm, but its only a small set*/for( int i = 0; i < MAX_CLASS; i++ ){if (newcfg.team_class[i].abr[0] != '\0'){for (int j = i+1; j < MAX_CLASS; j++){if (newcfg.team_class[j].abr[0] != '\0'){if ( newcfg.team_class[i].abr[0] == newcfg.team_class[j].abr[0]&& newcfg.team_class[i].abr[1] == newcfg.team_class[j].abr[1]){qDebug("Duplicate abr: %s", newcfg.team_class[i].abr);throw( "Error: Duplicate abbreviations detected");}}}}}/*** Sanity Test*/newcfg.nonequestrian_class = newcfg.lookup_class( newcfg.nonequestrian_class_abr );if( newcfg.equestrian_leg && newcfg.nonequestrian_class == 0 )MainWindow::showMessage( "WARNING: Non-equestrian class not found" );//qDebug("NE Index is: %d", newcfg.nonequestrian_class );/*** Sanity test of the data*/for( int i = 0; i < MAX_CLASS; i++ ){if( newcfg.team_class[i].abr[0] != '\0' && newcfg.class_winners[i] == 0 ){if (newcfg.nonequestrian_class != i+1){MainWindow::showMessage(QString("Warning: Class without winners:")+ newcfg.team_class[i].abr);}}}/*** Cannot mix winners for NE class and NE Winners for each class*/if (newcfg.nonequestrian_class){newcfg.class_ne_winners_by_class = false;for( int i = 0; i < MAX_CLASS; i++ ){if ( newcfg.class_ne_winners[i]){newcfg.class_ne_winners_by_class = true;break;}}if (newcfg.class_winners[newcfg.nonequestrian_class - 1] && newcfg.class_ne_winners_by_class ){MainWindow::showMessage( QString("Should not mix NE winners by each class and by NE Class"));//throw( "Error: Cannot mix NE winners by each class and by NE Class" );}}config = newcfg;config.write_config();updateChanged(false);// Force re-populate so that the index field is now correctpopulate();}catch (const char * str ){MainWindow::showMessage(str);}}void QmConfClass::cancel(void){populate();}QmConfClass::~QmConfClass(){}void QmConfClass::ctxMenu(const QPoint & pos){//qDebug("Context Menu");QMenu *menu = new QMenu;menu->addAction(tr("New Category"), this, SLOT(ctxMenuAddRow()));/*** Determine if we can delete an item** Can delete a category we added in this session** Can delete one if it has no teams using it.*/QTableWidgetItem *item = tableWidget->itemAt(pos);if (item){int row = item->row();//qDebug("Item is at row:%d", row);QTableWidgetItem *indexItem = tableWidget->item ( row, COL_INDEX );if (indexItem){int index = indexItem->text().toInt();//qDebug("Item is at row:%d, Index: %d", row, index);if (index < 0){menu->addAction(tr("Delete Newly added Category"), this, SLOT(ctxMenuDeleteRow()));}else{QTableWidgetItem *itemTotalCount = tableWidget->item ( row, COL_ST_TOTAL );if (itemTotalCount){int count = itemTotalCount->text().toInt();if (count == 0){menu->addAction(tr("Delete Category - its not used"), this, SLOT(ctxMenuDeleteRow()));}}}}}menu->exec(tableWidget->mapToGlobal(pos));}void QmConfClass::ctxMenuDeleteRow(void){//// Assume that if the menu was shown, then we can delete it////qDebug ("DELETE ROW: %d", tableWidget->currentRow () );//tableWidget->removeCellWidget(tableWidget->currentRow (), COL_ABR);//tableWidget->removeRow(tableWidget->currentRow ());tableWidget->item(tableWidget->currentRow (), COL_ABR)->setText("");tableWidget->item(tableWidget->currentRow (), COL_CLASS)->setText("");tableWidget->hideRow(tableWidget->currentRow ());updateChanged(true);}void QmConfClass::ctxMenuAddRow(void){tableWidget->setSortingEnabled(false);tableWidget->setRowCount( 1+ tableWidget->rowCount());/*** Insert non-editable fields in the status part of the table*/tableWidget->setItem(tableWidget->rowCount() - 1, COL_INDEX, new qmTwiNumber(-1));for (int ii= COL_ST_TOTAL; ii < COL_INDEX; ii++){tableWidget->setItem(tableWidget->rowCount() - 1, ii, new qmTwiString(""));}}void QmConfClass::cellChanged(int row,int col){if ( populating )return;if (col == COL_INDEX)return;updateChanged(true);populating = true;//qDebug("Cell changed: %d, %d", row, col);if (col == COL_NE){for (int ii = 0; ii < tableWidget->rowCount(); ii++){QTableWidgetItem *item = tableWidget->item(ii, COL_NE);if (item){item->setCheckState(ii == row ? Qt::Checked : Qt::Unchecked);}}}populating = false;}void QmConfClass::updateChanged(bool newDirty){if (newDirty != dirty){dirty = newDirty;if (dirty){pushButtonSave->setEnabled(true);pushButtonSave->setStyleSheet("background-color: rgb(255, 0, 0);");}else{pushButtonSave->setEnabled(false);pushButtonSave->setStyleSheet("");}}}void QmConfClass::changeEvent(QEvent *e){QWidget::changeEvent(e);switch (e->type()) {default:break;}}