VirtualBox

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

Last change on this file was 104393, checked in by vboxsync, 4 weeks ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the UIActionPool class.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use