VirtualBox

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

Last change on this file since 103977 was 103923, checked in by vboxsync, 9 months ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in log viewer classes.

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