VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

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