XML Mill  1.0.0
A GUI based XML editor with a memory.
gcremoveitemsform.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 "gcremoveitemsform.h"
00030 #include "ui_gcremoveitemsform.h"
00031 #include "db/gcdatabaseinterface.h"
00032 #include "utils/gcmessagespace.h"
00033 #include "utils/gcglobalspace.h"
00034 #include "utils/gctreewidgetitem.h"
00035 
00036 #include <QMessageBox>
00037 
00038 /*--------------------------------------------------------------------------------------*/
00039 
00040 GCRemoveItemsForm::GCRemoveItemsForm( QWidget* parent )
00041 : QDialog               ( parent ),
00042   ui                    ( new Ui::GCRemoveItemsForm ),
00043   m_currentElement      ( "" ),
00044   m_currentElementParent( "" ),
00045   m_currentAttribute    ( "" ),
00046   m_deletedElements     ()
00047 {
00048   ui->setupUi( this );
00049   ui->showAttributeHelpButton->setVisible( GCGlobalSpace::showHelpButtons() );
00050   ui->showElementHelpButton->setVisible( GCGlobalSpace::showHelpButtons() );
00051 
00052   connect( ui->showElementHelpButton, SIGNAL( clicked() ), this, SLOT( showElementHelp() ) );
00053   connect( ui->showAttributeHelpButton, SIGNAL( clicked() ), this, SLOT( showAttributeHelp() ) );
00054   connect( ui->updateValuesButton, SIGNAL( clicked() ), this, SLOT( updateAttributeValues() ) );
00055   connect( ui->deleteAttributeButton, SIGNAL( clicked() ), this, SLOT( deleteAttribute() ) );
00056   connect( ui->deleteElementButton, SIGNAL( clicked() ), this, SLOT( deleteElement() ) );
00057   connect( ui->removeFromParentButton, SIGNAL( clicked() ), this, SLOT( removeChildElement() ) );
00058 
00059   connect( ui->treeWidget, SIGNAL( gcCurrentItemSelected( GCTreeWidgetItem*, int ) ), this, SLOT( elementSelected( GCTreeWidgetItem*, int ) ) );
00060   connect( ui->comboBox, SIGNAL( currentIndexChanged( QString ) ), this, SLOT( attributeActivated( QString ) ) );
00061 
00062   ui->treeWidget->populateFromDatabase();
00063 
00064   setAttribute( Qt::WA_DeleteOnClose );
00065 }
00066 
00067 /*--------------------------------------------------------------------------------------*/
00068 
00069 GCRemoveItemsForm::~GCRemoveItemsForm()
00070 {
00071   delete ui;
00072 }
00073 
00074 /*--------------------------------------------------------------------------------------*/
00075 
00076 void GCRemoveItemsForm::elementSelected( GCTreeWidgetItem* item, int column )
00077 {
00078   Q_UNUSED( column )
00079   ;
00080 
00081   if( item )
00082   {
00083     if( item->gcParent() )
00084     {
00085       m_currentElementParent = item->gcParent()->name();
00086     }
00087 
00088     m_currentElement = item->name();
00089 
00090     /* Since it isn't illegal to have elements with children of the same name, we cannot
00091       block it in the DB, however, if we DO have elements with children of the same name,
00092       we don't want the user to delete the element since bad things will happen. */
00093     if( m_currentElement == m_currentElementParent )
00094     {
00095       ui->deleteElementButton->setEnabled( false );
00096     }
00097     else
00098     {
00099       ui->deleteElementButton->setEnabled( true );
00100     }
00101 
00102     QStringList attributes = GCDataBaseInterface::instance()->attributes( m_currentElement );
00103 
00104     ui->comboBox->clear();
00105     ui->comboBox->addItems( attributes );
00106   }
00107 }
00108 
00109 /*--------------------------------------------------------------------------------------*/
00110 
00111 void GCRemoveItemsForm::attributeActivated( const QString& attribute )
00112 {
00113   m_currentAttribute = attribute;
00114   QStringList attributeValues = GCDataBaseInterface::instance()->attributeValues( m_currentElement, m_currentAttribute );
00115 
00116   ui->plainTextEdit->clear();
00117 
00118   foreach( QString value, attributeValues )
00119   {
00120    ui->plainTextEdit->insertPlainText( QString( "%1\n" ).arg( value ) );
00121   }
00122 }
00123 
00124 /*--------------------------------------------------------------------------------------*/
00125 
00126 void GCRemoveItemsForm::deleteElement( const QString& element )
00127 {
00128   /* If the element name is empty, then this function was called directly by the user
00129     clicking on "delete" (as opposed to this function being called further down below
00130     during the recursive process of getting rid of the element's children) in that case,
00131     the first element to be removed is the current one (set in "elementSelected"). */
00132   QString currentElement = ( element.isEmpty() ) ? m_currentElement : element;
00133 
00134   QStringList children = GCDataBaseInterface::instance()->children( currentElement );
00135   m_deletedElements.clear();
00136 
00137   /* Attributes and values must be removed before we can remove elements and we must also
00138     ensure that children are removed before their parents.  To achieve this, we need to ensure
00139     that we clean the element tree from "the bottom up". */
00140   if( !children.isEmpty() )
00141   {
00142     foreach( QString child, children )
00143     {
00144       if( GCDataBaseInterface::instance()->isUniqueChildElement( currentElement, child ) )
00145       {
00146         deleteElement( child );
00147       }
00148     }
00149   }
00150 
00151   /* Remove all the attributes (and their known values) associated with this element. */
00152   QStringList attributes = GCDataBaseInterface::instance()->attributes( currentElement );
00153 
00154   foreach( QString attribute, attributes )
00155   {
00156     if( !GCDataBaseInterface::instance()->removeAttribute( currentElement, attribute ) )
00157     {
00158       GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00159     }
00160   }
00161 
00162   /* Now we can remove the element itself. */
00163   if( !GCDataBaseInterface::instance()->removeElement( currentElement ) )
00164   {
00165     GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00166   }
00167   else
00168   {
00169     m_deletedElements.append( currentElement );
00170   }
00171 
00172   /* Check if the user removed a root element. */
00173   if( GCDataBaseInterface::instance()->knownRootElements().contains( currentElement ) )
00174   {
00175     if( !GCDataBaseInterface::instance()->removeRootElement( currentElement ) )
00176     {
00177       GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00178     }
00179   }
00180 
00181   /* Remove the element from all its parents' first level child lists. */
00182   updateChildLists();
00183 
00184   ui->comboBox->clear();
00185   ui->plainTextEdit->clear();
00186   ui->treeWidget->populateFromDatabase();
00187 }
00188 
00189 /*--------------------------------------------------------------------------------------*/
00190 
00191 void GCRemoveItemsForm::removeChildElement()
00192 {
00193   if( !m_currentElementParent.isEmpty() )
00194   {
00195     if( !GCDataBaseInterface::instance()->removeChildElement( m_currentElementParent, m_currentElement ) )
00196     {
00197       GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00198     }
00199     else
00200     {
00201       if( GCDataBaseInterface::instance()->isUniqueChildElement( m_currentElementParent, m_currentElement ) )
00202       {
00203         bool accepted = GCMessageSpace::userAccepted( "RemoveUnlistedElement",
00204                                                       "Element not used",
00205                                                       QString( "\"%1\" is not assigned to any other element (i.e. "
00206                                                                "it isn't used anywhere else in the profile).\n"
00207                                                                "Would you like to remove the element completely?" ).arg( m_currentElement ),
00208                                                       GCMessageSpace::YesNo,
00209                                                       GCMessageSpace::No,
00210                                                       GCMessageSpace::Question );
00211 
00212         if( accepted )
00213         {
00214           deleteElement();
00215         }
00216       }
00217 
00218       ui->comboBox->clear();
00219       ui->plainTextEdit->clear();
00220       ui->treeWidget->populateFromDatabase();
00221     }
00222   }
00223 }
00224 
00225 /*--------------------------------------------------------------------------------------*/
00226 
00227 void GCRemoveItemsForm::updateAttributeValues()
00228 {
00229   QStringList attributes = ui->plainTextEdit->toPlainText().split( "\n" );
00230   attributes.removeAll( "" );
00231 
00232   if( attributes.isEmpty() )
00233   {
00234     bool accepted = GCMessageSpace::userAccepted( "UpdateEmptyAttributeValues",
00235                                                   "Update with empty attribute values?",
00236                                                   "All known values were removed. "
00237                                                   "Would you like to remove the attribute completely?",
00238                                                   GCMessageSpace::YesNo,
00239                                                   GCMessageSpace::No,
00240                                                   GCMessageSpace::Question );
00241 
00242     if( accepted )
00243     {
00244       deleteAttribute();
00245     }
00246   }
00247   else
00248   {
00249     /* All existing values will be replaced with whatever remained in the text edit by the time the
00250       user was done. */
00251     if( !GCDataBaseInterface::instance()->updateAttributeValues( m_currentElement, m_currentAttribute, attributes, true ) )
00252     {
00253       GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00254     }
00255     else
00256     {
00257       QMessageBox::information( this, "Success", "Done!" );
00258     }
00259   }
00260 }
00261 
00262 /*--------------------------------------------------------------------------------------*/
00263 
00264 void GCRemoveItemsForm::deleteAttribute()
00265 {
00266   if( !GCDataBaseInterface::instance()->removeAttribute( m_currentElement, m_currentAttribute ) )
00267   {
00268     GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00269   }
00270   else
00271   {
00272     /* Purely for cosmetic effect - updates the tree item to reflect the correct node text when
00273       in "verbose" mode. */
00274     ui->treeWidget->gcCurrentItem()->excludeAttribute( m_currentAttribute );
00275 
00276     ui->comboBox->removeItem( ui->comboBox->findText( m_currentAttribute ) );
00277   }
00278 }
00279 
00280 /*--------------------------------------------------------------------------------------*/
00281 
00282 void GCRemoveItemsForm::updateChildLists()
00283 {
00284   QStringList knownElements = GCDataBaseInterface::instance()->knownElements();
00285 
00286   foreach( QString element, knownElements )
00287   {
00288    QStringList children = GCDataBaseInterface::instance()->children( element );
00289 
00290    if( !children.isEmpty() )
00291    {
00292      foreach( QString deletedElement, m_deletedElements )
00293      {
00294        if( children.contains( deletedElement ) )
00295        {
00296          if( !GCDataBaseInterface::instance()->removeChildElement( element, deletedElement ) )
00297          {
00298            GCMessageSpace::showErrorMessageBox( this, GCDataBaseInterface::instance()->lastError() );
00299          }
00300        }
00301      }
00302    }
00303   }
00304 }
00305 
00306 /*--------------------------------------------------------------------------------------*/
00307 
00308 void GCRemoveItemsForm::showElementHelp()
00309 {
00310   QMessageBox::information( this,
00311                             "How this works...",
00312                             "\"Remove Child\" will remove the currently highlighted element "
00313                             "from its parent element's child list, i.e. it will only "
00314                             "affect the relationship between the two elements, the element "
00315                             "itself is not deleted in the process and will remain in the profile. \n\n"
00316                             "\"Delete Element\" will delete the element, the element's children, "
00317                             "the children's children (etc, etc), its associated attributes, the "
00318                             "associated attributes of its children (and their children, etc, etc), all "
00319                             "the known values for all the attributes thus deleted and finally also "
00320                             "remove the element (and its children and the children's children, etc etc) "
00321                             "from every single child list that contains it.\n\n"
00322                             "None of this can be undone. " );
00323 }
00324 
00325 /*--------------------------------------------------------------------------------------*/
00326 
00327 void GCRemoveItemsForm::showAttributeHelp()
00328 {
00329   QMessageBox::information( this,
00330                             "How this works...",
00331                             "\"Delete Attribute\" will also delete all its known values.\n\n"
00332                             "\"Update Attribute Values\" - Only those values remaining in the text edit when "
00333                             "\"Update Attribute Values\" is clicked will be saved against the attribute shown "
00334                             "in the drop down (this effectively means that you could also add new values "
00335                             "to the attribute if you wish).  Just make sure that all the values you want to "
00336                             "associate with the attribute when you're done appear on separate lines." );
00337 }
00338 
00339 /*--------------------------------------------------------------------------------------*/