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

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

Setup the PCDM main session process to run fork and setsid to start the process.
Also add the beginning of the changes to the xprocess class to change over to running C process commands rather than using a QProcess for starting the desktop session.
You just need to change the USECLIBS flag at the top of main.cpp to switch which one to use.

  • Property mode set to 100644
File size: 19.2 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=TRUE;
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  //Disable user input while confirming login
260  loginW->setEnabled(FALSE);
261  if(!simpleDESwitcher){ deSwitcher->setEnabled(FALSE); }
262  toolbar->setEnabled(FALSE);
263  //Try to login
264  emit xLoginAttempt(username, password, desktop);
265  //Return signals are connected to the slotLogin[Success/Failure] functions
266 
267}
268
269void PCDMgui::slotLoginSuccess(){
270  /*QString de;
271  if(simpleDESwitcher){ de = loginW->currentDE(); }
272  else{ de = deSwitcher->currentItem(); }
273  saveLastLogin( loginW->currentUsername(), de );*/
274  slotClosePCDM(); //now start to close down the PCDM GUI
275}
276
277void PCDMgui::slotLoginFailure(){
278  //Display an info box that the login failed
279  QMessageBox notice(this);
280    notice.setWindowTitle(tr("Invalid Username/Password"));
281    notice.setIcon(QMessageBox::Warning);
282    notice.setText(tr("Username/Password combination is invalid, please try again."));
283    notice.setInformativeText("("+tr("Tip: Make sure that caps-lock is turned off.")+")");
284    notice.setStandardButtons(QMessageBox::Ok);
285    notice.setDefaultButton(QMessageBox::Ok);
286    notice.exec();
287       
288  //Re-Enable user input
289  loginW->setEnabled(TRUE);
290  if(!simpleDESwitcher){ deSwitcher->setEnabled(TRUE); }
291  toolbar->setEnabled(TRUE);
292}
293
294void PCDMgui::slotUserChanged(QString newuser){
295  if( !newuser.isEmpty() ){
296    //Try to load the custom user icon
297    QString tmpIcon = Backend::getUserHomeDir(newuser) + "/.loginIcon.png";
298    if(!QFile::exists(tmpIcon) ){ tmpIcon= currentTheme->itemIcon("user"); }
299    if(!QFile::exists(tmpIcon) || tmpIcon.isEmpty() ){ tmpIcon=":/images/user.png"; }
300    loginW->changeButtonIcon("display",tmpIcon, currentTheme->itemIconSize("user"));
301  }
302}
303
304void PCDMgui::slotUserSelected(QString newuser){
305  if(DEBUG_MODE){ qDebug() << "User selection changed:" << newuser; }
306  if(newuser.isEmpty()){
307    if(!simpleDESwitcher){ deSwitcher->setVisible(FALSE); }
308  }else{
309    if(!simpleDESwitcher){ deSwitcher->setVisible(TRUE); }
310    //Try to load the custom user icon
311    slotUserChanged(newuser);
312  }
313  retranslateUi();
314}
315
316void PCDMgui::slotShutdownComputer(){
317  QMessageBox verify;
318  verify.setWindowTitle(tr("System Shutdown"));
319  verify.setText(tr("You are about to shut down the system."));
320  verify.setInformativeText(tr("Are you sure?"));
321  verify.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
322  verify.setDefaultButton(QMessageBox::No);
323  int ret = verify.exec();
324
325  if(ret == QMessageBox::Yes){
326    Backend::log("PCDM: Shutting down computer");
327    system("shutdown -p now");
328    close();
329  }
330}
331
332void PCDMgui::slotRestartComputer(){
333  QMessageBox verify;
334  verify.setWindowTitle(tr("System Restart"));
335  verify.setText(tr("You are about to restart the system."));
336  verify.setInformativeText(tr("Are you sure?"));
337  verify.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
338  verify.setDefaultButton(QMessageBox::No);
339  int ret = verify.exec();
340
341  if(ret == QMessageBox::Yes){
342    Backend::log("PCDM: Restarting computer");
343    system("shutdown -r now");
344    close();
345  }
346}
347
348void PCDMgui::slotClosePCDM(){
349  system("killall -9 xvkbd"); //be sure to close the virtual keyboard
350  QCoreApplication::exit(0);
351  close();
352}
353
354void PCDMgui::slotChangeLocale(){
355  //Open the selector
356  wLoc = new widgetLocale();
357  QLocale currLocale = this->locale();
358  Backend::log("Current PCDM Locale: " + currLocale.name() );
359  wLoc->setCurrentLocale(currLocale.name());
360  wLoc->setWindowModality(Qt::ApplicationModal);
361  wLoc->show();
362  wLoc->raise();
363
364  //Connect the language changed signal
365  connect(wLoc,SIGNAL(languageChanged(QString)),this,SLOT(slotLocaleChanged(QString)) );
366}
367
368void PCDMgui::slotLocaleChanged(QString langCode){
369  //Get the valid Locale code
370  QString translationFile;
371  if(!QFile::exists(translationDir+"PCDM_"+langCode+".qm")){
372    langCode = langCode.section("_",0,0);
373    if(!QFile::exists(translationDir+"PCDM_"+langCode+".qm")){
374      if(!QFile::exists(translationDir+"qt_"+langCode+".qm")){
375        Backend::log("Desired locale is not a valid translation: " + langCode);
376        return;
377      }else{
378        translationFile = translationDir+"qt_"+langCode+".qm";     
379      }
380    }else{
381      translationFile = translationDir+"PCDM_"+langCode+".qm";     
382    }
383  }else{
384    translationFile = translationDir+"PCDM_"+langCode+".qm";
385  }     
386       
387  Backend::log("Changing localization to " + langCode);
388
389  //Alternate method for changing Locale
390  QLocale locale(langCode);
391  this->setLocale(locale);
392  //this->setDefaultLocale(locale);
393  //Change the translator
394  if( !m_translator->isEmpty() ){
395    Backend::log("Remove the translator");
396    QCoreApplication::removeTranslator(m_translator);
397  }
398
399  if(m_translator->load(translationFile)){
400    Backend::log("Install the new translator: "+translationFile);
401    QCoreApplication::installTranslator(m_translator);   
402  }
403  //Re-draw the interface
404  retranslateUi();
405 
406  Backend::log("Current Locale after change: " + this->locale().name() );
407}
408
409void PCDMgui::slotChangeKeyboardLayout(){
410  //Fill a couple global variables
411  QStringList kModels = Backend::keyModels();
412  QStringList kLayouts = Backend::keyLayouts();
413  //Startup the GUI
414  wKey = new widgetKeyboard();
415  wKey->programInit(kModels, kLayouts);
416  wKey->setWindowModality(Qt::ApplicationModal);
417  wKey->show();
418  wKey->raise();
419}
420
421// Start xvkbd
422void PCDMgui::slotPushVirtKeyboard(){
423   QString cmd = "killall -9 "+VIRTUALKBDBIN.section(" ",0,0).section("/",-1)+"; "+VIRTUALKBDBIN+" &";
424   qDebug() << "Starting Virtual Keyboard:";
425   qDebug() << " - CMD: "+cmd;
426   system( cmd.toUtf8() );
427   loginW->resetFocus("password");
428}
429
430void PCDMgui::retranslateUi(){
431  //All the text-setting for the main interface needs to be done here
432  //virtual keyboard button
433  //if(currentTheme->itemIsEnabled("vkeyboard")){
434    virtkeyboardButton->setToolTip(tr("Virtual Keyboard"));
435    virtkeyboardButton->setText(tr("Virtual Keyboard"));
436  //}
437  //locale switcher button
438  localeButton->setToolTip(tr("Change locale"));
439  localeButton->setText(tr("Locale"));
440  //keyboard layout button
441  keyboardButton->setToolTip(tr("Change Keyboard Layout"));
442  keyboardButton->setText(tr("Keyboard Layout"));
443  //system button
444  systemButton->defaultAction()->setToolTip(tr("Shutdown the computer"));
445  systemButton->defaultAction()->setText(tr("System"));
446  //Menu entries for system button
447    systemMenu->clear();       
448    systemMenu->addAction( tr("Restart"),this, SLOT(slotRestartComputer()) );
449    systemMenu->addAction( tr("Shut Down"), this, SLOT(slotShutdownComputer()) );
450    if(DEBUG_MODE){systemMenu->addAction( tr("Close PCDM"), this, SLOT(slotClosePCDM()) ); }
451    systemButton->setMenu(systemMenu);
452  //The main login widget
453  if(hostname.isEmpty()){
454    //Find the system hostname
455    hostname = pcbsd::Utils::runShellCommand("hostname").join(" ").simplified();
456    loginW->displayHostName(hostname);   
457  }
458  loginW->retranslateUi();
459 
460  //The desktop switcher
461 
462    //Get the new desktop list (translated)
463    QStringList deList = Backend::getAvailableDesktops();
464    QString lastDE = Backend::getLastDE(loginW->currentUsername());
465    //Now fill the switcher
466    if(!simpleDESwitcher){
467      deSwitcher->removeAllItems();
468      for(int i=0; i<deList.length(); i++){
469        QString deIcon = Backend::getDesktopIcon(deList[i]);
470        if( deIcon.isEmpty() ){ deIcon = currentTheme->itemIcon("desktop"); } //set the default icon if none given
471        if( !QFile::exists(deIcon) ){ deIcon = ":/images/desktop.png"; }
472        //Now add the item back to the widget
473        deSwitcher->addItem( deList[i], deIcon, Backend::getDesktopComment(deList[i]) );
474      }
475      //Set the switcher to the last used desktop environment
476      if( !lastDE.isEmpty() ){ deSwitcher->setCurrentItem(lastDE); }
477
478    }else{
479      //Simple switcher on the login widget
480      QStringList deIcons, deInfo;
481      for(int i=0; i<deList.length(); i++){ 
482        QString ico = Backend::getDesktopIcon(deList[i]);
483        if(ico.isEmpty()){ ico = currentTheme->itemIcon("desktop"); }
484        if(!QFile::exists(ico)){ ico = ":/images/desktop.png"; }
485        deIcons << ico;
486        deInfo << Backend::getDesktopComment(deList[i]);
487      }
488      loginW->setDesktops(deList, deIcons, deInfo);
489      //Set the switcher to the last used desktop environment
490      loginW->setCurrentDE(lastDE);
491    }
492
493}
494
Note: See TracBrowser for help on using the repository browser.