1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2018-2021 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 35 namespace octave 36 { tab_bar(QWidget * p)37 tab_bar::tab_bar (QWidget *p) 38 : QTabBar (p), m_context_menu (new QMenu (this)) 39 { } 40 ~tab_bar(void)41 tab_bar::~tab_bar (void) 42 { 43 delete m_context_menu; 44 } 45 46 // slots for tab navigation switch_left_tab(void)47 void tab_bar::switch_left_tab (void) 48 { 49 switch_tab (-1); 50 } 51 switch_right_tab(void)52 void tab_bar::switch_right_tab (void) 53 { 54 switch_tab (1); 55 } 56 move_tab_left(void)57 void tab_bar::move_tab_left (void) 58 { 59 #if defined (HAVE_QTABWIDGET_SETMOVABLE) 60 switch_tab (-1, true); 61 #endif 62 } 63 move_tab_right(void)64 void tab_bar::move_tab_right (void) 65 { 66 #if defined (HAVE_QTABWIDGET_SETMOVABLE) 67 switch_tab (1, true); 68 #endif 69 } 70 switch_tab(int direction,bool movetab)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 #if defined (HAVE_QTABWIDGET_SETMOVABLE) 87 moveTab (old_pos, new_pos); 88 setCurrentIndex (old_pos); 89 setCurrentIndex (new_pos); 90 #endif 91 } 92 else 93 setCurrentIndex (new_pos); 94 } 95 sort_tabs_alph(void)96 void tab_bar::sort_tabs_alph (void) 97 { 98 QString current_title = tabText (currentIndex ()); 99 int tab_with_focus = 0; 100 101 // Get all tab title and sort 102 QStringList tab_texts; 103 104 for (int i = 0; i < count (); i++) 105 tab_texts.append (tabText (i)); 106 107 tab_texts.sort (); 108 109 // Move tab into the order of the generated string list 110 for (int title = 0; title < tab_texts.count (); title++) 111 { 112 // Target tab is same as place of title in QStringList. 113 // Find index of next title in string list, leaving out the 114 // tabs (or titles) that were already moved. 115 for (int tab = title; tab < count (); tab++) 116 { 117 if (tabText (tab) == tab_texts.at (title)) 118 { 119 // Index of next tile found, so move tab into next position 120 moveTab (tab, title); 121 122 if (tab_texts.at (title) == current_title) 123 tab_with_focus = title; 124 125 break; 126 } 127 } 128 } 129 130 setCurrentIndex (tab_with_focus); 131 } 132 133 // Reimplement mouse event for filtering out the desired mouse clicks mousePressEvent(QMouseEvent * me)134 void tab_bar::mousePressEvent (QMouseEvent *me) 135 { 136 QPoint click_pos; 137 int clicked_idx = -1; 138 139 // detect the tab where the click occurred 140 for (int i = 0; i < count (); i++) 141 { 142 click_pos = mapToGlobal (me->pos ()); 143 if (tabRect (i).contains (mapFromGlobal (click_pos))) 144 { 145 clicked_idx = i; 146 break; 147 } 148 } 149 150 // If a tab was clicked 151 if (clicked_idx >= 0) 152 { 153 int current_idx = currentIndex (); 154 int current_count = count (); 155 156 // detect the mouse click 157 if ((me->type () == QEvent::MouseButtonDblClick 158 && me->button() == Qt::LeftButton) 159 || (me->type () != QEvent::MouseButtonDblClick 160 && me->button() == Qt::MidButton)) 161 { 162 // Middle click or double click -> close the tab 163 // Make the clicked tab the current one and close it 164 setCurrentIndex (clicked_idx); 165 emit close_current_tab_signal (true); 166 // Was the closed tab before or after the previously current tab? 167 // According to the result, use previous index or reduce it by one 168 if (current_idx - clicked_idx > 0) 169 setCurrentIndex (current_idx - 1); 170 else if (current_idx - clicked_idx < 0) 171 setCurrentIndex (current_idx); 172 } 173 else if (me->type () != QEvent::MouseButtonDblClick 174 && me->button() == Qt::RightButton) 175 { 176 // Right click, show context menu 177 setCurrentIndex (clicked_idx); 178 if (! m_context_menu->exec (click_pos)) 179 { 180 // No action selected, back to previous tab 181 setCurrentIndex (current_idx); 182 } 183 else if (count () < current_count) 184 { 185 // A tab was closed: 186 // Was the possibly only closed tab before or after the 187 // previously current tab? According to the result, use previous 188 // index or reduce it by one. Also prevent using a too large 189 // index if other or all files were closed. 190 int new_idx = count () - 1; 191 if (new_idx > 0) 192 { 193 if (current_idx - clicked_idx > 0) 194 new_idx = current_idx - 1; 195 else if (current_idx - clicked_idx < 0) 196 new_idx = current_idx; 197 } 198 if (new_idx >= 0) 199 setCurrentIndex (new_idx); 200 } 201 } 202 else 203 { 204 // regular handling of the mouse event 205 QTabBar::mousePressEvent (me); 206 } 207 } 208 else 209 { 210 // regular handling of the mouse event 211 QTabBar::mousePressEvent (me); 212 } 213 } 214 } 215