VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerWidget.cpp@ 102493

Last change on this file since 102493 was 102269, checked in by vboxsync, 10 months ago

FE/Qt: bugref:10450: macOS: No need to QIToolBar layout/paint hacks since Qt6.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.0 KB
Line 
1/* $Id: UIVMLogViewerWidget.cpp 102269 2023-11-22 18:50:08Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIVMLogViewerWidget class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QCheckBox>
30#include <QDateTime>
31#include <QDir>
32#include <QFont>
33#include <QMenu>
34#include <QPainter>
35#include <QPlainTextEdit>
36#include <QScrollBar>
37#include <QStyle>
38#include <QStyleFactory>
39#include <QStylePainter>
40#include <QStyleOptionTab>
41#include <QTabBar>
42#include <QTextBlock>
43#include <QVBoxLayout>
44#ifdef RT_OS_SOLARIS
45# include <QFontDatabase>
46#endif
47
48/* GUI includes: */
49#include "QIFileDialog.h"
50#include "QITabWidget.h"
51#include "QIToolBar.h"
52#include "QIToolButton.h"
53#include "UIActionPool.h"
54#include "UICommon.h"
55#include "UIExtraDataManager.h"
56#include "UIIconPool.h"
57#include "UIMessageCenter.h"
58#include "UIVirtualMachineItem.h"
59#include "UIVMLogPage.h"
60#include "UIVMLogViewerWidget.h"
61#include "UIVMLogViewerPanel.h"
62
63/* COM includes: */
64#include "COMEnums.h"
65#include "CMachine.h"
66#include "CSystemProperties.h"
67
68/* Other VBox includes: */
69#include <iprt/assert.h>
70
71/** Limit the read string size to avoid bloated log viewer pages. */
72const ULONG uAllowedLogSize = _256M;
73
74class UILogTabCloseButton : public QIToolButton
75{
76 Q_OBJECT;
77
78public:
79
80 //UILogTabCloseButton(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName);
81 UILogTabCloseButton(QWidget *pParent, const QUuid &uMachineId)
82 : QIToolButton(pParent)
83 , m_uMachineId(uMachineId)
84 {
85 setAutoRaise(true);
86 setIcon(UIIconPool::iconSet(":/close_16px.png"));
87 }
88
89 const QUuid &machineId() const
90 {
91 return m_uMachineId;
92 }
93
94protected:
95
96 QUuid m_uMachineId;
97};
98/*********************************************************************************************************************************
99* UILabelTab definition. *
100*********************************************************************************************************************************/
101
102class UILabelTab : public UIVMLogTab
103{
104
105 Q_OBJECT;
106
107public:
108
109 UILabelTab(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName);
110
111protected:
112
113 void retranslateUi();
114};
115
116/*********************************************************************************************************************************
117* UITabBar definition. *
118*********************************************************************************************************************************/
119/** A QTabBar extention to be able to override paintEvent for custom tab coloring. */
120class UITabBar : public QTabBar
121{
122
123 Q_OBJECT;
124
125public:
126
127 UITabBar(QWidget *pParent = 0);
128
129protected:
130
131 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
132};
133
134/*********************************************************************************************************************************
135* UITabWidget definition. *
136*********************************************************************************************************************************/
137
138/** A QITabWidget used only for setTabBar since it is protected. */
139class UITabWidget : public QITabWidget
140{
141
142 Q_OBJECT;
143
144public:
145
146 UITabWidget(QWidget *pParent = 0);
147};
148
149/*********************************************************************************************************************************
150* UILabelTab implementation. *
151*********************************************************************************************************************************/
152
153UILabelTab::UILabelTab(QWidget *pParent, const QUuid &uMachineId, const QString &strMachineName)
154 : UIVMLogTab(pParent, uMachineId, strMachineName)
155{
156}
157
158void UILabelTab::retranslateUi()
159{
160}
161
162/*********************************************************************************************************************************
163* UITabBar implementation. *
164*********************************************************************************************************************************/
165
166UITabBar::UITabBar(QWidget *pParent /* = 0 */)
167 :QTabBar(pParent)
168{
169}
170
171void UITabBar::paintEvent(QPaintEvent *pEvent)
172{
173 Q_UNUSED(pEvent);
174 QStylePainter painter(this);
175 for (int i = 0; i < count(); i++)
176 {
177 QStyleOptionTab opt;
178 initStyleOption(&opt, i);
179 bool fLabelTab = tabData(i).toBool();
180
181 if (!fLabelTab)
182 painter.drawControl(QStyle::CE_TabBarTabShape, opt);
183 painter.drawControl(QStyle::CE_TabBarTabLabel, opt);
184 }
185}
186
187/*********************************************************************************************************************************
188* UITabWidget implementation. *
189*********************************************************************************************************************************/
190
191UITabWidget::UITabWidget(QWidget *pParent /* = 0 */)
192 :QITabWidget(pParent)
193{
194 setTabBar(new UITabBar(this));
195}
196
197
198/*********************************************************************************************************************************
199* UIVMLogViewerWidget implementation. *
200*********************************************************************************************************************************/
201
202UIVMLogViewerWidget::UIVMLogViewerWidget(EmbedTo enmEmbedding,
203 UIActionPool *pActionPool,
204 bool fShowToolbar /* = true */,
205 const QUuid &uMachineId /* = QUuid() */,
206 QWidget *pParent /* = 0 */)
207 : QIWithRetranslateUI<QWidget>(pParent)
208 , m_enmEmbedding(enmEmbedding)
209 , m_pActionPool(pActionPool)
210 , m_fShowToolbar(fShowToolbar)
211 , m_fIsPolished(false)
212 , m_pTabWidget(0)
213 , m_pMainLayout(0)
214 , m_pToolBar(0)
215 , m_bShowLineNumbers(true)
216 , m_bWrapLines(false)
217 , m_font(QFontDatabase::systemFont(QFontDatabase::FixedFont))
218 , m_pCornerButton(0)
219 , m_pMachineSelectionMenu(0)
220 , m_fCommitDataSignalReceived(false)
221 , m_pPreviousLogPage(0)
222 , m_pPanel(0)
223{
224 /* Prepare VM Log-Viewer: */
225 prepare();
226 if (!uMachineId.isNull())
227 setMachines(QVector<QUuid>() << uMachineId);
228 connect(&uiCommon(), &UICommon::sigAskToCommitData,
229 this, &UIVMLogViewerWidget::sltCommitDataSignalReceived);
230}
231
232UIVMLogViewerWidget::~UIVMLogViewerWidget()
233{
234}
235
236int UIVMLogViewerWidget::defaultLogPageWidth() const
237{
238 if (!m_pTabWidget)
239 return 0;
240
241 QWidget *pContainer = m_pTabWidget->currentWidget();
242 if (!pContainer)
243 return 0;
244
245 QPlainTextEdit *pBrowser = pContainer->findChild<QPlainTextEdit*>();
246 if (!pBrowser)
247 return 0;
248 /* Compute a width for 132 characters plus scrollbar and frame width: */
249#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
250 int iDefaultWidth = pBrowser->fontMetrics().horizontalAdvance(QChar('x')) * 132 +
251#else
252 int iDefaultWidth = pBrowser->fontMetrics().width(QChar('x')) * 132 +
253#endif
254 pBrowser->verticalScrollBar()->width() +
255 pBrowser->frameWidth() * 2;
256
257 return iDefaultWidth;
258}
259
260QMenu *UIVMLogViewerWidget::menu() const
261{
262 return m_pActionPool->action(UIActionIndex_M_LogWindow)->menu();
263}
264
265
266void UIVMLogViewerWidget::setSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
267{
268 QVector<QUuid> selectedMachines;
269
270 foreach (const UIVirtualMachineItem *item, items)
271 {
272 if (!item)
273 continue;
274 selectedMachines << item->id();
275 }
276 setMachines(selectedMachines);
277}
278
279void UIVMLogViewerWidget::addSelectedVMListItems(const QList<UIVirtualMachineItem*> &items)
280{
281 QVector<QUuid> selectedMachines(m_machines);
282
283 foreach (const UIVirtualMachineItem *item, items)
284 {
285 if (!item)
286 continue;
287 selectedMachines << item->id();
288 }
289 setMachines(selectedMachines);
290}
291
292void UIVMLogViewerWidget::setMachines(const QVector<QUuid> &machineIDs)
293{
294 /* List of machines that are newly added to selected machine list: */
295 QVector<QUuid> newSelections;
296 QVector<QUuid> unselectedMachines(m_machines);
297
298 foreach (const QUuid &id, machineIDs)
299 {
300 unselectedMachines.removeAll(id);
301 if (!m_machines.contains(id))
302 newSelections << id;
303 }
304 m_machines = machineIDs;
305
306 m_pTabWidget->hide();
307 /* Read logs and create pages/tabs for newly selected machines: */
308 createLogViewerPages(newSelections);
309 /* Remove the log pages/tabs of unselected machines from the tab widget: */
310 removeLogViewerPages(unselectedMachines);
311 /* Assign color indexes to tabs based on machines. We use two alternating colors to indicate different machine logs. */
312 markLabelTabs();
313 labelTabHandler();
314 m_pTabWidget->show();
315}
316
317void UIVMLogViewerWidget::markLabelTabs()
318{
319 if (!m_pTabWidget || !m_pTabWidget->tabBar() || m_pTabWidget->tabBar()->count() == 0)
320 return;
321 QTabBar *pTabBar = m_pTabWidget->tabBar();
322
323 for (int i = 0; i < pTabBar->count(); ++i)
324 {
325 if (qobject_cast<UILabelTab*>(m_pTabWidget->widget(i)))
326 {
327 pTabBar->setTabData(i, true);
328 /* Add close button only for dialog mode in manager UI. */
329 if (uiCommon().uiType() == UICommon::UIType_SelectorUI && m_enmEmbedding == EmbedTo_Dialog)
330 {
331 UIVMLogTab *pTab = logTab(i);
332 if (pTab)
333 {
334 UILogTabCloseButton *pCloseButton = new UILogTabCloseButton(0, pTab->machineId());
335 pCloseButton->setIcon(UIIconPool::iconSet(":/close_16px.png"));
336 pTabBar->setTabButton(i, QTabBar::RightSide, pCloseButton);
337 pCloseButton->setToolTip(tr("Close this machine's logs"));
338 connect(pCloseButton, &UILogTabCloseButton::clicked, this, &UIVMLogViewerWidget::sltTabCloseButtonClick);
339 }
340 }
341 }
342 else
343 {
344 pTabBar->setTabData(i, false);
345 }
346
347 }
348}
349
350QString UIVMLogViewerWidget::readLogFile(const CMachine &comConstMachine, int iLogFileId)
351{
352 CMachine comMachine(comConstMachine); // ReadLog is non const
353 QString strLogFileContent;
354 ULONG uOffset = 0;
355
356 while (true)
357 {
358 QVector<BYTE> data = comMachine.ReadLog(iLogFileId, uOffset, _1M);
359 /// @todo it's probably worth testing !comMachine.isOk() and show error message, or not :)
360 if (data.size() == 0)
361 break;
362 strLogFileContent.append(QString::fromUtf8((char*)data.data(), data.size()));
363 uOffset += data.size();
364 /* Don't read futher if we have reached the allowed size limit: */
365 if (uOffset >= uAllowedLogSize)
366 {
367 strLogFileContent.append("\n=========Log file has been truncated as it is too large.======");
368 break;
369 }
370 }
371 return strLogFileContent;
372}
373
374QFont UIVMLogViewerWidget::currentFont() const
375{
376 const UIVMLogPage* logPage = currentLogPage();
377 if (!logPage)
378 return QFont();
379 return logPage->currentFont();
380}
381
382bool UIVMLogViewerWidget::shouldBeMaximized() const
383{
384 return gEDataManager->logWindowShouldBeMaximized();
385}
386
387void UIVMLogViewerWidget::saveOptions()
388{
389 if (!m_fCommitDataSignalReceived)
390 gEDataManager->setLogViweverOptions(m_font, m_bWrapLines, m_bShowLineNumbers);
391}
392
393void UIVMLogViewerWidget::sltRefresh()
394{
395 if (!m_pTabWidget)
396 return;
397
398 UIVMLogPage *pCurrentPage = currentLogPage();
399 if (!pCurrentPage || pCurrentPage->logFileId() == -1)
400 return;
401
402 CMachine comMachine = uiCommon().virtualBox().FindMachine(pCurrentPage->machineId().toString());
403 if (comMachine.isNull())
404 return;
405
406 QString strLogContent = readLogFile(comMachine, pCurrentPage->logFileId());
407 pCurrentPage->setLogContent(strLogContent, false);
408
409 if (m_pPanel && m_pPanel->isVisible() &&
410 m_pPanel->currentIndex() == static_cast<UIVMLogViewerPaneContainer::Page>(UIVMLogViewerPaneContainer::Page_Search))
411 m_pPanel->refreshSearch();
412
413 /* Re-Apply the filter settings: */
414 if (m_pPanel)
415 m_pPanel->applyFilter();
416}
417
418void UIVMLogViewerWidget::sltReload()
419{
420 if (!m_pTabWidget)
421 return;
422
423 m_pTabWidget->blockSignals(true);
424 m_pTabWidget->hide();
425
426 removeAllLogPages();
427 createLogViewerPages(m_machines);
428
429 /* re-Apply the filter settings: */
430 if (m_pPanel)
431 m_pPanel->applyFilter();
432
433 m_pTabWidget->blockSignals(false);
434 markLabelTabs();
435 m_pTabWidget->show();
436}
437
438void UIVMLogViewerWidget::sltSave()
439{
440 UIVMLogPage *pLogPage = currentLogPage();
441 if (!pLogPage)
442 return;
443
444 if (pLogPage->machineId().isNull())
445 return;
446
447 const QString& fileName = pLogPage->logFileName();
448 if (fileName.isEmpty())
449 return;
450 /* Prepare "save as" dialog: */
451 const QFileInfo fileInfo(fileName);
452 /* Prepare default filename: */
453 const QDateTime dtInfo = fileInfo.lastModified();
454 const QString strDtString = dtInfo.toString("yyyy-MM-dd-hh-mm-ss");
455 const QString strDefaultFileName = QString("%1-%2.log").arg(pLogPage->machineName()).arg(strDtString);
456 const QString strDefaultFullName = QDir::toNativeSeparators(QDir::home().absolutePath() + "/" + strDefaultFileName);
457
458 const QString strNewFileName = QIFileDialog::getSaveFileName(strDefaultFullName,
459 "",
460 this,
461 tr("Save VirtualBox Log As"),
462 0 /* selected filter */,
463 true /* resolve symlinks */,
464 true /* confirm overwrite */);
465 /* Make sure file-name is not empty: */
466 if (!strNewFileName.isEmpty())
467 {
468 /* Delete the previous file if already exists as user already confirmed: */
469 if (QFile::exists(strNewFileName))
470 QFile::remove(strNewFileName);
471 /* Copy log into the file: */
472 QFile::copy(fileName, strNewFileName);
473 }
474}
475
476void UIVMLogViewerWidget::sltDeleteBookmarkByIndex(int index)
477{
478 UIVMLogPage* pLogPage = currentLogPage();
479 if (!pLogPage)
480 return;
481 pLogPage->deleteBookmarkByIndex(index);
482 if (m_pPanel)
483 m_pPanel->updateBookmarkList(pLogPage->bookmarkList());
484}
485
486void UIVMLogViewerWidget::sltDeleteAllBookmarks()
487{
488 UIVMLogPage* pLogPage = currentLogPage();
489 if (!pLogPage)
490 return;
491 pLogPage->deleteAllBookmarks();
492
493 if (m_pPanel)
494 m_pPanel->updateBookmarkList(pLogPage->bookmarkList());
495}
496
497void UIVMLogViewerWidget::sltUpdateBookmarkPanel()
498{
499 if (!currentLogPage() || !m_pPanel)
500 return;
501 m_pPanel->updateBookmarkList(currentLogPage()->bookmarkList());
502}
503
504void UIVMLogViewerWidget::gotoBookmark(int bookmarkIndex)
505{
506 if (!currentLogPage())
507 return;
508 currentLogPage()->scrollToBookmark(bookmarkIndex);
509}
510
511void UIVMLogViewerWidget::sltPanelContainerHidden()
512{
513 uncheckPaneActions();
514}
515
516void UIVMLogViewerWidget::sltPanelActionToggled(bool fChecked)
517{
518 if (!m_pPanel)
519 return;
520 QAction *pAction = qobject_cast<QAction*>(sender());
521
522 if (!m_paneActions.contains(pAction))
523 return;
524
525 foreach (QAction *pOther, m_paneActions)
526 {
527 if (pOther == pAction)
528 continue;
529 pOther->blockSignals(true);
530 pOther->setChecked(false);
531 pOther->blockSignals(false);
532 }
533
534 m_pPanel->setVisible(fChecked);
535 if (fChecked)
536 m_pPanel->setCurrentIndex(pAction->data().toInt());
537}
538
539void UIVMLogViewerWidget::sltSearchResultHighLigting()
540{
541 if (!m_pPanel || !currentLogPage())
542 return;
543 currentLogPage()->setScrollBarMarkingsVector(m_pPanel->matchLocationVector());
544}
545
546void UIVMLogViewerWidget::sltHandleSearchUpdated()
547{
548}
549
550void UIVMLogViewerWidget::sltCurrentTabChanged(int tabIndex)
551{
552 Q_UNUSED(tabIndex);
553
554 if (m_pPreviousLogPage)
555 m_pPreviousLogPage->saveScrollBarPosition();
556
557 if (labelTabHandler())
558 return;
559 /* Dont refresh the search here as it is refreshed by the filtering mechanism
560 which is updated as tab current index changes (see sltFilterApplied): */
561 if (m_pPanel)
562 m_pPanel->applyFilter();
563
564 /* We keep a separate QVector<LogBookmark> for each log page: */
565 if (m_pPanel && currentLogPage())
566 m_pPanel->updateBookmarkList(currentLogPage()->bookmarkList());
567
568 m_pPreviousLogPage = currentLogPage();
569 if (m_pPreviousLogPage)
570 m_pPreviousLogPage->restoreScrollBarPosition();
571}
572
573void UIVMLogViewerWidget::sltFilterApplied()
574{
575 /* Reapply the search to get highlighting etc. correctly */
576 if (m_pPanel)
577 m_pPanel->refreshSearch();
578}
579
580void UIVMLogViewerWidget::sltLogPageFilteredChanged(bool isFiltered)
581{
582 /* Disable bookmark panel since bookmarks are stored as line numbers within
583 the original log text and does not mean much in a reduced/filtered one. */
584 if (m_pPanel)
585 m_pPanel->disableEnableBookmarking(!isFiltered);
586}
587
588void UIVMLogViewerWidget::sltShowLineNumbers(bool bShowLineNumbers)
589{
590 if (m_bShowLineNumbers == bShowLineNumbers)
591 return;
592
593 m_bShowLineNumbers = bShowLineNumbers;
594 /* Set all log page instances. */
595 for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
596 {
597 UIVMLogPage* pLogPage = logPage(i);
598 if (pLogPage)
599 pLogPage->setShowLineNumbers(m_bShowLineNumbers);
600 }
601 saveOptions();
602}
603
604void UIVMLogViewerWidget::sltWrapLines(bool bWrapLines)
605{
606 if (m_bWrapLines == bWrapLines)
607 return;
608
609 m_bWrapLines = bWrapLines;
610 /* Set all log page instances. */
611 for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
612 {
613 UIVMLogPage* pLogPage = logPage(i);
614 if (pLogPage)
615 pLogPage->setWrapLines(m_bWrapLines);
616 }
617 saveOptions();
618}
619
620void UIVMLogViewerWidget::sltFontSizeChanged(int fontSize)
621{
622 if (m_font.pointSize() == fontSize)
623 return;
624 m_font.setPointSize(fontSize);
625 for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
626 {
627 UIVMLogPage* pLogPage = logPage(i);
628 if (pLogPage)
629 pLogPage->setCurrentFont(m_font);
630 }
631 saveOptions();
632}
633
634void UIVMLogViewerWidget::sltChangeFont(QFont font)
635{
636 if (m_font == font)
637 return;
638 m_font = font;
639 for (int i = 0; m_pTabWidget && (i < m_pTabWidget->count()); ++i)
640 {
641 UIVMLogPage* pLogPage = logPage(i);
642 if (pLogPage)
643 pLogPage->setCurrentFont(m_font);
644 }
645 saveOptions();
646}
647
648void UIVMLogViewerWidget::sltResetOptionsToDefault()
649{
650 sltShowLineNumbers(true);
651 sltWrapLines(false);
652 sltChangeFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
653
654 if (m_pPanel)
655 {
656 m_pPanel->setShowLineNumbers(true);
657 m_pPanel->setWrapLines(false);
658 m_pPanel->setFontSizeInPoints(m_font.pointSize());
659 }
660 saveOptions();
661}
662
663void UIVMLogViewerWidget::sltCloseMachineLogs()
664{
665 QAction *pAction = qobject_cast<QAction*>(sender());
666 if (!pAction)
667 return;
668 QUuid machineId = pAction->data().toUuid();
669 if (machineId.isNull())
670 return;
671 QVector<QUuid> machineList;
672 machineList << machineId;
673 removeLogViewerPages(machineList);
674}
675
676void UIVMLogViewerWidget::sltTabCloseButtonClick()
677{
678 UILogTabCloseButton *pButton = qobject_cast<UILogTabCloseButton*>(sender());
679 if (!pButton)
680 return;
681 if (pButton->machineId().isNull())
682 return;
683 QVector<QUuid> list;
684 list << pButton->machineId();
685 removeLogViewerPages(list);
686}
687
688void UIVMLogViewerWidget::sltCommitDataSignalReceived()
689{
690 m_fCommitDataSignalReceived = true;
691}
692
693void UIVMLogViewerWidget::sltPanelCurrentTabChanged(int iIndex)
694{
695 if (!m_pPanel || !m_pPanel->isVisible())
696 return;
697
698 uncheckPaneActions();
699
700 switch (static_cast<UIVMLogViewerPaneContainer::Page>(iIndex))
701 {
702 case UIVMLogViewerPaneContainer::Page_Search:
703 m_pActionPool->action(UIActionIndex_M_Log_T_Find)->setChecked(true);
704 break;
705 case UIVMLogViewerPaneContainer::Page_Filter:
706 m_pActionPool->action(UIActionIndex_M_Log_T_Filter)->setChecked(true);
707 break;
708 case UIVMLogViewerPaneContainer::Page_Bookmark:
709 m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark)->setChecked(true);
710 break;
711 case UIVMLogViewerPaneContainer::Page_Preferences:
712 m_pActionPool->action(UIActionIndex_M_Log_T_Preferences)->setChecked(true);
713 break;
714 default:
715 break;
716 }
717}
718
719void UIVMLogViewerWidget::sltShowSearchPane()
720{
721 AssertReturnVoid(m_pPanel);
722
723 m_pPanel->setVisible(true);
724 int iIndex = (int)UIVMLogViewerPaneContainer::Page_Search;
725 m_pPanel->setCurrentIndex(iIndex);
726 sltPanelCurrentTabChanged(iIndex);
727}
728
729void UIVMLogViewerWidget::prepare()
730{
731 /* Load options: */
732 loadOptions();
733
734 /* Prepare stuff: */
735 prepareActions();
736 /* Prepare widgets: */
737 prepareWidgets();
738
739 /* Apply language settings: */
740 retranslateUi();
741
742 uiCommon().setHelpKeyword(this, "log-viewer");
743}
744
745void UIVMLogViewerWidget::prepareActions()
746{
747 /* First of all, add actions which has smaller shortcut scope: */
748 addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Find));
749 addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Filter));
750 addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark));
751 addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Preferences));
752 addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Refresh));
753 addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Save));
754
755 m_paneActions.insert(m_pActionPool->action(UIActionIndex_M_Log_T_Find));
756 m_paneActions.insert(m_pActionPool->action(UIActionIndex_M_Log_T_Filter));
757 m_paneActions.insert(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark));
758 m_paneActions.insert(m_pActionPool->action(UIActionIndex_M_Log_T_Preferences));
759 uncheckPaneActions();
760
761 m_pActionPool->action(UIActionIndex_M_Log_T_Find)->setData((int)UIVMLogViewerPaneContainer::Page_Search);
762 m_pActionPool->action(UIActionIndex_M_Log_T_Filter)->setData((int)UIVMLogViewerPaneContainer::Page_Filter);
763 m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark)->setData((int)UIVMLogViewerPaneContainer::Page_Bookmark);
764 m_pActionPool->action(UIActionIndex_M_Log_T_Preferences)->setData((int)UIVMLogViewerPaneContainer::Page_Preferences);
765
766 /* Connect actions: */
767 connect(m_pActionPool->action(UIActionIndex_M_Log_T_Find), &QAction::toggled,
768 this, &UIVMLogViewerWidget::sltPanelActionToggled);
769 connect(m_pActionPool->action(UIActionIndex_M_Log_T_Filter), &QAction::toggled,
770 this, &UIVMLogViewerWidget::sltPanelActionToggled);
771 connect(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark), &QAction::toggled,
772 this, &UIVMLogViewerWidget::sltPanelActionToggled);
773 connect(m_pActionPool->action(UIActionIndex_M_Log_T_Preferences), &QAction::toggled,
774 this, &UIVMLogViewerWidget::sltPanelActionToggled);
775 connect(m_pActionPool->action(UIActionIndex_M_Log_S_Refresh), &QAction::triggered,
776 this, &UIVMLogViewerWidget::sltRefresh);
777 connect(m_pActionPool->action(UIActionIndex_M_Log_S_Reload), &QAction::triggered,
778 this, &UIVMLogViewerWidget::sltReload);
779 connect(m_pActionPool->action(UIActionIndex_M_Log_S_Save), &QAction::triggered,
780 this, &UIVMLogViewerWidget::sltSave);
781}
782
783void UIVMLogViewerWidget::prepareWidgets()
784{
785 /* Create main layout: */
786 m_pMainLayout = new QVBoxLayout(this);
787 AssertReturnVoid(m_pMainLayout);
788
789 /* Configure layout: */
790 m_pMainLayout->setContentsMargins(0, 0, 0, 0);
791#ifdef VBOX_WS_MAC
792 m_pMainLayout->setSpacing(10);
793#else
794 m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
795#endif
796
797 /* Prepare toolbar, if requested: */
798 if (m_fShowToolbar)
799 prepareToolBar();
800
801 /* Create VM Log-Viewer container: */
802 m_pTabWidget = new UITabWidget;
803 AssertReturnVoid(m_pTabWidget);
804 /* Add into layout: */
805 m_pMainLayout->addWidget(m_pTabWidget);
806 m_pTabWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
807 connect(m_pTabWidget, &QITabWidget::currentChanged, this, &UIVMLogViewerWidget::sltCurrentTabChanged);
808
809 m_pPanel = new UIVMLogViewerPaneContainer(0, this);
810 AssertReturnVoid(m_pPanel);
811 installEventFilter(m_pPanel);
812 m_pPanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
813 m_pPanel->setShowLineNumbers(m_bShowLineNumbers);
814 m_pPanel->setWrapLines(m_bWrapLines);
815 m_pPanel->setFontSizeInPoints(m_font.pointSize());
816 m_pPanel->setVisible(false);
817 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigHighlightingUpdated,
818 this, &UIVMLogViewerWidget::sltSearchResultHighLigting);
819 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigSearchUpdated,
820 this, &UIVMLogViewerWidget::sltHandleSearchUpdated);
821 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigFilterApplied,
822 this, &UIVMLogViewerWidget::sltFilterApplied);
823 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigDeleteBookmarkByIndex,
824 this, &UIVMLogViewerWidget::sltDeleteBookmarkByIndex);
825 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigDeleteAllBookmarks,
826 this, &UIVMLogViewerWidget::sltDeleteAllBookmarks);
827 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigBookmarkSelected,
828 this, &UIVMLogViewerWidget::gotoBookmark);
829 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigHidden,
830 this, &UIVMLogViewerWidget::sltPanelContainerHidden);
831
832 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigShowLineNumbers,
833 this, &UIVMLogViewerWidget::sltShowLineNumbers);
834 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigWrapLines,
835 this, &UIVMLogViewerWidget::sltWrapLines);
836 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigChangeFontSizeInPoints,
837 this, &UIVMLogViewerWidget::sltFontSizeChanged);
838 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigChangeFont,
839 this, &UIVMLogViewerWidget::sltChangeFont);
840 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigResetToDefaults,
841 this, &UIVMLogViewerWidget::sltResetOptionsToDefault);
842 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigCurrentTabChanged,
843 this, &UIVMLogViewerWidget::sltPanelCurrentTabChanged);
844 connect(m_pPanel, &UIVMLogViewerPaneContainer::sigShowSearchPane,
845 this, &UIVMLogViewerWidget::sltShowSearchPane);
846
847 m_pMainLayout->addWidget(m_pPanel);
848}
849
850void UIVMLogViewerWidget::prepareToolBar()
851{
852 /* Create toolbar: */
853 m_pToolBar = new QIToolBar(parentWidget());
854 if (m_pToolBar)
855 {
856 /* Configure toolbar: */
857 const int iIconMetric = (int)(QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize));
858 m_pToolBar->setIconSize(QSize(iIconMetric, iIconMetric));
859 m_pToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
860
861 /* Add toolbar actions: */
862 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Save));
863 m_pToolBar->addSeparator();
864 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Find));
865 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Filter));
866 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Bookmark));
867 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_T_Preferences));
868 m_pToolBar->addSeparator();
869 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Refresh));
870 m_pToolBar->addAction(m_pActionPool->action(UIActionIndex_M_Log_S_Reload));
871
872#ifdef VBOX_WS_MAC
873 /* Check whether we are embedded into a stack: */
874 if (m_enmEmbedding == EmbedTo_Stack)
875 {
876 /* Add into layout: */
877 m_pMainLayout->addWidget(m_pToolBar);
878 }
879#else
880 /* Add into layout: */
881 m_pMainLayout->addWidget(m_pToolBar);
882#endif
883 }
884}
885
886void UIVMLogViewerWidget::loadOptions()
887{
888 m_bWrapLines = gEDataManager->logViewerWrapLines();
889 m_bShowLineNumbers = gEDataManager->logViewerShowLineNumbers();
890 QFont loadedFont = gEDataManager->logViewerFont();
891 if (loadedFont != QFont())
892 m_font = loadedFont;
893}
894
895void UIVMLogViewerWidget::retranslateUi()
896{
897 if (m_pCornerButton)
898 m_pCornerButton->setToolTip(tr("Select machines to show their log"));
899}
900
901void UIVMLogViewerWidget::showEvent(QShowEvent *pEvent)
902{
903 QWidget::showEvent(pEvent);
904
905 /* One may think that QWidget::polish() is the right place to do things
906 * below, but apparently, by the time when QWidget::polish() is called,
907 * the widget style & layout are not fully done, at least the minimum
908 * size hint is not properly calculated. Since this is sometimes necessary,
909 * we provide our own "polish" implementation: */
910
911 if (m_fIsPolished)
912 return;
913
914 m_fIsPolished = true;
915}
916
917void UIVMLogViewerWidget::keyPressEvent(QKeyEvent *pEvent)
918{
919 /* Depending on key pressed: */
920 switch (pEvent->key())
921 {
922 /* Process Back key as switch to previous tab: */
923 case Qt::Key_Back:
924 {
925 if (m_pTabWidget->currentIndex() > 0)
926 {
927 m_pTabWidget->setCurrentIndex(m_pTabWidget->currentIndex() - 1);
928 return;
929 }
930 break;
931 }
932 /* Process Forward key as switch to next tab: */
933 case Qt::Key_Forward:
934 {
935 if (m_pTabWidget->currentIndex() < m_pTabWidget->count())
936 {
937 m_pTabWidget->setCurrentIndex(m_pTabWidget->currentIndex() + 1);
938 return;
939 }
940 break;
941 }
942 default:
943 break;
944 }
945 QWidget::keyPressEvent(pEvent);
946}
947
948QVector<UIVMLogTab*> UIVMLogViewerWidget::logTabs()
949{
950 QVector<UIVMLogTab*> tabs;
951 if (m_pTabWidget)
952 return tabs;
953 for (int i = 0; i < m_pTabWidget->count(); ++i)
954 {
955 UIVMLogTab *pPage = logTab(i);
956 if (pPage)
957 tabs << pPage;
958 }
959 return tabs;
960}
961
962void UIVMLogViewerWidget::createLogPage(const QString &strFileName,
963 const QString &strMachineName,
964 const QUuid &machineId, int iLogFileId,
965 const QString &strLogContent, bool noLogsToShow)
966{
967 if (!m_pTabWidget)
968 return;
969
970 /* Create page-container: */
971 UIVMLogPage* pLogPage = new UIVMLogPage(this, machineId, strMachineName);
972 if (pLogPage)
973 {
974 connect(pLogPage, &UIVMLogPage::sigBookmarksUpdated, this, &UIVMLogViewerWidget::sltUpdateBookmarkPanel);
975 connect(pLogPage, &UIVMLogPage::sigLogPageFilteredChanged, this, &UIVMLogViewerWidget::sltLogPageFilteredChanged);
976 /* Initialize setting for this log page */
977 pLogPage->setShowLineNumbers(m_bShowLineNumbers);
978 pLogPage->setWrapLines(m_bWrapLines);
979 pLogPage->setCurrentFont(m_font);
980 pLogPage->setLogFileId(iLogFileId);
981 /* Set the file name only if we really have log file to read. */
982 if (!noLogsToShow)
983 pLogPage->setLogFileName(strFileName);
984
985 int iIndex = m_pTabWidget->addTab(pLogPage, QFileInfo(strFileName).fileName());
986 /* !!Hack alert. Setting html to text edit while th tab is not current ends up in an empty text edit: */
987 if (noLogsToShow)
988 m_pTabWidget->setCurrentIndex(iIndex);
989
990 pLogPage->setLogContent(strLogContent, noLogsToShow);
991 pLogPage->setScrollBarMarkingsVector(m_pPanel->matchLocationVector());
992 }
993}
994
995const UIVMLogPage *UIVMLogViewerWidget::currentLogPage() const
996{
997 if (!m_pTabWidget)
998 return 0;
999 return qobject_cast<const UIVMLogPage*>(m_pTabWidget->currentWidget());
1000}
1001
1002UIVMLogPage *UIVMLogViewerWidget::currentLogPage()
1003{
1004 if (!m_pTabWidget)
1005 return 0;
1006 return qobject_cast<UIVMLogPage*>(m_pTabWidget->currentWidget());
1007}
1008
1009UIVMLogTab *UIVMLogViewerWidget::logTab(int iIndex)
1010{
1011 if (!m_pTabWidget)
1012 return 0;
1013 return qobject_cast<UIVMLogTab*>(m_pTabWidget->widget(iIndex));
1014}
1015
1016UIVMLogPage *UIVMLogViewerWidget::logPage(int iIndex)
1017{
1018 if (!m_pTabWidget)
1019 return 0;
1020 return qobject_cast<UIVMLogPage*>(m_pTabWidget->widget(iIndex));
1021}
1022
1023void UIVMLogViewerWidget::createLogViewerPages(const QVector<QUuid> &machineList)
1024{
1025 if (!m_pTabWidget)
1026 return;
1027 m_pTabWidget->blockSignals(true);
1028
1029 const CSystemProperties &sys = uiCommon().virtualBox().GetSystemProperties();
1030 unsigned cMaxLogs = sys.GetLogHistoryCount() + 1 /*VBox.log*/ + 1 /*VBoxHardening.log*/; /** @todo Add api for getting total possible log count! */
1031 foreach (const QUuid &machineId, machineList)
1032 {
1033 CMachine comMachine = uiCommon().virtualBox().FindMachine(machineId.toString());
1034 if (comMachine.isNull())
1035 continue;
1036
1037 QUuid uMachineId = comMachine.GetId();
1038 QString strMachineName = comMachine.GetName();
1039
1040 /* Add a label tab with machine name on it. Used only in manager UI: */
1041 if (uiCommon().uiType() == UICommon::UIType_SelectorUI)
1042 m_pTabWidget->addTab(new UILabelTab(this, uMachineId, strMachineName), strMachineName);
1043
1044 bool fNoLogFileForMachine = true;
1045 for (unsigned iLogFileId = 0; iLogFileId < cMaxLogs; ++iLogFileId)
1046 {
1047 QString strLogContent = readLogFile(comMachine, iLogFileId);
1048 if (!strLogContent.isEmpty())
1049 {
1050 fNoLogFileForMachine = false;
1051 createLogPage(comMachine.QueryLogFilename(iLogFileId),
1052 strMachineName,
1053 uMachineId, iLogFileId,
1054 strLogContent, false);
1055 }
1056 }
1057 if (fNoLogFileForMachine)
1058 {
1059 QString strDummyTabText = QString(tr("<p>No log files for the machine %1 found. Press the "
1060 "<b>Reload</b> button to reload the log folder "
1061 "<nobr><b>%2</b></nobr>.</p>")
1062 .arg(strMachineName).arg(comMachine.GetLogFolder()));
1063 createLogPage(QString("NoLogFile"), strMachineName, uMachineId, -1 /* iLogFileId */, strDummyTabText, true);
1064 }
1065 }
1066 m_pTabWidget->blockSignals(false);
1067 labelTabHandler();
1068}
1069
1070void UIVMLogViewerWidget::removeLogViewerPages(const QVector<QUuid> &machineList)
1071{
1072 /* Nothing to do: */
1073 if (machineList.isEmpty() || !m_pTabWidget)
1074 return;
1075
1076 QVector<QUuid> currentMachineList(m_machines);
1077 /* Make sure that we remove the machine(s) from our machine list: */
1078 foreach (const QUuid &id, machineList)
1079 currentMachineList.removeAll(id);
1080 if (currentMachineList.isEmpty())
1081 return;
1082 m_machines = currentMachineList;
1083
1084 m_pTabWidget->blockSignals(true);
1085 /* Cache log page pointers and tab titles: */
1086 QVector<QPair<UIVMLogTab*, QString> > logTabs;
1087 for (int i = 0; i < m_pTabWidget->count(); ++i)
1088 {
1089 UIVMLogTab *pTab = logTab(i);
1090 if (pTab)
1091 logTabs << QPair<UIVMLogTab*, QString>(pTab, m_pTabWidget->tabText(i));
1092 }
1093 /* Remove all the tabs from tab widget, note that this does not delete tab widgets: */
1094 m_pTabWidget->clear();
1095 QVector<UIVMLogTab*> pagesToRemove;
1096 /* Add tab widgets (log pages) back as long as machine id is not in machineList: */
1097 for (int i = 0; i < logTabs.size(); ++i)
1098 {
1099 if (!logTabs[i].first)
1100 continue;
1101 const QUuid &id = logTabs[i].first->machineId();
1102
1103 if (machineList.contains(id))
1104 pagesToRemove << logTabs[i].first;
1105 else
1106 m_pTabWidget->addTab(logTabs[i].first, logTabs[i].second);
1107 }
1108 /* Delete all the other pages: */
1109 qDeleteAll(pagesToRemove.begin(), pagesToRemove.end());
1110 m_pTabWidget->blockSignals(false);
1111 labelTabHandler();
1112 markLabelTabs();
1113}
1114
1115void UIVMLogViewerWidget::removeAllLogPages()
1116{
1117 if (!m_pTabWidget)
1118 return;
1119
1120 QVector<QWidget*> pagesToRemove;
1121 for (int i = 0; i < m_pTabWidget->count(); ++i)
1122 pagesToRemove << m_pTabWidget->widget(i);
1123 m_pTabWidget->clear();
1124 qDeleteAll(pagesToRemove.begin(), pagesToRemove.end());
1125}
1126
1127void UIVMLogViewerWidget::resetHighlighthing()
1128{
1129 /* Undo the document changes to remove highlighting: */
1130 UIVMLogPage* logPage = currentLogPage();
1131 if (!logPage)
1132 return;
1133 logPage->documentUndo();
1134 logPage->clearScrollBarMarkingsVector();
1135}
1136
1137bool UIVMLogViewerWidget::labelTabHandler()
1138{
1139 if (!m_pTabWidget || !qobject_cast<UILabelTab*>(m_pTabWidget->currentWidget()))
1140 return false;
1141 if (m_pTabWidget->currentIndex() < m_pTabWidget->count() - 1)
1142 m_pTabWidget->setCurrentIndex(m_pTabWidget->currentIndex() + 1);
1143 return true;
1144}
1145
1146void UIVMLogViewerWidget::uncheckPaneActions()
1147{
1148 foreach (QAction *pPanelAction, m_paneActions)
1149 {
1150 if (!pPanelAction)
1151 continue;
1152 pPanelAction->blockSignals(true);
1153 pPanelAction->setChecked(false);
1154 pPanelAction->blockSignals(false);
1155 }
1156}
1157
1158#include "UIVMLogViewerWidget.moc"
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