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

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

Fix the PCDM background to the main window only, and remove an extra function from the loginwidget class.

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