source: src-qt4/pc-mounttray/mountTray.cpp @ 891636a

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

Get the mountTray saving/loading it's settings properly, and integrate those settings with the new Settings dlialog. You can now turn the disk space watcher off/on and set the refresh times/flags within the settings dialog.

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