Tooltips and fonts stuff.
[qsnippetsmanager.git] / mainwindow.cpp
blob19bc16eb3d3545e039465c017a92b9d63bcccc86
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 );
23 // and their icons
24 ui->actionSave_all->setIcon( style()->standardIcon( QStyle::SP_DialogSaveButton ) );
25 ui->actionSave_snippets_as->setIcon( style()->standardIcon( QStyle::SP_DialogSaveButton ) );
26 ui->action_Save->setIcon( style()->standardIcon( QStyle::SP_DialogSaveButton ) );
27 ui->action_Save_2->setIcon( style()->standardIcon( QStyle::SP_DialogSaveButton ) );
28 ui->action_Delete->setIcon( style()->standardIcon( QStyle::SP_TrashIcon ) );
29 ui->action_Category->setIcon( style()->standardIcon( QStyle::SP_DirClosedIcon ) );
30 ui->action_Main_category->setIcon( style()->standardIcon( QStyle::SP_DirClosedIcon ) );
31 ui->action_Snippet->setIcon( style()->standardIcon( QStyle::SP_FileIcon ) );
32 ui->action_Work->setIcon( style()->standardIcon( QStyle::SP_MediaPlay ) );
34 // toolbar
35 ui->mainToolBar->addAction( ui->action_Snippet );
36 ui->mainToolBar->addAction( ui->action_Category );
37 ui->mainToolBar->addSeparator();
38 ui->mainToolBar->addAction( ui->action_Save );
39 ui->mainToolBar->addAction( ui->actionSave_all );
40 ui->mainToolBar->addSeparator();
41 ui->mainToolBar->addAction( ui->action_Work );
43 // load snippets from snippets.xml and set up view
44 loadSnippets();
45 ui->snippetTreeView->setModel( &model );
46 ui->snippetTreeView->expandAll();
47 workModeDialog.m_ui->treeView->expandAll();
49 // connections
50 connect( ui->searchLineEdit, SIGNAL( textChanged(QString) ),
51 this, SLOT(on_searchLineEdit_textChanged(QString)) );
52 connect( &model, SIGNAL( itemChanged( QStandardItem* ) ),
53 this, SLOT( updateSnippetsTitle( QStandardItem* ) ) );
54 disconnect( ui->descTextEdit );
57 MainWindow::~MainWindow()
59 on_actionClos_e_all_activated();
60 saveSnippets();
61 delete ui;
64 void MainWindow::on_action_Category_activated() {
65 bool ok;
66 QString name = QInputDialog::getText( this, tr( "New category..." ), tr( "Enter the new category name. "
67 "It will be added as a child of selected category." ),
68 QLineEdit::Normal, "", &ok );
70 if( ok && !name.isEmpty() ) {
71 // check if snippet's not selected
72 QStandardItem* parent = model.itemFromIndex( ui->snippetTreeView->currentIndex() );
73 Snippet* snippet = snippetForItem.value( parent );
75 if( !parent ) {
76 parent = model.invisibleRootItem();
77 snippet = snippetForItem.value( parent );
79 if( !snippet->isCategory() )
80 parent = parent->parent();
82 // model actions
83 QStandardItem* item = new QStandardItem( categoryIcon, name );
84 ui->snippetTreeView->setExpanded( parent->index(), true );
85 snippetForItem.insert( item, new Snippet( name, true ) );
86 insertItem( item, parent );
90 void MainWindow::on_action_Snippet_activated() {
91 bool ok;
92 QString name = QInputDialog::getText( this, tr( "New snippet..." ), tr( "Enter the new snippet name. "
93 "It will be added as a child of selected category." ),
94 QLineEdit::Normal, "", &ok );
96 if( ok && !name.isEmpty() ) {
97 // check if snippet's not selected
98 QStandardItem* parent = model.itemFromIndex( ui->snippetTreeView->currentIndex() );
99 Snippet* snippet = snippetForItem.value( parent );
101 if( !parent ) {
102 QMessageBox::warning( this, tr( "Error inserting snippet" ), tr( "A snippet cannot be added to the root of the tree." ) );
103 return;
105 if( !snippet->isCategory() )
106 parent = parent->parent();
108 // model actions
109 QStandardItem* item = new QStandardItem( snippetIcon, name );
110 ui->snippetTreeView->setExpanded( parent->index(), true );
112 snippet = new Snippet( name );
113 snippet->setToolTip( createToolTip( snippet ) );
114 snippetForItem.insert( item, snippet );
115 insertItem( item, parent );
117 // open snippet for editing
118 on_snippetTreeView_activated( item->index() );
122 void MainWindow::on_action_Save_activated() {
123 Snippet* snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
124 snippet->save( ui->descTextEdit->toPlainText() );
125 snippet->setToolTip( createToolTip( snippet ) );
126 snippet->setModified( false );
127 ui->tabWidget->setTabText( snippet->tabNumber(), ui->tabWidget->tabText( snippet->tabNumber() ).remove( 0, 1 ) );
129 // set up actions
130 ui->action_Save->setEnabled( false );
131 ui->actionSave_all->setEnabled( false );
132 ui->action_Close->setEnabled( true );
133 ui->actionClos_e_all->setEnabled( true );
136 void MainWindow::loadSnippets() {
137 model.setColumnCount( 1 );
139 QString errorStr;
140 int errorLine;
141 int errorColumn;
143 QDomDocument domDocument;
144 QFile file( "snippets.xml" );
145 if( !domDocument.setContent( &file, true, &errorStr, &errorLine, &errorColumn) ) {
146 QMessageBox::information(window(), tr("Snippets XML file"), tr("Parse error at line %1, column %2:\n%3")
147 .arg(errorLine)
148 .arg(errorColumn)
149 .arg(errorStr));
150 return;
153 snippetForItem.insert( model.invisibleRootItem(), new Snippet( domDocument.documentElement(), true ) );
155 QDomElement child = domDocument.documentElement().firstChildElement("category");
156 while ( !child.isNull() ) {
157 parseCategoryElement( child, model.invisibleRootItem() );
158 child = child.nextSiblingElement( "category" );
162 void MainWindow::parseCategoryElement( const QDomElement &element, QStandardItem* parent ) {
163 // create item with category's title
164 QStandardItem* titleItem = new QStandardItem( categoryIcon, element.firstChildElement("title").text() );
165 parent->setChild( parent->rowCount(), 0, titleItem );
166 snippetForItem.insert( titleItem, new Snippet( element, true ) );
168 // iterate over children of the element
169 QDomElement child = element.firstChildElement();
170 while( !child.isNull() ) {
171 if( child.tagName() == "category" ) {
172 parseCategoryElement( child, titleItem );
173 } else if( child.tagName() == "snippet" ) {
174 // create item with snippet's title
175 QStandardItem* title = new QStandardItem( snippetIcon, child.firstChildElement("title").text() );
177 // insert item and snippet into the hash and the model
178 Snippet* snippet = new Snippet( child );
179 snippet->setToolTip( createToolTip( snippet ) );
180 snippetForItem.insert( title, snippet );
181 titleItem->setChild( titleItem->rowCount(), 0, title );
183 child = child.nextSiblingElement();
187 void MainWindow::on_snippetTreeView_activated( QModelIndex index ) {
188 Snippet* snippet = snippetForItem.value( model.itemFromIndex( index ) );
189 if( !snippet->isCategory() ) {
190 if( snippet->isOpened() ) {
191 ui->tabWidget->setCurrentIndex( snippet->tabNumber() );
192 ui->descTextEdit->setEnabled( true );
193 if( snippet->isModified() ) {
194 ui->descTextEdit->setText( snippet->tempDescription() );
195 ui->action_Save->setEnabled( true );
196 ui->actionSave_all->setEnabled( true );
197 } else
198 ui->descTextEdit->setText( snippet->description() );
199 return;
200 } else if( ui->descDockWidget->isHidden() && !snippet->description().isEmpty() )
201 ui->statusBar->showMessage( tr( "Description available." ) );
203 TextEdit* edit = new TextEdit();
204 edit->setText( snippet->code() );
205 connect( edit, SIGNAL( textChanged() ), this, SLOT( snippetsCodeModified() ) );
206 snippet->setEdit( edit );
207 snippet->setOpened();
208 snippet->setTab( ui->tabWidget->addTab( edit, snippet->title() ) );
209 ui->tabWidget->setCurrentIndex( snippet->tabNumber() );
210 ui->descTextEdit->setText( snippet->description() );
211 ui->descTextEdit->setEnabled( true );
213 if( snippet->isModified() ) {
214 ui->action_Save->setEnabled( true );
215 ui->actionSave_all->setEnabled( true );
218 // set up actions
219 ui->action_Close->setEnabled( true );
220 ui->actionClos_e_all->setEnabled( true );
222 connect( ui->descTextEdit, SIGNAL( textChanged() ),
223 this, SLOT( on_descTextEdit_textChanged() ) );
227 void MainWindow::insertItem( QStandardItem* item, QStandardItem* parent ) {
228 int i;
229 if( snippetForItem.value( item )->isCategory() ) {
230 for( i = 0; i < parent->rowCount() && snippetForItem.value( parent->child( i, 0 ) )->isCategory()
231 && QString::compare( parent->child( i, 0 )->text(), item->text(), Qt::CaseInsensitive ) < 0; i++ ) ;
232 parent->insertRow( i, item );
233 } else {
234 for( i = 0; i < parent->rowCount() &&
235 ( ( snippetForItem.value( parent->child( i, 0 ) )->isCategory() ) ||
236 ( !snippetForItem.value( parent->child( i, 0 ) )->isCategory() && QString::compare( parent->child( i, 0 )->text(), item->text(), Qt::CaseInsensitive ) < 0 ) ); i++ ) ;
237 parent->insertRow( i, item );
239 ui->snippetTreeView->setExpanded( parent->index(), true );
242 void MainWindow::snippetsCodeModified() {
243 Snippet* snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
244 if( !snippet->isModified() ) {
245 snippet->setModified();
246 ui->tabWidget->setTabText( snippet->tabNumber(),
247 QString( "*" ) + ui->tabWidget->tabText( snippet->tabNumber() ) );
248 ui->action_Save->setEnabled( true );
249 ui->actionSave_all->setEnabled( true );
253 Snippet* MainWindow::findSnippetByTab( int atab ) {
254 QHash< QStandardItem*, Snippet* >::iterator i;
255 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
256 if( i.value()->isOpened() && i.value()->tabNumber() == atab )
257 return i.value();
258 return 0;
261 void MainWindow::saveSnippets( const QString& fileName ) {
262 QFile file;
263 if( fileName.isEmpty() )
264 file.setFileName( "snippets.xml" );
265 else
266 file.setFileName( fileName );
268 if( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) {
269 QMessageBox::critical( this, tr( "Error" ), tr( "Cannot open file snippets.xml for writing." ) );
270 return;
272 QString xml( "<snippets>\n" );
273 parseModel( model.invisibleRootItem(), xml );
274 xml += "</snippets>";
276 QTextStream out( &file );
277 out << xml;
280 void MainWindow::parseModel( QStandardItem* parent, QString& xml ) {
281 for( int i = 0; i < parent->rowCount(); i++ ) {
282 Snippet* snippet = snippetForItem.value( parent->child( i, 0 ) );
283 if( snippet->isCategory() ) {
284 xml += "<category><title>" + toValidXml( snippet->title() ) + "</title>\n";
285 parseModel( parent->child( i, 0 ), xml );
286 xml += "</category>\n";
287 } else {
288 xml += "<snippet><title>" + toValidXml( snippet->title() ) + "</title>\n";
289 xml += "<code>" + toValidXml( snippet->code() ) + "</code>\n";
290 xml += "<description>" + toValidXml( snippet->description() ) + "</description></snippet>\n";
295 void MainWindow::on_actionSave_all_activated() {
296 QHash< QStandardItem*, Snippet* >::iterator i;
297 int prevTab = ui->tabWidget->currentIndex();
298 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
299 if( i.value()->isOpened() ) {
300 ui->tabWidget->setCurrentIndex( i.value()->tabNumber() );
301 on_action_Save_activated();
303 ui->tabWidget->setCurrentIndex( prevTab );
306 void MainWindow::on_action_Close_activated() {
307 Snippet* snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
309 if( snippet->isModified() ) {
310 QMessageBox::StandardButton answer = QMessageBox::question( this, tr( "Save?" ),
311 tr( "Snippet %1 has been modified." ).arg( snippet->title() ),
312 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save );
313 if( answer == QMessageBox::Save )
314 on_action_Save_activated();
315 else if( answer != QMessageBox::Discard )
316 return;
319 ui->tabWidget->removeTab( snippet->tabNumber() );
320 snippet->setEdit( 0 );
321 snippet->setOpened( false );
323 restoreTabNumbers();
325 snippet = findSnippetByTab( ui->tabWidget->currentIndex() );
326 if( snippet )
327 on_snippetTreeView_activated( snippetForItem.key( snippet )->index() );
329 // set up actions
330 if( !ui->tabWidget->count() ) {
331 connect( ui->descTextEdit, SIGNAL( textChanged() ),
332 this, SLOT( on_descTextEdit_textChanged() ) );
333 ui->action_Close->setEnabled( false );
334 ui->actionClos_e_all->setEnabled( false);
335 ui->descTextEdit->clear();
336 ui->descTextEdit->setEnabled( false );
340 void MainWindow::on_actionClos_e_all_activated() {
341 QHash< QStandardItem*, Snippet* >::iterator i;
342 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
343 if( i.value()->isOpened() ) {
344 ui->tabWidget->setCurrentIndex( i.value()->tabNumber() );
345 on_action_Close_activated();
349 void MainWindow::restoreTabNumbers() {
350 QHash< QStandardItem*, Snippet* >::iterator i;
351 for( i = snippetForItem.begin(); i != snippetForItem.end(); i++ )
352 if( i.value()->isOpened() ) {
353 for( int k = 0; k < ui->tabWidget->count(); k++ )
354 if( i.value()->title() == ui->tabWidget->tabText( k )
355 || "*" + i.value()->title() == ui->tabWidget->tabText( k ) ) {
356 i.value()->setTab( k );
357 break;
362 void MainWindow::on_action_Main_category_activated() {
363 bool ok;
364 QString name = QInputDialog::getText( this, tr( "New category..." ), tr( "Enter the new language name. "
365 "It will be added to the root node." ),
366 QLineEdit::Normal, "", &ok );
368 if( ok && !name.isEmpty() ) {
369 QStandardItem* parent = model.invisibleRootItem();
371 // model actions
372 QStandardItem* item = new QStandardItem( categoryIcon, name );
373 ui->snippetTreeView->setExpanded( parent->index(), true );
374 snippetForItem.insert( item, new Snippet( name, true ) );
375 insertItem( item, parent );
379 void MainWindow::on_action_Save_2_activated() {
380 saveSnippets();
383 void MainWindow::on_actionSave_snippets_as_activated() {
384 QString fileName = QFileDialog::getSaveFileName( this, tr( "Save as..." ) );
385 if( !fileName.isEmpty() )
386 saveSnippets( fileName );
389 void MainWindow::on_action_Exit_activated() {
390 on_actionClos_e_all_activated();
391 qApp->quit();
394 void MainWindow::on_searchLineEdit_textChanged( QString searchString ) {
395 // check if we're in edit or work mode
396 if( workModeDialog.isVisible() )
397 ui->searchLineEdit->setText( workModeDialog.m_ui->searchLineEdit->text() );
398 else
399 workModeDialog.m_ui->searchLineEdit->setText( ui->searchLineEdit->text() );
401 workModeDialog.showSnippets();
403 showAllSnippets( model.invisibleRootItem() );
404 if( !searchString.isEmpty() )
405 searchModelForString( searchString, model.invisibleRootItem() );
407 if( !workModeDialog.underMouse() )
408 QTimer::singleShot( 3000, &workModeDialog, SLOT( hideSnippets() ) );
411 // returns false of all children of a parent were hidden during parse
412 bool MainWindow::searchModelForString( const QString &searchString, QStandardItem* parent ) {
413 int hiddenCount = 0;
415 for( int i = 0; i < parent->rowCount(); i++ ) {
416 QStandardItem* child = parent->child( i, 0 );
417 Snippet* snippet = snippetForItem.value( child );
419 if( ui->snippetTreeView->isRowHidden( i, parent->index() ) )
420 hiddenCount++;
421 else if( snippet->isCategory() && !child->text().contains( searchString, Qt::CaseInsensitive ) ) {
422 if( child->rowCount() ) {
423 if( !searchModelForString( searchString, child ) ) {
424 ui->snippetTreeView->setRowHidden( i, parent->index(), true );
425 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), true );
426 hiddenCount++;
428 } else {
429 ui->snippetTreeView->setRowHidden( i, parent->index(), true );
430 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), true );
431 hiddenCount++;
433 } else if( !snippet->isCategory()
434 && ( !child->text().contains( searchString, Qt::CaseInsensitive ) && !snippet->code().contains( searchString, Qt::CaseInsensitive ) ) ) {
435 ui->snippetTreeView->setRowHidden( i, parent->index(), true );
436 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), true );
437 hiddenCount++;
441 if( hiddenCount == parent->rowCount() )
442 return false;
444 return true;
447 void MainWindow::showAllSnippets( QStandardItem* parent ) {
448 for( int i = 0; i < parent->rowCount(); i++ ) {
449 ui->snippetTreeView->setRowHidden( i, parent->index(), false );
450 workModeDialog.m_ui->treeView->setRowHidden( i, parent->index(), false );
451 showAllSnippets( parent->child( i, 0 ) );
455 void MainWindow::on_action_Delete_activated() {
456 if( ui->snippetTreeView->currentIndex().isValid() ) {
457 QStandardItem* item = model.itemFromIndex( ui->snippetTreeView->currentIndex() );
459 QMessageBox::StandardButton answer = QMessageBox::question( this, tr( "Delete" ),
460 tr( "Do you really want to delete element %1 and all its subelements?" ).arg( item->text() ),
461 QMessageBox::Yes | QMessageBox::No );
462 if( answer == QMessageBox::Yes )
463 deleteChildItems( item );
467 void MainWindow::deleteChildItems( QStandardItem* parent ) {
468 while( parent->hasChildren() )
469 deleteChildItems( parent->child( 0, 0 ) );
471 if( snippetForItem.value( parent )->isOpened() ) {
472 Snippet* prev = findSnippetByTab( ui->tabWidget->currentIndex() );
473 ui->tabWidget->setCurrentIndex( snippetForItem.value( parent )->tabNumber() );
474 snippetForItem.value( parent )->setModified( false );
475 on_action_Close_activated();
476 if( prev )
477 ui->tabWidget->setCurrentIndex( prev->tabNumber() );
480 delete snippetForItem.value( parent );
481 snippetForItem.remove( parent );
482 if( !parent->parent() )
483 model.removeRow( parent->index().row() );
484 else
485 parent->parent()->removeRow( parent->index().row() );
488 void MainWindow::on_action_Work_activated() {
489 this->hide();
490 setToolTips();
491 workModeDialog.show();
493 ui->action_Normal->setEnabled( true );
494 ui->action_Work->setEnabled( false );
496 mainWindowGeometry = this->saveGeometry();
497 workModeDialog.restoreGeometry( workModeDialogGeometry );
499 qApp->setActiveWindow( &workModeDialog );
500 workModeDialog.m_ui->searchLineEdit->setFocus();
503 void MainWindow::on_action_Normal_activated() {
504 resetToolTips();
505 this->show();
506 workModeDialog.hide();
508 ui->action_Normal->setEnabled( false );
509 ui->action_Work->setEnabled( true );
511 workModeDialogGeometry = workModeDialog.saveGeometry();
512 this->restoreGeometry( mainWindowGeometry );
515 void MainWindow::on_WorkModeDialog_finished( int ) {
516 // ignore the result
517 on_action_Normal_activated();
520 void MainWindow::updateSnippetsTitle( QStandardItem* item ) {
521 Snippet* snippet = snippetForItem.value( item );
522 snippet->setTitle( item->text() );
525 QString MainWindow::toValidXml( QString string ) {
526 QString temp( string );
528 temp.replace( "&", "&amp;" );
529 temp.replace( "<", "&lt;" );
530 temp.replace( ">", "&gt;" );
532 return temp;
535 void MainWindow::on_actionHide_categories_activated() {
536 if( ui->leftDockWidget->isVisible() )
537 ui->leftDockWidget->hide();
538 else
539 ui->leftDockWidget->show();
542 void MainWindow::on_actionHide_description_activated() {
543 if( ui->descDockWidget->isVisible() )
544 ui->descDockWidget->hide();
545 else
546 ui->descDockWidget->show();
549 void MainWindow::on_descTextEdit_textChanged() {
550 if( ui->descTextEdit->toPlainText().isEmpty() )
551 return;
553 findSnippetByTab( ui->tabWidget->currentIndex() )->setTempDescription( ui->descTextEdit->toPlainText() );
555 if( findSnippetByTab( ui->tabWidget->currentIndex() )->description() != ui->descTextEdit->toPlainText() )
556 snippetsCodeModified();
559 void MainWindow::on_tabWidget_currentChanged( int index ) {
560 QStandardItem* item = snippetForItem.key( findSnippetByTab( index ) );
561 if( item ) {
562 on_snippetTreeView_activated( item->index() );
566 void MainWindow::on_action_About_activated() {
567 QMessageBox::about( this, tr( "About QSnippetManager" ),
568 tr( "Author: mslupny ( mslupny@gmail.com )\n"
569 "Use http://repo.or.cz/w/qsnippetsmanager.git if you'd like to contribute.\n"
570 "License: GPLv3." ) );
573 void MainWindow::on_actionAbout_Qt_activated() {
574 QMessageBox::aboutQt( this, tr( "About Qt" ) );
577 QString MainWindow::createToolTip( const Snippet* snippet ) {
578 QString toolTip;
579 if( !snippet->description().isEmpty() ) {
580 toolTip += tr( "<b>Description:<br></b>" ) + toValidXml( snippet->description() );
581 if( !snippet->code().isEmpty() )
582 toolTip += "<br><br>";
585 if( !snippet->code().isEmpty() ) {
586 toolTip += tr( "<b>Code:</b><br>" );
587 QString temp( snippet->code() );
588 bool changed = false;
589 while( temp.count( '\n' ) > 40 ) {
590 temp.truncate( temp.size() / 2 );
591 changed = true;
593 toolTip += toValidXml( temp );
594 if( changed )
595 toolTip += "...";
598 toolTip.replace( '\n', "<br>" );
599 toolTip.replace( '\t', "&nbsp;&nbsp;&nbsp;&nbsp;" );
601 return toolTip;
604 void MainWindow::resetToolTips() {
605 for( QHash< QStandardItem*, Snippet* >::const_iterator i = snippetForItem.constBegin();
606 i != snippetForItem.constEnd(); ++i )
607 i.key()->setToolTip( "" );
610 void MainWindow::setToolTips() {
611 for( QHash< QStandardItem*, Snippet* >::iterator i = snippetForItem.begin();
612 i != snippetForItem.end(); ++i )
613 if( !i.value()->isCategory() )
614 i.key()->setToolTip( i.value()->toolTip() );