this may be called beta... look for bugs
[qsnippetsmanager.git] / mainwindow.cpp
blobd2206572761d73c2ae423f965aa6eb2594c24f3a
1 #include "Snippet.h"
2 #include "mainwindow.h"
3 #include "ui_mainwindow.h"
4 #include "ui_workmodedialog.h"
6 MainWindow::MainWindow(QWidget *parent)
7 : QMainWindow(parent), ui(new Ui::MainWindowClass), model( this ), workModeDialog( this )
9 ui->setupUi(this);
10 ui->tabWidget->removeTab( 0 );
11 ui->searchLineEdit->setFocus();
13 // set up icons
14 categoryIcon.addPixmap( style()->standardPixmap( QStyle::SP_DirClosedIcon ), QIcon::Normal, QIcon::Off );
15 categoryIcon.addPixmap( style()->standardPixmap( QStyle::SP_DirOpenIcon ), QIcon::Normal, QIcon::On );
16 snippetIcon.addPixmap( style()->standardPixmap( QStyle::SP_FileIcon ) );
18 // set up actions
19 ui->action_Save->setEnabled( false );
20 ui->actionSave_all->setEnabled( false );
21 ui->action_Close->setEnabled( false );
22 ui->actionClos_e_all->setEnabled( false );
24 // load snippets from snippets.xml and set up view
25 loadSnippets();
26 ui->snippetTreeView->setModel( &model );
27 ui->snippetTreeView->expandAll();
28 workModeDialog.m_ui->treeView->expandAll();
30 // connections
31 connect( ui->searchLineEdit, SIGNAL( textChanged(QString) ),
32 this, SLOT(on_searchLineEdit_textChanged(QString)) );
33 connect( &model, SIGNAL( itemChanged( QStandardItem* ) ),
34 this, SLOT( updateSnippetsTitle( QStandardItem* ) ) );
35 disconnect( ui->descTextEdit );
38 MainWindow::~MainWindow()
40 on_actionClos_e_all_activated();
41 saveSnippets();
42 delete ui;
45 void MainWindow::on_action_Category_activated() {
46 bool ok;
47 QString name = QInputDialog::getText( this, tr( "New category..." ), tr( "Enter the new category name. "
48 "It will be added as a child of selected category." ),
49 QLineEdit::Normal, "", &ok );
51 if( ok && !name.isEmpty() ) {
52 // check if snippet's not selected
53 QStandardItem* parent = model.itemFromIndex( ui->snippetTreeView->currentIndex() );
54 Snippet* snippet = snippetForItem.value( parent );
56 if( !parent ) {
57 parent = model.invisibleRootItem();
58 snippet = snippetForItem.value( parent );
60 if( !snippet->isCategory() )
61 parent = parent->parent();
63 // model actions
64 QStandardItem* item = new QStandardItem( categoryIcon, name );
65 ui->snippetTreeView->setExpanded( parent->index(), true );
66 snippetForItem.insert( item, new Snippet( name, true ) );
67 insertItem( item, parent );
71 void MainWindow::on_action_Snippet_activated() {
72 bool ok;
73 QString name = QInputDialog::getText( this, tr( "New snippet..." ), tr( "Enter the new snippet name. "
74 "It will be added as a child of selected category." ),
75 QLineEdit::Normal, "", &ok );
77 if( ok && !name.isEmpty() ) {
78 // check if snippet's not selected
79 QStandardItem* parent = model.itemFromIndex( ui->snippetTreeView->currentIndex() );
80 Snippet* snippet = snippetForItem.value( parent );
82 if( !parent ) {
83 QMessageBox::warning( this, tr( "Error inserting snippet" ), tr( "A snippet cannot be added to the root of the tree." ) );
84 return;
86 if( !snippet->isCategory() )
87 parent = parent->parent();
89 // model actions
90 QStandardItem* item = new QStandardItem( snippetIcon, name );
91 ui->snippetTreeView->setExpanded( parent->index(), true );
93 snippet = new Snippet( name );
94 snippetForItem.insert( item, snippet );
95 insertItem( item, parent );
97 // open snippet for editing
98 on_snippetTreeView_activated( item->index() );
102 void MainWindow::on_action_Save_activated() {
103 Snippet* snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
104 snippet->save( ui->descTextEdit->toPlainText() );
105 snippet->setModified( false );
106 ui->tabWidget->setTabText( snippet->tabNumber(), ui->tabWidget->tabText( snippet->tabNumber() ).remove( 0, 1 ) );
108 // set up actions
109 ui->action_Save->setEnabled( false );
110 ui->actionSave_all->setEnabled( false );
111 ui->action_Close->setEnabled( true );
112 ui->actionClos_e_all->setEnabled( true );
115 void MainWindow::loadSnippets() {
116 model.setColumnCount( 1 );
118 QString errorStr;
119 int errorLine;
120 int errorColumn;
122 QDomDocument domDocument;
123 QFile file( "snippets.xml" );
124 if( !domDocument.setContent( &file, true, &errorStr, &errorLine, &errorColumn) ) {
125 QMessageBox::information(window(), tr("Snippets XML file"), tr("Parse error at line %1, column %2:\n%3")
126 .arg(errorLine)
127 .arg(errorColumn)
128 .arg(errorStr));
129 return;
132 snippetForItem.insert( model.invisibleRootItem(), new Snippet( domDocument.documentElement(), true ) );
134 QDomElement child = domDocument.documentElement().firstChildElement("category");
135 while ( !child.isNull() ) {
136 parseCategoryElement( child, model.invisibleRootItem() );
137 child = child.nextSiblingElement( "category" );
141 void MainWindow::parseCategoryElement( const QDomElement &element, QStandardItem* parent ) {
142 // create item with category's title
143 QStandardItem* titleItem = new QStandardItem( categoryIcon, element.firstChildElement("title").text() );
144 parent->setChild( parent->rowCount(), 0, titleItem );
145 snippetForItem.insert( titleItem, new Snippet( element, true ) );
147 // iterate over children of the element
148 QDomElement child = element.firstChildElement();
149 while( !child.isNull() ) {
150 if( child.tagName() == "category" ) {
151 parseCategoryElement( child, titleItem );
152 } else if( child.tagName() == "snippet" ) {
153 // create item with snippet's title
154 QStandardItem* title = new QStandardItem( snippetIcon, child.firstChildElement("title").text() );
156 // insert item and snippet into the hash and the model
157 Snippet* snippet = new Snippet( child );
158 snippetForItem.insert( title, snippet );
159 titleItem->setChild( titleItem->rowCount(), 0, title );
161 child = child.nextSiblingElement();
165 void MainWindow::on_snippetTreeView_activated( QModelIndex index ) {
166 Snippet* snippet = snippetForItem.value( model.itemFromIndex( index ) );
167 if( !snippet->isCategory() ) {
168 if( snippet->isOpened() ) {
169 ui->tabWidget->setCurrentIndex( snippet->tabNumber() );
170 ui->descTextEdit->setPlainText( snippet->tempDescription() );
171 ui->descTextEdit->setEnabled( true );
172 if( snippet->isModified() ) {
173 ui->action_Save->setEnabled( true );
174 ui->actionSave_all->setEnabled( true );
176 return;
178 QPlainTextEdit* edit = new QPlainTextEdit( snippet->code() );
179 connect( edit, SIGNAL( textChanged() ), this, SLOT( snippetsCodeModified() ) );
180 snippet->setEdit( edit );
181 snippet->setOpened();
182 snippet->setTab( ui->tabWidget->addTab( edit, snippet->title() ) );
183 ui->tabWidget->setCurrentIndex( snippet->tabNumber() );
184 ui->descTextEdit->setPlainText( snippet->description() );
185 ui->descTextEdit->setEnabled( true );
187 if( snippet->isModified() ) {
188 ui->action_Save->setEnabled( true );
189 ui->actionSave_all->setEnabled( true );
192 // set up actions
193 ui->action_Close->setEnabled( true );
194 ui->actionClos_e_all->setEnabled( true );
196 connect( ui->descTextEdit, SIGNAL( textChanged() ),
197 this, SLOT( on_descTextEdit_textChanged() ) );
201 void MainWindow::insertItem( QStandardItem* item, QStandardItem* parent ) {
202 int i;
203 if( snippetForItem.value( item )->isCategory() ) {
204 for( i = 0; i < parent->rowCount() && snippetForItem.value( parent->child( i, 0 ) )->isCategory()
205 && QString::compare( parent->child( i, 0 )->text(), item->text(), Qt::CaseInsensitive ) < 0; i++ ) ;
206 parent->insertRow( i, item );
207 } else {
208 for( i = 0; i < parent->rowCount() &&
209 ( ( snippetForItem.value( parent->child( i, 0 ) )->isCategory() ) ||
210 ( !snippetForItem.value( parent->child( i, 0 ) )->isCategory() && QString::compare( parent->child( i, 0 )->text(), item->text(), Qt::CaseInsensitive ) < 0 ) ); i++ ) ;
211 parent->insertRow( i, item );
215 void MainWindow::snippetsCodeModified() {
216 Snippet* snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
217 if( !snippet->isModified() ) {
218 snippet->setModified();
219 ui->tabWidget->setTabText( snippet->tabNumber(),
220 QString( "*" ) + ui->tabWidget->tabText( snippet->tabNumber() ) );
221 ui->action_Save->setEnabled( true );
222 ui->actionSave_all->setEnabled( true );
226 Snippet* MainWindow::findSnippetByTab( int atab ) {
227 QHash< QStandardItem*, Snippet* >::iterator i;
228 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
229 if( i.value()->isOpened() && i.value()->tabNumber() == atab )
230 return i.value();
231 return 0;
234 void MainWindow::saveSnippets( const QString& fileName ) {
235 QFile file;
236 if( fileName.isEmpty() )
237 file.setFileName( "snippets.xml" );
238 else
239 file.setFileName( fileName );
241 if( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) {
242 QMessageBox::critical( this, tr( "Error" ), tr( "Cannot open file snippets.xml for writing." ) );
243 return;
245 QString xml( "<snippets>\n" );
246 parseModel( model.invisibleRootItem(), xml );
247 xml += "</snippets>";
249 QTextStream out( &file );
250 out << xml;
253 void MainWindow::parseModel( QStandardItem* parent, QString& xml ) {
254 for( int i = 0; i < parent->rowCount(); i++ ) {
255 Snippet* snippet = snippetForItem.value( parent->child( i, 0 ) );
256 if( snippet->isCategory() ) {
257 xml += "<category><title>" + toValidXml( snippet->title() ) + "</title>\n";
258 parseModel( parent->child( i, 0 ), xml );
259 xml += "</category>\n";
260 } else {
261 xml += "<snippet><title>" + toValidXml( snippet->title() ) + "</title>\n";
262 xml += "<code>" + toValidXml( snippet->code() ) + "</code>\n";
263 xml += "<description>" + toValidXml( snippet->description() ) + "</description></snippet>\n";
268 void MainWindow::on_actionSave_all_activated() {
269 QHash< QStandardItem*, Snippet* >::iterator i;
270 int prevTab = ui->tabWidget->currentIndex();
271 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
272 if( i.value()->isOpened() ) {
273 ui->tabWidget->setCurrentIndex( i.value()->tabNumber() );
274 on_action_Save_activated();
276 ui->tabWidget->setCurrentIndex( prevTab );
279 void MainWindow::on_action_Close_activated() {
280 Snippet* snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
282 if( snippet->isModified() ) {
283 QMessageBox::StandardButton answer = QMessageBox::question( this, tr( "Save?" ),
284 tr( "Snippet %1 has been modified." ).arg( snippet->title() ),
285 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save );
286 if( answer == QMessageBox::Save )
287 on_action_Save_activated();
288 else if( answer != QMessageBox::Discard )
289 return;
292 ui->tabWidget->removeTab( snippet->tabNumber() );
293 snippet->setEdit( 0 );
294 snippet->setOpened( false );
296 restoreTabNumbers();
298 // set up actions
299 if( ui->tabWidget->count() ) {
300 ui->action_Close->setEnabled( true );
301 ui->actionClos_e_all->setEnabled( true );
303 if( findSnippetByTab( ui->tabWidget->currentIndex() )->isModified() ) {
304 ui->action_Save->setEnabled( true );
305 ui->actionSave_all->setEnabled( true );
307 } else {
308 connect( ui->descTextEdit, SIGNAL( textChanged() ),
309 this, SLOT( on_descTextEdit_textChanged() ) );
310 ui->action_Close->setEnabled( false );
311 ui->actionClos_e_all->setEnabled( false);
312 ui->descTextEdit->clear();
313 ui->descTextEdit->setEnabled( false );
317 void MainWindow::on_actionClos_e_all_activated() {
318 QHash< QStandardItem*, Snippet* >::iterator i;
319 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
320 if( i.value()->isOpened() ) {
321 ui->tabWidget->setCurrentIndex( i.value()->tabNumber() );
322 on_action_Close_activated();
326 void MainWindow::restoreTabNumbers() {
327 QHash< QStandardItem*, Snippet* >::iterator i;
328 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
329 if( i.value()->isOpened() ) {
330 for( int k = 0; k < ui->tabWidget->count(); k++ )
331 if( i.value()->title() == ui->tabWidget->tabText( k )
332 || "*" + i.value()->title() == ui->tabWidget->tabText( k ) ) {
333 i.value()->setTab( k );
334 break;
339 void MainWindow::on_action_Main_category_activated() {
340 bool ok;
341 QString name = QInputDialog::getText( this, tr( "New category..." ), tr( "Enter the new category name. "
342 "It will be added to the root node." ),
343 QLineEdit::Normal, "", &ok );
345 if( ok && !name.isEmpty() ) {
346 QStandardItem* parent = model.invisibleRootItem();
348 // model actions
349 QStandardItem* item = new QStandardItem( categoryIcon, name );
350 ui->snippetTreeView->setExpanded( parent->index(), true );
351 snippetForItem.insert( item, new Snippet( name, true ) );
352 insertItem( item, parent );
356 void MainWindow::on_action_Save_2_activated() {
357 saveSnippets();
360 void MainWindow::on_actionSave_snippets_as_activated() {
361 QString fileName = QFileDialog::getSaveFileName( this, tr( "Save as..." ) );
362 if( !fileName.isEmpty() )
363 saveSnippets( fileName );
366 void MainWindow::on_action_Exit_activated() {
367 on_actionClos_e_all_activated();
368 qApp->quit();
371 void MainWindow::on_searchLineEdit_textChanged( QString searchString ) {
372 // check if we're in edit or work mode
373 if( workModeDialog.isVisible() )
374 ui->searchLineEdit->setText( workModeDialog.m_ui->searchLineEdit->text() );
375 else
376 workModeDialog.m_ui->searchLineEdit->setText( ui->searchLineEdit->text() );
378 workModeDialog.showSnippets();
380 showAllSnippets( model.invisibleRootItem() );
381 if( !searchString.isEmpty() )
382 searchModelForString( searchString, model.invisibleRootItem() );
384 if( !workModeDialog.underMouse() )
385 QTimer::singleShot( 3000, &workModeDialog, SLOT( hideSnippets() ) );
388 // returns false of all children of a parent were hidden during parse
389 bool MainWindow::searchModelForString( const QString &searchString, QStandardItem* parent ) {
390 int hiddenCount = 0;
392 for( int i = 0; i < parent->rowCount(); i++ ) {
393 QStandardItem* child = parent->child( i, 0 );
394 Snippet* snippet = snippetForItem.value( child );
396 if( ui->snippetTreeView->isRowHidden( i, parent->index() ) )
397 hiddenCount++;
398 else if( snippet->isCategory() && !child->text().contains( searchString, Qt::CaseInsensitive ) ) {
399 if( child->rowCount() ) {
400 if( !searchModelForString( searchString, child ) ) {
401 ui->snippetTreeView->setRowHidden( i, parent->index(), true );
402 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), true );
403 hiddenCount++;
405 } else {
406 ui->snippetTreeView->setRowHidden( i, parent->index(), true );
407 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), true );
408 hiddenCount++;
410 } else if( !snippet->isCategory()
411 && ( !child->text().contains( searchString, Qt::CaseInsensitive ) && !snippet->code().contains( searchString, Qt::CaseInsensitive ) ) ) {
412 ui->snippetTreeView->setRowHidden( i, parent->index(), true );
413 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), true );
414 hiddenCount++;
418 if( hiddenCount == parent->rowCount() )
419 return false;
421 return true;
424 void MainWindow::showAllSnippets( QStandardItem* parent ) {
425 for( int i = 0; i < parent->rowCount(); i++ ) {
426 ui->snippetTreeView->setRowHidden( i, parent->index(), false );
427 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), false );
428 showAllSnippets( parent->child( i, 0 ) );
432 void MainWindow::on_action_Delete_activated() {
433 if( ui->snippetTreeView->currentIndex().isValid() ) {
434 QStandardItem* item = model.itemFromIndex( ui->snippetTreeView->currentIndex() );
436 QMessageBox::StandardButton answer = QMessageBox::question( this, tr( "Delete" ),
437 tr( "Do you really want to delete element %1 and all its subelements?" ).arg( item->text() ),
438 QMessageBox::Yes | QMessageBox::No );
439 if( answer == QMessageBox::Yes )
440 deleteChildItems( item );
444 void MainWindow::deleteChildItems( QStandardItem* parent ) {
445 while( parent->hasChildren() )
446 deleteChildItems( parent->child( 0, 0 ) );
448 if( snippetForItem.value( parent )->isOpened() ) {
449 Snippet* prev = findSnippetByTab( ui->tabWidget->currentIndex() );
450 ui->tabWidget->setCurrentIndex( snippetForItem.value( parent )->tabNumber() );
451 snippetForItem.value( parent )->setModified( false );
452 on_action_Close_activated();
453 if( prev )
454 ui->tabWidget->setCurrentIndex( prev->tabNumber() );
457 delete snippetForItem.value( parent );
458 snippetForItem.remove( parent );
459 if( !parent->parent() )
460 model.removeRow( parent->index().row() );
461 else
462 parent->parent()->removeRow( parent->index().row() );
465 void MainWindow::on_action_Work_activated() {
466 this->hide();
467 workModeDialog.show();
469 mainWindowGeometry = this->saveGeometry();
470 workModeDialog.restoreGeometry( workModeDialogGeometry );
472 qApp->setActiveWindow( &workModeDialog );
473 workModeDialog.m_ui->searchLineEdit->setFocus();
476 void MainWindow::on_action_Normal_activated() {
477 this->show();
478 workModeDialog.hide();
480 workModeDialogGeometry = workModeDialog.saveGeometry();
481 this->restoreGeometry( mainWindowGeometry );
484 void MainWindow::on_WorkModeDialog_finished( int ) {
485 // ignore the result
486 on_action_Normal_activated();
489 void MainWindow::updateSnippetsTitle( QStandardItem* item ) {
490 Snippet* snippet = snippetForItem.value( item );
491 snippet->setTitle( item->text() );
494 QString MainWindow::toValidXml( QString string ) {
495 QString temp( string );
497 temp.replace( "&", "&amp;" );
498 temp.replace( "<", "&lt;" );
499 temp.replace( ">", "&gt;" );
501 return temp;
504 void MainWindow::on_actionHide_categories_activated() {
505 if( ui->leftDockWidget->isVisible() )
506 ui->leftDockWidget->hide();
507 else
508 ui->leftDockWidget->show();
511 void MainWindow::on_actionHide_description_activated() {
512 if( ui->descDockWidget->isVisible() )
513 ui->descDockWidget->hide();
514 else
515 ui->descDockWidget->show();
518 void MainWindow::on_descTextEdit_textChanged() {
519 if( ui->descTextEdit->toPlainText().isEmpty() )
520 return;
522 findSnippetByTab( ui->tabWidget->currentIndex() )->setTempDescription( ui->descTextEdit->toPlainText() );
524 if( findSnippetByTab( ui->tabWidget->currentIndex() )->description() != ui->descTextEdit->toPlainText() )
525 snippetsCodeModified();
528 void MainWindow::on_tabWidget_currentChanged( int index ) {
529 QStandardItem* item = snippetForItem.key( findSnippetByTab( index ) );
530 if( item ) {
531 on_snippetTreeView_activated( item->index() );