source: src-qt4/pc-mounttray/mountTray.cpp @ 1b29242

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

Switch the mount tray over to using "xdg-open" to open up the media directory, now that xdg-open has been fixed for KDE.

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