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