source: src-qt4/pc-mounttray/mountTray.cpp @ 176289d

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

Fix the mount tray message clicked functionality, it should now either mount the device and/or open up where the device is mounted in the file manager.

  • Property mode set to 100644
File size: 17.0 KB
Line 
1
2/* Qt */
3#include <QDebug>
4#include <QFile>
5#include <QDir>
6#include <QImage>
7#include <QMenu>
8#include <QTranslator>
9
10#include "mountTray.h"
11#include <pcbsd-hardware.h>
12#include <pcbsd-utils.h>
13
14MountTray::~MountTray(){
15}
16
17void MountTray::programInit()
18{
19  DCheck = new DevCheck(); //initialize class for checking devices
20  qDebug() << "pc-mounttray: starting up";
21  getInitialUsername(); //try to detect the non-root user who is running the program with root permissions
22  getDefaultFileManager(); //try to detect the default file-manager for opening the mount directory
23  loadSavedSettings();
24 
25  trayIcon = new QSystemTrayIcon(this);
26  trayIconMenu = new QMenu();
27  //Generate the system menu options (these don't change)
28  sysMenu = new QMenu( tr("More Options") );
29    sysMenu->setIcon( QIcon(":icons/config.png") );
30    //Add the additional options
31    sysMenu->addAction( QIcon(":icons/folder.png"), tr("Open Media Directory"), this, SLOT(slotOpenMediaDir()) );
32    sysMenu->addAction( QIcon(":icons/harddrive.png"), tr("View Disk Usage"),this,SLOT(slotOpenFSDialog()) );
33    sysMenu->addAction( QIcon(":icons/refresh.png"),tr("Rescan Devices"), this, SLOT(slotRescan()) );
34    //Add the setting dialog option seperately
35    sysMenu->addSeparator();
36    sysMenu->addAction( QIcon(":icons/dvd.png"), tr("Load ISO File"), this, SLOT(slotOpenISO()) );
37    sysMenu->addAction( QIcon(":icons/config.png"), tr("Change Settings"), this, SLOT(slotOpenSettings()) );
38    //Add the Close button seperately
39    sysMenu->addSeparator();
40    sysMenu->addAction( QIcon(":icons/application-exit.png"), tr("Close Tray"), this, SLOT(closeTray()) );
41 
42  // Tie the left-click signal to open the context menu
43  connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(slotTrayActivated(QSystemTrayIcon::ActivationReason)) );
44  //Connect the message clicked slot
45  connect(trayIcon,SIGNAL(messageClicked()),this,SLOT(slotPopupClicked()) );
46  //Set the default Tray Icon (will change once tray menus are set)
47  trayIcon->setIcon(QIcon(":icons/CDdevices-inactive.png"));
48  trayIcon->show();
49
50  //Startup the devd watching process
51  qDebug() << "-Starting up the DEVD watcher";
52  devdTimer = new QTimer();
53  devdTimer->setSingleShot(TRUE);
54  connect(devdTimer,SIGNAL(timeout()),this,SLOT(slotDevChanges()));
55  startupDevdProc();
56 
57  //Do an initial scan of the devices with dmesg
58  qDebug() << "-Performing initial device scan";
59  scanInitialDevices();
60 
61  //Start up the filesystem watcher
62  diskWatcher = new FSWatcher();
63  connect(diskWatcher,SIGNAL(FSWarning(QString,QString)),this,SLOT(slotDisplayWarning(QString,QString)));
64  if(useDiskWatcher){ 
65    qDebug() << "-Starting up the disk space alert system";
66    diskWatcher->start(diskTimerMaxMS); 
67  }
68 
69  //Update the tray menu and icons
70  updateMenu();
71
72  qDebug() << "-Program now ready for use";
73}
74
75void MountTray::updateMenu(){
76  //Clear the menu
77  trayIconMenu->clear();
78  trayIconMenu->disconnect();
79  numAvail = 0;
80  numMount = 0;
81  //Iterate through all the devices and add them as necessary
82  for(int i=0; i<deviceList.length(); i++){
83    if(deviceList[i]->isConnected()) { 
84      trayIconMenu->addAction(deviceList[i]);
85      numAvail++;
86      if(deviceList[i]->isMounted() ){ numMount++; }
87      deviceList[i]->updateItem(); //refresh the item now as well
88    }else{
89      //Remove any devices that are not connected
90      deviceList.removeAt(i);
91      i--; //roll back one to catch the new index values for the list
92    }
93  }
94  //Separate the extra options at the end
95  trayIconMenu->addSeparator();
96  trayIconMenu->addMenu(sysMenu);
97  //Apply the menu to the Tray
98  trayIcon->setContextMenu(trayIconMenu);
99
100  //Update the main icon based upon whether devices have been found
101  if(numAvail==0){
102    trayIcon->setIcon( QIcon(":icons/CDdevices-inactive.png") );
103  }else{
104    if(numMount==0){
105      trayIcon->setIcon( QIcon(":icons/CDdevices.png") );
106    }else{
107      trayIcon->setIcon( QIcon(":icons/CDdevices.png") );
108    }
109  }
110}
111
112void MountTray::scanInitialDevices(){
113  slotDevChanges(FALSE);
114  return;
115}
116
117int MountTray::findDeviceInList(QString newDev){
118  for(int i=0; i<deviceList.length(); i++){
119    if( deviceList[i]->device == newDev ){ return i; }
120  }
121  return -1;
122}
123
124bool MountTray::addDevice(QString dev, QString label, QString type, QString filesys){
125  if(!dev.startsWith(DEVICEDIR)){ dev.prepend(DEVICEDIR); }
126 
127  //Check if the device is already in the list
128  int tot=0;
129  for(int i=0; i<deviceList.length(); i++){
130    if( deviceList[i]->device == dev ){ return false; } //already exists, do nothing
131    if( deviceList[i]->getDeviceName().startsWith(label) ){ tot++; }
132  }
133  //See if the label is unique as well, otherwise add a number to the end to make it unique
134  if(tot > 0 && !label.isEmpty()){ label.append("-"+QString::number(tot)); }
135 
136  qDebug() << "Valid Device Connection:" << dev << type << label << filesys;
137  //Create the menu item (will automount if necessary)
138  MenuItem *tmp = new MenuItem(this, dev, label, type, filesys);
139  //connect the signals/slots
140  connect(tmp, SIGNAL(itemMounted(QString)), this, SLOT(openMediaDir(QString)) );
141  connect(tmp, SIGNAL(newMessage(QString,QString)), this, SLOT(slotDisplayPopup(QString,QString)) );
142  connect(tmp, SIGNAL(itemRemoved(QString)), this, SLOT(removeDevice(QString)) );
143  deviceList << tmp;
144  //Update the menu
145  updateMenu();
146  return true;
147}
148
149void MountTray::removeDevice(QString dev){
150  if(!dev.startsWith(DEVICEDIR)){ dev.prepend(DEVICEDIR); }
151 
152  //Find the device in the list
153  int index = findDeviceInList(dev);
154  if( index == -1 ){ return; } //does not exist, do nothing
155  //Remove the menu entry from the list
156  deviceList[index]->cleanup(); //make sure it is unmounted with mountpoint removed
157  deviceList.removeAt(index);
158  //Update the menu
159  updateMenu();
160  qDebug() << "Valid Device Removal:" <<  dev;
161}
162
163void MountTray::slotTrayActivated(QSystemTrayIcon::ActivationReason reason) {
164   if(reason == QSystemTrayIcon::Trigger) {
165     //Make sure all the items are updated
166     for(int i=0; i<deviceList.length(); i++){
167        deviceList[i]->updateItem();
168     }
169     trayIcon->contextMenu()->popup(QCursor::pos());
170   }
171}
172
173
174void MountTray::startupDevdProc(){
175  devdProc = new QLocalSocket(this);
176  devdProc->connectToServer("/var/run/devd.pipe",QIODevice::ReadOnly | QIODevice::Text);
177  if( devdProc->waitForConnected(3000) ){ //Max wait of 3 sec
178    connect(devdProc, SIGNAL(readyRead()), this, SLOT(newDevdMessage()) );
179  }else{
180    qDebug() << " - Could not startup the devd watching process";
181  }
182}
183
184void MountTray::newDevdMessage(){       
185  devdTimer->start(1500); //wait 1.5 seconds before checking for device changes
186  return;
187}
188
189void MountTray::slotDevChanges(bool showPopup){
190  //This function actually checks the system device list for changes
191  //  and updates the available devices appropriately
192 
193  if(DEBUG_MODE){ qDebug() << "Checking for Device Changes:"; }
194  //Get the current list of devices
195  QStringList nsd = DCheck->devChildren("");
196  //Remove all the currently managed devices
197  qDebug() << "Rescanning Device List";
198  for(int i=0; i<deviceList.length(); i++){
199    QString dev = deviceList[i]->device.section("/",-1);
200    if(DEBUG_MODE){ qDebug() << " - Check device:" << dev; }
201    int ni = nsd.indexOf(dev);
202    if(ni == -1){
203      //Device Removed
204      if(DEBUG_MODE){ qDebug() << " - Device no longer connected:" << dev; }
205      removeDevice(dev);
206      i--;
207    }else{
208      //Probe the device for validity if not currently mounted
209      if( !deviceList[i]->isMounted() ){
210        QString ja, jb, jc, jd; //junk variables
211        if( !DCheck->devInfo(dev,&ja,&jb,&jc,&jd) ){
212          if(DEBUG_MODE){ qDebug() << " - Device no longer valid:" << dev; }
213          //no longer valid device
214          removeDevice(dev);
215          i--;
216        }
217      }
218      nsd.removeAt(ni);
219    }
220  }
221  //Now Iterate through all available devices and probe them for validity
222  // (This should catch devices that do not "announce" their presence by creating a new device node)
223  for(int i=0; i<nsd.length(); i++){
224    //Check if it is a good device
225    QString dlabel, dtype, dfs, dsize; //additional output info
226    bool good = DCheck->devInfo(nsd[i],&dtype,&dlabel,&dfs,&dsize);
227    if(good){
228      //Now create a new entry for this device
229      bool added = addDevice(nsd[i],dlabel,dtype,dfs); 
230      //Show a message bubble
231      if(showPopup && added){ //make sure this is not shown for previously added devices
232        QString title = tr("New Device");
233        QString message = QString( tr("%1 can now be accessed")).arg(dlabel);
234        slotDisplayPopup(title, message, nsd[i]);
235      }
236    }
237  }
238 
239  //Run the disk space check if appropriate
240  if(useDiskWatcher && useDiskTimerDevd && showPopup){ diskWatcher->checkFS(); }
241}
242
243void MountTray::closeTray(){
244  qDebug() << "pc-mounttray: closing down";
245  //Kill the devd watching process
246  qDebug() << " -Shutting down DEVD watcher";
247  devdProc->disconnectFromServer();
248  qDebug() << " -Unmounting managed devices and mount points";
249  for(int i=0; i<deviceList.length(); i++){
250    deviceList[i]->cleanup();
251  }
252  //Close down the application
253  exit(0);
254}
255
256void MountTray::getInitialUsername(){
257  //Get the original user who started the tray app
258  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
259  //qDebug() << "System Environment:" << env.toStringList();
260  QString username = env.value( "LOGNAME" );
261  //qDebug() << "First attempt user detected:" << username;
262  if(username=="root"){
263    username = env.value( "SUDO_USER" ); //try this environment variable instead
264    //qDebug() << "Second attempt user detected:" << username;
265  }
266  if(username=="root" || username.isEmpty() ){
267   //attempt another method of determining the username
268   QStringList uList = pcbsd::Utils::runShellCommand("who");
269   if( uList.length() > 0 ){ 
270     username = uList[0].section(" ",0,0,QString::SectionSkipEmpty);
271     for(int i=0; i<uList.length(); i++){
272       if( !uList[i].section(" ",0,0,QString::SectionSkipEmpty).contains(username) ){
273         username = "root"; //too many users logged in; this method will not work
274         break;
275       }
276     }
277     //qDebug() << "Third attempt user detected:" << username;
278   }
279  }
280  if(username=="root"){
281    QMessageBox::warning(this,tr("User Detection Error"),tr("Unable to determine the non-root user who started the application \nCan not open the file manager with root permissions") );
282    return;
283  }
284  USERNAME=username.simplified(); //set the global variable
285  if(DEBUG_MODE){ qDebug() << "-User detected:" << USERNAME; }
286}
287
288void MountTray::getDefaultFileManager(){
289  //Get the command to open the appropriate file manager
290  QString fmcmd = pcbsd::Utils::runShellCommand("de-info -fileman").join(" ");
291  //qDebug() << "de-info result:" << fmcmd;
292  fmcmd = fmcmd.remove("%s").simplified();
293  //qDebug() << "FM command found:" << fmcmd;
294  if( fmcmd.isEmpty() || fmcmd.contains("File ") ){fmcmd= "openwith"; } //Default to the "openwith" command
295
296  FILEMAN = fmcmd.simplified();
297  if(DEBUG_MODE){ qDebug() << "-File manager detected:" << FILEMAN; }
298}
299
300void MountTray::slotOpenMediaDir(){
301  openMediaDir(MOUNTDIR);
302}
303
304void MountTray::openMediaDir(QString dir){
305  //Open the default file-manager to the directory listed
306  if(dir.isEmpty()){ dir = MOUNTDIR; }
307  if(!dir.endsWith("/")){ dir.append("/"); } //make sure the filemanager knows it is a directory
308  //Make sure we can setup user permissions
309  if(USERNAME=="root"){
310    qDebug() << "Cannot open filemanager with root permissions";
311    return;
312  }
313  //Open the default file manager to the given directory as that user
314  qDebug() << "Opening the media directory with user permissions";
315  QString cmd = "su -m "+USERNAME+" -c \'"+FILEMAN+" \""+dir+"\"\' &";
316  if(DEBUG_MODE){ qDebug() << " -cmd:" << cmd; }
317  system( cmd.toUtf8() ); 
318}
319
320void MountTray::slotRescan(){
321  //Display a notification
322  qDebug() << "Re-scanning devices:";
323  slotDisplayPopup(tr("Please Wait"),tr("Rescanning devices attached to the system"));
324  //Rescan the device list for new devices
325  scanInitialDevices();
326  //Check that all the existing devices still exist
327  for(int i=0; i<deviceList.length(); i++){
328    deviceList[i]->updateItem();
329  }
330  //Run the disk check if appropriate
331  if(useDiskWatcher){ diskWatcher->checkFS(); }
332}
333
334void MountTray::slotOpenFSDialog(){
335  //Open up the Filesystem disk space monitoring dialog
336  diskDisplay = new FSDialog();
337  diskDisplay->show();
338}
339
340void MountTray::slotOpenSettings(){
341  //Stop the refresh timer on the watcher
342  diskWatcher->stop();
343  //Open up the settings window and apply changes as necessary
344  SettingsDialog *sdlg = new SettingsDialog();
345  sdlg->useDiskWatcher = useDiskWatcher;
346  sdlg->useDiskAutoTimer = useDiskTimerDevd;
347  sdlg->diskRefreshMS = diskTimerMaxMS;
348  sdlg->showDialog();
349  //Now parse the output and save if necessary
350  if(sdlg->SettingsChanged){
351    useDiskWatcher = sdlg->useDiskWatcher;
352    useDiskTimerDevd = sdlg->useDiskAutoTimer;
353    diskTimerMaxMS = sdlg->diskRefreshMS;
354    qDebug() << "INFO: Saving updated settings to file";
355    saveCurrentSettings(); //update the saved settings
356  }
357  //Now restart the disk watcher if enabled
358  if(useDiskWatcher){ diskWatcher->start(diskTimerMaxMS); }
359}
360
361void MountTray::slotOpenISO(){
362  //prompt for the user to select a file
363  QString file = QFileDialog::getOpenFileName( this, tr("Select ISO File"), QDir::homePath(), tr("ISO Files (*.iso)") );
364  if(file.isEmpty()){ return; } //cancelled
365  //check for available device node number /dev/md<number>
366  int num = 1;
367  while( QFile::exists("/dev/md"+QString::number(num)) ){ num++; }
368  //add it to the device tree (will automatically get picked up by the device detection method)
369  QString cmd = "mdconfig -a -f "+file+" -u "+QString::number(num);
370  system(cmd.toUtf8());
371}
372
373void MountTray::slotSingleInstance()
374{
375  trayIcon->show();
376}
377
378void MountTray::slotDisplayPopup(QString title, QString msg, QString device){
379  popupSave = device; //so we know what to do when it is clicked
380  //Display a popup bubble with the given message for 3 seconds
381  trayIcon->contextMenu()->hide(); //close the menu list
382  trayIcon->showMessage(title, msg , QSystemTrayIcon::NoIcon,3000 );
383}
384
385void MountTray::slotDisplayWarning(QString title, QString msg){
386  popupSave="FSCHECK";
387  //Display a popup bubble with the given message for 5 seconds
388  trayIcon->contextMenu()->hide(); //close the menu list
389  trayIcon->showMessage(title, msg , QSystemTrayIcon::Warning,5000 );
390}
391
392void MountTray::slotPopupClicked(){
393  //Check the saved variable for what to do with this popup
394  if(popupSave == "FSCHECK"){
395    //Open up the filesystem disk space UI
396    slotOpenFSDialog();
397  }else if(!popupSave.isEmpty()){
398    //Check if it is a currently valid device
399    if(!popupSave.startsWith(DEVICEDIR)){ popupSave.prepend(DEVICEDIR); }
400    for(int i=0; i<deviceList.length(); i++){
401      if( deviceList[i]->device == popupSave){
402        //See if the device is mounted
403        if(deviceList[i]->isMounted()){
404          //Open up the mountpoint directory
405          openMediaDir(deviceList[i]->mountpoint);
406        }else{
407          //Mount the device
408          deviceList[i]->mountItem();
409        }
410        break;
411      }
412    }
413  }
414
415}
416
417void MountTray::loadSavedSettings(){
418  //The saved settings file
419  QString filename = QDir::homePath()+"/.mounttray.settings";
420  //Set the defaults
421  useDiskWatcher=TRUE; useDiskTimerDevd=TRUE;
422  diskTimerMaxMS=3600000; //1 hour refresh timer
423  //Now load the file
424  QFile file(filename);
425  if(file.exists()){
426    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){ 
427      qDebug() << "-Could not open settings file: using defaults";
428      return; 
429    }
430    QTextStream in(&file);
431    while(!in.atEnd()){
432      QString line = in.readLine();
433      if(!line.startsWith("#")){ //skip comment lines
434        QString var = line.section(")",0,0,QString::SectionSkipEmpty).simplified();
435        QString val = line.section(")",1,30,QString::SectionSkipEmpty).simplified();
436        if(var=="UseDiskSpaceMonitoring"){ 
437          if(val.toLower() == "true"){ useDiskWatcher = TRUE;}
438          else{ useDiskWatcher = FALSE; }
439        }else if(var=="UseDiskSpaceDevdTiming"){
440          if(val.toLower() == "true"){ useDiskTimerDevd = TRUE;}
441          else{ useDiskTimerDevd = FALSE; }     
442        }else if(var=="DiskSpaceTimingMaxMilliseconds"){
443          diskTimerMaxMS = val.toInt(); 
444        }
445      }
446    }
447    file.close();
448  }else{
449    qDebug() << "-Creating new settings file with defaults";
450    saveCurrentSettings();
451  }
452}
453
454void MountTray::saveCurrentSettings(){
455  //The saved settings file
456  QString filename = QDir::homePath()+"/.mounttray.settings";
457  //Now write the current values to the file
458  QFile file(filename);
459  if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
460    qDebug() << "ERROR: Could not open file to save settings:"<<filename;
461    return;
462  }
463  QTextStream out(&file);
464  out << "#pc-mounttray saved settings file\n";
465  out << "# DO NOT EDIT: Use the settings dialog in the application instead!!\n";
466  //Save the settings
467  out << "UseDiskSpaceMonitoring)";
468  if(useDiskWatcher){ out << "true\n";}
469  else{ out << "false\n"; }
470  out << "UseDiskSpaceDevdTiming)";
471  if(useDiskTimerDevd){ out << "true\n";}
472  else{ out << "false\n"; }
473  out << "DiskSpaceTimingMaxMilliseconds)"+QString::number(diskTimerMaxMS)+"\n";
474  //Now close the file
475  file.close();
476}
Note: See TracBrowser for help on using the repository browser.