VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/ui/VBoxVMLogViewer.ui.h@ 3805

Last change on this file since 3805 was 3805, checked in by vboxsync, 17 years ago

2124: VirtualBox LogViewer search mechanism:

LogViewer search mechanism improvements done:

  1. [Help]...[Find][Save][Refresh][Close] dialog button's configuration.
  2. Standard Help button.
  3. Find button for toggling search-panel visibility on/off.
  4. Name filter for log files added (*.log *.log.*).
  5. Search mechanism starts by any alpha-numeric key (w/o modifiers).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 21.6 KB
Line 
1/**
2 *
3 * VBox frontends: Qt GUI ("VirtualBox"):
4 * "Virtual Log Viewer" dialog UI include (Qt Designer)
5 */
6
7/*
8 * Copyright (C) 2006 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23/****************************************************************************
24** ui.h extension file, included from the uic-generated form implementation.
25**
26** If you wish to add, delete or rename functions or slots use
27** Qt Designer which will update this file, preserving your code. Create an
28** init() function in place of a constructor, and a destroy() function in
29** place of a destructor.
30*****************************************************************************/
31
32
33class VBoxLogSearchPanel : public QWidget
34{
35 Q_OBJECT
36
37public:
38
39 VBoxLogSearchPanel (QWidget *aParent,
40 VBoxVMLogViewer *aViewer,
41 const char *aName)
42 : QWidget (aParent, aName)
43 , mViewer (aViewer)
44 , mButtonClose (0)
45 , mSearchName (0), mSearchString (0)
46 , mButtonPrev (0), mButtonNext (0)
47 , mCaseSensitive (0)
48 , mWarningSpacer (0), mWarningIcon (0), mWarningString (0)
49 {
50 mButtonClose = new QToolButton (this);
51 mButtonClose->setAutoRaise (true);
52 mButtonClose->setFocusPolicy (QWidget::TabFocus);
53 mButtonClose->setAccel (QKeySequence (Qt::Key_Escape));
54 connect (mButtonClose, SIGNAL (clicked()), this, SLOT (hide()));
55 mButtonClose->setIconSet (VBoxGlobal::iconSet ("delete_16px.png",
56 "delete_dis_16px.png"));
57
58 mSearchName = new QLabel (this);
59 mSearchString = new QLineEdit (this);
60 mSearchString->setSizePolicy (QSizePolicy::Preferred,
61 QSizePolicy::Fixed);
62 connect (mSearchString, SIGNAL (textChanged (const QString &)),
63 this, SLOT (findCurrent (const QString &)));
64
65 mButtonPrev = new QToolButton (this);
66 mButtonPrev->setEnabled (false);
67 mButtonPrev->setAutoRaise (true);
68 mButtonPrev->setFocusPolicy (QWidget::TabFocus);
69 mButtonPrev->setUsesTextLabel (true);
70 mButtonPrev->setTextPosition (QToolButton::BesideIcon);
71 connect (mButtonPrev, SIGNAL (clicked()), this, SLOT (findBack()));
72 mButtonPrev->setIconSet (VBoxGlobal::iconSet ("list_moveup_16px.png",
73 "list_moveup_disabled_16px.png"));
74
75 mButtonNext = new QToolButton (this);
76 mButtonNext->setEnabled (false);
77 mButtonNext->setAutoRaise (true);
78 mButtonNext->setFocusPolicy (QWidget::TabFocus);
79 mButtonNext->setUsesTextLabel (true);
80 mButtonNext->setTextPosition (QToolButton::BesideIcon);
81 connect (mButtonNext, SIGNAL (clicked()), this, SLOT (findNext()));
82 mButtonNext->setIconSet (VBoxGlobal::iconSet ("list_movedown_16px.png",
83 "list_movedown_disabled_16px.png"));
84
85 mCaseSensitive = new QCheckBox (this);
86
87 mWarningSpacer = new QSpacerItem (0, 0, QSizePolicy::Fixed,
88 QSizePolicy::Minimum);
89 mWarningIcon = new QLabel (this);
90 mWarningIcon->hide();
91 QImage img = QMessageBox::standardIcon (QMessageBox::Warning).
92 convertToImage();
93 if (!img.isNull())
94 {
95 img = img.smoothScale (16, 16);
96 QPixmap pixmap;
97 pixmap.convertFromImage (img);
98 mWarningIcon->setPixmap (pixmap);
99 }
100 mWarningString = new QLabel (this);
101 mWarningString->hide();
102
103 QSpacerItem *spacer = new QSpacerItem (0, 0, QSizePolicy::Expanding,
104 QSizePolicy::Minimum);
105
106 QHBoxLayout *mainLayout = new QHBoxLayout (this, 5, 5);
107 mainLayout->addWidget (mButtonClose);
108 mainLayout->addWidget (mSearchName);
109 mainLayout->addWidget (mSearchString);
110 mainLayout->addWidget (mButtonNext);
111 mainLayout->addWidget (mButtonPrev);
112 mainLayout->addWidget (mCaseSensitive);
113 mainLayout->addItem (mWarningSpacer);
114 mainLayout->addWidget (mWarningIcon);
115 mainLayout->addWidget (mWarningString);
116 mainLayout->addItem (spacer);
117
118 setFocusProxy (mCaseSensitive);
119
120 languageChange();
121 }
122
123 void languageChange()
124 {
125 QToolTip::add (mButtonClose, tr ("Close the search panel"));
126 mSearchName->setText (tr ("Find "));
127 QToolTip::add (mSearchString, tr ("Enter a search string here"));
128 mButtonPrev->setTextLabel (tr ("&Previous"));
129 mButtonPrev->setAccel (QKeySequence (tr ("Alt+P")));
130 QToolTip::add (mButtonPrev,
131 tr ("Search for the previous occurrence of the string"));
132 mButtonNext->setTextLabel (tr ("&Next"));
133 mButtonNext->setAccel (QKeySequence (tr ("Alt+N")));
134 QToolTip::add (mButtonNext,
135 tr ("Search for the next occurrence of the string"));
136 mCaseSensitive->setText (tr ("C&ase Sensitive"));
137 QToolTip::add (mCaseSensitive,
138 tr ("Perform case sensitive search (when checked)"));
139 mWarningString->setText (tr ("String not found"));
140 }
141
142 void setText (const QString &aText)
143 {
144 mSearchString->setText (aText);
145 }
146
147private slots:
148
149 void findNext()
150 {
151 search (true);
152 }
153
154 void findBack()
155 {
156 search (false);
157 }
158
159 void findCurrent (const QString &aSearchString)
160 {
161 mButtonNext->setEnabled (aSearchString.length());
162 mButtonPrev->setEnabled (aSearchString.length());
163 toggleWarning (!aSearchString.length());
164 if (aSearchString.length())
165 search (true, true);
166 else
167 mViewer->currentLogPage()->removeSelection();
168 }
169
170private:
171
172 void search (bool aForward, bool aStartCurrent = false)
173 {
174 QTextBrowser *browser = mViewer->currentLogPage();
175 int startPrg = 0, endPrg = 0;
176 int startInd = 0, endInd = 0;
177 if (browser->hasSelectedText())
178 browser->getSelection (&startPrg, &startInd, &endPrg, &endInd);
179
180 bool found = false;
181 int increment = aForward ? 1 : -1;
182 int border = aForward ? browser->paragraphs() : -1;
183 int startFrom = aStartCurrent ? startInd : startInd + increment;
184 int paragraph = startFrom < 0 ? startPrg + increment : startPrg;
185 for (; paragraph != border; paragraph += increment)
186 {
187 QString text = browser->text (paragraph);
188 int res = aForward ?
189 text.find (mSearchString->text(), startFrom,
190 mCaseSensitive->isChecked()) :
191 text.findRev (mSearchString->text(), startFrom,
192 mCaseSensitive->isChecked());
193 if (res != -1)
194 {
195 found = true;
196 browser->setSelection (paragraph, res, paragraph,
197 res + mSearchString->text().length());
198 break;
199 }
200 startFrom = aForward ? 0 : -1;
201 }
202
203 toggleWarning (found);
204 if (!found)
205 browser->setSelection (startPrg, startInd, endPrg, endInd);
206 }
207
208 bool eventFilter (QObject *aObject, QEvent *aEvent)
209 {
210 switch (aEvent->type())
211 {
212 case QEvent::KeyPress:
213 {
214 QKeyEvent *e = static_cast<QKeyEvent*> (aEvent);
215
216 /* processing the return keypress for the mSearchString
217 * widget as the search next string action */
218 if (aObject == mSearchString &&
219 (e->state() == 0 || e->state() & Keypad) &&
220 (e->key() == Key_Enter || e->key() == Key_Return))
221 {
222 findNext();
223 return true;
224 }
225 /* processing other search next/previous shortcuts */
226 else if (e->key() == Key_F3)
227 {
228 if (e->state() == 0)
229 findNext();
230 else if (e->state() == ShiftButton)
231 findBack();
232 return true;
233 }
234 /* processing ctrl-f key combination as the shortcut to
235 * move to the search field */
236 else if (e->state() == ControlButton && e->key() == Key_F)
237 {
238 mSearchString->setFocus();
239 return true;
240 }
241
242 break;
243 }
244 default:
245 break;
246 }
247 return false;
248 }
249
250 void showEvent (QShowEvent *aEvent)
251 {
252 QWidget::showEvent (aEvent);
253 qApp->installEventFilter (this);
254 mSearchString->setFocus();
255 mSearchString->selectAll();
256 }
257
258 void hideEvent (QHideEvent *aEvent)
259 {
260 if (focusData()->focusWidget()->parent() == this)
261 focusNextPrevChild (true);
262 qApp->removeEventFilter (this);
263 QWidget::hideEvent (aEvent);
264 }
265
266 void toggleWarning (bool aHide)
267 {
268 mWarningSpacer->changeSize (aHide ? 0 : 16, 0, QSizePolicy::Fixed,
269 QSizePolicy::Minimum);
270 mWarningIcon->setHidden (aHide);
271 mWarningString->setHidden (aHide);
272 }
273
274 VBoxVMLogViewer *mViewer;
275 QToolButton *mButtonClose;
276 QLabel *mSearchName;
277 QLineEdit *mSearchString;
278 QToolButton *mButtonPrev;
279 QToolButton *mButtonNext;
280 QCheckBox *mCaseSensitive;
281 QSpacerItem *mWarningSpacer;
282 QLabel *mWarningIcon;
283 QLabel *mWarningString;
284};
285
286
287VBoxVMLogViewer::LogViewersMap VBoxVMLogViewer::mSelfArray = LogViewersMap();
288
289void VBoxVMLogViewer::createLogViewer (CMachine &aMachine)
290{
291 if (mSelfArray.find (aMachine.GetName()) == mSelfArray.end())
292 {
293 /* creating new log viewer if there is no one existing */
294 mSelfArray [aMachine.GetName()] = new VBoxVMLogViewer (0,
295 "VBoxVMLogViewer", WType_TopLevel | WDestructiveClose);
296 /* read new machine data for this log viewer */
297 mSelfArray [aMachine.GetName()]->setup (aMachine);
298 }
299
300 VBoxVMLogViewer *viewer = mSelfArray [aMachine.GetName()];
301 viewer->show();
302 viewer->setWindowState (viewer->windowState() & ~WindowMinimized);
303 viewer->setActiveWindow();
304}
305
306
307void VBoxVMLogViewer::init()
308{
309 /* prepare dialog to first run */
310 mFirstRun = true;
311
312 /* dialog initially is not polished */
313 mIsPolished = false;
314
315 /* search the default button */
316 mDefaultButton = searchDefaultButton();
317 qApp->installEventFilter (this);
318
319 /* setup a dialog icon */
320 setIcon (QPixmap::fromMimeSource ("show_logs_16px.png"));
321
322 /* statusbar initially disabled */
323 statusBar()->setHidden (true);
324
325 /* setup size grip */
326 mSizeGrip = new QSizeGrip (centralWidget(), "mSizeGrip");
327 mSizeGrip->resize (mSizeGrip->sizeHint());
328 mSizeGrip->stackUnder (mCloseButton);
329
330 /* logs list creation */
331 mLogList = new QTabWidget (mLogsFrame, "mLogList");
332 QVBoxLayout *logsFrameLayout = new QVBoxLayout (mLogsFrame);
333 logsFrameLayout->addWidget (mLogList);
334
335 /* search panel creation */
336 mSearchPanel = new VBoxLogSearchPanel (mLogsFrame, this,
337 "VBoxLogSearchPanel");
338 logsFrameLayout->addWidget (mSearchPanel);
339 mSearchPanel->hide();
340
341 /* fix the tab order to ensure the dialog keys are always the last */
342 setTabOrder (mSearchPanel->focusProxy(), mHelpButton);
343 setTabOrder (mHelpButton, mFindButton);
344 setTabOrder (mFindButton, mSaveButton);
345 setTabOrder (mSaveButton, mRefreshButton);
346 setTabOrder (mRefreshButton, mCloseButton);
347 setTabOrder (mCloseButton, mLogList);
348
349 /* applying language settings */
350 languageChangeImp();
351}
352
353
354void VBoxVMLogViewer::destroy()
355{
356 mSelfArray.erase (mMachine.GetName());
357}
358
359
360void VBoxVMLogViewer::setup (CMachine &aMachine)
361{
362 /* saving related machine */
363 mMachine = aMachine;
364
365 /* reading log files */
366 refresh();
367
368 /* loading language constants */
369 languageChangeImp();
370}
371
372
373const CMachine& VBoxVMLogViewer::machine()
374{
375 return mMachine;
376}
377
378
379void VBoxVMLogViewer::languageChangeImp()
380{
381 /* setup a dialog caption */
382 if (!mMachine.isNull())
383 setCaption (tr ("%1 - VirtualBox Log Viewer").arg (mMachine.GetName()));
384 /* translate a search panel */
385 if (mSearchPanel)
386 mSearchPanel->languageChange();
387}
388
389
390QPushButton* VBoxVMLogViewer::searchDefaultButton()
391{
392 /* this mechanism is used for searching the default dialog button
393 * and similar the same mechanism in Qt::QDialog inner source */
394 QPushButton *button = 0;
395 QObjectList *list = queryList ("QPushButton");
396 QObjectListIt it (*list);
397 while ((button = (QPushButton*)it.current()) && !button->isDefault())
398 ++ it;
399 return button;
400}
401
402
403bool VBoxVMLogViewer::eventFilter (QObject *aObject, QEvent *aEvent)
404{
405 switch (aEvent->type())
406 {
407 /* auto-default button focus-in processor used to move the "default"
408 * button property into the currently focused button */
409 case QEvent::FocusIn:
410 {
411 if (aObject->inherits ("QPushButton") &&
412 aObject->parent() == centralWidget())
413 {
414 ((QPushButton*)aObject)->setDefault (aObject != mDefaultButton);
415 if (mDefaultButton)
416 mDefaultButton->setDefault (aObject == mDefaultButton);
417 }
418 break;
419 }
420 /* auto-default button focus-out processor used to remove the "default"
421 * button property from the previously focused button */
422 case QEvent::FocusOut:
423 {
424 if (aObject->inherits ("QPushButton") &&
425 aObject->parent() == centralWidget())
426 {
427 if (mDefaultButton)
428 mDefaultButton->setDefault (aObject != mDefaultButton);
429 ((QPushButton*)aObject)->setDefault (aObject == mDefaultButton);
430 }
431 break;
432 }
433 default:
434 break;
435 }
436 return QMainWindow::eventFilter (aObject, aEvent);
437}
438
439
440bool VBoxVMLogViewer::event (QEvent *aEvent)
441{
442 bool result = QMainWindow::event (aEvent);
443 switch (aEvent->type())
444 {
445 case QEvent::LanguageChange:
446 {
447 languageChangeImp();
448 break;
449 }
450 default:
451 break;
452 }
453 return result;
454}
455
456
457void VBoxVMLogViewer::keyPressEvent (QKeyEvent *aEvent)
458{
459 if (aEvent->state() == 0 ||
460 (aEvent->state() & Keypad && aEvent->key() == Key_Enter))
461 {
462 switch (aEvent->key())
463 {
464 /* processing the return keypress for the auto-default button */
465 case Key_Enter:
466 case Key_Return:
467 {
468 QPushButton *currentDefault = searchDefaultButton();
469 if (currentDefault)
470 currentDefault->animateClick();
471 break;
472 }
473 /* processing the escape keypress as the close dialog action */
474 case Key_Escape:
475 {
476 mCloseButton->animateClick();
477 break;
478 }
479 }
480 if (aEvent->key() >= Qt::Key_Exclam &&
481 aEvent->key() <= Qt::Key_AsciiTilde &&
482 mLogList->isEnabled())
483 {
484 mSearchPanel->show();
485 mSearchPanel->setText (aEvent->text());
486 }
487 }
488 else if (aEvent->state() == Qt::ControlButton &&
489 aEvent->key() == Qt::Key_F)
490 {
491 if (mLogList->isEnabled())
492 mSearchPanel->show();
493 }
494 else
495 aEvent->ignore();
496}
497
498
499void VBoxVMLogViewer::showEvent (QShowEvent *aEvent)
500{
501 QMainWindow::showEvent (aEvent);
502
503 /* one may think that QWidget::polish() is the right place to do things
504 * below, but apparently, by the time when QWidget::polish() is called,
505 * the widget style & layout are not fully done, at least the minimum
506 * size hint is not properly calculated. Since this is sometimes necessary,
507 * we provide our own "polish" implementation. */
508
509 if (mIsPolished)
510 return;
511
512 mIsPolished = true;
513
514 VBoxGlobal::centerWidget (this, parentWidget());
515}
516
517
518void VBoxVMLogViewer::resizeEvent (QResizeEvent*)
519{
520 /* adjust the size-grip location for the current resize event */
521 mSizeGrip->move (centralWidget()->rect().bottomRight() -
522 QPoint (mSizeGrip->rect().width() - 1,
523 mSizeGrip->rect().height() - 1));
524}
525
526
527void VBoxVMLogViewer::refresh()
528{
529 /* clearing old data if any */
530 mLogFilesList.clear();
531 while (mLogList->count())
532 {
533 QWidget *logPage = mLogList->page (0);
534 mLogList->removePage (logPage);
535 delete logPage;
536 }
537
538 bool isAnyLogPresent = false;
539
540 /* entering log files folder */
541 QString logFilesPath = mMachine.GetLogFolder();
542 QDir logFilesDir (logFilesPath);
543 if (logFilesDir.exists())
544 {
545 /* reading log files folder */
546 logFilesDir.setNameFilter ("*.log *.log.*");
547 QStringList logList = logFilesDir.entryList (QDir::Files);
548 if (!logList.empty()) isAnyLogPresent = true;
549 for (QStringList::Iterator it = logList.begin(); it != logList.end(); ++it)
550 loadLogFile (logFilesDir.filePath (*it));
551 }
552
553 /* create an empty log page if there are no logs at all */
554 if (!isAnyLogPresent)
555 {
556 QTextBrowser *dummyLog = createLogPage ("VBox.log");
557 dummyLog->setTextFormat (Qt::RichText);
558 dummyLog->setWordWrap (QTextEdit::WidgetWidth);
559 dummyLog->setText (tr ("<p>No log files found. Press the <b>Refresh</b> "
560 "button to rescan the log folder <nobr><b>%1</b></nobr>.</p>")
561 .arg (logFilesPath));
562 /* we don't want it to remain white */
563 dummyLog->setPaper (backgroundBrush());
564 }
565
566 /* restore previous tab-widget margin which was reseted when
567 * the tab widget's children was removed */
568 mLogList->setMargin (10);
569
570 /* show the first tab widget's page after the refresh */
571 mLogList->showPage (mLogList->page(0));
572
573 /* enable/disable save button & tab widget according log presence */
574 mFindButton->setEnabled (isAnyLogPresent);
575 mSaveButton->setEnabled (isAnyLogPresent);
576 mLogList->setEnabled (isAnyLogPresent);
577
578 if (mFirstRun)
579 {
580 /* resize the whole log-viewer to fit 80 symbols in text-browser for
581 * the first time started */
582 QTextBrowser *firstPage = static_cast <QTextBrowser *> (mLogList->page(0));
583 int fullWidth = firstPage->fontMetrics().width (QChar ('x')) * 80 +
584 firstPage->verticalScrollBar()->width() +
585 firstPage->frameWidth() * 2 +
586 5 + 4 /* left text margin + QTabWidget frame width */ +
587 mLogList->margin() * 2 +
588 centralWidget()->layout()->margin() * 2;
589 resize (fullWidth, height());
590 mFirstRun = false;
591 }
592}
593
594
595void VBoxVMLogViewer::loadLogFile (const QString &aFileName)
596{
597 /* prepare log file */
598 QFile logFile (aFileName);
599 if (!logFile.exists() || !logFile.open (IO_ReadOnly))
600 return;
601
602 /* read log file and write it into the log page */
603 QTextBrowser *logViewer = createLogPage (QFileInfo (aFileName).fileName());
604 logViewer->setText (logFile.readAll());
605
606 mLogFilesList << aFileName;
607}
608
609
610QTextBrowser* VBoxVMLogViewer::createLogPage (const QString &aName)
611{
612 QTextBrowser *logViewer = new QTextBrowser();
613 logViewer->setTextFormat (Qt::PlainText);
614 QFont font = logViewer->currentFont();
615 font.setFamily ("Courier New,courier");
616 logViewer->setFont (font);
617 logViewer->setWordWrap (QTextEdit::NoWrap);
618 logViewer->setVScrollBarMode (QScrollView::AlwaysOn);
619 mLogList->addTab (logViewer, aName);
620 return logViewer;
621}
622
623
624QTextBrowser* VBoxVMLogViewer::currentLogPage()
625{
626 return static_cast<QTextBrowser*> (mLogList->currentPage());
627}
628
629
630void VBoxVMLogViewer::save()
631{
632 /* prepare "save as" dialog */
633 QFileInfo fileInfo (mLogFilesList [mLogList->currentPageIndex()]);
634 QDateTime dtInfo = fileInfo.lastModified();
635 QString dtString = dtInfo.toString ("yyyy-MM-dd-hh-mm-ss");
636 QString defaultFileName = QString ("%1-%2.log")
637 .arg (mMachine.GetName()).arg (dtString);
638 QString defaultFullName = QDir::convertSeparators (QDir::home().absPath() +
639 "/" + defaultFileName);
640
641 QString newFileName = QFileDialog::getSaveFileName (defaultFullName,
642 QString::null, this, "SaveLogAsDialog", tr ("Save VirtualBox Log As"));
643
644 /* save new log into the file */
645 if (!newFileName.isEmpty())
646 {
647 /* reread log data */
648 QFile oldFile (mLogFilesList [mLogList->currentPageIndex()]);
649 QFile newFile (newFileName);
650 if (!oldFile.open (IO_ReadOnly) || !newFile.open (IO_WriteOnly))
651 return;
652
653 /* save log data into the new file */
654 newFile.writeBlock (oldFile.readAll());
655 }
656}
657
658void VBoxVMLogViewer::search()
659{
660 mSearchPanel->isHidden() ? mSearchPanel->show() : mSearchPanel->hide();
661}
662
663#include "VBoxVMLogViewer.ui.moc"
664
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette