[26719] | 1 | /* $Id: VBoxVMLogViewer.cpp 30356 2010-06-22 08:42:22Z vboxsync $ */
|
---|
[10156] | 2 | /** @file
|
---|
| 3 | *
|
---|
| 4 | * VBox frontends: Qt4 GUI ("VirtualBox"):
|
---|
| 5 | * VBoxVMLogViewer class implementation
|
---|
| 6 | */
|
---|
| 7 |
|
---|
| 8 | /*
|
---|
[28800] | 9 | * Copyright (C) 2006-2008 Oracle Corporation
|
---|
[10156] | 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 |
|
---|
[25526] | 20 | #ifdef VBOX_WITH_PRECOMPILED_HEADERS
|
---|
| 21 | # include "precomp.h"
|
---|
| 22 | #else /* !VBOX_WITH_PRECOMPILED_HEADERS */
|
---|
[30192] | 23 | #include "QITabWidget.h"
|
---|
| 24 | #include "UIIconPool.h"
|
---|
[30356] | 25 | #include "UISpecialControls.h"
|
---|
[24107] | 26 | #include "VBoxGlobal.h"
|
---|
| 27 | #include "VBoxProblemReporter.h"
|
---|
| 28 | #include "VBoxUtils.h"
|
---|
[30192] | 29 | #include "VBoxVMLogViewer.h"
|
---|
[10156] | 30 |
|
---|
| 31 | /* Qt includes */
|
---|
| 32 | #include <QCheckBox>
|
---|
[30192] | 33 | #include <QDateTime>
|
---|
[10156] | 34 | #include <QDir>
|
---|
| 35 | #include <QFileDialog>
|
---|
[30192] | 36 | #include <QKeyEvent>
|
---|
| 37 | #include <QLabel>
|
---|
| 38 | #include <QLineEdit>
|
---|
[10156] | 39 | #include <QPushButton>
|
---|
[30192] | 40 | #include <QScrollBar>
|
---|
| 41 | #include <QStyle>
|
---|
| 42 | #include <QTextEdit>
|
---|
[25526] | 43 | #endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
|
---|
[10156] | 44 |
|
---|
| 45 | VBoxVMLogViewer::LogViewersMap VBoxVMLogViewer::mSelfArray = LogViewersMap();
|
---|
| 46 |
|
---|
[12695] | 47 | void VBoxVMLogViewer::createLogViewer (QWidget *aCenterWidget, CMachine &aMachine)
|
---|
[10156] | 48 | {
|
---|
[12695] | 49 | if (!mSelfArray.contains (aMachine.GetName()))
|
---|
[10156] | 50 | {
|
---|
[12695] | 51 | /* Creating new log viewer if there is no one existing */
|
---|
[12782] | 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 |
|
---|
[12695] | 58 | lv->centerAccording (aCenterWidget);
|
---|
[15185] | 59 | connect (vboxGlobal().mainWindow(), SIGNAL (closing()), lv, SLOT (close()));
|
---|
[10156] | 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 |
|
---|
[13553] | 83 | /* Apply window icons */
|
---|
[30192] | 84 | setWindowIcon(UIIconPool::iconSetFull(QSize (32, 32), QSize (16, 16),
|
---|
| 85 | ":/vm_show_logs_32px.png", ":/show_logs_16px.png"));
|
---|
[13553] | 86 |
|
---|
[10156] | 87 | /* Enable size grip without using a status bar. */
|
---|
| 88 | setSizeGripEnabled (true);
|
---|
| 89 |
|
---|
| 90 | /* Logs list creation */
|
---|
[29972] | 91 | mLogList = new QITabWidget (mLogsFrame);
|
---|
[10156] | 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);
|
---|
[12695] | 106 | mBtnFind = mButtonBox->addButton (QString::null, QDialogButtonBox::ActionRole);
|
---|
[10156] | 107 | mBtnSave = mButtonBox->button (QDialogButtonBox::Save);
|
---|
[12695] | 108 | mBtnRefresh = mButtonBox->addButton (QString::null, QDialogButtonBox::ActionRole);
|
---|
[10156] | 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();
|
---|
[10335] | 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);
|
---|
[24107] | 126 | # ifdef VBOX_DARWIN_USE_NATIVE_CONTROLS
|
---|
| 127 | logsFrameLayout->setSpacing (4);
|
---|
| 128 | # endif /* VBOX_DARWIN_USE_NATIVE_CONTROLS */
|
---|
[10335] | 129 | #endif /* Q_WS_MAC */
|
---|
[10156] | 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 */
|
---|
[28344] | 163 | mLogFiles.clear();
|
---|
[10156] | 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 |
|
---|
[28344] | 174 | const CSystemProperties &sys = vboxGlobal().virtualBox().GetSystemProperties();
|
---|
| 175 | int cMaxLogs = sys.GetLogHistoryCount();
|
---|
| 176 | for (int i=0; i <= cMaxLogs; ++i)
|
---|
[10156] | 177 | {
|
---|
[28344] | 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 | }
|
---|
[10156] | 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>")
|
---|
[28344] | 215 | .arg (mMachine.GetLogFolder()));
|
---|
[10156] | 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);
|
---|
[10335] | 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);
|
---|
[10156] | 234 | }
|
---|
| 235 |
|
---|
| 236 | void VBoxVMLogViewer::save()
|
---|
| 237 | {
|
---|
| 238 | /* Prepare "save as" dialog */
|
---|
[28344] | 239 | QFileInfo fileInfo (mLogFiles.at(mLogList->currentIndex()).first);
|
---|
[10156] | 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);
|
---|
[13580] | 244 | QString defaultFullName = QDir::toNativeSeparators (
|
---|
[10156] | 245 | QDir::home().absolutePath() + "/" + defaultFileName);
|
---|
| 246 | QString newFileName = QFileDialog::getSaveFileName (this,
|
---|
| 247 | tr ("Save VirtualBox Log As"), defaultFullName);
|
---|
| 248 |
|
---|
[29381] | 249 | /* Copy log into the file */
|
---|
[10156] | 250 | if (!newFileName.isEmpty())
|
---|
[29381] | 251 | QFile::copy(mMachine.QueryLogFilename(mLogList->currentIndex()), newFileName);
|
---|
[10156] | 252 | }
|
---|
| 253 |
|
---|
| 254 | void VBoxVMLogViewer::search()
|
---|
| 255 | {
|
---|
| 256 | mSearchPanel->isHidden() ? mSearchPanel->show() : mSearchPanel->hide();
|
---|
| 257 | }
|
---|
| 258 |
|
---|
| 259 | void VBoxVMLogViewer::currentLogPageChanged (int aIndex)
|
---|
| 260 | {
|
---|
[12695] | 261 | if (aIndex >= 0 &&
|
---|
[28344] | 262 | aIndex < mLogFiles.count())
|
---|
| 263 | setFileForProxyIcon(mLogFiles.at(aIndex).first);
|
---|
[10156] | 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"));
|
---|
[12695] | 277 | mBtnSave->setText (tr ("&Save"));
|
---|
| 278 | mBtnClose->setText (tr ("Close"));
|
---|
[10156] | 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 | }
|
---|
[25044] | 313 |
|
---|
| 314 | /* Make sure the log view widget has the focus */
|
---|
| 315 | QWidget *w = currentLogPage();
|
---|
| 316 | if (w)
|
---|
| 317 | w->setFocus();
|
---|
[10156] | 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)
|
---|
[24107] | 346 | , mButtonsNextPrev (0)
|
---|
[10156] | 347 | , mCaseSensitive (0)
|
---|
| 348 | , mWarningSpacer (0), mWarningIcon (0), mWarningString (0)
|
---|
| 349 | {
|
---|
[30356] | 350 | mButtonClose = new UIMiniCancelButton (this);
|
---|
[10156] | 351 | connect (mButtonClose, SIGNAL (clicked()), this, SLOT (hide()));
|
---|
| 352 |
|
---|
| 353 | mSearchName = new QLabel (this);
|
---|
[30356] | 354 | mSearchString = new UISearchField (this);
|
---|
[10156] | 355 | mSearchString->setSizePolicy (QSizePolicy::Preferred,
|
---|
| 356 | QSizePolicy::Fixed);
|
---|
| 357 | connect (mSearchString, SIGNAL (textChanged (const QString &)),
|
---|
| 358 | this, SLOT (findCurrent (const QString &)));
|
---|
| 359 |
|
---|
[30356] | 360 | mButtonsNextPrev = new UIRoundRectSegmentedButton(2, this);
|
---|
[24107] | 361 | mButtonsNextPrev->setEnabled (0, false);
|
---|
[30188] | 362 | mButtonsNextPrev->setEnabled (1, false);
|
---|
| 363 | #ifndef Q_WS_MAC
|
---|
| 364 | /* No icons on the Mac */
|
---|
[30196] | 365 | mButtonsNextPrev->setIcon(0, UIIconPool::defaultIcon(UIIconPool::ArrowBackIcon, this));
|
---|
| 366 | mButtonsNextPrev->setIcon(1, UIIconPool::defaultIcon(UIIconPool::ArrowForwardIcon, this));
|
---|
[30188] | 367 | #endif /* !Q_WS_MAC */
|
---|
[24107] | 368 | connect (mButtonsNextPrev, SIGNAL (clicked (int)), this, SLOT (find (int)));
|
---|
[10156] | 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 |
|
---|
[30192] | 377 | QIcon icon = UIIconPool::defaultIcon(UIIconPool::MessageBoxWarningIcon, this);
|
---|
[10156] | 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 |
|
---|
[24107] | 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 |
|
---|
[10156] | 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);
|
---|
[24107] | 400 | mainLayout->addWidget (mButtonsNextPrev);
|
---|
[10156] | 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 |
|
---|
[30196] | 420 | mButtonsNextPrev->setTitle (0, tr ("&Previous"));
|
---|
| 421 | mButtonsNextPrev->setToolTip (0, tr ("Search for the previous occurrence "
|
---|
[25087] | 422 | "of the string"));
|
---|
[10156] | 423 |
|
---|
[30196] | 424 | mButtonsNextPrev->setTitle (1, tr ("&Next"));
|
---|
| 425 | mButtonsNextPrev->setToolTip (1, tr ("Search for the next occurrence of "
|
---|
| 426 | "the string"));
|
---|
[24107] | 427 |
|
---|
[10156] | 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 | {
|
---|
[24107] | 437 | mButtonsNextPrev->setEnabled (0, aSearchString.length());
|
---|
| 438 | mButtonsNextPrev->setEnabled (1, aSearchString.length());
|
---|
[10156] | 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 | {
|
---|
[25044] | 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;
|
---|
[10156] | 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 | {
|
---|
[30196] | 514 | mButtonsNextPrev->animateClick (1);
|
---|
[10156] | 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)
|
---|
[30196] | 521 | mButtonsNextPrev->animateClick (1);
|
---|
| 522 | else if (e->QInputEvent::modifiers() == Qt::ShiftModifier)
|
---|
[24107] | 523 | mButtonsNextPrev->animateClick (0);
|
---|
[10156] | 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);
|
---|
[24107] | 580 | if (!aHide)
|
---|
| 581 | mSearchString->markError();
|
---|
| 582 | else
|
---|
| 583 | mSearchString->unmarkError();
|
---|
[10156] | 584 | mWarningIcon->setHidden (aHide);
|
---|
| 585 | mWarningString->setHidden (aHide);
|
---|
| 586 | }
|
---|
| 587 |
|
---|
[24107] | 588 | void VBoxLogSearchPanel::find (int aButton)
|
---|
| 589 | {
|
---|
| 590 | if (aButton == 0)
|
---|
[30196] | 591 | findBack();
|
---|
| 592 | else
|
---|
[24107] | 593 | findNext();
|
---|
| 594 | }
|
---|
[26719] | 595 |
|
---|