GNU Octave  8.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-2023 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 
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 
OCTAVE_END_NAMESPACE(octave)
void close_current_tab_signal(bool)
QList< QAction * > m_ctx_actions
Definition: tab-bar.h:84
int m_rotated
Definition: tab-bar.h:85
void move_tab_left(void)
Definition: tab-bar.cc:61
void set_rotated(int rotated)
Definition: tab-bar.cc:45
void ctx_menu_activated(QAction *a)
Definition: tab-bar.cc:276
QMenu * m_context_menu
Definition: tab-bar.h:83
void paintEvent(QPaintEvent *e)
Definition: tab-bar.cc:146
void switch_right_tab(void)
Definition: tab-bar.cc:56
void switch_tab(int direction, bool movetab=false)
Definition: tab-bar.cc:71
QSize tabSizeHint(int idx) const
Definition: tab-bar.cc:136
void move_tab_right(void)
Definition: tab-bar.cc:66
void switch_left_tab(void)
Definition: tab-bar.cc:51
void sort_tabs_alph(void)
Definition: tab-bar.cc:94
void mousePressEvent(QMouseEvent *event)
Definition: tab-bar.cc:178
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn