1 //===========================================
2 // Lumina-DE source code
3 // Copyright (c) 2015, Ken Moore
4 // Available under the 3-clause BSD license
5 // See the LICENSE file for full details
6 //===========================================
7 #include "TermWindow.h"
8 //#include "ui_TermWindow.h"
9
10 #include <QDesktopWidget>
11 #include <QDebug>
12 #include <QTimer>
13 #include <QApplication>
14 #include <QVBoxLayout>
15 #include "TerminalWidget.h"
16
17 // ===============
18 // PUBLIC
19 // ===============
TermWindow(QSettings * set)20 TermWindow::TermWindow(QSettings *set) : QWidget(0, Qt::Window | Qt::BypassWindowManagerHint){//, ui(new Ui::TermWindow){
21 this->setWindowOpacity(0.85);
22 CLOSING = false; //internal flag
23 settings = set;
24 //Create the Window
25 this->setLayout(new QVBoxLayout());
26 this->setCursor(Qt::SplitVCursor);
27 tabWidget = new QTabWidget(this);
28 tabWidget->clear(); //just in case
29 tabWidget->setCursor(Qt::ArrowCursor);
30 tabWidget->setTabBarAutoHide(true);
31 tabWidget->setTabsClosable(true);
32 tabWidget->setMovable(true);
33 tabWidget->setUsesScrollButtons(true);
34 tabWidget->setFocusPolicy(Qt::ClickFocus);
35 this->layout()->addWidget(tabWidget);
36 //Setup the animation
37 ANIM = new QPropertyAnimation(this, "geometry", this);
38 ANIM->setDuration(300); //1/3 second animation
39 connect(ANIM, SIGNAL(finished()), this, SLOT(AnimFinished()) );
40 activeTimer = new QTimer(this);
41 activeTimer->setInterval(50);
42 activeTimer->setSingleShot(true);
43 connect(activeTimer, SIGNAL(timeout()), this, SLOT(activeStatusChanged()) );
44 connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), activeTimer, SLOT(start()) );
45 //Create the keyboard shortcuts
46 //hideS = new QShortcut(QKeySequence(Qt::Key_Escape),this);
47 closeS = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q),this);
48 newTabS = new QShortcut(QKeySequence::AddTab,this);
49 closeTabS = new QShortcut(QKeySequence::Close,this);
50 prevTabS = new QShortcut(QKeySequence::PreviousChild,this);
51 nextTabS = new QShortcut(QKeySequence::NextChild,this);
52 //Print out all the keyboard shortcuts onto the screen
53 qDebug() << "New Tab Shortcut:" << QKeySequence::keyBindings(QKeySequence::AddTab);
54 qDebug() << "Close Tab Shortcut:" << QKeySequence::keyBindings(QKeySequence::Close);
55 qDebug() << "Next Tab Shortcut:" << QKeySequence::keyBindings(QKeySequence::NextChild);
56 qDebug() << "Previous Tab Shortcut:" << QKeySequence::keyBindings(QKeySequence::PreviousChild);
57 //Connect the signals/slots
58 connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(Close_Tab(int)) );
59 connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(focusOnWidget()) );
60 connect(closeTabS, SIGNAL(activated()), this, SLOT(Close_Tab()) );
61 connect(newTabS, SIGNAL(activated()), this, SLOT(New_Tab()) );
62 //connect(hideS, SIGNAL(activated()), this, SLOT(HideWindow()) );
63 connect(closeS, SIGNAL(activated()), this, SLOT(CloseWindow()) );
64 connect(prevTabS, SIGNAL(activated()), this, SLOT(Prev_Tab()) );
65 connect(nextTabS, SIGNAL(activated()), this, SLOT(Next_Tab()) );
66 //Now set the defaults
67 screennum = 0; //default value
68 setTopOfScreen(true); //default value
69 if(settings->contains("lastSize")){
70 //qDebug() << "Re-use last size:" << settings->value("lastSize").toSize();
71 this->resize( settings->value("lastSize").toSize() );
72 CalculateGeom();
73 //qDebug() << "After size:" << this->size();
74 }
75
76 //this->resize(this->width(),300);
77 //this->setMinimumSize(20, 300);
78
79 }
80
81
~TermWindow()82 TermWindow::~TermWindow(){
83
84 }
85
cleanup()86 void TermWindow::cleanup(){
87 //called right before the window is closed
88 //Make sure to close any open tabs/processes
89 CLOSING = true;
90 for(int i=0; i<tabWidget->count(); i++){
91 static_cast<TerminalWidget*>(tabWidget->widget(i))->aboutToClose();
92 }
93 }
94
OpenDirs(QStringList dirs)95 void TermWindow::OpenDirs(QStringList dirs){
96 for(int i=0; i<dirs.length(); i++){
97 //Open a new tab for each directory
98 TerminalWidget *page = new TerminalWidget(tabWidget, dirs[i]);
99 QString ID = GenerateTabID();
100 page->setWhatsThis(ID);
101 tabWidget->addTab(page, ID);
102 tabWidget->setCurrentWidget(page);
103 QTimer::singleShot(500, this, SLOT(focusOnWidget()));//page->setFocus();
104 qDebug() << "New Tab:" << ID << dirs[i];
105 connect(page, SIGNAL(ProcessClosed(QString)), this, SLOT(Close_Tab(QString)) );
106 }
107 }
108
setCurrentScreen(int num)109 void TermWindow::setCurrentScreen(int num){
110 screennum = num;
111 QTimer::singleShot(0,this, SLOT(ReShowWindow()));
112 }
113
setTopOfScreen(bool ontop)114 void TermWindow::setTopOfScreen(bool ontop){
115 onTop = ontop;
116 this->layout()->setContentsMargins(0, (onTop ? 0 : 3), 0, (onTop ? 3 : 0));
117 tabWidget->setTabPosition(onTop ? QTabWidget::South : QTabWidget::North);
118 QTimer::singleShot(0,this, SLOT(ReShowWindow()));
119 }
120
121 // =======================
122 // PUBLIC SLOTS
123 // =======================
ShowWindow()124 void TermWindow::ShowWindow(){
125 if(animRunning>=0){ return; } //something running
126 animRunning = 1;
127 this->hide();
128 QApplication::processEvents();
129 CalculateGeom();
130 //Now setup the animation
131 ANIM->setEndValue(this->geometry());
132 if(onTop){ //use top edge
133 ANIM->setStartValue( QRect(this->x(), this->y(), this->width(), 0) ); //same location - no height
134 }else{
135 ANIM->setStartValue( QRect(this->x(), this->geometry().bottom(), this->width(), 0) ); //same location - no height
136 }
137 this->show();
138 //qDebug() << "Start Animation" << ANIM->startValue() << ANIM->endValue();
139 ANIM->start();
140 }
141
HideWindow()142 void TermWindow::HideWindow(){
143 if(animRunning>=0){ return; } //something running
144 //Now setup the animation
145 //Note: Do *not* use the private settings/variables because it may be changing right now - use the current geometry *ONLY*
146 animRunning = 0;
147 ANIM->setStartValue(this->geometry());
148 QDesktopWidget *desk = QApplication::desktop();
149 int screen = desk->screenNumber(this); //which screen it is currently on
150 if(desk->availableGeometry(screen).top() == this->geometry().top()){ //use top edge
151 ANIM->setEndValue( QRect(this->x(), this->y(), this->width(), 0) ); //same location - no height
152 }else{
153 ANIM->setEndValue( QRect(this->x(), this->y()+this->height(), this->width(), 0) ); //same location - no height
154 }
155 this->show();
156 ANIM->start();
157 }
158
CloseWindow()159 void TermWindow::CloseWindow(){
160 if(animRunning>=0){ return; } //something running
161 //Now setup the animation
162 animRunning = 2;
163 ANIM->setStartValue(this->geometry());
164 if(onTop){ //use top edge
165 ANIM->setEndValue( QRect(this->x(), this->y(), this->width(), 0) ); //same location - no height
166 }else{
167 ANIM->setEndValue( QRect(this->x(), this->geometry().bottom(), this->width(), 0) ); //same location - no height
168 }
169 this->show();
170 ANIM->start();
171 }
172
ReShowWindow()173 void TermWindow::ReShowWindow(){
174 if(this->isVisible()){
175 HideWindow(); //start with same animation as hide
176 animRunning = 3; //flag as a re-show (hide, then show);
177 }else{
178 //Already hidden, just show it
179 ShowWindow();
180 }
181 }
182 // =======================
183 // PRIVATE
184 // =======================
CalculateGeom()185 void TermWindow::CalculateGeom(){
186 //qDebug() << "Calculating Geom:" << this->size();
187 QDesktopWidget *desk = QApplication::desktop();
188 if(desk->screenCount() <= screennum){ screennum = desk->primaryScreen(); } //invalid screen detected
189 //Now align the window with the proper screen edge
190 QRect workarea = desk->availableGeometry(screennum); //this respects the WORKAREA property
191 if(onTop){
192 this->setGeometry( workarea.x(), workarea.y(), workarea.width(), this->height()); //maintain current hight of window
193
194 }else{
195 this->setGeometry( workarea.x(), workarea.y() + workarea.height() - this->height(), workarea.width(), this->height()); //maintain current hight of window
196 }
197 this->setFixedWidth(this->width()); //Make sure the window is not re-sizeable in the width dimension
198 this->setMinimumHeight(0);
199 }
200
GenerateTabID()201 QString TermWindow::GenerateTabID(){
202 //generate a unique ID for this new tab
203 int num = 1;
204 for(int i=0; i<tabWidget->count(); i++){
205 if(tabWidget->widget(i)->whatsThis().toInt() >= num){ num = tabWidget->widget(i)->whatsThis().toInt()+1; }
206 }
207 return QString::number(num);
208 }
209
210 // =======================
211 // PRIVATE SLOTS
212 // =======================
213
214 //Tab Interactions
New_Tab()215 void TermWindow::New_Tab(){
216 OpenDirs(QStringList() << QDir::homePath());
217 }
218
Close_Tab(int tab)219 void TermWindow::Close_Tab(int tab){
220 qDebug() << "Close Tab:" << tab;
221 if(tab<0){ tab = tabWidget->currentIndex(); }
222 static_cast<TerminalWidget*>(tabWidget->widget(tab))->aboutToClose();
223 tabWidget->widget(tab)->deleteLater(); //delete the page within the tag
224 tabWidget->removeTab(tab); // remove the tab itself
225 //Let the tray know when the last terminal is closed
226 if(tabWidget->count() < 1){
227 emit TerminalFinished();
228 }
229 }
230
Close_Tab(QString ID)231 void TermWindow::Close_Tab(QString ID){
232 //Close a tab based on it's ID instead of it's tab number
233 qDebug() << "Close Tab by ID:" << ID;
234 for(int i=0; i<tabWidget->count(); i++){
235 if(tabWidget->widget(i)->whatsThis()==ID){
236 qDebug() << " - Start close by number:" << i;
237 Close_Tab(i);
238 return; //all done
239 }
240 }
241 }
242
Next_Tab()243 void TermWindow::Next_Tab(){
244 qDebug() << "Next Tab";
245 int next = tabWidget->currentIndex()+1;
246 if(next>=tabWidget->count()){ next = 0; }
247 tabWidget->setCurrentIndex(next);
248 }
249
Prev_Tab()250 void TermWindow::Prev_Tab(){
251 qDebug() << "Previous Tab";
252 int next = tabWidget->currentIndex()-1;
253 if(next<0){ next = tabWidget->count()-1; }
254 tabWidget->setCurrentIndex(next);
255 }
256
focusOnWidget()257 void TermWindow::focusOnWidget(){
258 if(tabWidget->currentWidget()!=0){
259 //qDebug() << "Focus on Widget";
260 tabWidget->currentWidget()->setFocus();
261 }
262 }
263
264 //Animation finishing
AnimFinished()265 void TermWindow::AnimFinished(){
266 if(animRunning <0){ return; } //nothing running
267 if(animRunning==0){
268 //Hide Event
269 this->hide(); //need to hide the whole thing now
270 this->setGeometry( ANIM->startValue().toRect() ); //reset back to initial size after hidden
271 emit TerminalHidden();
272 }else if(animRunning==1){
273 //Show Event
274 this->activateWindow();
275 tabWidget->currentWidget()->setFocus();
276 emit TerminalVisible();
277 }else if(animRunning==2){
278 //Close Event
279 this->hide(); //need to hide the whole thing now
280 emit TerminalClosed();
281 }else if(animRunning>2){
282 //Re-Show event
283 this->hide();
284 this->setGeometry( ANIM->startValue().toRect() ); //reset back to initial size after hidden
285 //Now re-show it
286 QTimer::singleShot(0,this, SLOT(ShowWindow()));
287 }
288 animRunning = -1; //done
289 }
290
activeStatusChanged()291 void TermWindow::activeStatusChanged(){
292 if(animRunning>=0){ return; } //ignore this event - already changing
293 QWidget *active = QApplication::activeWindow();
294 if(active==0 && this->isVisible()){ HideWindow(); }
295 }
296
297 // ===================
298 // PROTECTED
299 // ===================
mouseMoveEvent(QMouseEvent * ev)300 void TermWindow::mouseMoveEvent(QMouseEvent *ev){
301 //Note: With mouse tracking turned off, this event only happens when the user is holding down the mouse button
302 if(onTop){
303 //Move the bottom edge to the current point
304 if( (ev->globalPos().y() - this->y()) < 50){ return; } //quick check that the window is not smaller than 20 pixels
305 QRect geom = this->geometry();
306 geom.setBottom(ev->globalPos().y());
307 this->setGeometry(geom);
308 }else{
309 //Move the top edge to the current point
310 if( (this->y() + this->height() -ev->globalPos().y()) < 50){ return; } //quick check that the window is not smaller than 20 pixels
311 QRect geom = this->geometry();
312 geom.setTop(ev->globalPos().y());
313 this->setGeometry(geom);
314 }
315 settings->setValue("lastSize",this->geometry().size());
316 }
317