1 /*
2     SPDX-FileCopyrightText: 2007 Nicolas Ternisien <nicolas.ternisien@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "tabLogViewsWidget.h"
8 
9 #include <QMenu>
10 #include <QPushButton>
11 
12 #include <KLocalizedString>
13 #include <QIcon>
14 
15 #include "ksystemlog_debug.h"
16 
17 #include "logViewExport.h"
18 #include "view.h"
19 
20 #include "defaults.h"
21 #include "logManager.h"
22 #include "logMode.h"
23 #include "logViewWidget.h"
24 #include "tabLogManager.h"
25 
TabLogViewsWidget(QWidget * parent)26 TabLogViewsWidget::TabLogViewsWidget(QWidget *parent)
27     : QTabWidget(parent)
28 {
29     auto tabNewTabButton = new QPushButton(QIcon::fromTheme(QStringLiteral("tab-new")), QLatin1String(""), this);
30     connect(tabNewTabButton, &QAbstractButton::clicked, this, &TabLogViewsWidget::createTab);
31 
32     tabNewTabButton->setToolTip(i18n("Create a new tab"));
33     tabNewTabButton->setWhatsThis(i18n("Creates a new tab which can display another log."));
34 
35     auto tabCloseTabButton = new QPushButton(QIcon::fromTheme(QStringLiteral("tab-close")), QLatin1String(""), this);
36     connect(tabCloseTabButton, &QAbstractButton::clicked, this, &TabLogViewsWidget::closeTab);
37 
38     tabCloseTabButton->setToolTip(i18n("Close the current tab"));
39     tabCloseTabButton->setWhatsThis(i18n("Closes the current tab."));
40 
41     setCornerWidget(tabNewTabButton, Qt::TopLeftCorner);
42     setCornerWidget(tabCloseTabButton, Qt::TopRightCorner);
43 
44     setUsesScrollButtons(true);
45 
46     // The context menu is managed manually
47     setContextMenuPolicy(Qt::ActionsContextMenu);
48 
49     // connect(this, SIGNAL(mouseDoubleClick()), this, SLOT(createTab()));
50     // connect(this, SIGNAL(contextMenu(QPoint)), this, SLOT(showContextMenu(QPoint)));
51     // connect(this, SIGNAL(contextMenu(QWidget*,QPoint)), this, SLOT(showContextMenu(QWidget*,QPoint)));
52 
53     // TODO Use this (need to connect to movedTab(int, int) signal and update the QList
54     // setTabReorderingEnabled(true);
55 
56     connect(this, &QTabWidget::currentChanged, this, &TabLogViewsWidget::changeCurrentTab);
57 }
58 
~TabLogViewsWidget()59 TabLogViewsWidget::~TabLogViewsWidget()
60 {
61     const QList<TabLogManager *> copy = mTabLogManagers;
62 
63     for (TabLogManager *tabLogManager : copy) {
64         mTabLogManagers.removeAll(tabLogManager);
65         delete tabLogManager;
66     }
67 }
68 
newTab(View * view)69 void TabLogViewsWidget::newTab(View *view)
70 {
71     qCDebug(KSYSTEMLOG) << "Inserting to a new tab the view ";
72 
73     // Add a tab at the end of the widget
74     insertTab(count(), view, QIcon::fromTheme(QStringLiteral(NO_MODE_ICON)), i18n("No Log"));
75 
76     tabBar()->setVisible(count() > 1);
77 }
78 
changeTab(View * view,const QIcon & icon,const QString & label)79 void TabLogViewsWidget::changeTab(View *view, const QIcon &icon, const QString &label)
80 {
81     qCDebug(KSYSTEMLOG) << "Changing tab " << label;
82     const int index = indexOf(view);
83     setTabIcon(index, icon);
84     setTabText(index, label);
85 }
86 
logManagers() const87 QList<LogManager *> TabLogViewsWidget::logManagers() const
88 {
89     QList<LogManager *> logManagers;
90     const auto tabLogManagers = mTabLogManagers;
91     logManagers.reserve(tabLogManagers.count());
92     for (TabLogManager *tabLogManager : tabLogManagers) {
93         logManagers.append(tabLogManager->logManager());
94     }
95 
96     return logManagers;
97 }
98 
findRelatedLogManager(View * view)99 LogManager *TabLogViewsWidget::findRelatedLogManager(View *view)
100 {
101     return findRelatedTabLogManager(view)->logManager();
102 }
103 
findRelatedTabLogManager(View * view) const104 TabLogManager *TabLogViewsWidget::findRelatedTabLogManager(View *view) const
105 {
106     for (TabLogManager *tabLogManager : std::as_const(mTabLogManagers)) {
107         if (tabLogManager->logManager()->usedView() == view) {
108             return tabLogManager;
109         }
110     }
111 
112     qCCritical(KSYSTEMLOG) << "No log manager found";
113     return nullptr;
114 }
115 
activeTabLogManager() const116 TabLogManager *TabLogViewsWidget::activeTabLogManager() const
117 {
118     View *currentView = static_cast<View *>(currentWidget());
119 
120     return findRelatedTabLogManager(currentView);
121 }
122 
activeLogManager() const123 LogManager *TabLogViewsWidget::activeLogManager() const
124 {
125     TabLogManager *tabLogManager = activeTabLogManager();
126     if (tabLogManager) {
127         return tabLogManager->logManager();
128     }
129     return nullptr;
130 }
131 
createTab()132 LogManager *TabLogViewsWidget::createTab()
133 {
134     qCDebug(KSYSTEMLOG) << "Creating a new tab";
135 
136     return newTabLogManager()->logManager();
137 }
138 
moveTabLeft()139 void TabLogViewsWidget::moveTabLeft()
140 {
141     qCDebug(KSYSTEMLOG) << "Duplicate tab to the left";
142 
143     TabLogManager *currentTabLogManager = activeTabLogManager();
144     const int position = indexOf(currentTabLogManager->logManager()->usedView());
145 
146     if (position <= 0) {
147         qCCritical(KSYSTEMLOG) << "Tab Position <= 0 : " << position;
148         return;
149     }
150 
151     mTabLogManagers.removeAt(position);
152     mTabLogManagers.insert(position - 1, currentTabLogManager);
153 
154     tabBar()->moveTab(position, position - 1);
155 }
156 
moveTabRight()157 void TabLogViewsWidget::moveTabRight()
158 {
159     qCDebug(KSYSTEMLOG) << "Duplicate tab to the right";
160 
161     TabLogManager *currentTabLogManager = activeTabLogManager();
162     const int position = indexOf(currentTabLogManager->logManager()->usedView());
163 
164     if (position >= count() - 1) {
165         qCCritical(KSYSTEMLOG) << "Tab Position >= count()-1 : " << position;
166         return;
167     }
168 
169     mTabLogManagers.removeAt(position);
170     mTabLogManagers.insert(position + 1, currentTabLogManager);
171 
172     tabBar()->moveTab(position, position + 1);
173 }
174 
duplicateTab()175 LogManager *TabLogViewsWidget::duplicateTab()
176 {
177     qCDebug(KSYSTEMLOG) << "Duplicate current tab";
178 
179     TabLogManager *currentManager = activeTabLogManager();
180 
181     TabLogManager *tabLogManager = newTabLogManager();
182 
183     LogMode *mode = currentManager->logManager()->logMode();
184 
185     load(mode, tabLogManager->logManager());
186 
187     // Returns the newly created LogManager
188     return tabLogManager->logManager();
189 }
190 
newTabLogManager()191 TabLogManager *TabLogViewsWidget::newTabLogManager()
192 {
193     qCDebug(KSYSTEMLOG) << "Creating new View...";
194 
195     View *view = new View(this);
196 
197     qCDebug(KSYSTEMLOG) << "Creating new LogManager...";
198 
199     auto logManager = new LogManager(view);
200 
201     // Signals from LogManager to Main Class
202     connect(logManager, &LogManager::tabTitleChanged, this, &TabLogViewsWidget::changeTab);
203     connect(logManager, &LogManager::logUpdated, this, &TabLogViewsWidget::changeTitleAddedLines);
204 
205     auto tabLogManager = new TabLogManager(logManager);
206     mTabLogManagers.append(tabLogManager);
207 
208     qCDebug(KSYSTEMLOG) << "New LogManager created";
209 
210     // Finally add the view to the tabs
211     newTab(view);
212 
213     Q_EMIT logManagerCreated(logManager);
214 
215     setCurrentIndex(count() - 1);
216 
217     // Set focus to the list
218     view->logViewWidget()->setFocus();
219 
220     // Returns the newly created TabLogManager
221     return tabLogManager;
222 }
223 
closeTab()224 void TabLogViewsWidget::closeTab()
225 {
226     if (count() == 1) {
227         qCCritical(KSYSTEMLOG) << "Cannot close tab, one tab left";
228         return;
229     }
230 
231     TabLogManager *currentTabLogManager = activeTabLogManager();
232 
233     mTabLogManagers.removeAll(currentTabLogManager);
234 
235     removeTab(indexOf(currentTabLogManager->logManager()->usedView()));
236     if (count() == 1) {
237         tabBar()->hide();
238     }
239 
240     delete currentTabLogManager;
241 }
242 
load(LogMode * logMode,LogManager * manager,const QVariant & analyzerOptions)243 void TabLogViewsWidget::load(LogMode *logMode, LogManager *manager, const QVariant &analyzerOptions)
244 {
245     qCDebug(KSYSTEMLOG) << "Loading a new mode : " << logMode->name();
246 
247     if (!manager) {
248         qCCritical(KSYSTEMLOG) << "Error while loading a manager ";
249         return;
250     }
251 
252     // The manager is now using the Log mode passed in parameter
253     manager->initialize(logMode, analyzerOptions);
254 
255     // Launch the reading
256     manager->reload();
257 }
258 
reloadCurrent()259 void TabLogViewsWidget::reloadCurrent()
260 {
261     qCDebug(KSYSTEMLOG) << "Reloading current log manager...";
262 
263     LogManager *manager = activeLogManager();
264 
265     if (manager) {
266         manager->reload();
267     }
268 }
269 
reloadAll()270 void TabLogViewsWidget::reloadAll()
271 {
272     qCDebug(KSYSTEMLOG) << "Reloading all tabs...";
273 
274     const auto tabLogManagers = mTabLogManagers;
275     for (TabLogManager *tabLogManager : tabLogManagers) {
276         // Log manager without log mode does not need to be reloaded
277         if (!tabLogManager->logManager()->logMode()) {
278             continue;
279         }
280 
281         // Do a simple reload if it is an open log mode
282         if (tabLogManager->logManager()->logMode()->id() == QLatin1String("openLogMode")) {
283             tabLogManager->logManager()->reload();
284             continue;
285         }
286 
287         // Do a full loading of other log modes (needed if log files have been modified)
288         load(tabLogManager->logManager()->logMode(), tabLogManager->logManager(), tabLogManager->logManager()->analyzerOptions());
289     }
290 }
291 
changeCurrentTab(int index)292 void TabLogViewsWidget::changeCurrentTab(int index)
293 {
294     qCDebug(KSYSTEMLOG) << "Changing current tab...";
295 
296     if (index == -1) {
297         return;
298     }
299 
300     TabLogManager *tabLogManager = activeTabLogManager();
301 
302     // Reinit the new lines count since last selection
303     tabLogManager->initNewLinesCount();
304 
305     // If the tab displayed the new added line count, rename it to the default log mode name
306     changeTab(tabLogManager->logManager()->usedView(), logModeIcon(tabLogManager->logManager()->logMode()), tabLogManager->title());
307 
308     qCDebug(KSYSTEMLOG) << "Current tab changed";
309 }
310 
changeReloadingTab(View * view,bool reloading)311 void TabLogViewsWidget::changeReloadingTab(View *view, bool reloading)
312 {
313     TabLogManager *tabLogManager = findRelatedTabLogManager(view);
314 
315     if (reloading) {
316         changeTab(tabLogManager->logManager()->usedView(), QIcon::fromTheme(QStringLiteral("view-refresh")), tabLogManager->title());
317     } else {
318         changeTab(tabLogManager->logManager()->usedView(), logModeIcon(tabLogManager->logManager()->logMode()), tabLogManager->title());
319     }
320 }
321 
changeTitleAddedLines(View * view,int addedLinesSinceLastUpdate)322 void TabLogViewsWidget::changeTitleAddedLines(View *view, int addedLinesSinceLastUpdate)
323 {
324     qCDebug(KSYSTEMLOG) << "Changing title" << addedLinesSinceLastUpdate << " added lines...";
325     LogManager *currentManager = activeLogManager();
326 
327     // Only display added line on tab title if this is not an update of the current tab
328     if (currentManager->usedView() != view) {
329         TabLogManager *tabLogManager = findRelatedTabLogManager(view);
330         tabLogManager->addNewLinesCount(addedLinesSinceLastUpdate);
331 
332         // Update the tab title
333         changeTab(tabLogManager->logManager()->usedView(), logModeIcon(tabLogManager->logManager()->logMode()), tabLogManager->title());
334     }
335 }
336 
expandAllCurrentView()337 void TabLogViewsWidget::expandAllCurrentView()
338 {
339     activeLogManager()->usedView()->logViewWidget()->expandAll();
340 }
341 
collapseAllCurrentView()342 void TabLogViewsWidget::collapseAllCurrentView()
343 {
344     activeLogManager()->usedView()->logViewWidget()->collapseAll();
345 }
346 
selectAllCurrentView()347 void TabLogViewsWidget::selectAllCurrentView()
348 {
349     activeLogManager()->usedView()->logViewWidget()->selectAll();
350 }
351 
fileSaveCurrentView()352 void TabLogViewsWidget::fileSaveCurrentView()
353 {
354     LogViewExport logViewExport(this, activeLogManager()->usedView()->logViewWidget());
355     connect(&logViewExport, &LogViewExport::statusBarChanged, this, &TabLogViewsWidget::statusBarChanged);
356     logViewExport.fileSave();
357 }
358 
copyToClipboardCurrentView()359 void TabLogViewsWidget::copyToClipboardCurrentView()
360 {
361     LogViewExport logViewExport(this, activeLogManager()->usedView()->logViewWidget());
362     connect(&logViewExport, &LogViewExport::statusBarChanged, this, &TabLogViewsWidget::statusBarChanged);
363     logViewExport.copyToClipboard();
364 }
365 
sendMailCurrentView()366 void TabLogViewsWidget::sendMailCurrentView()
367 {
368     LogViewExport logViewExport(this, activeLogManager()->usedView()->logViewWidget());
369     connect(&logViewExport, &LogViewExport::statusBarChanged, this, &TabLogViewsWidget::statusBarChanged);
370     logViewExport.sendMail();
371 }
372 
printSelectionCurrentView()373 void TabLogViewsWidget::printSelectionCurrentView()
374 {
375     LogViewExport logViewExport(this, activeLogManager()->usedView()->logViewWidget());
376     connect(&logViewExport, &LogViewExport::statusBarChanged, this, &TabLogViewsWidget::statusBarChanged);
377     logViewExport.printSelection();
378 }
379 
printPreviewSelectionCurrentView()380 void TabLogViewsWidget::printPreviewSelectionCurrentView()
381 {
382     LogViewExport logViewExport(this, activeLogManager()->usedView()->logViewWidget());
383     connect(&logViewExport, &LogViewExport::statusBarChanged, this, &TabLogViewsWidget::statusBarChanged);
384     logViewExport.printPreview();
385 }
386 
logModeIcon(LogMode * logMode) const387 QIcon TabLogViewsWidget::logModeIcon(LogMode *logMode) const
388 {
389     if (!logMode) {
390         return QIcon::fromTheme(QStringLiteral(NO_MODE_ICON));
391     } else {
392         return logMode->icon();
393     }
394 }
395 
prepareContextMenu(bool)396 void TabLogViewsWidget::prepareContextMenu(bool /*onTab*/)
397 {
398     if (!mContextMenu) {
399         mContextMenu = new QMenu(this);
400         mContextMenu->addActions(actions());
401     }
402 
403     // TODO Disable some actions, depending of the onTab value
404 }
405 
showContextMenu(const QPoint & cursorPosition)406 void TabLogViewsWidget::showContextMenu(const QPoint &cursorPosition)
407 {
408     qCDebug(KSYSTEMLOG) << "Showing context menu at " << cursorPosition;
409 
410     prepareContextMenu(false);
411 
412     mContextMenu->popup(cursorPosition);
413 }
414 
showContextMenu(QWidget * tab,const QPoint & cursorPosition)415 void TabLogViewsWidget::showContextMenu(QWidget *tab, const QPoint &cursorPosition)
416 {
417     qCDebug(KSYSTEMLOG) << "Showing context menu at " << cursorPosition << " at " << tab->objectName();
418 
419     prepareContextMenu(true);
420 
421     mContextMenu->popup(cursorPosition);
422 }
423