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