GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
tab-bar.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2018-2022 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26// This file implements a tab bar derived from QTabBar with a contextmenu
27// and possibility to close a tab via double-left or middle mouse click.
28
29#if defined (HAVE_CONFIG_H)
30# include "config.h"
31#endif
32
33#include "tab-bar.h"
34
35namespace octave
36{
37 //
38 // Reimplemented QTabbar
39 //
40
42 : QTabBar (p), m_context_menu (new QMenu (this))
43 { }
44
45 void tab_bar::set_rotated (int rotated)
46 {
47 m_rotated = rotated;
48 }
49
50 // slots for tab navigation
52 {
53 switch_tab (-1);
54 }
55
57 {
58 switch_tab (1);
59 }
60
62 {
63 switch_tab (-1, true);
64 }
65
67 {
68 switch_tab (1, true);
69 }
70
71 void tab_bar::switch_tab (int direction, bool movetab)
72 {
73 int tabs = count ();
74
75 if (tabs < 2)
76 return;
77
78 int old_pos = currentIndex ();
79 int new_pos = currentIndex () + direction;
80
81 if (new_pos < 0 || new_pos >= tabs)
82 new_pos = new_pos - direction*tabs;
83
84 if (movetab)
85 {
86 moveTab (old_pos, new_pos);
87 setCurrentIndex (old_pos);
88 setCurrentIndex (new_pos);
89 }
90 else
91 setCurrentIndex (new_pos);
92 }
93
95 {
96 QString current_title = tabText (currentIndex ());
97 int tab_with_focus = 0;
98
99 // Get all tab title and sort
100 QStringList tab_texts;
101
102 for (int i = 0; i < count (); i++)
103 tab_texts.append (tabText (i));
104
105 tab_texts.sort ();
106
107 // Move tab into the order of the generated string list
108 for (int title = 0; title < tab_texts.count (); title++)
109 {
110 // Target tab is same as place of title in QStringList.
111 // Find index of next title in string list, leaving out the
112 // tabs (or titles) that were already moved.
113 for (int tab = title; tab < count (); tab++)
114 {
115 if (tabText (tab) == tab_texts.at (title))
116 {
117 // Index of next tile found, so move tab into next position
118 moveTab (tab, title);
119
120 if (tab_texts.at (title) == current_title)
121 tab_with_focus = title;
122
123 break;
124 }
125 }
126 }
127
128 setCurrentIndex (tab_with_focus);
129 }
130
131 // The following two functions are reimplemented for allowing rotated
132 // tabs and are based on this answer on stack overflow:
133 // https://stackoverflow.com/a/50579369
134
135 // Reimplemented size hint allowing rotated tabs
136 QSize tab_bar::tabSizeHint (int idx) const
137 {
138 QSize s = QTabBar::tabSizeHint (idx);
139 if (m_rotated)
140 s.transpose();
141
142 return s;
143 }
144
145 // Reimplemented paint event allowing rotated tabs
146 void tab_bar::paintEvent(QPaintEvent *e)
147 {
148 // Just process the original event if not rotated
149 if (! m_rotated)
150 return QTabBar::paintEvent (e);
151
152 // Process the event for rotated tabs
153 QStylePainter painter (this);
154 QStyleOptionTab opt;
155
156 for (int idx = 0; idx < count(); idx++)
157 {
158 initStyleOption (&opt, idx);
159 painter.drawControl (QStyle::CE_TabBarTabShape, opt);
160 painter.save ();
161
162 QSize s = opt.rect.size();
163 s.transpose();
164 QRect rect (QPoint (), s);
165 rect.moveCenter (opt.rect.center ());
166 opt.rect = rect;
167
168 QPoint p = tabRect (idx).center ();
169 painter.translate (p);
170 painter.rotate (-m_rotated*90);
171 painter.translate (-p);
172 painter.drawControl (QStyle::CE_TabBarTabLabel, opt);
173 painter.restore ();
174 }
175 }
176
177 // Reimplement mouse event for filtering out the desired mouse clicks
178 void tab_bar::mousePressEvent (QMouseEvent *me)
179 {
180 QPoint click_pos;
181 int clicked_idx = -1;
182
183 // detect the tab where the click occurred
184 for (int i = 0; i < count (); i++)
185 {
186 click_pos = mapToGlobal (me->pos ());
187 if (tabRect (i).contains (mapFromGlobal (click_pos)))
188 {
189 clicked_idx = i;
190 break;
191 }
192 }
193
194 // If a tab was clicked
195 if (clicked_idx >= 0)
196 {
197 int current_idx = currentIndex ();
198 int current_count = count ();
199
200 // detect the mouse click
201 if ((me->type () == QEvent::MouseButtonDblClick
202 && me->button() == Qt::LeftButton)
203 || (me->type () != QEvent::MouseButtonDblClick
204 && me->button() == Qt::MiddleButton))
205 {
206 // Middle click or double click -> close the tab
207 // Make the clicked tab the current one and close it
208 setCurrentIndex (clicked_idx);
209 emit close_current_tab_signal (true);
210 // Was the closed tab before or after the previously current tab?
211 // According to the result, use previous index or reduce it by one
212 if (current_idx - clicked_idx > 0)
213 setCurrentIndex (current_idx - 1);
214 else if (current_idx - clicked_idx < 0)
215 setCurrentIndex (current_idx);
216 }
217 else if (me->type () != QEvent::MouseButtonDblClick
218 && me->button() == Qt::RightButton)
219 {
220 // Right click, show context menu
221 setCurrentIndex (clicked_idx);
222
223 // Fill context menu with actions for selecting current tabs
224 m_ctx_actions = m_context_menu->actions (); // Copy of basic actions
225 QMenu ctx_menu; // The menu actually used
226 connect (&ctx_menu, &QMenu::triggered,
228
229 for (int i = count () - 1; i >= 0; i--)
230 {
231 // Prepend an action for each tab
232 QAction *a = new QAction (tabIcon (i), tabText (i), &ctx_menu);
233 m_ctx_actions.prepend (a);
234 }
235 // Add all actions to our menu
236 ctx_menu.insertActions (nullptr, m_ctx_actions);
237
238 if (! ctx_menu.exec (click_pos))
239 {
240 // No action selected, back to previous tab
241 setCurrentIndex (current_idx);
242 }
243 else if (count () < current_count)
244 {
245 // A tab was closed:
246 // Was the possibly only closed tab before or after the
247 // previously current tab? According to the result, use previous
248 // index or reduce it by one. Also prevent using a too large
249 // index if other or all files were closed.
250 int new_idx = count () - 1;
251 if (new_idx > 0)
252 {
253 if (current_idx - clicked_idx > 0)
254 new_idx = current_idx - 1;
255 else if (current_idx - clicked_idx < 0)
256 new_idx = current_idx;
257 }
258 if (new_idx >= 0)
259 setCurrentIndex (new_idx);
260 }
261 }
262 else
263 {
264 // regular handling of the mouse event
265 QTabBar::mousePressEvent (me);
266 }
267 }
268 else
269 {
270 // regular handling of the mouse event
271 QTabBar::mousePressEvent (me);
272 }
273 }
274
275 // Slot if a menu entry in the context menu is activated
277 {
278 // If the index of the activated action is in the range of
279 // the current tabs, set the related current tab. The basic actions
280 // are handled by the editor
281 int i = m_ctx_actions.indexOf (a);
282 if ((i > -1) && (i < count ()))
283 setCurrentIndex (i);
284 }
285
286}
void paintEvent(QPaintEvent *e)
Definition: tab-bar.cc:146
void close_current_tab_signal(bool)
void mousePressEvent(QMouseEvent *event)
Definition: tab-bar.cc:178
void switch_left_tab(void)
Definition: tab-bar.cc:51
void move_tab_left(void)
Definition: tab-bar.cc:61
void set_rotated(int rotated)
Definition: tab-bar.cc:45
void switch_tab(int direction, bool movetab=false)
Definition: tab-bar.cc:71
QMenu * m_context_menu
Definition: tab-bar.h:83
tab_bar(QWidget *p)
Definition: tab-bar.cc:41
void sort_tabs_alph(void)
Definition: tab-bar.cc:94
void ctx_menu_activated(QAction *a)
Definition: tab-bar.cc:276
void switch_right_tab(void)
Definition: tab-bar.cc:56
QSize tabSizeHint(int idx) const
Definition: tab-bar.cc:136
void move_tab_right(void)
Definition: tab-bar.cc:66
QList< QAction * > m_ctx_actions
Definition: tab-bar.h:84