1 // For license of this file, see <project-root-folder>/LICENSE.md.
2
3 #include "gui/tabwidget.h"
4
5 #include "definitions/definitions.h"
6 #include "gui/dialogs/formmain.h"
7 #include "gui/feedmessageviewer.h"
8 #include "gui/feedsview.h"
9 #include "gui/messagesview.h"
10 #include "gui/newspaperpreviewer.h"
11 #include "gui/reusable/plaintoolbutton.h"
12 #include "gui/tabbar.h"
13 #include "miscellaneous/application.h"
14 #include "miscellaneous/iconfactory.h"
15 #include "miscellaneous/settings.h"
16 #include "miscellaneous/textfactory.h"
17 #include "network-web/webfactory.h"
18
19 #if defined(USE_WEBENGINE)
20 #include "gui/webbrowser.h"
21 #endif
22
23 #include <QMenu>
24 #include <QToolButton>
25
TabWidget(QWidget * parent)26 TabWidget::TabWidget(QWidget* parent) : QTabWidget(parent), m_menuMain(nullptr) {
27 setTabBar(new TabBar(this));
28 setupMainMenuButton();
29 initializeTabs();
30 createConnections();
31 }
32
~TabWidget()33 TabWidget::~TabWidget() {
34 qDebugNN << LOGSEC_GUI << "Destroying TabWidget instance.";
35 }
36
setupMainMenuButton()37 void TabWidget::setupMainMenuButton() {
38 m_btnMainMenu = new PlainToolButton(this);
39 m_btnMainMenu->setAutoRaise(true);
40 m_btnMainMenu->setPadding(3);
41 m_btnMainMenu->setToolTip(tr("Displays main menu."));
42 m_btnMainMenu->setIcon(qApp->icons()->fromTheme(QSL("go-home")));
43 m_btnMainMenu->setPopupMode(QToolButton::ToolButtonPopupMode::InstantPopup);
44 connect(m_btnMainMenu, &PlainToolButton::clicked, this, &TabWidget::openMainMenu);
45 }
46
openMainMenu()47 void TabWidget::openMainMenu() {
48 if (m_menuMain == nullptr) {
49 m_menuMain = new QMenu(tr("Main menu"), this);
50 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuFile);
51 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuView);
52 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuAccounts);
53 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuFeeds);
54 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuMessages);
55 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuWebBrowserTabs);
56 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuTools);
57 m_menuMain->addMenu(qApp->mainForm()->m_ui->m_menuHelp);
58 }
59
60 QPoint button_position = m_btnMainMenu->pos();
61 const QSize target_size = m_btnMainMenu->size() / 2.0;
62
63 button_position.setX(button_position.x() + target_size.width());
64 button_position.setY(button_position.y() + target_size.height());
65 m_menuMain->exec(mapToGlobal(button_position));
66 }
67
showDownloadManager()68 void TabWidget::showDownloadManager() {
69 for (int i = 0; i < count(); i++) {
70 if (widget(i)->metaObject()->className() == QSL("DownloadManager")) {
71 setCurrentIndex(i);
72 return;
73 }
74 }
75
76 // Download manager is not opened. Create tab with it.
77 qApp->downloadManager()->setParent(this);
78 addTab(qApp->downloadManager(),
79 qApp->icons()->fromTheme(QSL("emblem-downloads")),
80 tr("Downloads"),
81 TabBar::TabType::DownloadManager);
82 setCurrentIndex(count() - 1);
83 }
84
checkTabBarVisibility()85 void TabWidget::checkTabBarVisibility() {
86 const bool should_be_visible = count() > 1 ||
87 !qApp->settings()->value(GROUP(GUI), SETTING(GUI::HideTabBarIfOnlyOneTab)).toBool();
88
89 if (should_be_visible) {
90 setCornerWidget(m_btnMainMenu, Qt::Corner::TopLeftCorner);
91 m_btnMainMenu->setVisible(true);
92 }
93 else {
94 setCornerWidget(nullptr, Qt::Corner::TopLeftCorner);
95 setCornerWidget(nullptr, Qt::Corner::TopRightCorner);
96 m_btnMainMenu->setVisible(false);
97 }
98
99 tabBar()->setVisible(should_be_visible);
100 }
101
tabInserted(int index)102 void TabWidget::tabInserted(int index) {
103 QTabWidget::tabInserted(index);
104 checkTabBarVisibility();
105 const int count_of_tabs = count();
106
107 if (index < count_of_tabs - 1 && count_of_tabs > 1) {
108 // New tab was inserted and the tab is not the last one.
109 fixContentsAfterMove(index, count_of_tabs - 1);
110 }
111 }
112
tabRemoved(int index)113 void TabWidget::tabRemoved(int index) {
114 QTabWidget::tabRemoved(index);
115 checkTabBarVisibility();
116 const int count_of_tabs = count();
117
118 if (index < count_of_tabs && count_of_tabs > 1) {
119 // Some tab was removed and the tab was not the last one.
120 fixContentsAfterMove(index, count_of_tabs - 1);
121 }
122 }
123
createConnections()124 void TabWidget::createConnections() {
125 connect(tabBar(), &TabBar::tabCloseRequested, this, &TabWidget::closeTab);
126 connect(tabBar(), &TabBar::emptySpaceDoubleClicked, this, &TabWidget::addEmptyBrowser);
127 connect(tabBar(), &TabBar::tabMoved, this, &TabWidget::fixContentsAfterMove);
128 connect(feedMessageViewer()->messagesView(), &MessagesView::openMessagesInNewspaperView, this, &TabWidget::addNewspaperView);
129 connect(feedMessageViewer()->feedsView(), &FeedsView::openMessagesInNewspaperView, this, &TabWidget::addNewspaperView);
130 }
131
initializeTabs()132 void TabWidget::initializeTabs() {
133 // Create widget for "Feeds" page and add it.
134 m_feedMessageViewer = new FeedMessageViewer(this);
135 const int index_of_browser = addTab(m_feedMessageViewer, QIcon(), tr("Feeds"), TabBar::TabType::FeedReader);
136
137 setTabToolTip(index_of_browser, tr("Browse your feeds and articles"));
138 }
139
setupIcons()140 void TabWidget::setupIcons() {
141 // Iterate through all tabs and update icons
142 // accordingly.
143 for (int index = 0; index < count(); index++) {
144 // Index 0 usually contains widget which displays feeds & messages.
145 if (tabBar()->tabType(index) == TabBar::TabType::FeedReader) {
146 setTabIcon(index, qApp->icons()->fromTheme(QSL("application-rss+xml")));
147 }
148 }
149 }
150
closeTab(int index)151 bool TabWidget::closeTab(int index) {
152 if (tabBar()->tabType(index) == TabBar::TabType::Closable) {
153 removeTab(index, true);
154 return true;
155 }
156 else if (tabBar()->tabType(index) == TabBar::TabType::DownloadManager) {
157 removeTab(index, false);
158 return true;
159 }
160 else {
161 return false;
162 }
163 }
164
closeAllTabsExceptCurrent()165 void TabWidget::closeAllTabsExceptCurrent() {
166 // Close tabs after active tab.
167 int index_of_active = currentIndex();
168
169 for (int i = count() - 1; i >= 0; i--) {
170 if (i != index_of_active) {
171 if (i < index_of_active) {
172 index_of_active--;
173 }
174
175 closeTab(i);
176 }
177 }
178 }
179
closeAllTabs()180 void TabWidget::closeAllTabs() {
181 for (int i = count() - 1; i >= 0; i--) {
182 closeTab(i);
183 }
184 }
185
addNewspaperView(RootItem * root,const QList<Message> & messages)186 int TabWidget::addNewspaperView(RootItem* root, const QList<Message>& messages) {
187 int msg_height = height() - tabBar()->height() - 50;
188 NewspaperPreviewer* prev = new NewspaperPreviewer(msg_height, root, messages, this);
189
190 connect(prev, &NewspaperPreviewer::markMessageRead,
191 m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageReadById);
192 connect(prev, &NewspaperPreviewer::markMessageImportant,
193 m_feedMessageViewer->messagesView()->sourceModel(), &MessagesModel::setMessageImportantById);
194
195 int index = addTab(prev,
196 qApp->icons()->fromTheme(QSL("format-justify-fill")),
197 tr("Newspaper view"),
198 TabBar::TabType::Closable);
199
200 // NOTE: Do not bring "newspaper" tabs to front anymore.
201 //setCurrentIndex(index);
202
203 return index;
204 }
205
addEmptyBrowser()206 int TabWidget::addEmptyBrowser() {
207 return addBrowser(false, true);
208 }
209
addLinkedBrowser(const QUrl & initial_url)210 int TabWidget::addLinkedBrowser(const QUrl& initial_url) {
211 return addBrowser(false, false, initial_url);
212 }
213
addLinkedBrowser(const QString & initial_url)214 int TabWidget::addLinkedBrowser(const QString& initial_url) {
215 return addLinkedBrowser(QUrl(initial_url));
216 }
217
addBrowser(bool move_after_current,bool make_active,const QUrl & initial_url)218 int TabWidget::addBrowser(bool move_after_current, bool make_active, const QUrl& initial_url) {
219 #if defined(USE_WEBENGINE)
220 // Create new WebBrowser.
221 WebBrowser* browser = new WebBrowser(this);
222 int final_index;
223 QString browser_tab_name = tr("Web browser");
224
225 #if defined(Q_OS_MACOSOS)
226 browser_tab_name = browser_tab_name.prepend(QSL(" "));
227 #endif
228
229 if (move_after_current) {
230 // Insert web browser after current tab.
231 final_index = insertTab(currentIndex() + 1, browser, qApp->icons()->fromTheme(QSL("text-html")),
232 browser_tab_name, TabBar::TabType::Closable);
233 }
234 else {
235 // Add new browser as the last tab.
236 final_index = addTab(browser, qApp->icons()->fromTheme(QSL("text-html")),
237 browser_tab_name,
238 TabBar::TabType::Closable);
239 }
240
241 // Make connections.
242 connect(browser, &WebBrowser::titleChanged, this, &TabWidget::changeTitle);
243 connect(browser, &WebBrowser::iconChanged, this, &TabWidget::changeIcon);
244
245 // Setup the tab index.
246 browser->setIndex(final_index);
247
248 // Load initial web page if desired.
249 if (initial_url.isValid()) {
250 browser->loadUrl(initial_url);
251 }
252
253 // Make new web browser active if desired.
254 if (make_active) {
255 setCurrentIndex(final_index);
256 browser->setFocus(Qt::FocusReason::OtherFocusReason);
257 }
258
259 return final_index;
260 #else
261 Q_UNUSED(move_after_current)
262 Q_UNUSED(make_active)
263 qApp->web()->openUrlInExternalBrowser(initial_url.toString());
264 return -1;
265 #endif
266 }
267
gotoNextTab()268 void TabWidget::gotoNextTab() {
269 if (currentIndex() == count() - 1) {
270 setCurrentIndex(0);
271 }
272 else {
273 setCurrentIndex(currentIndex() + 1);
274 }
275 }
276
gotoPreviousTab()277 void TabWidget::gotoPreviousTab() {
278 if (currentIndex() == 0) {
279 setCurrentIndex(count() - 1);
280 }
281 else {
282 setCurrentIndex(currentIndex() - 1);
283 }
284 }
285
indentTabText(int index)286 void TabWidget::indentTabText(int index) {
287 #if defined(Q_OS_MACOSOS)
288 if (tabBar()->tabType(index) != TabBar::FeedReader && !tabIcon(index).isNull()) {
289 // We have closable tab with some icon, fix the title.
290 const QString text = tabText(index);
291
292 if (!text.startsWith(QSL(" "))) {
293 setTabText(index, QSL(" ") + text);
294 }
295 }
296 #else
297 Q_UNUSED(index)
298 #endif
299 }
300
removeTab(int index,bool clear_from_memory)301 void TabWidget::removeTab(int index, bool clear_from_memory) {
302 if (clear_from_memory) {
303 widget(index)->deleteLater();
304 }
305
306 QTabWidget::removeTab(index);
307 }
308
addTab(TabContent * widget,const QIcon & icon,const QString & label,TabBar::TabType type)309 int TabWidget::addTab(TabContent* widget, const QIcon& icon, const QString& label, TabBar::TabType type) {
310 const int index = QTabWidget::addTab(widget, icon, label);
311
312 tabBar()->setTabType(index, type);
313 indentTabText(index);
314 return index;
315 }
316
addTab(TabContent * widget,const QString & label,TabBar::TabType type)317 int TabWidget::addTab(TabContent* widget, const QString& label, TabBar::TabType type) {
318 const int index = QTabWidget::addTab(widget, label);
319
320 tabBar()->setTabType(index, type);
321 indentTabText(index);
322 return index;
323 }
324
insertTab(int index,QWidget * widget,const QIcon & icon,const QString & label,TabBar::TabType type)325 int TabWidget::insertTab(int index, QWidget* widget, const QIcon& icon, const QString& label, TabBar::TabType type) {
326 const int tab_index = QTabWidget::insertTab(index, widget, icon, label);
327
328 tabBar()->setTabType(tab_index, type);
329 indentTabText(index);
330 return tab_index;
331 }
332
insertTab(int index,QWidget * widget,const QString & label,TabBar::TabType type)333 int TabWidget::insertTab(int index, QWidget* widget, const QString& label, TabBar::TabType type) {
334 const int tab_index = QTabWidget::insertTab(index, widget, label);
335
336 tabBar()->setTabType(tab_index, type);
337 indentTabText(index);
338 return tab_index;
339 }
340
changeIcon(int index,const QIcon & new_icon)341 void TabWidget::changeIcon(int index, const QIcon& new_icon) {
342 setTabIcon(index, new_icon);
343 indentTabText(index);
344 }
345
changeTitle(int index,const QString & new_title)346 void TabWidget::changeTitle(int index, const QString& new_title) {
347 setTabText(index, TextFactory::shorten(new_title));
348 setTabToolTip(index, new_title);
349 indentTabText(index);
350 }
351
fixContentsAfterMove(int from,int to)352 void TabWidget::fixContentsAfterMove(int from, int to) {
353 from = qMin(from, to);
354 to = qMax(from, to);
355
356 for (; from <= to; from++) {
357 auto* content = widget(from);
358
359 content->setIndex(from);
360 }
361 }
362