VirtualBox

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

最後變更 在這個檔案從4164是 4164,由 vboxsync 提交於 18 年 前

2246: Log window catches keyboard:

Hmm, such a beautiful bug :) Fixed.

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

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