1 //===========================================
2 //  Lumina-DE source code
3 //  Copyright (c) 2014, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 #include "LTaskButton.h"
8 #include "LSession.h"
9 
10 #ifndef DEBUG
11 #define DEBUG 0
12 #endif
13 
LTaskButton(QWidget * parent,bool smallDisplay)14 LTaskButton::LTaskButton(QWidget *parent, bool smallDisplay) : LTBWidget(parent){
15   actMenu = new QMenu(this);
16   winMenu = new QMenu(this);
17   UpdateMenus();
18   showText = !smallDisplay;
19   this->setAutoRaise(false); //make sure these always look like buttons
20   this->setContextMenuPolicy(Qt::CustomContextMenu);
21   this->setFocusPolicy(Qt::NoFocus);
22   this->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
23   winMenu->setContextMenuPolicy(Qt::CustomContextMenu);
24   connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openActionMenu()) );
25   connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked()) );
26   connect(winMenu, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(openActionMenu()) );
27   connect(winMenu, SIGNAL(triggered(QAction*)), this, SLOT(winClicked(QAction*)) );
28   connect(winMenu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed()));
29   connect(actMenu, SIGNAL(aboutToHide()), this, SIGNAL(MenuClosed()));
30 }
31 
~LTaskButton()32 LTaskButton::~LTaskButton(){
33 
34 }
35 
36 //===========
37 //      PUBLIC
38 //===========
windows()39 QList<WId> LTaskButton::windows(){
40   QList<WId> list;
41   for(int i=0; i<WINLIST.length(); i++){
42     list << WINLIST[i].windowID();
43   }
44   return list;
45 }
46 
classname()47 QString LTaskButton::classname(){
48   return cname;
49 }
50 
isActive()51 bool LTaskButton::isActive(){
52   return cstate == LXCB::ACTIVE;
53 }
54 
addWindow(WId win)55 void LTaskButton::addWindow(WId win){
56   WINLIST << LWinInfo(win);
57   UpdateButton();
58 }
59 
rmWindow(WId win)60 void LTaskButton::rmWindow(WId win){
61   for(int i=0; i<WINLIST.length(); i++){
62     if(WINLIST[i].windowID() == win){
63       WINLIST.removeAt(i);
64       break;
65     }
66   }
67   UpdateButton();
68 }
69 
70 //==========
71 //    PRIVATE
72 //==========
currentWindow()73 LWinInfo LTaskButton::currentWindow(){
74   if(WINLIST.length() == 1 || cWin.windowID()==0){
75     return WINLIST[0]; //only 1 window - this must be it
76   }else{
77     return cWin;
78   }
79 }
80 
81 //=============
82 //   PUBLIC SLOTS
83 //=============
84 
UpdateButton()85 void LTaskButton::UpdateButton(){
86   if(winMenu->isVisible()){ return; } //skip this if the window menu is currently visible for now
87   bool statusOnly = (WINLIST.length() == LWINLIST.length());
88   LWINLIST = WINLIST;
89 
90   winMenu->clear();
91   LXCB::WINDOWVISIBILITY showstate = LXCB::IGNORE;
92   for(int i=0; i<WINLIST.length(); i++){
93     if(WINLIST[i].windowID() == 0){
94       WINLIST.removeAt(i);
95       i--;
96       continue;
97     }
98     if(i==0 && !statusOnly){
99       //Update the button visuals from the first window
100       this->setIcon(WINLIST[i].icon(noicon));
101       cname = WINLIST[i].Class();
102       if(cname.isEmpty()){
103 	//Special case (chrome/chromium does not register *any* information with X except window title)
104 	cname = WINLIST[i].text();
105 	if(cname.contains(" - ")){ cname = cname.section(" - ",-1); }
106       }
107       this->setToolTip(cname);
108     }
109     bool junk;
110     QAction *tmp = winMenu->addAction( WINLIST[i].icon(junk), WINLIST[i].text() );
111       tmp->setData(i); //save which number in the WINLIST this entry is for
112     LXCB::WINDOWVISIBILITY stat = WINLIST[i].status(true); //update the saved state for the window
113     if(stat<LXCB::ACTIVE && WINLIST[i].windowID() == LSession::handle()->activeWindow()){ stat = LXCB::ACTIVE; }
114     if(stat > showstate){ showstate = stat; } //higher priority
115   }
116   //Now setup the button appropriately
117   // - visibility
118   if(showstate == LXCB::IGNORE || WINLIST.length() < 1){ this->setVisible(false); }
119   else{ this->setVisible(true); }
120   // - functionality
121   if(WINLIST.length() == 1){
122     //single window
123     this->setPopupMode(QToolButton::DelayedPopup);
124     this->setMenu(actMenu);
125     if(showText){
126       QString txt = WINLIST[0].text();
127       if(txt.length()>30){ txt.truncate(27); txt.append("..."); }
128       else if(txt.length()<30){ txt = txt.leftJustified(30, ' '); }
129       this->setText(txt);
130      }else if(noicon){ this->setText( cname ); }
131     else{ this->setText(""); }
132     this->setToolTip(WINLIST[0].text());
133   }else if(WINLIST.length() > 1){
134     //multiple windows
135     this->setPopupMode(QToolButton::InstantPopup);
136     this->setMenu(winMenu);
137     if(noicon || showText){ this->setText("("+QString::number(WINLIST.length())+") "+cname); }
138     else{ this->setText("("+QString::number(WINLIST.length())+")"); }
139   }
140   this->setState(showstate); //Make sure this is after the button setup so that it properly sets the margins/etc
141   cstate = showstate; //save this for later
142 }
143 
UpdateMenus()144 void LTaskButton::UpdateMenus(){
145   //Action menu should be auto-created for the state of the current window (cWin/cstate)
146   actMenu->clear();
147   if(cstate!=LXCB::ACTIVE){
148     actMenu->addAction( LXDG::findIcon("edit-select",""), tr("Activate Window"), this, SLOT(triggerWindow()) );
149   }
150   if(cstate!=LXCB::INVISIBLE){
151     actMenu->addAction( LXDG::findIcon("view-close",""), tr("Minimize Window"), this, SLOT(minimizeWindow()) );
152     if(LSession::handle()->XCB->WindowIsMaximized(cWin.windowID()) ){
153       actMenu->addAction( LXDG::findIcon("view-restore",""), tr("Restore Window"), this, SLOT(maximizeWindow()) );
154     }else{
155       actMenu->addAction( LXDG::findIcon("view-fullscreen",""), tr("Maximize Window"), this, SLOT(maximizeWindow()) );
156     }
157   }
158   actMenu->addAction( LXDG::findIcon("window-close",""), tr("Close Window"), this, SLOT(closeWindow()) );
159   if(WINLIST.length()>1 && !winMenu->isVisible()){
160     actMenu->addSeparator();
161      actMenu->addAction( LXDG::findIcon("layer-visible-on",""), tr("Show All Windows"), this, SLOT(showAllWindows()) );
162      actMenu->addAction( LXDG::findIcon("layer-visible-off",""), tr("Minimize All Windows"), this, SLOT(hideAllWindows()) );
163      actMenu->addAction( LXDG::findIcon("window-close",""), tr("Close All Windows"), this, SLOT(closeAllWindows()) );
164   }
165 }
166 
167 //=============
168 //   PRIVATE SLOTS
169 //=============
buttonClicked()170 void LTaskButton::buttonClicked(){
171   if(WINLIST.length() > 1){
172     winMenu->popup(QCursor::pos());
173   }else{
174     triggerWindow();
175   }
176 }
177 
closeWindow()178 void LTaskButton::closeWindow(){
179   if(DEBUG){ qDebug() << "Close Window:" << this->text(); }
180   if(winMenu->isVisible()){ winMenu->hide(); }
181   LWinInfo win = currentWindow();
182   LSession::handle()->XCB->CloseWindow(win.windowID());
183   cWin = LWinInfo(); //clear the current
184 }
185 
maximizeWindow()186 void LTaskButton::maximizeWindow(){
187   if(DEBUG){ qDebug() << "Maximize Window:" << this->text(); }
188   if(winMenu->isVisible()){ winMenu->hide(); }
189   LWinInfo win = currentWindow();
190   LSession::handle()->XCB->MaximizeWindow(win.windowID());
191   //LSession::handle()->adjustWindowGeom(win.windowID(), true); //run this for now until the WM works properly
192   cWin = LWinInfo(); //clear the current
193 }
194 
minimizeWindow()195 void LTaskButton::minimizeWindow(){
196   if(DEBUG){ qDebug() << "Minimize Window:" << this->text(); }
197   if(winMenu->isVisible()){ winMenu->hide(); }
198   LWinInfo win = currentWindow();
199   LSession::handle()->XCB->MinimizeWindow(win.windowID());
200   cWin = LWinInfo(); //clear the current
201   QTimer::singleShot(100, this, SLOT(UpdateButton()) ); //make sure to update this button if losing active status
202 }
203 
showAllWindows()204 void LTaskButton::showAllWindows(){
205   for(int i=WINLIST.length()-1; i>=0; i--){
206     if(WINLIST[i].status()==LXCB::INVISIBLE){
207       LSession::handle()->XCB->RestoreWindow(WINLIST[i].windowID());
208     }
209   }
210 }
211 
hideAllWindows()212 void LTaskButton::hideAllWindows(){
213   for(int i=WINLIST.length()-1; i>=0; i--){
214     LXCB::WINDOWVISIBILITY state = WINLIST[i].status();
215     if(state==LXCB::VISIBLE || state==LXCB::ACTIVE){
216       LSession::handle()->XCB->MinimizeWindow(WINLIST[i].windowID());
217     }
218   }
219 }
220 
closeAllWindows()221 void LTaskButton::closeAllWindows(){
222   for(int i=WINLIST.length()-1; i>=0; i--){
223     LSession::handle()->XCB->CloseWindow(WINLIST[i].windowID());
224   }
225 }
226 
triggerWindow()227 void LTaskButton::triggerWindow(){
228   LWinInfo win = currentWindow();
229   //Check which state the window is currently in and flip it to the other
230   //LXCB::WINDOWSTATE state = cstate;
231   //if(DEBUG){ qDebug() << "Window State: " << state; }
232   if(cstate == LXCB::ACTIVE){
233     if(DEBUG){ qDebug() << "Minimize Window:" << this->text(); }
234     LSession::handle()->XCB->MinimizeWindow(win.windowID());
235     QTimer::singleShot(100, this, SLOT(UpdateButton()) ); //make sure to update this button if losing active status
236   }else{
237     if(DEBUG){ qDebug() << "Activate Window:" << this->text(); }
238     LSession::handle()->XCB->ActivateWindow(win.windowID());
239   }
240   cWin = LWinInfo(); //clear the current
241 }
242 
winClicked(QAction * act)243 void LTaskButton::winClicked(QAction* act){
244   //Get the window from the action
245   if(act->data().toInt() < WINLIST.length()){
246     cWin = WINLIST[act->data().toInt()];
247     cstate = cWin.status();
248   }else{ cWin = LWinInfo(); } //clear it
249   //Now trigger the window
250   triggerWindow();
251 }
252 
openActionMenu()253 void LTaskButton::openActionMenu(){
254   //Get the Window the mouse is currently over
255   QPoint curpos = QCursor::pos();
256   QAction *act = winMenu->actionAt(winMenu->mapFromGlobal(curpos));
257   //qDebug() << "Button Context Menu:" << curpos.x() << curpos.y() << winMenu->geometry().x() << winMenu->geometry().y() << winMenu->geometry().width() << winMenu->geometry().height();
258   cWin = WINLIST[0]; //default to the first window
259   if( act != 0 && winMenu->isVisible() ){
260     //Get the window from the action
261     //qDebug() << "Found Action:" << act->data().toInt();
262     if(act->data().toInt() < WINLIST.length()){
263       cWin = WINLIST[act->data().toInt()];
264     }
265   }
266   cstate = cWin.status();
267   //Now show the action menu
268   UpdateMenus();
269   actMenu->popup(QCursor::pos());
270 }
271