source: src-qt4/PCDM/src/pcdm-gui.cpp @ 90dc161

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 90dc161 was 90dc161, checked in by Ken Moore <ken@…>, 18 months ago

Completely revamp how PCDM uses the previous login information. Now you no longer need to specify which desktop to auto-login to if you have auto-login selected. It will first try to check for previous login information for that user, then fall back on the last login for the system in order to try and auto login. Also fix the PCDMd script a bit so that autologins work properly when triggered.

  • Property mode set to 100644
File size: 19.3 KB
Line 
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#include <QProcess>
8#include <QTimer>
9#include <QGraphicsPixmapItem>
10#include <QStyle>
11#include <pcbsd-utils.h>
12
13#include "pcdm-gui.h"
14#include "pcdm-backend.h"
15#include "fancySwitcher.h"
16
17bool DEBUG_MODE=FALSE;
18QString VIRTUALKBDBIN="/usr/local/bin/xvkbd -compact";
19
20PCDMgui::PCDMgui() : QMainWindow()
21{
22    //Load the Theme
23    loadTheme();
24    //Create the GUI based upon the current Theme
25    createGUIfromTheme();
26    //Now make sure that the login widget has keyboard focus
27    loginW->resetFocus();
28    this->setObjectName("PCDM-background");
29}
30
31PCDMgui::~PCDMgui()
32{
33    //delete ui;
34}
35
36void PCDMgui::loadTheme(){
37  currentTheme = new ThemeStruct();
38  QString themeFile = Config::themeFile();
39  if(!QFile::exists(themeFile)){ themeFile = ":samples/themes/default/default.theme"; }
40  currentTheme->loadThemeFile( themeFile );
41  //Check the Theme, using default values as necessary
42  QStringList invalid = currentTheme->invalidItems();
43  if( !invalid.isEmpty() ){
44    ThemeStruct* defaultTheme = new ThemeStruct();
45    defaultTheme->loadThemeFile( ":samples/themes/default/default.theme" );
46    for( int i=0; i<invalid.length(); i++){
47      //Replace the invalid items with the defaults
48      qDebug() << "Invalid Theme Item, Using defaults:" << invalid[i];
49      currentTheme->importItem( invalid[i] , defaultTheme->exportItem(invalid[i]) );
50    }
51  }
52 
53}
54
55void PCDMgui::createGUIfromTheme(){
56  QString style;
57  QString tmpIcon; //used for checking image files before loading them
58  //Set the background image
59  if(DEBUG_MODE){ qDebug() << "Setting Background Image"; }
60  if( currentTheme->itemIsEnabled("background") ){
61    tmpIcon = currentTheme->itemIcon("background");
62    if( tmpIcon.isEmpty() || !QFile::exists(tmpIcon) ){ tmpIcon = ":/images/backgroundimage.jpg"; }
63    //use "border-image" instead of "background-image" to stretch rather than tile the image
64    QString bgstyle = "QMainWindow#PCDM-background {border-image: url(BGIMAGE) stretch;}"; 
65    bgstyle.replace("BGIMAGE", tmpIcon);
66    style.append(bgstyle);
67  }
68  //Set the application style sheet
69  style.append(" "+ currentTheme->styleSheet() );
70  this->setStyleSheet( style.simplified() );
71
72  //Check for whether the desktop switcher is on the toolbar or not
73  simpleDESwitcher = (currentTheme->itemValue("desktop") == "simple");
74 
75  //get the default translation directory
76  if(DEBUG_MODE){ qDebug() << "Load Translations"; }
77  translationDir = "/usr/local/share/PCDM/i18n/";
78  //Fill the translator
79  m_translator = new QTranslator();
80  //Create the Toolbar
81  toolbar = new QToolBar();
82  //Add the Toolbar to the window
83  if(DEBUG_MODE){ qDebug() << "Create Toolbar"; }
84    //use the theme location   
85    QString tarea = currentTheme->itemValue("toolbar");
86    if(tarea == "left"){
87      this->addToolBar( Qt::LeftToolBarArea, toolbar );             
88    }else if( tarea=="top"){
89      this->addToolBar( Qt::TopToolBarArea, toolbar );             
90    }else if(tarea=="right"){
91      this->addToolBar( Qt::RightToolBarArea, toolbar );           
92    }else{ //bottom is default
93      this->addToolBar( Qt::BottomToolBarArea, toolbar );       
94    }
95    //Set toolbar flags
96    toolbar->setVisible(TRUE);
97    toolbar->setMovable(FALSE);
98    toolbar->setFloatable(FALSE);
99    //Set the default style and icon sizes
100    QString tstyle = currentTheme->itemIcon("toolbar").toLower(); //use the theme style
101    if(tstyle=="textonly"){ toolbar->setToolButtonStyle(Qt::ToolButtonTextOnly); }
102    else if(tstyle=="textbesideicon"){ toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); }
103    else if(tstyle=="textundericon"){ toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); }
104    else{ toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); } //default to icon only
105   
106    toolbar->setIconSize( currentTheme->itemIconSize("toolbar") ); //use theme size
107    toolbar->setFocusPolicy( Qt::NoFocus );
108  //Populate the Toolbar with items (starts at leftmost/topmost)
109    //----Virtual Keyboard
110    if(currentTheme->itemIsEnabled("vkeyboard") ){
111      if(DEBUG_MODE){ qDebug() << " - Create Virtual Keyboard Button"; }
112      tmpIcon = currentTheme->itemIcon("vkeyboard");
113      if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/input-keyboard.png"; }
114      virtkeyboardButton = new QAction( QIcon(tmpIcon),tr("Virtual Keyboard"),this );
115      toolbar->addAction(virtkeyboardButton);
116      connect( virtkeyboardButton, SIGNAL(triggered()), this, SLOT(slotPushVirtKeyboard()) );
117    }else{
118      virtkeyboardButton = new QAction(this);
119    }
120
121    //----Locale Switcher
122    if(DEBUG_MODE){ qDebug() << " - Create Locale Button"; }
123    tmpIcon = currentTheme->itemIcon("locale");
124    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/language.png"; }
125    localeButton = new QAction( QIcon(tmpIcon),tr("Locale"),this );
126    toolbar->addAction(localeButton);
127    connect( localeButton, SIGNAL(triggered()), this, SLOT(slotChangeLocale()) );
128   
129    //----Keyboard Layout Switcher
130    if(DEBUG_MODE){ qDebug() << " - Create Keyboard Layout Button"; }
131    tmpIcon = currentTheme->itemIcon("keyboard");
132    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/keyboard.png"; }
133    keyboardButton = new QAction( QIcon(tmpIcon),tr("Keyboard Layout"),this );
134    toolbar->addAction(keyboardButton);
135    connect( keyboardButton, SIGNAL(triggered()), this, SLOT(slotChangeKeyboardLayout()) );
136
137    //----Add a spacer
138    QWidget* spacer = new QWidget();
139    spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
140    toolbar->addWidget(spacer);
141       
142    //----System Shutdown/Restart
143    if(DEBUG_MODE){ qDebug() << " - Create System Button"; }
144    tmpIcon = currentTheme->itemIcon("system");
145    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/system.png"; }
146    QAction* act = new QAction( QIcon(tmpIcon),tr("System"),this );
147    systemButton = new QToolButton();
148    systemButton->setDefaultAction(act);
149    systemMenu = new QMenu();
150    toolbar->addWidget(systemButton);
151    systemButton->setPopupMode( QToolButton::InstantPopup );
152    systemButton->setFocusPolicy( Qt::NoFocus );
153   
154  //Create the grid layout
155  QGridLayout* grid = new QGridLayout;
156  if(DEBUG_MODE){ qDebug() << "Fill Desktop Area"; }
157  //Populate the grid with items
158    //----Header Image
159    QLabel* header = new QLabel; 
160    if( currentTheme->itemIsEnabled("header") ){
161      if(DEBUG_MODE){ qDebug() << " - Create Header"; }
162      tmpIcon = currentTheme->itemIcon("header");
163      if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/banner.png"; }
164      QPixmap tmp( tmpIcon );
165      header->setPixmap( tmp.scaled( currentTheme->itemIconSize("header") ) );
166      grid->addWidget( header, currentTheme->itemLocation("header","row"), \
167                      currentTheme->itemLocation("header","col"), \
168                      currentTheme->itemLocation("header","rowspan"), \
169                      currentTheme->itemLocation("header","colspan"), Qt::AlignCenter);
170    }
171   
172    //Username/Password/Login widget
173    if(DEBUG_MODE){ qDebug() << " - Create Login Widget"; }
174    loginW = new LoginWidget;
175    loginW->setUsernames(Backend::getSystemUsers()); //add in the detected users
176    QString lastUser = Backend::getLastUser();
177    if(!lastUser.isEmpty()){ //set the previously used user
178        loginW->setCurrentUser(lastUser); 
179    } 
180    //Set Icons from theme
181    tmpIcon = currentTheme->itemIcon("login");
182    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/next.png"; }
183    loginW->changeButtonIcon("login",tmpIcon, currentTheme->itemIconSize("login"));
184    tmpIcon = currentTheme->itemIcon("user");
185    slotUserChanged(loginW->currentUsername()); //Make sure that we have the correct user icon
186    tmpIcon = currentTheme->itemIcon("password");
187    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/password.png"; }
188    loginW->changeButtonIcon("pwview",tmpIcon, currentTheme->itemIconSize("password"));
189    //Enable/disable the password view functionality
190    loginW->allowPasswordView( Config::allowPasswordView() );
191    //Add item to the grid
192    grid->addWidget( loginW, currentTheme->itemLocation("login","row"), \
193                      currentTheme->itemLocation("login","col"), \
194                      currentTheme->itemLocation("login","rowspan"), \
195                      currentTheme->itemLocation("login","colspan"), Qt::AlignCenter);
196    //Connect the signals/slots
197    connect(loginW,SIGNAL(loginRequested(QString,QString)),this,SLOT(slotStartLogin(QString,QString)));
198    connect(loginW,SIGNAL(escapePressed()),this,SLOT(slotShutdownComputer()));
199    connect(loginW,SIGNAL(UserSelected(QString)), this, SLOT(slotUserSelected(QString)) );
200    connect(loginW,SIGNAL(UserChanged(QString)), this, SLOT(slotUserChanged(QString)) );
201   
202    //----Desktop Environment Switcher
203    if(simpleDESwitcher){
204      loginW->setDesktopIconSize(currentTheme->itemIconSize("desktop"));
205    }else{
206      if(DEBUG_MODE){ qDebug() << " - Create DE Switcher"; }
207      //Create the switcher
208      deSwitcher = new FancySwitcher(this, !currentTheme->itemIsVertical("desktop") );
209      QSize deSize = currentTheme->itemIconSize("desktop");
210      deSwitcher->setIconSize(deSize.height());
211      tmpIcon = currentTheme->itemIcon("nextde");
212      if( !tmpIcon.isEmpty() && QFile::exists(tmpIcon) ){ deSwitcher->changeButtonIcon("forward", tmpIcon); }
213      tmpIcon = currentTheme->itemIcon("previousde");
214      if( !tmpIcon.isEmpty() && QFile::exists(tmpIcon) ){ deSwitcher->changeButtonIcon("back", tmpIcon); }
215      //Figure out if we need to smooth out the animation
216      deSwitcher->setNumberAnimationFrames("4"); 
217      //NOTE: A transparent widget background slows the full animation to a crawl with a stretched background image
218
219      grid->addWidget( deSwitcher, currentTheme->itemLocation("desktop","row"), \
220                      currentTheme->itemLocation("desktop","col"), \
221                      currentTheme->itemLocation("desktop","rowspan"), \
222                      currentTheme->itemLocation("desktop","colspan"), Qt::AlignCenter);
223    }
224    //----WINDOW SPACERS
225    QStringList spacers = currentTheme->getSpacers();
226    for(int i=0; i<spacers.length(); i++){
227      bool isVertical = (spacers[i].section("::",0,0) == "true");
228      int row = spacers[i].section("::",1,1).toInt();
229      int col = spacers[i].section("::",2,2).toInt();
230      //qDebug() << "Add Spacer:" << isVertical << row << col;
231      if(isVertical){
232        grid->setRowStretch(row,1);
233      }else{ //horizontal
234        grid->setColumnStretch(col,1);
235      }
236    }
237
238  //Connect the grid to the Window
239    QWidget* widget = new QWidget;
240    widget->setLayout(grid);
241    this->setCentralWidget(widget);
242   
243  //Now translate the UI and set all the text
244  if(DEBUG_MODE){ qDebug() << " - Fill GUI with data"; }
245  retranslateUi();
246  if(DEBUG_MODE){ qDebug() << "Done with initialization"; }
247
248}
249
250void PCDMgui::slotStartLogin(QString displayname, QString password){
251  //Get user inputs
252  QString username = Backend::getUsernameFromDisplayname(displayname);
253  QString desktop;
254  if(simpleDESwitcher){
255    desktop = loginW->currentDE();
256  }else{
257    desktop = deSwitcher->currentItem();
258  }
259  QString homedir = Backend::getUserHomeDir(username);
260  //Disable user input while confirming login
261  loginW->setEnabled(FALSE);
262  if(!simpleDESwitcher){ deSwitcher->setEnabled(FALSE); }
263  toolbar->setEnabled(FALSE);
264  //Try to login
265  emit xLoginAttempt(username, password, homedir, desktop);
266  //Return signals are connected to the slotLogin[Success/Failure] functions
267 
268}
269
270void PCDMgui::slotLoginSuccess(){
271  /*QString de;
272  if(simpleDESwitcher){ de = loginW->currentDE(); }
273  else{ de = deSwitcher->currentItem(); }
274  saveLastLogin( loginW->currentUsername(), de );*/
275  slotClosePCDM(); //now start to close down the PCDM GUI
276}
277
278void PCDMgui::slotLoginFailure(){
279  //Display an info box that the login failed
280  QMessageBox notice(this);
281    notice.setWindowTitle(tr("Invalid Username/Password"));
282    notice.setIcon(QMessageBox::Warning);
283    notice.setText(tr("Username/Password combination is invalid, please try again."));
284    notice.setInformativeText("("+tr("Tip: Make sure that caps-lock is turned off.")+")");
285    notice.setStandardButtons(QMessageBox::Ok);
286    notice.setDefaultButton(QMessageBox::Ok);
287    notice.exec();
288       
289  //Re-Enable user input
290  loginW->setEnabled(TRUE);
291  if(!simpleDESwitcher){ deSwitcher->setEnabled(TRUE); }
292  toolbar->setEnabled(TRUE);
293}
294
295void PCDMgui::slotUserChanged(QString newuser){
296  if( !newuser.isEmpty() ){
297    //Try to load the custom user icon
298    QString tmpIcon = Backend::getUserHomeDir(newuser) + "/.loginIcon.png";
299    if(!QFile::exists(tmpIcon) ){ tmpIcon= currentTheme->itemIcon("user"); }
300    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/user.png"; }
301    loginW->changeButtonIcon("display",tmpIcon, currentTheme->itemIconSize("user"));
302  }
303}
304
305void PCDMgui::slotUserSelected(QString newuser){
306  if(DEBUG_MODE){ qDebug() << "User selection changed:" << newuser; }
307  if(newuser.isEmpty()){
308    if(!simpleDESwitcher){ deSwitcher->setVisible(FALSE); }
309  }else{
310    if(!simpleDESwitcher){ deSwitcher->setVisible(TRUE); }
311    //Try to load the custom user icon
312    slotUserChanged(newuser);
313  }
314  retranslateUi();
315}
316
317void PCDMgui::slotShutdownComputer(){
318  QMessageBox verify;
319  verify.setWindowTitle(tr("System Shutdown"));
320  verify.setText(tr("You are about to shut down the system."));
321  verify.setInformativeText(tr("Are you sure?"));
322  verify.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
323  verify.setDefaultButton(QMessageBox::No);
324  int ret = verify.exec();
325
326  if(ret == QMessageBox::Yes){
327    Backend::log("PCDM: Shutting down computer");
328    system("shutdown -p now");
329    close();
330  }
331}
332
333void PCDMgui::slotRestartComputer(){
334  QMessageBox verify;
335  verify.setWindowTitle(tr("System Restart"));
336  verify.setText(tr("You are about to restart the system."));
337  verify.setInformativeText(tr("Are you sure?"));
338  verify.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
339  verify.setDefaultButton(QMessageBox::No);
340  int ret = verify.exec();
341
342  if(ret == QMessageBox::Yes){
343    Backend::log("PCDM: Restarting computer");
344    system("shutdown -r now");
345    close();
346  }
347}
348
349void PCDMgui::slotClosePCDM(){
350  system("killall -9 xvkbd"); //be sure to close the virtual keyboard
351  QCoreApplication::exit(0);
352  close();
353}
354
355void PCDMgui::slotChangeLocale(){
356  //Open the selector
357  wLoc = new widgetLocale();
358  QLocale currLocale = this->locale();
359  Backend::log("Current PCDM Locale: " + currLocale.name() );
360  wLoc->setCurrentLocale(currLocale.name());
361  wLoc->setWindowModality(Qt::ApplicationModal);
362  wLoc->show();
363  wLoc->raise();
364
365  //Connect the language changed signal
366  connect(wLoc,SIGNAL(languageChanged(QString)),this,SLOT(slotLocaleChanged(QString)) );
367}
368
369void PCDMgui::slotLocaleChanged(QString langCode){
370  //Get the valid Locale code
371  QString translationFile;
372  if(!QFile::exists(translationDir+"PCDM_"+langCode+".qm")){
373    langCode = langCode.section("_",0,0);
374    if(!QFile::exists(translationDir+"PCDM_"+langCode+".qm")){
375      if(!QFile::exists(translationDir+"qt_"+langCode+".qm")){
376        Backend::log("Desired locale is not a valid translation: " + langCode);
377        return;
378      }else{
379        translationFile = translationDir+"qt_"+langCode+".qm";     
380      }
381    }else{
382      translationFile = translationDir+"PCDM_"+langCode+".qm";     
383    }
384  }else{
385    translationFile = translationDir+"PCDM_"+langCode+".qm";
386  }     
387       
388  Backend::log("Changing localization to " + langCode);
389
390  //Alternate method for changing Locale
391  QLocale locale(langCode);
392  this->setLocale(locale);
393  //this->setDefaultLocale(locale);
394  //Change the translator
395  if( !m_translator->isEmpty() ){
396    Backend::log("Remove the translator");
397    QCoreApplication::removeTranslator(m_translator);
398  }
399
400  if(m_translator->load(translationFile)){
401    Backend::log("Install the new translator: "+translationFile);
402    QCoreApplication::installTranslator(m_translator);   
403  }
404  //Re-draw the interface
405  retranslateUi();
406 
407  Backend::log("Current Locale after change: " + this->locale().name() );
408}
409
410void PCDMgui::slotChangeKeyboardLayout(){
411  //Fill a couple global variables
412  QStringList kModels = Backend::keyModels();
413  QStringList kLayouts = Backend::keyLayouts();
414  //Startup the GUI
415  wKey = new widgetKeyboard();
416  wKey->programInit(kModels, kLayouts);
417  wKey->setWindowModality(Qt::ApplicationModal);
418  wKey->show();
419  wKey->raise();
420}
421
422// Start xvkbd
423void PCDMgui::slotPushVirtKeyboard(){
424   QString cmd = "killall -9 "+VIRTUALKBDBIN.section(" ",0,0).section("/",-1)+"; "+VIRTUALKBDBIN+" &";
425   qDebug() << "Starting Virtual Keyboard:";
426   qDebug() << " - CMD: "+cmd;
427   system( cmd.toUtf8() );
428   loginW->resetFocus("password");
429}
430
431void PCDMgui::retranslateUi(){
432  //All the text-setting for the main interface needs to be done here
433  //virtual keyboard button
434  //if(currentTheme->itemIsEnabled("vkeyboard")){
435    virtkeyboardButton->setToolTip(tr("Virtual Keyboard"));
436    virtkeyboardButton->setText(tr("Virtual Keyboard"));
437  //}
438  //locale switcher button
439  localeButton->setToolTip(tr("Change locale"));
440  localeButton->setText(tr("Locale"));
441  //keyboard layout button
442  keyboardButton->setToolTip(tr("Change Keyboard Layout"));
443  keyboardButton->setText(tr("Keyboard Layout"));
444  //system button
445  systemButton->defaultAction()->setToolTip(tr("Shutdown the computer"));
446  systemButton->defaultAction()->setText(tr("System"));
447  //Menu entries for system button
448    systemMenu->clear();       
449    systemMenu->addAction( tr("Restart"),this, SLOT(slotRestartComputer()) );
450    systemMenu->addAction( tr("Shut Down"), this, SLOT(slotShutdownComputer()) );
451    if(DEBUG_MODE){systemMenu->addAction( tr("Close PCDM"), this, SLOT(slotClosePCDM()) ); }
452    systemButton->setMenu(systemMenu);
453  //The main login widget
454  if(hostname.isEmpty()){
455    //Find the system hostname
456    hostname = pcbsd::Utils::runShellCommand("hostname").join(" ").simplified();
457    loginW->displayHostName(hostname);   
458  }
459  loginW->retranslateUi();
460 
461  //The desktop switcher
462 
463    //Get the new desktop list (translated)
464    QStringList deList = Backend::getAvailableDesktops();
465    QString lastDE = Backend::getLastDE(loginW->currentUsername());
466    //Now fill the switcher
467    if(!simpleDESwitcher){
468      deSwitcher->removeAllItems();
469      for(int i=0; i<deList.length(); i++){
470        QString deIcon = Backend::getDesktopIcon(deList[i]);
471        if( deIcon.isEmpty() ){ deIcon = currentTheme->itemIcon("desktop"); } //set the default icon if none given
472        if( !QFile::exists(deIcon) ){ deIcon = ":/images/desktop.png"; }
473        //Now add the item back to the widget
474        deSwitcher->addItem( deList[i], deIcon, Backend::getDesktopComment(deList[i]) );
475      }
476      //Set the switcher to the last used desktop environment
477      if( !lastDE.isEmpty() ){ deSwitcher->setCurrentItem(lastDE); }
478
479    }else{
480      //Simple switcher on the login widget
481      QStringList deIcons, deInfo;
482      for(int i=0; i<deList.length(); i++){ 
483        QString ico = Backend::getDesktopIcon(deList[i]);
484        if(ico.isEmpty()){ ico = currentTheme->itemIcon("desktop"); }
485        if(!QFile::exists(ico)){ ico = ":/images/desktop.png"; }
486        deIcons << ico;
487        deInfo << Backend::getDesktopComment(deList[i]);
488      }
489      loginW->setDesktops(deList, deIcons, deInfo);
490      //Set the switcher to the last used desktop environment
491      loginW->setCurrentDE(lastDE);
492    }
493
494}
495
Note: See TracBrowser for help on using the repository browser.