source: src-qt4/PCDM/src/pcdm-gui.cpp @ 1f0939e

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