source: src-qt4/pc-updategui/mainWin.cpp @ 88de14c

9.2-releasereleng/10.0releng/10.0.1
Last change on this file since 88de14c was 88de14c, checked in by Kris Moore <kris@…>, 6 months ago

Fix some bugs doing freebsd-update / major updates via the GUI.

When an update is going to require a reboot, we must ensure the GUI
does NOT check for updates again, possibly trampling the freebsd-update
cache directories / files

  • Property mode set to 100644
File size: 19.6 KB
Line 
1/****************************************************************************
2** ui.h extension file, included from the uic-generated form implementation.
3**
4** If you want to add, delete, or rename functions or slots, use
5** Qt Designer to update this file, preserving your code.
6**
7** You should not define a constructor or destructor in this file.
8** Instead, write your code in functions called init() and destroy().
9** These will automatically be called by the form's constructor and
10** destructor.
11*****************************************************************************/
12#include <fcntl.h>
13#include <QDateTime>
14#include <QDebug>
15#include <QDir>
16#include <QProcess>
17#include <QProgressDialog>
18#include <QSocketNotifier>
19#include <QString>
20#include <QTextStream>
21#include <pcbsd-utils.h>
22#include "mainWin.h"
23#include "../config.h"
24
25void mainWin::ProgramInit(QString ch, QString ip)
26{
27  // Set any warden directories
28  groupDetails->setVisible(false);
29  doingUpdate=false;
30  lastError="";
31  wDir = ch;
32  wIP = ip;
33  if ( ! wDir.isEmpty() )
34     setWindowTitle(tr("Updates for Jail:") + " " + wIP );
35
36  //Grab the username
37  //username = QString::fromLocal8Bit(getenv("LOGNAME"));
38  connect(buttonRescan, SIGNAL(clicked()), this, SLOT(slotRescanUpdates()));
39  connect(pushInstallUpdates, SIGNAL(clicked()), this, SLOT(slotInstallClicked()));
40  connect(pushClose, SIGNAL(clicked()), this, SLOT(slotCloseClicked()));
41  connect(checkAll, SIGNAL(clicked()), this, SLOT(slotSelectAllClicked()));
42  connect(listViewUpdates, SIGNAL(itemClicked(QListWidgetItem *)),this,SLOT(slotListClicked()));
43  connect(listViewUpdates, SIGNAL(itemActivated(QListWidgetItem *)),this,SLOT(slotListClicked()));
44  connect(listViewUpdates, SIGNAL(itemChanged(QListWidgetItem *)),this,SLOT(slotListClicked()));
45  connect(listViewUpdates, SIGNAL(itemPressed(QListWidgetItem *)),this,SLOT(slotListClicked()));
46  connect(listViewUpdates, SIGNAL(itemDoubleClicked(QListWidgetItem *)),this,SLOT(slotListDoubleClicked(QListWidgetItem *)));
47  progressUpdate->setHidden(true);
48
49  QTimer::singleShot(100, this, SLOT(slotRescanUpdates() ) );
50}
51
52void mainWin::slotListDoubleClicked(QListWidgetItem *cItem)
53{
54  if ( listUpdates.at(listViewUpdates->row(cItem)).at(7).isEmpty() )
55     return;
56
57  QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
58  QString username = env.value( "LOGNAME" );
59
60
61  QString url = listUpdates.at(listViewUpdates->row(cItem)).at(7);
62  qDebug() << url;
63  system("su -m " + username.toLatin1() + " -c 'openwith " + url.toLatin1() + "' &"); 
64}
65
66//Check whether an update was selected to enable the button
67void mainWin::slotListClicked(){
68  bool found = false;
69
70  // Get the total number of updates
71  for (int z=0; z < listViewUpdates->count(); ++z)
72    if ( listViewUpdates->item(z)->checkState() == Qt::Checked )
73      found = true;
74
75  if(!found)
76    pushInstallUpdates->setEnabled(false);
77  else
78    pushInstallUpdates->setEnabled(true);
79
80
81  // See if we have description text to update
82  groupDetails->setVisible(false);
83  textDesc->setText(QString());
84  if ( ! listViewUpdates->currentItem() )
85     return;
86
87  int myRow = listViewUpdates->currentRow();
88
89  if ( listUpdates.at(myRow).at(1) == "FBSDUPDATE" ) {
90    groupDetails->setVisible(true);
91
92    QString desc;
93    for (int p=2; p < listUpdates.at(myRow).count(); p++)
94      desc += listUpdates.at(myRow).at(p) + "<br>";
95    textDesc->setText(desc);
96  }
97}
98
99bool mainWin::sanityCheck()
100{
101  bool haveUP, haveMU;
102  int sa = 0;
103  haveUP = false;
104  haveMU = false;
105
106  for (int z=0; z < listViewUpdates->count(); ++z) {
107    if ( listViewUpdates->item(z)->checkState() == Qt::Checked ) {
108      if ( listUpdates.at(z).at(1) == "PACKAGE") {
109        haveUP = true;
110      }
111      if ( listUpdates.at(z).at(1) == "PATCH") {
112        haveUP = true;
113        if ( listUpdates.at(z).at(5) == "YES" ) 
114          sa++;
115      }
116      if ( listUpdates.at(z).at(1) == "SYSUPDATE") {
117        haveMU = true;
118      }
119    }
120  }
121
122  if ( (haveMU && haveUP ) || sa > 1 ) {
123    QMessageBox::warning(this, tr("Update Conflict"), tr("More than one stand-alone update has been selected! Please unselect all other updates and try again."));
124    return false;
125  }
126
127  return true;
128}
129
130void mainWin::doUpdates()
131{
132  // Set our UI elements
133  progressUpdate->setHidden(false);
134
135  curUpdate = -1;
136  curUpdateIndex = 0;
137  totUpdate = 0;
138
139  // Get the total number of updates
140  for (int z=0; z < listViewUpdates->count(); ++z)
141    if ( listViewUpdates->item(z)->checkState() == Qt::Checked )
142      totUpdate++;
143
144  // Start our loop processing updates
145  slotUpdateLoop();
146}
147
148void mainWin::slotUpdateLoop()
149{
150  QString tmp, tmp2, mUrl, PkgSet, Version, Arch;
151
152  // Check if the last update process finished
153  if ( curUpdate != -1 ) {
154    qDebug() << "Finished Update";
155    if ( uProc->exitStatus() != QProcess::NormalExit || uProc->exitCode() != 0)
156    {
157      // Read any remaining buffers
158      slotReadUpdateOutput();
159
160      // Warn user that this update failed
161      if ( lastError.isEmpty() )
162         QMessageBox::critical(this, tr("Update Failed!"), tr("Failed to install:") + listUpdates.at(curUpdate).at(0) + " " + tr("An unknown error occured!")); 
163      else
164         QMessageBox::critical(this, tr("Update Failed!"), tr("Failed to install:") + listUpdates.at(curUpdate).at(0) + " " + lastError); 
165    } else {
166      // If successfull system update download
167      if ( listUpdates.at(curUpdate).at(1) == "SYSUPDATE" )
168        QMessageBox::information(this, tr("Update Ready"), tr("Please reboot to start the update to PC-BSD version \"") + listUpdates.at(curUpdate).at(0) + "\". " + tr("This process may take a while, please do NOT interrupt the process.")); 
169    }
170
171    // Remove the lock file
172    if ( listUpdates.at(curUpdate).at(1) == "FBSDUPDATE" ) {
173        system("rm /tmp/.fbsdup-lock");
174    }
175
176    listViewUpdates->item(curUpdate)->setIcon(QIcon());
177    setWindowTitle(tr("Update Manager"));
178  }
179
180  // Start looking for the next update
181  for (int z=0; z < listViewUpdates->count(); ++z) {
182    if ( listViewUpdates->item(z)->checkState() == Qt::Checked && curUpdate < z ) 
183    {
184       
185      // Check for a freebsd-update lock file
186      if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
187        if ( QFile::exists("/tmp/.fbsdup-lock") ) {
188          QMessageBox::critical(this, tr("Update Failed!"), tr("Could not run freebsd-update, another process is already running!")); 
189          slotUpdateFinished();
190          return;
191        } 
192        // Lock out freebsd-update
193        system("touch /tmp/.fbsdup-lock");
194      }
195
196      curUpdate = z;
197      curUpdateIndex++;
198      progressUpdate->setHidden(false);
199      progressUpdate->setRange(0, 0);
200      tmp.setNum(curUpdateIndex);
201      tmp2.setNum(totUpdate);
202      setWindowTitle(tr("Updating:") + " " + listUpdates.at(z).at(0));
203
204      textLabel->setText(tr("Starting Update: %1 (%2 of %3)")
205                         .arg(listUpdates.at(z).at(0))
206                         .arg(tmp)
207                         .arg(tmp2));
208
209      // Get the icon
210      listViewUpdates->item(z)->setIcon(QIcon(":images/current-item.png"));
211
212      // Get the install tag
213      QString tag;
214      if ( listUpdates.at(z).at(1) == "SYSUPDATE" )
215        tag = listUpdates.at(z).at(4);
216      if ( listUpdates.at(z).at(1) == "PATCH" )
217        tag = listUpdates.at(z).at(3);
218
219      // Show tray that we are doing a download
220      QFile sysTrig( SYSTRIGGER );
221      if ( sysTrig.open( QIODevice::WriteOnly ) ) {
222        QTextStream streamTrig( &sysTrig );
223        streamTrig << "DOWNLOADING: ";
224      }
225
226      // Setup the upgrade process
227      uProc = new QProcess();
228      QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
229      env.insert("PCFETCHGUI", "YES");
230      uProc->setProcessEnvironment(env);
231      uProc->setProcessChannelMode(QProcess::MergedChannels);
232
233      // Connect the slots
234      connect( uProc, SIGNAL(readyReadStandardOutput()), this, SLOT(slotReadUpdateOutput()) );
235      connect( uProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotUpdateLoop()) );
236
237      // If doing FreeBSD Update run freebsd-update cmd
238      if ( wDir.isEmpty() ) {
239         if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
240           uProc->start("freebsd-update", QStringList() << "install"); 
241           system("touch /tmp/.fbsdup-reboot");
242         } else {
243           uProc->start("pc-updatemanager", QStringList() << "install" << tag ); 
244         }
245      } else {
246         // Doing a warden update in a chroot environment
247         if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
248           uProc->start("chroot", QStringList() << wDir << "freebsd-update" << "install"); 
249         } 
250      }
251      qDebug() << "Update started";
252      return;
253    }
254
255  }
256
257  // If we get here, no more updates to do
258  slotUpdateFinished();
259}
260
261void mainWin::slotReadUpdateOutput()
262{
263  QString line, cI, tI, tmp;
264  bool ok, ok2;
265  cI.setNum(curUpdateIndex);
266  tI.setNum(totUpdate);
267
268
269  while (uProc->canReadLine()) {
270    line = uProc->readLine().simplified();
271
272    if ( line.indexOf("FETCH:") == 0 ) {
273      tmp = line;
274      tmp = tmp.remove(0, tmp.lastIndexOf("/") + 1);
275      textLabel->setText(tr("Downloading: %1 (Update %2 of %3)")
276                         .arg(tmp)
277                         .arg(cI)
278                         .arg(tI));
279      continue;
280    }
281   
282    if ( line.indexOf("SIZE:") == 0 ) {
283      line.section(" ", 1,1).toInt(&ok);
284      line.section(" ", 3,3).toInt(&ok2);
285      if ( ok && ok2 ) {
286        progressUpdate->setRange(0, line.section(" ", 1,1).toInt(&ok));
287        progressUpdate->setValue(line.section(" ", 3,3).toInt(&ok));
288      }
289      continue;
290    }
291    if ( line.indexOf("FETCHDONE") == 0 ) {
292      progressUpdate->setRange(0, 0);
293      textLabel->setText(tr("Updating: %1 (%2 of %3)")
294                         .arg(listUpdates.at(curUpdate).at(0))
295                         .arg(cI)
296                         .arg(tI));
297      continue;
298    }
299
300    if ( line.indexOf("TOTALSTEPS:") == 0 ) {
301      line.section(" ", 1,1).toInt(&ok);
302      if ( ok )
303        progressUpdate->setRange(0, line.section(" ", 1, 1).toInt(&ok));
304      continue;
305    }
306    if ( line.indexOf("SETSTEPS:") == 0 ) {
307      line.section(" ", 1,1).toInt(&ok);
308      if ( ok )
309        progressUpdate->setValue(line.section(" ", 1, 1).toInt(&ok));
310      continue;
311    }
312    if ( line.indexOf("ERROR:") == 0 ) {
313       lastError = line;
314       continue; 
315    }
316    qDebug() << line;
317  }
318}
319
320void mainWin::slotUpdateFinished()
321{
322  qDebug() << "Updates all Finished";
323  QFile sysTrig( SYSTRIGGER );
324  if ( sysTrig.open( QIODevice::WriteOnly ) ) {
325    QTextStream streamTrig( &sysTrig );
326     streamTrig << "INSTALLFINISHED: ";
327  }
328  sysTrig.close();
329
330  progressUpdate->setHidden(true);
331  slotRescanUpdates();
332}
333
334void mainWin::slotInstallClicked()
335{
336  // Sanity check our install choices
337  if (!sanityCheck())
338    return;
339
340  // Start the installation
341  doUpdates();
342}
343
344void mainWin::slotSelectAllClicked()
345{
346  for (int z=0; z < listViewUpdates->count(); ++z) {
347    listViewUpdates->item(z)->setCheckState(checkAll->checkState());   
348  }
349
350  slotListClicked();
351}
352
353void mainWin::slotRescanUpdates()
354{
355  if ( doingUpdate )
356     return;
357  groupDetails->setVisible(false);
358  groupUpdates->setEnabled(false);
359  listUpdates.clear();
360
361  if ( QFile::exists("/tmp/.fbsdup-reboot") ) {
362     textLabel->setText(tr("Reboot required for update to finish!"));
363     return;
364  }
365
366  textLabel->setText(tr("Checking for updates... Please Wait..."));
367  slotReadUpdateData();
368  slotDisplayUpdates();
369  qDebug() << listUpdates;
370  //disable the "select all" checkbox if no updates available
371  if(listUpdates.isEmpty() ){
372    checkAll->setEnabled(false);
373  }
374  pushInstallUpdates->setEnabled(false); //disable the button until an update is selected
375  if ( ! doingUpdate )
376    groupUpdates->setEnabled(true);
377}
378
379void mainWin::slotDisplayUpdates()
380{
381  if ( ! doingUpdate )
382    groupUpdates->setEnabled(true);
383  listViewUpdates->clear();
384
385  // Check if the system has an upgrade available
386  if ( QFile::exists("/usr/local/tmp/update-stagedir/doupdate.sh") ) {
387    textLabel->setText(tr("A system upgrade is waiting to be installed. Please reboot to begin!"));
388    return;
389  }
390
391
392  // Any system updates?
393  if ( listUpdates.isEmpty() ) {
394    textLabel->setText(tr("Your system is fully updated!"));
395    groupUpdates->setTitle("");
396    groupUpdates->setEnabled(true);
397    return;
398  }
399
400  textLabel->setText(tr("System updates available!"));
401  groupUpdates->setTitle(tr("Available Updates"));
402
403  // Start parsing the updates and list whats available
404  for (int z=0; z < listUpdates.count(); ++z) {
405    if ( listUpdates.at(z).at(1) == "SYSUPDATE" ) {
406      QListWidgetItem *item = new QListWidgetItem(tr("System Upgrade: %1 (%2)")
407                                                  .arg(listUpdates.at(z).at(2))
408                                                  .arg(listUpdates.at(z).at(3)));
409      item->setToolTip(tr("PC-BSD Version:") + "<br>" + listUpdates.at(z).at(2) + "<hr>" + tr("This update must be installed by itself.") + "<br>" + tr("Creating a backup of your data first is recommended."));
410      item->setCheckState(Qt::Unchecked);
411      listViewUpdates->addItem(item);
412    }
413    if ( listUpdates.at(z).at(1) == "PATCH" ) {
414      QListWidgetItem *item = new QListWidgetItem(tr("Patch: %1 (%2)")
415                                                  .arg(listUpdates.at(z).at(0))
416                                                  .arg(listUpdates.at(z).at(2)));
417      item->setCheckState(Qt::Unchecked);
418      item->setToolTip(tr("This is a patch for your version of PC-BSD") + "<hr>" + tr("Patch Size:") + " " + listUpdates.at(z).at(4) + "MB<br>");
419      listViewUpdates->addItem(item);
420    }
421
422    if ( listUpdates.at(z).at(1) == "FBSDUPDATE" ) {
423      QString fileNameList;
424      for (int p=2; p < listUpdates.at(z).count(); p++)
425        fileNameList += listUpdates.at(z).at(p) + "<br>";
426
427      QListWidgetItem *item = new QListWidgetItem(tr("Base System Updates"));
428      item->setCheckState(Qt::Unchecked);
429      item->setToolTip(fileNameList);
430     
431      listViewUpdates->addItem(item);
432    }
433
434    if ( listUpdates.at(z).at(1) == "PACKAGE" ) {
435      QString pkgNameList;
436      for (int p=2; p < listUpdates.at(z).count(); p=p+5) {
437        if ( listUpdates.at(z).count() < p + 4 ) 
438          break;
439
440        pkgNameList += listUpdates.at(z).at((p)) + " " + listUpdates.at(z).at((p+1)) + " -> " + listUpdates.at(z).at((p+2)) + "<br>";
441      }
442      QListWidgetItem *item = new QListWidgetItem(tr("System Package Updates"));
443      item->setCheckState(Qt::Unchecked);
444      item->setToolTip(tr("The following package updates are available:") + "<hr>" + pkgNameList);
445     
446      listViewUpdates->addItem(item);
447    }
448  }
449}
450
451void mainWin::slotReadUpdateData()
452{
453  // If on the base system, check for PC-BSD updates
454  if ( wDir.isEmpty() )
455    checkPCUpdates();
456
457  // Check for FreeBSD Updates now
458  checkFBSDUpdates();
459
460}
461
462void mainWin::checkPCUpdates() {
463
464  QString line, tmp, name, type, version, date, tag, url, size, sa, rr;
465  QStringList up;
466
467  QProcess p;
468  p.start(QString("pc-updatemanager"), QStringList() << "check");
469  while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
470     QCoreApplication::processEvents();
471
472  while (p.canReadLine()) {
473    line = p.readLine().simplified();
474    if ( line.indexOf("NAME: ") == 0) {
475       name = line.replace("NAME: ", "");
476       continue;
477    }
478    if ( line.indexOf("TYPE: ") == 0) {
479       type = line.replace("TYPE: ", "");
480       continue;
481    }
482
483    if ( type == "SYSUPDATE" ) {
484      if ( line.indexOf("VERSION: ") == 0) {
485         version = line.replace("VERSION: ", "");
486         continue;
487      }
488      if ( line.indexOf("DATE: ") == 0) {
489         date = line.replace("DATE: ", "");
490         continue;
491      }
492      if ( line.indexOf("TAG: ") == 0) {
493         tag = line.replace("TAG: ", "");
494         continue;
495      }
496      if ( line.indexOf("DETAILS: ") == 0) {
497         url = line.replace("DETAILS: ", "");
498         continue;
499      }
500
501      if ( line.indexOf("To install:") == 0) {
502         up.clear();
503         up << name << type << version << date << tag << url;
504         listUpdates.append(up);
505         type=""; name="", version="", date="", tag="", url="";
506         continue;
507      }
508
509    }
510    if ( type == "PATCH" ) {
511      if ( line.indexOf("DATE: ") == 0) {
512         date = line.replace("DATE: ", "");
513         continue;
514      }
515      if ( line.indexOf("TAG: ") == 0) {
516         tag = line.replace("TAG: ", "");
517         continue;
518      }
519      if ( line.indexOf("SIZE: ") == 0) {
520         size = line.replace("SIZE: ", "");
521         continue;
522      }
523      if ( line.indexOf("STANDALONE: ") == 0) {
524         sa = line.replace("STANDALONE: ", "");
525         continue;
526      }
527      if ( line.indexOf("REQUIRESREBOOT: ") == 0) {
528         rr = line.replace("REQUIRESREBOOT: ", "");
529         continue;
530      }
531      if ( line.indexOf("DETAILS: ") == 0) {
532         url = line.replace("DETAILS: ", "");
533         continue;
534      }
535      if ( line.indexOf("To install:") == 0) {
536         // TODO add this update to list
537         up.clear();
538         up << name << type << date << tag << size << sa << rr << url;
539         listUpdates.append(up);
540         type=""; name="", date="", tag="", size="", sa="", rr="", url="";
541         continue;
542      }
543    }
544
545  }
546
547
548}
549
550void mainWin::checkFBSDUpdates() {
551  QString line, toPatchVer, tmp;
552  QStringList up, listDesc, listPkgs;
553
554  if ( QFile::exists("/tmp/.fbsdup-reboot") ) {
555     qDebug() << "Skipping update check - Waiting for reboot";
556     return;
557  }
558
559  if ( QFile::exists("/tmp/.fbsdup-lock") ) {
560     qDebug() << "Skipping update check - freebsd-update is running elsewhere";
561     return;
562  }
563
564  // Now check if there are freebsd-updates to install
565  QProcess f;
566  if ( wDir.isEmpty() )
567     f.start(QString("pc-fbsdupdatecheck"), QStringList() << "update" );
568  else {
569     QProcess::execute("cp /usr/local/bin/pc-fbsdupdatecheck " + wDir + "/tmp/.fbupdatechk");
570     QProcess::execute("chmod 755 " + wDir + "/tmp/.fbupdatechk");
571     f.start(QString("chroot"), QStringList() << wDir << "/tmp/.fbupdatechk" << "update" );
572  }
573  while(f.state() == QProcess::Starting || f.state() == QProcess::Running)
574     QCoreApplication::processEvents();
575
576  bool fUp = false;
577 
578  while (f.canReadLine()) {
579    line = f.readLine().simplified();
580    qDebug() << line;
581    if ( line.indexOf("The following files will be ") == 0) {
582       toPatchVer= line.remove(0, line.lastIndexOf(" "));
583       toPatchVer=toPatchVer.section("-", 2,2);
584       toPatchVer=toPatchVer.section(":", 0,0);
585       toPatchVer=toPatchVer.section("p", 1,1);
586       fUp = true;
587       listPkgs << " " << tr("The following files will be updated:");
588       continue;
589    }
590
591    if ( fUp )
592       listPkgs << line;
593  }
594
595  if ( ! wDir.isEmpty() )
596     QProcess::execute("rm " + wDir + "/tmp/.fbupdatechk");
597
598  // Are there freebsd updates to install?
599  if ( fUp ) {
600    QString mySysVer;
601    QString myPatchVer;
602
603    // Lets try and fetch the desc file
604    QProcess::execute("fetch -o /tmp/.fbsdupdesc http://fbsd-update.pcbsd.org/updates.desc");
605
606    // Get the current system ver
607    QProcess p;
608    p.start(QString("uname"), QStringList() << "-r");
609    while(p.state() == QProcess::Starting || p.state() == QProcess::Running)
610       QCoreApplication::processEvents();
611    tmp = p.readLine().simplified();
612    mySysVer = tmp;
613    myPatchVer = tmp;
614    mySysVer = mySysVer.section("-", 0, 1);
615    mySysVer = mySysVer.section("-", 0, 1);
616    myPatchVer = myPatchVer.section("-", 2, 2);
617    myPatchVer = myPatchVer.section(":", 0, 0);
618    myPatchVer = myPatchVer.section("p", 1, 1);
619
620    QFile file("/tmp/.fbsdupdesc");
621    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
622        listDesc << tr("Update Details:");
623      while (!file.atEnd()) {
624         line = file.readLine();
625         tmp = line;
626         if ( tmp.section(":::", 0, 0) != mySysVer ) 
627            continue;
628         if ( tmp.section(":::", 1, 1) <= myPatchVer )
629            continue;
630         if ( tmp.section(":::", 1, 1) > toPatchVer )
631            continue;
632         listDesc << tmp.section(":::", 2, 2);
633      }
634    }
635
636    up.clear();
637    up << "FreeBSD Security Updates" << "FBSDUPDATE";
638    up.append(listDesc + listPkgs);
639    listUpdates.append(up);
640  }
641
642}
643
644void mainWin::slotSingleInstance() {
645   this->hide();
646   this->showNormal();
647   this->activateWindow();
648   this->raise();
649}
650
651void mainWin::slotCloseClicked() {
652   close();
653}
654
Note: See TracBrowser for help on using the repository browser.