XML Mill  1.0.0
A GUI based XML editor with a memory.
gcaddsnippetsform.cpp
00001 /* Copyright (c) 2012 - 2013 by William Hallatt.
00002  *
00003  * This file forms part of "XML Mill".
00004  *
00005  * The official website for this project is <http://www.goblincoding.com> and,
00006  * although not compulsory, it would be appreciated if all works of whatever
00007  * nature using this source code (in whole or in part) include a reference to
00008  * this site.
00009  *
00010  * Should you wish to contact me for whatever reason, please do so via:
00011  *
00012  *                 <http://www.goblincoding.com/contact>
00013  *
00014  * This program is free software: you can redistribute it and/or modify it under
00015  * the terms of the GNU General Public License as published by the Free Software
00016  * Foundation, either version 3 of the License, or (at your option) any later
00017  * version.
00018  *
00019  * This program is distributed in the hope that it will be useful, but WITHOUT
00020  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00021  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License along with
00024  * this program (GNUGPL.txt).  If not, see
00025  *
00026  *                    <http://www.gnu.org/licenses/>
00027  */
00028 
00029 #include "gcaddsnippetsform.h"
00030 #include "ui_gcaddsnippetsform.h"
00031 #include "db/gcdatabaseinterface.h"
00032 #include "utils/gccombobox.h"
00033 #include "utils/gcmessagespace.h"
00034 #include "utils/gcglobalspace.h"
00035 #include "utils/gctreewidgetitem.h"
00036 
00037 #include <QCheckBox>
00038 #include <QMessageBox>
00039 
00040 /*--------------------------------------------------------------------------------------*/
00041 
00042 const int LABELCOLUMN = 0;
00043 const int COMBOCOLUMN = 1;
00044 const int INCRCOLUMN = 2;
00045 
00046 /*--------------------------------------------------------------------------------------*/
00047 
00048 GCAddSnippetsForm::GCAddSnippetsForm( const QString& elementName, GCTreeWidgetItem* parentItem, QWidget* parent )
00049 : QDialog            ( parent ),
00050   ui                 ( new Ui::GCAddSnippetsForm ),
00051   m_parentItem       ( parentItem ),
00052   m_treeItemActivated( false )
00053 {
00054   ui->setupUi( this );
00055   ui->tableWidget->setFont( QFont( GCGlobalSpace::FONT, GCGlobalSpace::FONTSIZE ) );
00056   ui->tableWidget->horizontalHeader()->setFont( QFont( GCGlobalSpace::FONT, GCGlobalSpace::FONTSIZE ) );
00057 
00058   ui->tableWidget->setColumnWidth( INCRCOLUMN, 40 );  // restricted for checkbox
00059   ui->treeWidget->setColumnWidth( 0, 50 );           // restricted for checkbox
00060   ui->showHelpButton->setVisible( GCGlobalSpace::showHelpButtons() );
00061 
00062   ui->treeWidget->populateFromDatabase( elementName );
00063   ui->treeWidget->setAllCheckStates( Qt::Checked );
00064   elementSelected( ui->treeWidget->gcCurrentItem(), 0 );
00065 
00066   connect( ui->closeButton, SIGNAL( clicked() ), this, SLOT( close() ) );
00067   connect( ui->addButton, SIGNAL( clicked() ), this, SLOT( addSnippet() ) );
00068   connect( ui->showHelpButton, SIGNAL( clicked() ), this, SLOT( showHelp() ) );
00069   connect( ui->tableWidget, SIGNAL( itemChanged( QTableWidgetItem* ) ), this, SLOT( attributeChanged( QTableWidgetItem* ) ) );
00070   connect( ui->treeWidget, SIGNAL( gcCurrentItemSelected( GCTreeWidgetItem*, int ) ), this, SLOT( elementSelected( GCTreeWidgetItem*, int ) ) );
00071 
00072   setAttribute( Qt::WA_DeleteOnClose );
00073 }
00074 
00075 /*--------------------------------------------------------------------------------------*/
00076 
00077 GCAddSnippetsForm::~GCAddSnippetsForm()
00078 {
00079   delete ui;
00080 }
00081 
00082 /*--------------------------------------------------------------------------------------*/
00083 
00084 void GCAddSnippetsForm::elementSelected( GCTreeWidgetItem* item, int column )
00085 {
00086   Q_UNUSED( column );
00087 
00088   if( item )
00089   {
00090     m_treeItemActivated = true;
00091 
00092     ui->tableWidget->clearContents();   // also deletes current items
00093     ui->tableWidget->setRowCount( 0 );
00094 
00095     /* Populate the table widget with the attributes and values associated with the element selected. */
00096     QString elementName = item->name();
00097     QStringList attributeNames = GCDataBaseInterface::instance()->attributes( elementName );
00098 
00099     /* Create and add the "increment" checkbox to the first column of the table widget, add all the
00100     known attribute names to the cells in the second column of the table widget, create and populate
00101     combo boxes with the values associated with the attributes in question and insert the combo boxes
00102     into the third column of the table widget. */
00103     for( int i = 0; i < attributeNames.count(); ++i )
00104     {
00105       ui->tableWidget->setRowCount( i + 1 );
00106 
00107       QCheckBox* checkBox = new QCheckBox;
00108 
00109       /* Overrides main style sheet. */
00110       checkBox->setStyleSheet( "QCheckBox{ padding-right: 1px; }"
00111                                "QCheckBox::indicator{ subcontrol-position: center; width: 15px; height: 15px; }" );
00112 
00113       ui->tableWidget->setCellWidget( i, INCRCOLUMN, checkBox );
00114       connect( checkBox, SIGNAL( clicked() ), this, SLOT( attributeValueChanged() ) );
00115 
00116       QDomAttr attribute = item->element().attributeNode( attributeNames.at( i ) ).toAttr();
00117       checkBox->setChecked( item->incrementAttribute( attribute.name() ) );
00118 
00119       /* Items are editable by default, disable this option. */
00120       QTableWidgetItem* label = new QTableWidgetItem( attributeNames.at( i ) );
00121       label->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable );
00122       ui->tableWidget->setItem( i, LABELCOLUMN, label );
00123 
00124       GCComboBox* attributeCombo = new GCComboBox;
00125       attributeCombo->addItems( GCDataBaseInterface::instance()->attributeValues( elementName, attributeNames.at( i ) ) );
00126       attributeCombo->setEditable( true );
00127       attributeCombo->setCurrentIndex( attributeCombo->findText( item->element().attribute( attributeNames.at( i ) ) ) );
00128 
00129       connect( attributeCombo, SIGNAL( currentIndexChanged( QString ) ), this, SLOT( attributeValueChanged() ) );
00130 
00131       if( item->attributeIncluded( attributeNames.at( i ) ) )
00132       {
00133         label->setCheckState( Qt::Checked );
00134         attributeCombo->setEnabled( true );
00135       }
00136       else
00137       {
00138         label->setCheckState( Qt::Unchecked );
00139         attributeCombo->setEnabled( false );
00140       }
00141 
00142       ui->tableWidget->setCellWidget( i, COMBOCOLUMN, attributeCombo );
00143 
00144       if( item->checkState( 0 ) == Qt::Unchecked )
00145       {
00146         ui->tableWidget->setEnabled( false );
00147       }
00148       else
00149       {
00150         ui->tableWidget->setEnabled( true );
00151       }
00152     }
00153 
00154     updateCheckStates( item );
00155 
00156     ui->tableWidget->horizontalHeader()->setSectionResizeMode( LABELCOLUMN, QHeaderView::Stretch );
00157     ui->tableWidget->horizontalHeader()->setSectionResizeMode( COMBOCOLUMN, QHeaderView::Stretch );
00158     ui->tableWidget->horizontalHeader()->setSectionResizeMode( INCRCOLUMN, QHeaderView::Fixed );
00159 
00160     m_treeItemActivated = false;
00161   }
00162 }
00163 
00164 /*--------------------------------------------------------------------------------------*/
00165 
00166 void GCAddSnippetsForm::attributeChanged( QTableWidgetItem* item ) const
00167 {
00168   if( !m_treeItemActivated )
00169   {
00170     GCTreeWidgetItem* treeItem = ui->treeWidget->gcCurrentItem();
00171     GCComboBox* attributeValueCombo = dynamic_cast< GCComboBox* >( ui->tableWidget->cellWidget( item->row(), COMBOCOLUMN ) );
00172     QCheckBox* checkBox = dynamic_cast< QCheckBox* >( ui->tableWidget->cellWidget( item->row(), INCRCOLUMN ) );
00173 
00174     if( item->checkState() == Qt::Checked )
00175     {
00176       attributeValueCombo->setEnabled( true );
00177       checkBox->setEnabled( true );
00178       treeItem->includeAttribute( item->text(), attributeValueCombo->currentText() );
00179     }
00180     else
00181     {
00182       attributeValueCombo->setEnabled( false );
00183       checkBox->setEnabled( false );
00184       treeItem->excludeAttribute( item->text() );
00185     }
00186   }
00187 }
00188 
00189 /*--------------------------------------------------------------------------------------*/
00190 
00191 void GCAddSnippetsForm::attributeValueChanged() const
00192 {
00193   /* Update the element's attribute inclusions, values and value increment flags. */
00194   GCTreeWidgetItem* treeItem = ui->treeWidget->gcCurrentItem();
00195   QDomNamedNodeMap attributes = treeItem->element().attributes();
00196 
00197   /* The table doesn't know which attributes are included or excluded and contains
00198     rows corresponding to all the attributes associated with the element. We only
00199     wish to act on attributes currently included. */
00200   for( int i = 0; i < attributes.size(); ++i )
00201   {
00202     for( int j = 0; j < ui->tableWidget->rowCount(); ++j )
00203     {
00204       QString attributeName = ui->tableWidget->item( j, LABELCOLUMN )->text();
00205 
00206       if( attributeName == attributes.item( i ).nodeName() )
00207       {
00208         QCheckBox* checkBox = dynamic_cast< QCheckBox* >( ui->tableWidget->cellWidget( j, INCRCOLUMN ) );
00209         GCComboBox* comboBox = dynamic_cast< GCComboBox* >( ui->tableWidget->cellWidget( j, COMBOCOLUMN ) );
00210         QString attributeValue = comboBox->currentText();
00211 
00212         if( treeItem->attributeIncluded( attributeName ) )
00213         {
00214           treeItem->includeAttribute( attributeName, attributeValue );
00215         }
00216 
00217         treeItem->setIncrementAttribute( attributeName, checkBox->isChecked() );
00218       }
00219     }
00220   }
00221 }
00222 
00223 /*--------------------------------------------------------------------------------------*/
00224 
00225 void GCAddSnippetsForm::addSnippet()
00226 {
00227   QList< GCTreeWidgetItem* > includedItems = ui->treeWidget->includedTreeWidgetItems();
00228 
00229   /* Add the required number of snippets. */
00230   for( int i = 0; i < ui->spinBox->value(); ++i )
00231   {
00232     /* Update all the included elements and attribute values. */
00233     for( int j = 0; j < includedItems.size(); ++j )
00234     {
00235       GCTreeWidgetItem* localItem = includedItems.at( j );
00236 
00237       /* Sets a "restore point" so that we may increment attribute values and return
00238         to the previously fixed values (to avoid incrementing an incremented value). */
00239       localItem->fixAttributeValues();
00240 
00241       QString elementName = localItem->element().tagName();
00242       QDomNamedNodeMap attributes = localItem->element().attributes();
00243 
00244       for( int k = 0; k < attributes.size(); ++k )
00245       {
00246         QDomAttr attr = attributes.item( k ).toAttr();
00247         QString attributeValue = localItem->fixedValue( attr.name() );
00248 
00249         if( localItem->incrementAttribute( attr.name() ) )
00250         {
00251           /* Check if this is a number (if it contains any non-digit character). */
00252           if( !attributeValue.contains( QRegExp( "\\D+" ) ) )
00253           {
00254             bool ok( false );
00255             int intValue = attributeValue.toInt( &ok );
00256 
00257             if( ok )
00258             {
00259               intValue += i;
00260               attributeValue = QString( "%1" ).arg( intValue );
00261             }
00262           }
00263           else
00264           {
00265             /* If the value contains some string characters, it's a string value and that's all
00266               there is to it (it's not our responsibility to check that someone isn't incrementing
00267               "false", e.g.). */
00268             attributeValue += QString( "%1" ).arg( i );
00269           }
00270 
00271           localItem->element().setAttribute( attr.name(), attributeValue );
00272         }
00273 
00274         /* This call does nothing if the attribute value already exists. */
00275         GCDataBaseInterface::instance()->updateAttributeValues( elementName, attr.name(), QStringList( attributeValue ) );
00276       }
00277     }
00278 
00279     emit snippetAdded( m_parentItem, ui->treeWidget->cloneDocument().toElement() );
00280 
00281     /* Restore values. */
00282     for( int j = 0; j < includedItems.size(); ++j )
00283     {
00284       includedItems.at( j )->revertToFixedValues();
00285     }
00286   }
00287 }
00288 
00289 /*--------------------------------------------------------------------------------------*/
00290 
00291 void GCAddSnippetsForm::updateCheckStates( GCTreeWidgetItem* item ) const
00292 {
00293   /* Checking or unchecking an item must recursively update its children as well. */
00294   if( item->checkState( 0 ) == Qt::Checked )
00295   {
00296     item->setExcludeElement( false );
00297 
00298     /* When a low-level child is activated, we need to also update its parent tree all the way
00299       up to the root element since including a child automatically implies that the parent
00300       element is included. */
00301     GCTreeWidgetItem* parent = item->gcParent();
00302 
00303     while( parent && ( parent->checkState( 0 ) != Qt::Checked ) )
00304     {
00305       parent->setExcludeElement( false );
00306       parent->setCheckState( 0, Qt::Checked );
00307       parent = parent->gcParent();
00308     }
00309   }
00310   else
00311   {
00312     item->setExcludeElement( true );
00313 
00314     for( int i = 0; i < item->childCount(); ++i )
00315     {
00316       item->child( i )->setCheckState( 0, item->checkState( 0 ) );
00317       updateCheckStates( item->gcChild( i ) );
00318     }
00319   }
00320 }
00321 
00322 /*--------------------------------------------------------------------------------------*/
00323 
00324 void GCAddSnippetsForm::showHelp()
00325 {
00326   QMessageBox::information( this,
00327                             "How this works...",
00328                             "Use this form to create XML snippets with the default values "
00329                             "you specify. \n\n"
00330                             "Unchecking an element will exclude it (and all of its children) from "
00331                             "the snippet (similarly, check attributes that you want to be included in "
00332                             "your snippet(s)). \n\n"
00333                             "If you tick the \"Incr\" (increment) option next to an attribute value, then the "
00334                             "value you provided will be incremented with \"1\" for however many "
00335                             "snippets you generate. \n\n"
00336                             "For example, if you specify \"10\" as an attribute value, then the "
00337                             "first snippet will assign \"10\" to the attribute in question, "
00338                             "the second will have \"11\", the third, \"12\", etc. \n\n"
00339                             "Strings will have the incremented value appended to the name (\"true\" "
00340                             "and \"false\" values are treated as strings, so be careful)." );
00341 }
00342 
00343 /*--------------------------------------------------------------------------------------*/