source: src-qt4/pc-mounttray/mountTray.cpp @ 03aedf0

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

Setup the pc-mounttray to allow for mounting a *.iso file via memory disk. This option is available under the "Load ISO File" option in the menu. Once the ISO file is unmounted, the memory disk is also detached from the system.

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