1 /* PCDM Login Manager:
2 *  Written by Ken Moore (ken@pcbsd.org) 2012/2013
3 *  Copyright(c) 2013 by the PC-BSD Project
4 *  Available under the 3-clause BSD license
5 */
6 
7 /*
8 Sub-classed fancy widget for selecting a desktop environment
9 */
10 
11 #include "fancySwitcher.h"
12 
FancySwitcher(QWidget * parent,bool horizontal)13 FancySwitcher::FancySwitcher(QWidget* parent,bool horizontal) : QWidget(parent)
14 {
15   //Set the default values for the current item
16   idL.clear(); icL.clear();
17   item=-1; oldItem=0; //default values for the current/old item number
18   frames=-1; //smoothest animation possible (1 pixel at a time)
19   //Save whether it is a horizontal or vertical display
20   isHorizontal = horizontal;
21   //Set the default size for the images
22   iconSize = 64; //default all icons in the viewer to 64x64 (can be changed later)
23   //Create the Grid layout
24   QGridLayout* layout = new QGridLayout();
25   //Add the buttons to the grid (back is always left/up)
26   pushForward = new QPushButton;
27   pushBack = new QPushButton;
28   pushForward->setIconSize(QSize(iconSize/2,iconSize/2));
29   pushBack->setIconSize(QSize(iconSize/2,iconSize/2));
30 
31   textLabel = new QLabel;
32   textLabel->setAlignment(Qt::AlignCenter);
33 
34   iconViewer = new QGraphicsView();
35   iconViewer->setInteractive(false);
36   iconViewer->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
37   iconViewer->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
38   iconViewer->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
39   if(isHorizontal){
40     iconViewer->setFixedSize(2.5*iconSize,iconSize+2);
41     pushBack->setIcon(QIcon(":images/left.png"));
42     pushBack->setShortcut(tr("Alt+Left"));
43     pushForward->setIcon(QIcon(":images/right.png"));
44     pushForward->setShortcut(tr("Alt+Right"));
45     layout->addWidget(pushBack,1,1);
46     layout->addWidget(iconViewer,1,2);
47     layout->addWidget(pushForward,1,3);
48     layout->addWidget(textLabel,2,1,1,3);
49   }else{ //vertical
50     iconViewer->setFixedSize(iconSize+2,2.5*iconSize);
51     pushBack->setIcon(QIcon(":images/up.png"));
52     pushBack->setShortcut(tr("Alt+Up"));
53     pushForward->setIcon(QIcon(":images/down.png"));
54     pushForward->setShortcut(tr("Alt+Down"));
55     layout->addWidget(pushBack,1,1);
56     layout->addWidget(iconViewer,2,1);
57     layout->addWidget(textLabel,3,1);
58     layout->addWidget(pushForward,4,1);
59   }
60   //set this layout as the layout for the FancySwitcher
61   this->setLayout(layout);
62   //connect the buttons to their appropriate slots
63   connect(pushBack,SIGNAL(clicked()),this,SLOT(slotPushBack()));
64   connect(pushForward,SIGNAL(clicked()),this,SLOT(slotPushForward()));
65 
66   //Create the default scene for the iconViewer
67   updateIconViewer();
68   //Enable/disable the forward/back buttons as appropriate
69   checkButtons();
70 }
71 
~FancySwitcher()72 FancySwitcher::~FancySwitcher(){
73 }
74 
updateIconViewer()75 void FancySwitcher::updateIconViewer(){
76   //detect the base dimensions
77   int xsize,ysize;
78   if(isHorizontal){
79     xsize = (idL.length()+1+(idL.length()-1)*5.0/4)*iconSize;
80     ysize = iconSize;
81   }else{ //vertical
82     xsize = iconSize;
83     ysize = (idL.length()+1+(idL.length()-1)*5.0/4)*iconSize;
84   }
85   //qDebug() << "scene dimensions:" << xsize << ysize;
86   //Create the scene with the proper dimensions
87   scene = new QGraphicsScene(0,0,xsize,ysize);
88   //Fill the scene with the given images
89   int offset = iconSize*3/4; //initial offset ( (viewer width-iconsize)/2 )
90   for(int i=0; i<idL.length(); i++){
91     //qDebug() << "offset" << offset;
92     if(isHorizontal){
93       icL[i]->setOffset(offset,0);
94     }else{
95       icL[i]->setOffset(0,offset);
96     }
97     scene->addItem(icL[i]);
98     offset = offset + 5*iconSize/4; //have a 1/4 iconsize spacing between icons
99   }
100   //set the iconViewer to display the scene
101   iconViewer->setScene(scene);
102   displayCurrentItem(false);
103 
104 }
105 
displayCurrentItem(bool showAnimation)106 void FancySwitcher::displayCurrentItem(bool showAnimation){
107   //Check that the new item is valid
108   //if( (item >= idL.length()) || (item < 0) ){item = oldItem; return;} //reset to the previous value and don't change the display
109 
110   if(item == -1){
111     iconViewer->centerOn(0,0);
112     iconViewer->show();
113     return;
114   }
115   //Get the current/old display pixel
116   int cPixel = (1 + oldItem*5.0/4)*iconSize;
117   //Get the new/desired display pixel
118   int tPixel = (1 + item*5.0/4)*iconSize;
119   //detect if we are moving back/forward
120   bool moveForward = true;
121   if(tPixel < cPixel){ moveForward  = false; }
122   if(tPixel == cPixel){ showAnimation = false; } //same item, skip the animation loop and just refresh
123   //qDebug() << "displayItem:" << item << oldItem << cPixel << tPixel << moveForward;
124   //Show the scrolling animation if desired
125   if(showAnimation && (frames!=0)){
126     int pixchange = 1;
127     if(frames > 0){ pixchange=abs(tPixel-cPixel)/frames; } //draw fewer frames per animation
128 
129     if(moveForward){
130       while(cPixel < (tPixel-pixchange+1)){
131         cPixel += pixchange;
132 	if(isHorizontal){
133       	  iconViewer->centerOn(cPixel,0);
134    	}else{
135       	  iconViewer->centerOn(0,cPixel);
136     	}
137         iconViewer->show();
138         QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 22);
139       }
140     }else{
141       while(cPixel > (tPixel+pixchange-1)){
142         cPixel -= pixchange;
143 	if(isHorizontal){
144       	  iconViewer->centerOn(cPixel,0);
145    	}else{
146       	  iconViewer->centerOn(0,cPixel);
147     	}
148         iconViewer->show();
149         QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 22);
150       }
151     }
152     iconViewer->centerOn(icL[item]);
153     iconViewer->show();
154   }else{
155     //Just center on the desired item (skip animation)
156     iconViewer->centerOn(icL[item]);
157     iconViewer->show();
158   }
159 
160   //Save the (now) current item as the old item for animation purposes later
161   oldItem = item;
162   textLabel->setText(idL[item]); //set the info text
163 }
164 
checkButtons()165 void FancySwitcher::checkButtons(){
166   //Sanity check the current item value
167   if(item > (idL.length()-1)){ item = idL.length()-1; }
168   if(item < -1){ item = -1; }
169   //Enable/Disable the pushbuttons as appropriate
170   if(item == (idL.length()-1)){ pushForward->setEnabled(false); }
171   else{ pushForward->setEnabled(true); }
172   if(item < 1){ pushBack->setEnabled(false); }
173   else{ pushBack->setEnabled(true); };
174 }
175 
slotPushBack()176 void FancySwitcher::slotPushBack(){
177   item--;
178   if(item < 0){ item = 0; } //cannot go lower than the first item
179   displayCurrentItem(true); //show animation of changing items
180   checkButtons();
181   emit itemChanged();
182   emit itemChanged(idL[item]);
183 }
184 
slotPushForward()185 void FancySwitcher::slotPushForward(){
186   item++;
187   if(item >= idL.length()){ item = idL.length()-1; } //cannot go higher than the last item
188   displayCurrentItem(true);
189   checkButtons();
190   emit itemChanged();
191   emit itemChanged(idL[item]);
192 }
193 
194 //-----------------------------
195 //     PUBLIC FUNCTIONS
196 //-----------------------------
addItem(QString id,QString icon,QString tooltip)197 void FancySwitcher::addItem(QString id, QString icon, QString tooltip){
198   if(!QFile::exists(icon)){
199     qDebug() << "FancySwitcher: invalid icon file" << icon;
200     return;
201   }
202   if(idL.indexOf(id)==-1){
203     idL << id;
204     icL << new QGraphicsPixmapItem(QPixmap(icon).scaled(iconSize,iconSize));
205     if(!tooltip.isEmpty()){
206       int index = idL.indexOf(id);
207       icL[index]->setToolTip(tooltip);
208     }
209   }
210   if(item==-1){item = 0;} //There is now an item that can be selected
211   updateIconViewer();
212   checkButtons();
213 }
214 
addItems(QStringList idList,QStringList iconList,QStringList tooltipList)215 void FancySwitcher::addItems(QStringList idList, QStringList iconList, QStringList tooltipList){
216   //Check that all the lists are the same length
217   if(!( (idList.length()==iconList.length()) && (idList.length()==tooltipList.length()) )){
218     qDebug() << "FancySwitcher: item lists are not equal length";
219     return;
220   }
221   //Check each item and add it to the list if applicable
222   for(int i=0; i<idList.length(); i++){
223     if(!QFile::exists(iconList[i])){
224       qDebug() << "FancySwitcher: invalid icon file" << iconList[i];
225       return;
226     }else{
227       if(idL.indexOf(idList[i].toLower()) == -1){
228         idL << idList[i];
229         icL << new QGraphicsPixmapItem(QPixmap(iconList[i]).scaled(iconSize,iconSize));
230 	if(!tooltipList[i].isEmpty()){
231           int index = idL.indexOf(idList[i]);
232           icL[index]->setToolTip(tooltipList[i]);
233 	}
234       }
235     }
236   }
237   if(item==-1){item = 0;} //There is now an item to be selected
238   updateIconViewer();
239   checkButtons();
240 }
241 
removeItem(QString id)242 void FancySwitcher::removeItem(QString id){
243   int index = idL.indexOf(id.toLower());
244   if(index == -1){
245     qDebug() << "FancySwitcher: Item does not exist -" <<id;
246     return;
247   }else{
248     idL.removeAt(index);
249     icL.removeAt(index);
250   }
251   updateIconViewer();
252   checkButtons();
253 }
254 
removeItems(QStringList idList)255 void FancySwitcher::removeItems(QStringList idList){
256   for(int i=0; i<idList.length(); i++){
257     int index = idL.indexOf(idList[i].toLower());
258     if(index == -1){
259       qDebug() << "FancySwitcher: Item does not exist -" <<idList[i];
260       return;
261     }else{
262       idL.removeAt(index);
263       icL.removeAt(index);
264     }
265   }
266   updateIconViewer();
267   checkButtons();
268 }
269 
removeAllItems()270 void FancySwitcher::removeAllItems(){
271   if( idL.isEmpty() ){ return; } //do nothing if nothing to change
272   //clear the internal lists
273   idL.clear();
274   icL.clear();
275   item = -1;
276   //now refresh the display
277   updateIconViewer();
278   checkButtons();
279 }
280 
currentItem()281 QString FancySwitcher::currentItem(){
282   QString id = idL[item];
283   return id;
284 }
285 
setCurrentItem(QString id)286 void FancySwitcher::setCurrentItem(QString id){
287   int index = idL.indexOf(id);
288   if(index == -1){
289     qDebug() << "FancySwitcher: Item does not exist -" << id;
290     return;
291   }else{
292     item = index;
293     displayCurrentItem(false); //do not show animation
294     checkButtons();
295   }
296 }
297 
setNumberAnimationFrames(QString numframes)298 void FancySwitcher::setNumberAnimationFrames(QString numframes){
299   if(numframes.toLower()=="smooth"){frames=-1; return;}  //auto-detect max
300   if(numframes.toLower()=="instant"){frames=0; return;}   //don't use animation
301   bool ok;
302   int num = numframes.toInt(&ok);
303   if(!ok){qDebug() << "FancySwitcher: invalid input for number of animation frames. Must be \"smooth\", \"instant\", or an integer > 0."; return; }
304   if(num < 2){ frames=0; } //1 frame = instant
305   else{ frames=num; } //don't worry about huge numbers, min pixel animation will always be 1 at a time
306 
307 }
308 
setIconSize(int xysize)309 void FancySwitcher::setIconSize(int xysize){
310   iconSize = xysize; //change the saved icon size
311   //Update the pushbuttons icons
312   pushBack->setIconSize(QSize(iconSize/2,iconSize/2));
313   pushForward->setIconSize(QSize(iconSize/2,iconSize/2));
314   //Update the iconViewer sizes
315   if(isHorizontal){
316     iconViewer->setFixedSize(2.5*iconSize,iconSize+2);
317   }else{
318     iconViewer->setFixedSize(iconSize+2,2.5*iconSize);
319   }
320   updateIconViewer();
321 }
322 
changeButtonIcon(QString button,QString iconFile)323 void FancySwitcher::changeButtonIcon(QString button, QString iconFile){
324   if(!QFile::exists(iconFile)){ qDebug() << "FancySwitcher: invalid image file"<<iconFile; return; }
325   if(button.toLower() == "back"){ pushBack->setIcon(QIcon(iconFile)); }
326   else if(button.toLower() == "forward"){ pushForward->setIcon(QIcon(iconFile)); }
327   else{
328     qDebug() << "FancySwitcher: Cannot change the icon for button" << button << " - valid buttons are \"forward\" and \"back\"";
329   }
330 }
331 
setStyleSheet(QString style)332 void FancySwitcher::setStyleSheet(QString style){
333   //Propagate the style sheet to both the buttons and the iconViewer
334   pushForward->setStyleSheet(style);
335   pushBack->setStyleSheet(style);
336   iconViewer->setStyleSheet("QGraphicsView { "+style+" } "); //make sure the tooltips are not affected
337   textLabel->setStyleSheet(style);
338 }
339 
340