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

9.1-release9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 1620346 was 1620346, checked in by Kris Moore <kris@…>, 20 months ago

Initial import of PC-BSD /current/ SVN repo

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