1 | /* $Id: VBoxVMLogViewer.cpp 30356 2010-06-22 08:42:22Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | *
|
---|
4 | * VBox frontends: Qt4 GUI ("VirtualBox"):
|
---|
5 | * VBoxVMLogViewer class implementation
|
---|
6 | */
|
---|
7 |
|
---|
8 | /*
|
---|
9 | * Copyright (C) 2006-2008 Oracle Corporation
|
---|
10 | *
|
---|
11 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
12 | * available from http://www.virtualbox.org. This file is free software;
|
---|
13 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
14 | * General Public License (GPL) as published by the Free Software
|
---|
15 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
16 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
17 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
18 | */
|
---|
19 |
|
---|
20 | #ifdef VBOX_WITH_PRECOMPILED_HEADERS
|
---|
21 | # include "precomp.h"
|
---|
22 | #else /* !VBOX_WITH_PRECOMPILED_HEADERS */
|
---|
23 | #include "QITabWidget.h"
|
---|
24 | #include "UIIconPool.h"
|
---|
25 | #include "UISpecialControls.h"
|
---|
26 | #include "VBoxGlobal.h"
|
---|
27 | #include "VBoxProblemReporter.h"
|
---|
28 | #include "VBoxUtils.h"
|
---|
29 | #include "VBoxVMLogViewer.h"
|
---|
30 |
|
---|
31 | /* Qt includes */
|
---|
32 | #include <QCheckBox>
|
---|
33 | #include <QDateTime>
|
---|
34 | #include <QDir>
|
---|
35 | #include <QFileDialog>
|
---|
36 | #include <QKeyEvent>
|
---|
37 | #include <QLabel>
|
---|
38 | #include <QLineEdit>
|
---|
39 | #include <QPushButton>
|
---|
40 | #include <QScrollBar>
|
---|
41 | #include <QStyle>
|
---|
42 | #include <QTextEdit>
|
---|
43 | #endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
|
---|
44 |
|
---|
45 | VBoxVMLogViewer::LogViewersMap VBoxVMLogViewer::mSelfArray = LogViewersMap();
|
---|
46 |
|
---|
47 | void VBoxVMLogViewer::createLogViewer (QWidget *aCenterWidget, CMachine &aMachine)
|
---|
48 | {
|
---|
49 | if (!mSelfArray.contains (aMachine.GetName()))
|
---|
50 | {
|
---|
51 | /* Creating new log viewer if there is no one existing */
|
---|
52 | #ifdef Q_WS_MAC
|
---|
53 | VBoxVMLogViewer *lv = new VBoxVMLogViewer (aCenterWidget, Qt::Window, aMachine);
|
---|
54 | #else /* Q_WS_MAC */
|
---|
55 | VBoxVMLogViewer *lv = new VBoxVMLogViewer (NULL, Qt::Window, aMachine);
|
---|
56 | #endif /* Q_WS_MAC */
|
---|
57 |
|
---|
58 | lv->centerAccording (aCenterWidget);
|
---|
59 | connect (vboxGlobal().mainWindow(), SIGNAL (closing()), lv, SLOT (close()));
|
---|
60 | lv->setAttribute (Qt::WA_DeleteOnClose);
|
---|
61 | mSelfArray [aMachine.GetName()] = lv;
|
---|
62 | }
|
---|
63 |
|
---|
64 | VBoxVMLogViewer *viewer = mSelfArray [aMachine.GetName()];
|
---|
65 | viewer->show();
|
---|
66 | viewer->raise();
|
---|
67 | viewer->setWindowState (viewer->windowState() & ~Qt::WindowMinimized);
|
---|
68 | viewer->activateWindow();
|
---|
69 | }
|
---|
70 |
|
---|
71 |
|
---|
72 | VBoxVMLogViewer::VBoxVMLogViewer (QWidget *aParent,
|
---|
73 | Qt::WindowFlags aFlags,
|
---|
74 | const CMachine &aMachine)
|
---|
75 | : QIWithRetranslateUI2<QIMainDialog> (aParent, aFlags)
|
---|
76 | , mIsPolished (false)
|
---|
77 | , mFirstRun (true)
|
---|
78 | , mMachine (aMachine)
|
---|
79 | {
|
---|
80 | /* Apply UI decorations */
|
---|
81 | Ui::VBoxVMLogViewer::setupUi (this);
|
---|
82 |
|
---|
83 | /* Apply window icons */
|
---|
84 | setWindowIcon(UIIconPool::iconSetFull(QSize (32, 32), QSize (16, 16),
|
---|
85 | ":/vm_show_logs_32px.png", ":/show_logs_16px.png"));
|
---|
86 |
|
---|
87 | /* Enable size grip without using a status bar. */
|
---|
88 | setSizeGripEnabled (true);
|
---|
89 |
|
---|
90 | /* Logs list creation */
|
---|
91 | mLogList = new QITabWidget (mLogsFrame);
|
---|
92 | QVBoxLayout *logsFrameLayout = new QVBoxLayout (mLogsFrame);
|
---|
93 | logsFrameLayout->setContentsMargins (0, 0, 0, 0);
|
---|
94 | logsFrameLayout->addWidget (mLogList);
|
---|
95 |
|
---|
96 | connect (mLogList, SIGNAL (currentChanged (int)),
|
---|
97 | this, SLOT (currentLogPageChanged (int)));
|
---|
98 |
|
---|
99 | /* Search panel creation */
|
---|
100 | mSearchPanel = new VBoxLogSearchPanel (mLogsFrame, this);
|
---|
101 | logsFrameLayout->addWidget (mSearchPanel);
|
---|
102 | mSearchPanel->hide();
|
---|
103 |
|
---|
104 | /* Add missing buttons & retrieve standard buttons */
|
---|
105 | mBtnHelp = mButtonBox->button (QDialogButtonBox::Help);
|
---|
106 | mBtnFind = mButtonBox->addButton (QString::null, QDialogButtonBox::ActionRole);
|
---|
107 | mBtnSave = mButtonBox->button (QDialogButtonBox::Save);
|
---|
108 | mBtnRefresh = mButtonBox->addButton (QString::null, QDialogButtonBox::ActionRole);
|
---|
109 | mBtnClose = mButtonBox->button (QDialogButtonBox::Close);
|
---|
110 |
|
---|
111 | /* Setup connections */
|
---|
112 | connect (mButtonBox, SIGNAL (helpRequested()),
|
---|
113 | &vboxProblem(), SLOT (showHelpHelpDialog()));
|
---|
114 | connect (mBtnFind, SIGNAL (clicked()), this, SLOT (search()));
|
---|
115 | connect (mBtnSave, SIGNAL (clicked()), this, SLOT (save()));
|
---|
116 | connect (mBtnRefresh, SIGNAL (clicked()), this, SLOT (refresh()));
|
---|
117 |
|
---|
118 | /* Reading log files */
|
---|
119 | refresh();
|
---|
120 | /* Set the focus to the initial default button */
|
---|
121 | defaultButton()->setDefault (true);
|
---|
122 | defaultButton()->setFocus();
|
---|
123 | #ifdef Q_WS_MAC
|
---|
124 | /* We have to force this to get the default button L&F on the mac. */
|
---|
125 | defaultButton()->setEnabled (true);
|
---|
126 | # ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
|
---|
127 | logsFrameLayout->setSpacing (4);
|
---|
128 | # endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
|
---|
129 | #endif /* Q_WS_MAC */
|
---|
130 | /* Loading language constants */
|
---|
131 | retranslateUi();
|
---|
132 | }
|
---|
133 |
|
---|
134 | VBoxVMLogViewer::~VBoxVMLogViewer()
|
---|
135 | {
|
---|
136 | if (!mMachine.isNull())
|
---|
137 | mSelfArray.remove (mMachine.GetName());
|
---|
138 | }
|
---|
139 |
|
---|
140 | QTextEdit* VBoxVMLogViewer::currentLogPage()
|
---|
141 | {
|
---|
142 | if (mLogList->isEnabled())
|
---|
143 | {
|
---|
144 | QWidget *container = mLogList->currentWidget();
|
---|
145 | QTextEdit *browser = container->findChild<QTextEdit*>();
|
---|
146 | Assert (browser);
|
---|
147 | return browser ? browser : 0;
|
---|
148 | }
|
---|
149 | else
|
---|
150 | return 0;
|
---|
151 | }
|
---|
152 |
|
---|
153 |
|
---|
154 | bool VBoxVMLogViewer::close()
|
---|
155 | {
|
---|
156 | mSearchPanel->hide();
|
---|
157 | return QIMainDialog::close();
|
---|
158 | }
|
---|
159 |
|
---|
160 | void VBoxVMLogViewer::refresh()
|
---|
161 | {
|
---|
162 | /* Clearing old data if any */
|
---|
163 | mLogFiles.clear();
|
---|
164 | mLogList->setEnabled (true);
|
---|
165 | while (mLogList->count())
|
---|
166 | {
|
---|
167 | QWidget *firstPage = mLogList->widget (0);
|
---|
168 | mLogList->removeTab (0);
|
---|
169 | delete firstPage;
|
---|
170 | }
|
---|
171 |
|
---|
172 | bool isAnyLogPresent = false;
|
---|
173 |
|
---|
174 | const CSystemProperties &sys = vboxGlobal().virtualBox().GetSystemProperties();
|
---|
175 | int cMaxLogs = sys.GetLogHistoryCount();
|
---|
176 | for (int i=0; i <= cMaxLogs; ++i)
|
---|
177 | {
|
---|
178 | /* Query the log file name for index i */
|
---|
179 | QString file = mMachine.QueryLogFilename(i);
|
---|
180 | if (!file.isEmpty())
|
---|
181 | {
|
---|
182 | /* Try to read the log file with the index i */
|
---|
183 | ULONG uOffset = 0;
|
---|
184 | QString text;
|
---|
185 | while (true)
|
---|
186 | {
|
---|
187 | QVector<BYTE> data = mMachine.ReadLog(i, uOffset, _1M);
|
---|
188 | if (data.size() == 0)
|
---|
189 | break;
|
---|
190 | text.append(QString::fromUtf8((char*)data.data(), data.size()));
|
---|
191 | uOffset += data.size();
|
---|
192 | }
|
---|
193 | /* Anything read at all? */
|
---|
194 | if (uOffset > 0)
|
---|
195 | {
|
---|
196 | /* Create a log viewer page and append the read text to it */
|
---|
197 | QTextEdit *logViewer = createLogPage(QFileInfo(file).fileName());
|
---|
198 | logViewer->setPlainText(text);
|
---|
199 | /* Add the actual file name and the QTextEdit containing the
|
---|
200 | content to a list. */
|
---|
201 | mLogFiles << qMakePair(file, logViewer);
|
---|
202 | isAnyLogPresent = true;
|
---|
203 | }
|
---|
204 | }
|
---|
205 | }
|
---|
206 |
|
---|
207 | /* Create an empty log page if there are no logs at all */
|
---|
208 | if (!isAnyLogPresent)
|
---|
209 | {
|
---|
210 | QTextEdit *dummyLog = createLogPage ("VBox.log");
|
---|
211 | dummyLog->setWordWrapMode (QTextOption::WordWrap);
|
---|
212 | dummyLog->setHtml (tr ("<p>No log files found. Press the "
|
---|
213 | "<b>Refresh</b> button to rescan the log folder "
|
---|
214 | "<nobr><b>%1</b></nobr>.</p>")
|
---|
215 | .arg (mMachine.GetLogFolder()));
|
---|
216 | /* We don't want it to remain white */
|
---|
217 | QPalette pal = dummyLog->palette();
|
---|
218 | pal.setColor (QPalette::Base, pal.color (QPalette::Window));
|
---|
219 | dummyLog->setPalette (pal);
|
---|
220 | }
|
---|
221 |
|
---|
222 | /* Show the first tab widget's page after the refresh */
|
---|
223 | mLogList->setCurrentIndex (0);
|
---|
224 | currentLogPageChanged (0);
|
---|
225 |
|
---|
226 | /* Enable/Disable save button & tab widget according log presence */
|
---|
227 | mBtnFind->setEnabled (isAnyLogPresent);
|
---|
228 | mBtnSave->setEnabled (isAnyLogPresent);
|
---|
229 | mLogList->setEnabled (isAnyLogPresent);
|
---|
230 | /* Default to the save button if there are any log files otherwise to the
|
---|
231 | * close button. The initial automatic of the main dialog has to be
|
---|
232 | * overwritten */
|
---|
233 | setDefaultButton (isAnyLogPresent ? mBtnSave:mBtnClose);
|
---|
234 | }
|
---|
235 |
|
---|
236 | void VBoxVMLogViewer::save()
|
---|
237 | {
|
---|
238 | /* Prepare "save as" dialog */
|
---|
239 | QFileInfo fileInfo (mLogFiles.at(mLogList->currentIndex()).first);
|
---|
240 | QDateTime dtInfo = fileInfo.lastModified();
|
---|
241 | QString dtString = dtInfo.toString ("yyyy-MM-dd-hh-mm-ss");
|
---|
242 | QString defaultFileName = QString ("%1-%2.log")
|
---|
243 | .arg (mMachine.GetName()).arg (dtString);
|
---|
244 | QString defaultFullName = QDir::toNativeSeparators (
|
---|
245 | QDir::home().absolutePath() + "/" + defaultFileName);
|
---|
246 | QString newFileName = QFileDialog::getSaveFileName (this,
|
---|
247 | tr ("Save VirtualBox Log As"), defaultFullName);
|
---|
248 |
|
---|
249 | /* Copy log into the file */
|
---|
250 | if (!newFileName.isEmpty())
|
---|
251 | QFile::copy(mMachine.QueryLogFilename(mLogList->currentIndex()), newFileName);
|
---|
252 | }
|
---|
253 |
|
---|
254 | void VBoxVMLogViewer::search()
|
---|
255 | {
|
---|
256 | mSearchPanel->isHidden() ? mSearchPanel->show() : mSearchPanel->hide();
|
---|
257 | }
|
---|
258 |
|
---|
259 | void VBoxVMLogViewer::currentLogPageChanged (int aIndex)
|
---|
260 | {
|
---|
261 | if (aIndex >= 0 &&
|
---|
262 | aIndex < mLogFiles.count())
|
---|
263 | setFileForProxyIcon(mLogFiles.at(aIndex).first);
|
---|
264 | }
|
---|
265 |
|
---|
266 | void VBoxVMLogViewer::retranslateUi()
|
---|
267 | {
|
---|
268 | /* Translate uic generated strings */
|
---|
269 | Ui::VBoxVMLogViewer::retranslateUi (this);
|
---|
270 |
|
---|
271 | /* Setup a dialog caption */
|
---|
272 | if (!mMachine.isNull())
|
---|
273 | setWindowTitle (tr ("%1 - VirtualBox Log Viewer").arg (mMachine.GetName()));
|
---|
274 |
|
---|
275 | mBtnFind->setText (tr ("&Find"));
|
---|
276 | mBtnRefresh->setText (tr ("&Refresh"));
|
---|
277 | mBtnSave->setText (tr ("&Save"));
|
---|
278 | mBtnClose->setText (tr ("Close"));
|
---|
279 | }
|
---|
280 |
|
---|
281 | void VBoxVMLogViewer::showEvent (QShowEvent *aEvent)
|
---|
282 | {
|
---|
283 | QIMainDialog::showEvent (aEvent);
|
---|
284 |
|
---|
285 | /* One may think that QWidget::polish() is the right place to do things
|
---|
286 | * below, but apparently, by the time when QWidget::polish() is called,
|
---|
287 | * the widget style & layout are not fully done, at least the minimum
|
---|
288 | * size hint is not properly calculated. Since this is sometimes necessary,
|
---|
289 | * we provide our own "polish" implementation. */
|
---|
290 |
|
---|
291 | if (mIsPolished)
|
---|
292 | return;
|
---|
293 |
|
---|
294 | mIsPolished = true;
|
---|
295 |
|
---|
296 | if (mFirstRun)
|
---|
297 | {
|
---|
298 | /* Resize the whole log-viewer to fit 80 symbols in
|
---|
299 | * text-browser for the first time started */
|
---|
300 | QTextEdit *firstPage = currentLogPage();
|
---|
301 | if (firstPage)
|
---|
302 | {
|
---|
303 | int fullWidth =
|
---|
304 | firstPage->fontMetrics().width (QChar ('x')) * 80 +
|
---|
305 | firstPage->verticalScrollBar()->width() +
|
---|
306 | firstPage->frameWidth() * 2 +
|
---|
307 | /* mLogList margin */ 10 * 2 +
|
---|
308 | /* CentralWidget margin */ 10 * 2;
|
---|
309 | resize (fullWidth, height());
|
---|
310 | mFirstRun = false;
|
---|
311 | }
|
---|
312 | }
|
---|
313 |
|
---|
314 | /* Make sure the log view widget has the focus */
|
---|
315 | QWidget *w = currentLogPage();
|
---|
316 | if (w)
|
---|
317 | w->setFocus();
|
---|
318 | }
|
---|
319 |
|
---|
320 | QTextEdit* VBoxVMLogViewer::createLogPage (const QString &aName)
|
---|
321 | {
|
---|
322 | QWidget *pageContainer = new QWidget();
|
---|
323 | QVBoxLayout *pageLayout = new QVBoxLayout (pageContainer);
|
---|
324 | QTextEdit *logViewer = new QTextEdit (pageContainer);
|
---|
325 | pageLayout->addWidget (logViewer);
|
---|
326 | pageLayout->setContentsMargins (10, 10, 10, 10);
|
---|
327 |
|
---|
328 | QFont font = logViewer->currentFont();
|
---|
329 | font.setFamily ("Courier New,courier");
|
---|
330 | logViewer->setFont (font);
|
---|
331 | logViewer->setWordWrapMode (QTextOption::NoWrap);
|
---|
332 | logViewer->setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
|
---|
333 | logViewer->setReadOnly (true);
|
---|
334 |
|
---|
335 | mLogList->addTab (pageContainer, aName);
|
---|
336 | return logViewer;
|
---|
337 | }
|
---|
338 |
|
---|
339 |
|
---|
340 | VBoxLogSearchPanel::VBoxLogSearchPanel (QWidget *aParent,
|
---|
341 | VBoxVMLogViewer *aViewer)
|
---|
342 | : QIWithRetranslateUI<QWidget> (aParent)
|
---|
343 | , mViewer (aViewer)
|
---|
344 | , mButtonClose (0)
|
---|
345 | , mSearchName (0), mSearchString (0)
|
---|
346 | , mButtonsNextPrev (0)
|
---|
347 | , mCaseSensitive (0)
|
---|
348 | , mWarningSpacer (0), mWarningIcon (0), mWarningString (0)
|
---|
349 | {
|
---|
350 | mButtonClose = new UIMiniCancelButton (this);
|
---|
351 | connect (mButtonClose, SIGNAL (clicked()), this, SLOT (hide()));
|
---|
352 |
|
---|
353 | mSearchName = new QLabel (this);
|
---|
354 | mSearchString = new UISearchField (this);
|
---|
355 | mSearchString->setSizePolicy (QSizePolicy::Preferred,
|
---|
356 | QSizePolicy::Fixed);
|
---|
357 | connect (mSearchString, SIGNAL (textChanged (const QString &)),
|
---|
358 | this, SLOT (findCurrent (const QString &)));
|
---|
359 |
|
---|
360 | mButtonsNextPrev = new UIRoundRectSegmentedButton(2, this);
|
---|
361 | mButtonsNextPrev->setEnabled (0, false);
|
---|
362 | mButtonsNextPrev->setEnabled (1, false);
|
---|
363 | #ifndef Q_WS_MAC
|
---|
364 | /* No icons on the Mac */
|
---|
365 | mButtonsNextPrev->setIcon(0, UIIconPool::defaultIcon(UIIconPool::ArrowBackIcon, this));
|
---|
366 | mButtonsNextPrev->setIcon(1, UIIconPool::defaultIcon(UIIconPool::ArrowForwardIcon, this));
|
---|
367 | #endif /* !Q_WS_MAC */
|
---|
368 | connect (mButtonsNextPrev, SIGNAL (clicked (int)), this, SLOT (find (int)));
|
---|
369 |
|
---|
370 | mCaseSensitive = new QCheckBox (this);
|
---|
371 |
|
---|
372 | mWarningSpacer = new QSpacerItem (0, 0, QSizePolicy::Fixed,
|
---|
373 | QSizePolicy::Minimum);
|
---|
374 | mWarningIcon = new QLabel (this);
|
---|
375 | mWarningIcon->hide();
|
---|
376 |
|
---|
377 | QIcon icon = UIIconPool::defaultIcon(UIIconPool::MessageBoxWarningIcon, this);
|
---|
378 | if (!icon.isNull())
|
---|
379 | mWarningIcon->setPixmap (icon.pixmap (16, 16));
|
---|
380 | mWarningString = new QLabel (this);
|
---|
381 | mWarningString->hide();
|
---|
382 |
|
---|
383 | QSpacerItem *spacer = new QSpacerItem (0, 0, QSizePolicy::Expanding,
|
---|
384 | QSizePolicy::Minimum);
|
---|
385 |
|
---|
386 | #ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
|
---|
387 | QFont font = mSearchName->font();
|
---|
388 | font.setPointSize (::darwinSmallFontSize());
|
---|
389 | mSearchName->setFont (font);
|
---|
390 | mCaseSensitive->setFont (font);
|
---|
391 | mWarningString->setFont (font);
|
---|
392 | #endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
|
---|
393 |
|
---|
394 | QHBoxLayout *mainLayout = new QHBoxLayout (this);
|
---|
395 | mainLayout->setSpacing (5);
|
---|
396 | mainLayout->setContentsMargins (0, 0, 0, 0);
|
---|
397 | mainLayout->addWidget (mButtonClose);
|
---|
398 | mainLayout->addWidget (mSearchName);
|
---|
399 | mainLayout->addWidget (mSearchString);
|
---|
400 | mainLayout->addWidget (mButtonsNextPrev);
|
---|
401 | mainLayout->addWidget (mCaseSensitive);
|
---|
402 | mainLayout->addItem (mWarningSpacer);
|
---|
403 | mainLayout->addWidget (mWarningIcon);
|
---|
404 | mainLayout->addWidget (mWarningString);
|
---|
405 | mainLayout->addItem (spacer);
|
---|
406 |
|
---|
407 | setFocusProxy (mCaseSensitive);
|
---|
408 | qApp->installEventFilter (this);
|
---|
409 |
|
---|
410 | retranslateUi();
|
---|
411 | }
|
---|
412 |
|
---|
413 | void VBoxLogSearchPanel::retranslateUi()
|
---|
414 | {
|
---|
415 | mButtonClose->setToolTip (tr ("Close the search panel"));
|
---|
416 |
|
---|
417 | mSearchName->setText (tr ("Find "));
|
---|
418 | mSearchString->setToolTip (tr ("Enter a search string here"));
|
---|
419 |
|
---|
420 | mButtonsNextPrev->setTitle (0, tr ("&Previous"));
|
---|
421 | mButtonsNextPrev->setToolTip (0, tr ("Search for the previous occurrence "
|
---|
422 | "of the string"));
|
---|
423 |
|
---|
424 | mButtonsNextPrev->setTitle (1, tr ("&Next"));
|
---|
425 | mButtonsNextPrev->setToolTip (1, tr ("Search for the next occurrence of "
|
---|
426 | "the string"));
|
---|
427 |
|
---|
428 | mCaseSensitive->setText (tr ("C&ase Sensitive"));
|
---|
429 | mCaseSensitive->setToolTip (tr ("Perform case sensitive search "
|
---|
430 | "(when checked)"));
|
---|
431 |
|
---|
432 | mWarningString->setText (tr ("String not found"));
|
---|
433 | }
|
---|
434 |
|
---|
435 | void VBoxLogSearchPanel::findCurrent (const QString &aSearchString)
|
---|
436 | {
|
---|
437 | mButtonsNextPrev->setEnabled (0, aSearchString.length());
|
---|
438 | mButtonsNextPrev->setEnabled (1, aSearchString.length());
|
---|
439 | toggleWarning (!aSearchString.length());
|
---|
440 | if (aSearchString.length())
|
---|
441 | search (true, true);
|
---|
442 | else
|
---|
443 | {
|
---|
444 | QTextEdit *browser = mViewer->currentLogPage();
|
---|
445 | if (browser && browser->textCursor().hasSelection())
|
---|
446 | {
|
---|
447 | QTextCursor cursor = browser->textCursor();
|
---|
448 | cursor.setPosition (cursor.anchor());
|
---|
449 | browser->setTextCursor (cursor);
|
---|
450 | }
|
---|
451 | }
|
---|
452 | }
|
---|
453 |
|
---|
454 | void VBoxLogSearchPanel::search (bool aForward,
|
---|
455 | bool aStartCurrent)
|
---|
456 | {
|
---|
457 | QTextEdit *browser = mViewer->currentLogPage();
|
---|
458 | if (!browser) return;
|
---|
459 |
|
---|
460 | QTextCursor cursor = browser->textCursor();
|
---|
461 | int pos = cursor.position();
|
---|
462 | int anc = cursor.anchor();
|
---|
463 |
|
---|
464 | QString text = browser->toPlainText();
|
---|
465 | int diff = aStartCurrent ? 0 : 1;
|
---|
466 |
|
---|
467 | int res = -1;
|
---|
468 | if (aForward && (aStartCurrent || pos < text.size() - 1))
|
---|
469 | res = text.indexOf (mSearchString->text(),
|
---|
470 | anc + diff,
|
---|
471 | mCaseSensitive->isChecked() ?
|
---|
472 | Qt::CaseSensitive : Qt::CaseInsensitive);
|
---|
473 | else if (!aForward && anc > 0)
|
---|
474 | res = text.lastIndexOf (mSearchString->text(), anc - 1,
|
---|
475 | mCaseSensitive->isChecked() ?
|
---|
476 | Qt::CaseSensitive : Qt::CaseInsensitive);
|
---|
477 |
|
---|
478 | if (res != -1)
|
---|
479 | {
|
---|
480 | cursor.movePosition (QTextCursor::Start,
|
---|
481 | QTextCursor::MoveAnchor);
|
---|
482 | cursor.movePosition (QTextCursor::NextCharacter,
|
---|
483 | QTextCursor::MoveAnchor, res);
|
---|
484 | cursor.movePosition (QTextCursor::NextCharacter,
|
---|
485 | QTextCursor::KeepAnchor,
|
---|
486 | mSearchString->text().size());
|
---|
487 | browser->setTextCursor (cursor);
|
---|
488 | }
|
---|
489 |
|
---|
490 | toggleWarning (res != -1);
|
---|
491 | }
|
---|
492 |
|
---|
493 | bool VBoxLogSearchPanel::eventFilter (QObject *aObject, QEvent *aEvent)
|
---|
494 | {
|
---|
495 | /* Check that the object is a child of the parent of the search panel. If
|
---|
496 | * not do not proceed, cause we get all key events from all windows here. */
|
---|
497 | QObject *pp = aObject;
|
---|
498 | while(pp && pp != parentWidget()) { pp = pp->parent(); };
|
---|
499 | if (!pp)
|
---|
500 | return false;
|
---|
501 | switch (aEvent->type())
|
---|
502 | {
|
---|
503 | case QEvent::KeyPress:
|
---|
504 | {
|
---|
505 | QKeyEvent *e = static_cast<QKeyEvent*> (aEvent);
|
---|
506 |
|
---|
507 | /* handle the Enter keypress for mSearchString
|
---|
508 | * widget as a search next string action */
|
---|
509 | if (aObject == mSearchString &&
|
---|
510 | (e->QInputEvent::modifiers() == 0 ||
|
---|
511 | e->QInputEvent::modifiers() & Qt::KeypadModifier) &&
|
---|
512 | (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return))
|
---|
513 | {
|
---|
514 | mButtonsNextPrev->animateClick (1);
|
---|
515 | return true;
|
---|
516 | }
|
---|
517 | /* handle other search next/previous shortcuts */
|
---|
518 | else if (e->key() == Qt::Key_F3)
|
---|
519 | {
|
---|
520 | if (e->QInputEvent::modifiers() == 0)
|
---|
521 | mButtonsNextPrev->animateClick (1);
|
---|
522 | else if (e->QInputEvent::modifiers() == Qt::ShiftModifier)
|
---|
523 | mButtonsNextPrev->animateClick (0);
|
---|
524 | return true;
|
---|
525 | }
|
---|
526 | /* handle ctrl-f key combination as a shortcut to
|
---|
527 | * move to the search field */
|
---|
528 | else if (e->QInputEvent::modifiers() == Qt::ControlModifier &&
|
---|
529 | e->key() == Qt::Key_F)
|
---|
530 | {
|
---|
531 | if (mViewer->currentLogPage())
|
---|
532 | {
|
---|
533 | if (isHidden()) show();
|
---|
534 | mSearchString->setFocus();
|
---|
535 | return true;
|
---|
536 | }
|
---|
537 | }
|
---|
538 | /* handle alpha-numeric keys to implement the
|
---|
539 | * "find as you type" feature */
|
---|
540 | else if ((e->QInputEvent::modifiers() & ~Qt::ShiftModifier) == 0 &&
|
---|
541 | e->key() >= Qt::Key_Exclam &&
|
---|
542 | e->key() <= Qt::Key_AsciiTilde)
|
---|
543 | {
|
---|
544 | if (mViewer->currentLogPage())
|
---|
545 | {
|
---|
546 | if (isHidden()) show();
|
---|
547 | mSearchString->setFocus();
|
---|
548 | mSearchString->insert (e->text());
|
---|
549 | return true;
|
---|
550 | }
|
---|
551 | }
|
---|
552 | break;
|
---|
553 | }
|
---|
554 | default:
|
---|
555 | break;
|
---|
556 | }
|
---|
557 | return false;
|
---|
558 | }
|
---|
559 |
|
---|
560 | void VBoxLogSearchPanel::showEvent (QShowEvent *aEvent)
|
---|
561 | {
|
---|
562 | QWidget::showEvent (aEvent);
|
---|
563 | mSearchString->setFocus();
|
---|
564 | mSearchString->selectAll();
|
---|
565 | }
|
---|
566 |
|
---|
567 | void VBoxLogSearchPanel::hideEvent (QHideEvent *aEvent)
|
---|
568 | {
|
---|
569 | QWidget *focus = QApplication::focusWidget();
|
---|
570 | if (focus &&
|
---|
571 | focus->parent() == this)
|
---|
572 | focusNextPrevChild (true);
|
---|
573 | QWidget::hideEvent (aEvent);
|
---|
574 | }
|
---|
575 |
|
---|
576 | void VBoxLogSearchPanel::toggleWarning (bool aHide)
|
---|
577 | {
|
---|
578 | mWarningSpacer->changeSize (aHide ? 0 : 16, 0, QSizePolicy::Fixed,
|
---|
579 | QSizePolicy::Minimum);
|
---|
580 | if (!aHide)
|
---|
581 | mSearchString->markError();
|
---|
582 | else
|
---|
583 | mSearchString->unmarkError();
|
---|
584 | mWarningIcon->setHidden (aHide);
|
---|
585 | mWarningString->setHidden (aHide);
|
---|
586 | }
|
---|
587 |
|
---|
588 | void VBoxLogSearchPanel::find (int aButton)
|
---|
589 | {
|
---|
590 | if (aButton == 0)
|
---|
591 | findBack();
|
---|
592 | else
|
---|
593 | findNext();
|
---|
594 | }
|
---|
595 |
|
---|