![]() |
XML Mill
1.0.0
A GUI based XML editor with a memory.
|
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 "gcsearchform.h" 00030 #include "ui_gcsearchform.h" 00031 #include "utils/gctreewidgetitem.h" 00032 00033 #include <QMessageBox> 00034 #include <QTextBlock> 00035 00036 /*-------------------------------- NON MEMBER FUNCTIONS --------------------------------*/ 00037 00038 bool lessThan( GCTreeWidgetItem* lhs, GCTreeWidgetItem* rhs ) 00039 { 00040 return ( lhs->index() < rhs->index() ); 00041 } 00042 00043 /*--------------------------------------------------------------------------------------*/ 00044 00045 bool greaterThan( GCTreeWidgetItem* lhs, GCTreeWidgetItem* rhs ) 00046 { 00047 return ( lhs->index() > rhs->index() ); 00048 } 00049 00050 /*---------------------------------- MEMBER FUNCTIONS ----------------------------------*/ 00051 00052 GCSearchForm::GCSearchForm( const QList< GCTreeWidgetItem* >& items, QPlainTextEdit* textEdit, QWidget* parent ) 00053 : QDialog ( parent ), 00054 ui ( new Ui::GCSearchForm ), 00055 m_text ( textEdit ), 00056 m_savedBackground(), 00057 m_savedForeground(), 00058 m_wasFound ( false ), 00059 m_searchUp ( false ), 00060 m_firstRun ( true ), 00061 m_previousIndex ( -1 ), 00062 m_searchFlags ( 0 ), 00063 m_items ( items ) 00064 { 00065 ui->setupUi( this ); 00066 ui->lineEdit->setFocus(); 00067 //m_text->setText( docContents ); 00068 00069 connect( ui->searchButton, SIGNAL( clicked() ), this, SLOT( search() ) ); 00070 connect( ui->closeButton, SIGNAL( clicked() ), this, SLOT( close() ) ); 00071 00072 connect( ui->caseSensitiveCheckBox, SIGNAL( clicked() ), this, SLOT( caseSensitive() ) ); 00073 connect( ui->wholeWordsCheckBox, SIGNAL( clicked() ), this, SLOT( wholeWords() ) ); 00074 connect( ui->searchUpCheckBox, SIGNAL( clicked() ), this, SLOT( searchUp() ) ); 00075 00076 setAttribute( Qt::WA_DeleteOnClose ); 00077 } 00078 00079 /*--------------------------------------------------------------------------------------*/ 00080 00081 GCSearchForm::~GCSearchForm() 00082 { 00083 /* The QDomDocument m_doc points at is owned externally. */ 00084 delete ui; 00085 } 00086 00087 /*--------------------------------------------------------------------------------------*/ 00088 00089 void GCSearchForm::search() 00090 { 00091 m_firstRun = false; 00092 QString searchText = ui->lineEdit->text(); 00093 bool found = m_text->find( searchText, m_searchFlags ); 00094 00095 /* The first time we enter this function, if the text does not exist 00096 within the document, "found" and "m_wasFound" will both be false. 00097 However, if the text does exist, we wish to know that we found a match 00098 at least once so that, when we reach the end of the document and "found" 00099 is once more false, we can reset all indices and flags in order to start 00100 again from the beginning. */ 00101 if( ( ( found != m_wasFound ) && m_wasFound ) || 00102 ( !m_wasFound && m_searchUp ) ) 00103 { 00104 resetCursor(); 00105 found = m_text->find( searchText, m_searchFlags ); 00106 } 00107 00108 if( found ) 00109 { 00110 m_wasFound = true; 00111 00112 /* Highlight the entire node (element, attributes and attribute values) 00113 in which the match was found. */ 00114 m_text->moveCursor( QTextCursor::StartOfLine ); 00115 m_text->moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); 00116 00117 /* Find all tree widget items whose corresponding element node matches the 00118 highlighted text. */ 00119 QString nodeText = m_text->textCursor().selectedText().trimmed(); 00120 QList< GCTreeWidgetItem* > matchingItems = gatherMatchingItems( nodeText ); 00121 00122 if ( !matchingItems.empty() ) 00123 { 00124 if( !m_searchUp ) 00125 { 00126 /* Sort ascending. */ 00127 qSort( matchingItems.begin(), matchingItems.end(), lessThan ); 00128 findMatchingTreeItem( matchingItems, true ); 00129 } 00130 else 00131 { 00132 /* Sort descending. */ 00133 qSort( matchingItems.begin(), matchingItems.end(), greaterThan ); 00134 findMatchingTreeItem( matchingItems, false ); 00135 } 00136 } 00137 else 00138 { 00139 highlightFind(); 00140 } 00141 } 00142 else 00143 { 00144 QMessageBox::information( this, "Not Found", QString( "Can't find the text:\"%1\"" ).arg( searchText ) ); 00145 } 00146 } 00147 00148 /*--------------------------------------------------------------------------------------*/ 00149 00150 void GCSearchForm::resetCursor() 00151 { 00152 /* Reset cursor so that we may keep cycling through the document content. */ 00153 if( ui->searchUpCheckBox->isChecked() ) 00154 { 00155 m_text->moveCursor( QTextCursor::End ); 00156 QMessageBox::information( this, "Reached Top", "Search reached top, continuing at bottom." ); 00157 m_previousIndex = 9999999; 00158 } 00159 else 00160 { 00161 m_text->moveCursor( QTextCursor::Start ); 00162 QMessageBox::information( this, "Reached Bottom", "Search reached bottom, continuing at top." ); 00163 m_previousIndex = -1; 00164 } 00165 } 00166 00167 /*--------------------------------------------------------------------------------------*/ 00168 00169 void GCSearchForm::searchUp() 00170 { 00171 /* If the user ticks the "Search Up" box before anything else, we need to set the 00172 previous index to a large value to ensure we start at the very bottom. */ 00173 if( m_firstRun ) 00174 { 00175 m_previousIndex = 9999999; 00176 } 00177 00178 if( ui->searchUpCheckBox->isChecked() ) 00179 { 00180 m_searchFlags |= QTextDocument::FindBackward; 00181 m_searchUp = true; 00182 } 00183 else 00184 { 00185 m_searchFlags ^= QTextDocument::FindBackward; 00186 m_searchUp = false; 00187 } 00188 } 00189 00190 /*--------------------------------------------------------------------------------------*/ 00191 00192 void GCSearchForm::caseSensitive() 00193 { 00194 /* Reset found flag every time the user changes the search options. */ 00195 m_wasFound = false; 00196 00197 if( ui->caseSensitiveCheckBox->isChecked() ) 00198 { 00199 m_searchFlags |= QTextDocument::FindCaseSensitively; 00200 } 00201 else 00202 { 00203 m_searchFlags ^= QTextDocument::FindCaseSensitively; 00204 } 00205 } 00206 00207 /*--------------------------------------------------------------------------------------*/ 00208 00209 void GCSearchForm::wholeWords() 00210 { 00211 /* Reset found flag every time the user changes the search options. */ 00212 m_wasFound = false; 00213 00214 if( ui->wholeWordsCheckBox->isChecked() ) 00215 { 00216 m_searchFlags |= QTextDocument::FindWholeWords; 00217 } 00218 else 00219 { 00220 m_searchFlags ^= QTextDocument::FindWholeWords; 00221 } 00222 } 00223 00224 /*--------------------------------------------------------------------------------------*/ 00225 00226 QList< GCTreeWidgetItem* > GCSearchForm::gatherMatchingItems( const QString& nodeText ) 00227 { 00228 QList< GCTreeWidgetItem* > matchingItems; 00229 00230 for( int i = 0; i < m_items.size(); ++i ) 00231 { 00232 GCTreeWidgetItem* treeItem = m_items.at( i ); 00233 00234 if( treeItem->toString() == nodeText ) 00235 { 00236 matchingItems.append( treeItem ); 00237 } 00238 } 00239 00240 return matchingItems; 00241 } 00242 00243 /*--------------------------------------------------------------------------------------*/ 00244 00245 void GCSearchForm::findMatchingTreeItem( const QList< GCTreeWidgetItem* > matchingItems, bool ascending ) 00246 { 00247 for( int i = 0; i < matchingItems.size(); ++i ) 00248 { 00249 GCTreeWidgetItem* treeItem = matchingItems.at( i ); 00250 00251 if( ( ascending && treeItem->index() > m_previousIndex ) || 00252 ( !ascending && treeItem->index() < m_previousIndex ) ) 00253 { 00254 m_previousIndex = treeItem->index(); 00255 resetHighlights(); 00256 emit foundItem( treeItem ); 00257 break; 00258 } 00259 } 00260 00261 if( ui->searchButton->text() == "Search" ) 00262 { 00263 ui->searchButton->setText( "Next" ); 00264 } 00265 } 00266 00267 /*--------------------------------------------------------------------------------------*/ 00268 00269 void GCSearchForm::resetHighlights() 00270 { 00271 QList< QTextEdit::ExtraSelection > extras = m_text->extraSelections(); 00272 00273 for( int i = 0; i < extras.size(); ++i ) 00274 { 00275 extras[ i ].format.setProperty( QTextFormat::FullWidthSelection, true ); 00276 extras[ i ].format.setBackground( m_savedBackground ); 00277 extras[ i ].format.setForeground( m_savedForeground ); 00278 } 00279 00280 m_text->setExtraSelections( extras ); 00281 } 00282 00283 /*--------------------------------------------------------------------------------------*/ 00284 00285 void GCSearchForm::highlightFind() 00286 { 00287 /* First we reset all previous selections. */ 00288 resetHighlights(); 00289 00290 /* Now we can set the highlighted text. */ 00291 m_savedBackground = m_text->textCursor().blockCharFormat().background(); 00292 m_savedForeground = m_text->textCursor().blockCharFormat().foreground(); 00293 00294 QTextEdit::ExtraSelection extra; 00295 extra.cursor = m_text->textCursor(); 00296 extra.format.setBackground( QApplication::palette().highlight() ); 00297 extra.format.setForeground( QApplication::palette().highlightedText() ); 00298 00299 QList< QTextEdit::ExtraSelection > extras; 00300 extras << extra; 00301 m_text->setExtraSelections( extras ); 00302 } 00303 00304 /*--------------------------------------------------------------------------------------*/