]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
Make undocking work
[bacula/bacula] / bacula / src / qt-console / restore / restoretree.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28  
29 /*
30  *   Version $Id$
31  *
32  *  Restore Class 
33  *
34  *   Kern Sibbald, February MMVII
35  *
36  */ 
37
38 #include "bat.h"
39 #include "restoretree.h"
40 #include "pages.h"
41
42 restoreTree::restoreTree()
43 {
44    setupUi(this);
45    m_name = tr("Version Browser");
46    pgInitialize();
47    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48    thisitem->setIcon(0, QIcon(QString::fromUtf8(":images/browse.png")));
49
50    m_closeable = true;
51    m_populated = false;
52
53    m_debugCnt = 0;
54    m_debugTrap = true;
55
56    QGridLayout *gridLayout = new QGridLayout(this);
57    gridLayout->setSpacing(6);
58    gridLayout->setMargin(9);
59    gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
60
61    m_splitter = new QSplitter(Qt::Vertical, this);
62    QScrollArea *area = new QScrollArea();
63    area->setObjectName(QString::fromUtf8("area"));
64    area->setWidget(widget);
65    area->setWidgetResizable(true);
66    m_splitter->addWidget(area);
67    m_splitter->addWidget(splitter);
68    splitter->setChildrenCollapsible(false);
69
70    gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
71
72    /* progress widgets */
73    prBar1->setVisible(false);
74    prBar2->setVisible(false);
75    prLabel1->setVisible(false);
76    prLabel2->setVisible(false);
77
78    /* Set Defaults for check and spin for limits */
79    limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
80    limitSpinBox->setValue(mainWin->m_recordLimitVal);
81    daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
82    daysSpinBox->setValue(mainWin->m_daysLimitVal);
83    readSettings();
84    m_nullFileNameId = -1;
85 }
86
87 restoreTree::~restoreTree()
88 {
89    writeSettings();
90 }
91
92 /*
93  * Called from the constructor to set up the page widgets and connections.
94  */
95 void restoreTree::setupPage()
96 {
97    connect(refreshButton, SIGNAL(pressed()), this, SLOT(refreshButtonPushed()));
98    connect(restoreButton, SIGNAL(pressed()), this, SLOT(restoreButtonPushed()));
99    connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(jobComboChanged(int)));
100    connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateRefresh()));
101    connect(clientCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateRefresh()));
102    connect(fileSetCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateRefresh()));
103    connect(limitCheckBox, SIGNAL(stateChanged(int)), this, SLOT(updateRefresh()));
104    connect(daysCheckBox, SIGNAL(stateChanged(int)), this, SLOT(updateRefresh()));
105    connect(daysSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateRefresh()));
106    connect(limitSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateRefresh()));
107    connect(directoryTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
108            this, SLOT(directoryCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
109    connect(directoryTree, SIGNAL(itemExpanded(QTreeWidgetItem *)),
110            this, SLOT(directoryItemExpanded(QTreeWidgetItem *)));
111    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
112            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
113    connect(fileTable, SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
114            this, SLOT(fileCurrentItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
115    connect(jobTable, SIGNAL(cellClicked(int, int)),
116            this, SLOT(jobTableCellClicked(int, int)));
117
118    QStringList titles = QStringList() << tr("Directories");
119    directoryTree->setHeaderLabels(titles);
120    clientCombo->addItems(m_console->client_list);
121    fileSetCombo->addItem(tr("Any"));
122    fileSetCombo->addItems(m_console->fileset_list);
123    jobCombo->addItem(tr("Any"));
124    jobCombo->addItems(m_console->job_list);
125
126    directoryTree->setContextMenuPolicy(Qt::ActionsContextMenu);
127 }
128
129 void restoreTree::updateRefresh()
130 {
131    if (mainWin->m_rtPopDirDebug) Pmsg2(000, "testing prev=\"%s\" current=\"%s\"\n", m_prevJobCombo.toUtf8().data(), jobCombo->currentText().toUtf8().data());
132    m_dropdownChanged = (m_prevJobCombo != jobCombo->currentText())
133                        || (m_prevClientCombo != clientCombo->currentText())
134                        || (m_prevFileSetCombo != fileSetCombo->currentText()
135                        || (m_prevLimitSpinBox != limitSpinBox->value())
136                        || (m_prevDaysSpinBox != daysSpinBox->value())
137                        || (m_prevLimitCheckState != limitCheckBox->checkState())
138                        || (m_prevDaysCheckState != daysCheckBox->checkState())
139    );
140    if (m_dropdownChanged) {
141       if (mainWin->m_rtPopDirDebug) Pmsg0(000, "In restoreTree::updateRefresh Is CHANGED\n");
142       refreshLabel->setText(tr("Refresh From Re-Select"));
143    } else {
144       if (mainWin->m_rtPopDirDebug) Pmsg0(000, "In restoreTree::updateRefresh Is not Changed\n");
145       refreshLabel->setText(tr("Refresh From JobChecks"));
146    }
147 }
148
149 /*
150  * When refresh button is pushed, perform a query getting the directories and
151  * use parseDirectory and addDirectory to populate the directory tree with items.
152  */
153 void restoreTree::populateDirectoryTree()
154 {
155    m_debugTrap = true;
156    m_debugCnt = 0;
157    m_slashTrap = false;
158    m_dirPaths.clear();
159    directoryTree->clear();
160    fileTable->clear();
161    fileTable->setRowCount(0);
162    fileTable->setColumnCount(0);
163    versionTable->clear();
164    versionTable->setRowCount(0);
165    versionTable->setColumnCount(0);
166    m_fileExceptionHash.clear();
167    m_fileExceptionMulti.clear();
168    m_versionExceptionHash.clear();
169    m_directoryIconStateHash.clear();
170
171    updateRefresh();
172    int taskcount = 3, ontask = 1;
173    if (m_dropdownChanged) taskcount += 1;
174    
175    /* Set progress bars and repaint */
176    prBar1->setVisible(true);
177    prBar1->setRange(0,taskcount);
178    prBar1->setValue(0);
179    prLabel1->setText(tr("Task %1 of %2").arg(ontask).arg(taskcount));
180    prLabel1->setVisible(true);
181    prBar2->setVisible(true);
182    prBar2->setRange(0,0);
183    prLabel2->setText(tr("Querying Database"));
184    prLabel2->setVisible(true);
185    repaint();
186
187    if (m_dropdownChanged) {
188       m_prevJobCombo = jobCombo->currentText();
189       m_prevClientCombo = clientCombo->currentText();
190       m_prevFileSetCombo = fileSetCombo->currentText();
191       m_prevLimitSpinBox = limitSpinBox->value();
192       m_prevDaysSpinBox = daysSpinBox->value();
193       m_prevLimitCheckState = limitCheckBox->checkState();
194       m_prevDaysCheckState = daysCheckBox->checkState();
195       updateRefresh();
196       prBar1->setValue(ontask++);
197       prLabel1->setText(tr("Task %1 of %2").arg(ontask).arg(taskcount));
198       prBar2->setValue(0);
199       prBar2->setRange(0,0);
200       prLabel2->setText(tr("Querying Jobs"));
201       repaint();
202       populateJobTable();
203    }
204    setJobsCheckedList();
205    if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating from checks in Job Table\n");
206
207    if (m_checkedJobs != "") {
208       /* First get the filenameid of where the nae is null.  These will be the directories
209        * This could be done in a subquery but postgres's query analyzer won't do the right
210        * thing like I want */
211       if (m_nullFileNameId == -1) {
212          QString cmd = "SELECT FilenameId FROM Filename WHERE name=''";
213          if (mainWin->m_sqlDebug)
214             Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
215          QStringList qres;
216          if (m_console->sql_cmd(cmd, qres)) {
217             if (qres.count()) {
218                QStringList fieldlist = qres[0].split("\t");
219                QString field = fieldlist[0];
220                bool ok;
221                int val = field.toInt(&ok, 10);
222                if (ok) m_nullFileNameId = val;
223             }
224          }
225       }
226       /* now create the query to get the list of paths */
227       QString cmd =
228          "SELECT DISTINCT Path.Path AS Path, File.PathId AS PathId"
229          " FROM File"
230          " INNER JOIN Path ON (File.PathId=Path.PathId)";
231       if (m_nullFileNameId != -1)
232          cmd += " WHERE File.FilenameId=" + QString("%1").arg(m_nullFileNameId);
233       else
234          cmd += " WHERE File.FilenameId IN (SELECT FilenameId FROM Filename WHERE Name='')";
235       cmd += " AND File.Jobid IN (" + m_checkedJobs + ")"
236          " ORDER BY Path";
237       if (mainWin->m_sqlDebug)
238          Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
239       prBar1->setValue(ontask++);
240       prLabel1->setText(tr("Task %1 of %2").arg(ontask).arg(taskcount));
241       prBar2->setValue(0);
242       prBar2->setRange(0,0);
243       prLabel2->setText(tr("Querying for Directories"));
244       repaint();
245       QStringList results;
246       m_directoryPathIdHash.clear();
247       bool querydone = false;
248       if (m_console->sql_cmd(cmd, results)) {
249          if (!querydone) {
250             querydone = true;
251             prLabel2->setText(tr("Processing Directories"));
252             prBar2->setRange(0,results.count());
253             repaint();
254          }
255          if (mainWin->m_miscDebug)
256             Pmsg1(000, "Done with query %i results\n", results.count());
257          QStringList fieldlist;
258          foreach(QString resultline, results) {
259             /* Update progress bar periodically */
260             if ((++m_debugCnt && 0x3FF) == 0) {
261                prBar2->setValue(m_debugCnt);
262             }
263             fieldlist = resultline.split("\t");
264             int fieldcnt = 0;
265             QString field;
266             /* Iterate through fields in the record */
267             foreach (field, fieldlist) {
268                if (fieldcnt == 0 ) {
269                   parseDirectory(field);
270                } else if (fieldcnt == 1) {
271                   bool ok;
272                   int pathid = field.toInt(&ok, 10);
273                   if (ok)
274                      m_directoryPathIdHash.insert(fieldlist[0], pathid);
275                }
276                fieldcnt += 1;
277             }
278          }
279       }
280    } else {
281      QMessageBox::warning(this, "Bat",
282         tr("No jobs were selected in the job query !!!.\n"
283       "Press OK to continue"),
284       QMessageBox::Ok );
285    }
286    prBar1->setVisible(false);
287    prBar2->setVisible(false);
288    prLabel1->setVisible(false);
289    prLabel2->setVisible(false);
290 }
291
292 /*
293  *  Function to set m_checkedJobs from the jobs that are checked in the table
294  *  of jobs
295  */     
296 void restoreTree::setJobsCheckedList()
297 {
298    m_JobsCheckedList = "";
299    bool first = true;
300    /* Update the items in the version table */
301    int cnt = jobTable->rowCount();
302    for (int row=0; row<cnt; row++) {
303       QTableWidgetItem* jobItem = jobTable->item(row, 0);
304       if (jobItem->checkState() == Qt::Checked) {
305          if (!first)
306             m_JobsCheckedList += ",";
307          m_JobsCheckedList += jobItem->text();
308          first = false;
309          jobItem->setBackground(Qt::green);
310       } else {
311          if (jobItem->flags())
312             jobItem->setBackground(Qt::gray);
313          else
314             jobItem->setBackground(Qt::darkYellow);
315       }
316    }
317    m_checkedJobs = m_JobsCheckedList;
318 }
319
320 /*
321  * Function to parse a directory into all possible subdirectories, then add to
322  * The tree.
323  */
324 void restoreTree::parseDirectory(QString &dir_in)
325 {
326    /* m_debugTrap is to only print debugs for a few occurennces of calling parseDirectory
327     * instead of printing out what could potentially a whole bunch */
328    if (m_debugCnt > 2)
329       m_debugTrap = false;
330    /* Truncate everything after the last / */
331    if (dir_in.right(1) != "/") {
332       dir_in.truncate(dir_in.lastIndexOf("/") + 1);
333    }
334    if ((mainWin->m_miscDebug) && (m_debugTrap))
335       Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
336
337    /* split and add if not in yet */
338    QString direct, path;
339    int index;
340    bool done = false;
341    QStringList pathAfter, dirAfter;
342    /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/ 
343     * if not added into tree, then try /etc/ and somedir/ if not added, then try
344     * / and etc/ .  That should succeed, then add the ones that failed in reverse */
345    while (((index = dir_in.lastIndexOf("/", -2)) != -1) && (!done)) {
346       direct = path = dir_in;
347       path.replace(index+1, dir_in.length()-index-1,"");
348       direct.replace(0, index+1, "");
349       if ((mainWin->m_miscDebug) && (m_debugTrap)) {
350          QString msg = QString("length = \"%1\" index = \"%2\" Adding \"%3\" \"%4\"\n")
351                     .arg(dir_in.length()).arg(index).arg(path).arg(direct);
352          Pmsg0(000, msg.toUtf8().data());
353       }
354       if (addDirectory(path, direct)) { done = true; }
355       else {
356          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
357             Pmsg0(000, "Saving for later\n");
358          }
359          pathAfter.prepend(path);
360          dirAfter.prepend(direct);
361       }
362       dir_in = path;
363    }
364
365    for (int k=0; k<pathAfter.count(); k++) {
366       if (addDirectory(pathAfter[k], dirAfter[k])) {
367          if ((mainWin->m_miscDebug) && (m_debugTrap))
368             Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
369       } else {
370          if ((mainWin->m_miscDebug) && (m_debugTrap))
371             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
372       }
373    }
374 }
375
376
377 /*
378  * Function called from fill directory when a directory is found to see if this
379  * directory exists in the directory pane and then add it to the directory pane
380  */
381 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
382 {
383    QString newdir = newdirr;
384    QString fullPath = m_cwd + newdirr;
385    bool ok = true, added = false;
386
387    if ((mainWin->m_miscDebug) && (m_debugTrap)) {
388       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
389                     .arg(m_cwd)
390                     .arg(newdir);
391       Pmsg0(000, msg.toUtf8().data());
392    }
393
394    if (!m_slashTrap) {
395       /* add unix '/' directory first */
396       if (m_dirPaths.empty() && !isWin32Path(fullPath)) {
397          m_slashTrap = true;
398          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
399          QString text("/");
400          item->setText(0, text.toUtf8().data());
401          item->setData(0, Qt::UserRole, QVariant(text));
402          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
403          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
404          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
405             Pmsg1(000, "Pre Inserting %s\n", text.toUtf8().data());
406          }
407          m_dirPaths.insert(text, item);
408       }
409       /* no need to check for windows drive if unix */
410       if (isWin32Path(m_cwd)) {
411          if (!m_dirPaths.contains(m_cwd)) {
412             if (m_cwd.count('/') > 1) { return false; }
413             /* this is a windows drive add the base widget */
414             QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
415             item->setText(0, m_cwd);
416             item->setData(0, Qt::UserRole, QVariant(fullPath));
417             item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
418             item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
419             if ((mainWin->m_miscDebug) && (m_debugTrap)) {
420                Pmsg0(000, "Added Base \"letter\":/\n");
421             }
422             m_dirPaths.insert(m_cwd, item);
423          }
424       }
425    }
426  
427    /* is it already existent ?? */
428    if (!m_dirPaths.contains(fullPath)) {
429       QTreeWidgetItem *item = NULL;
430       QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
431       if (parent) {
432          /* new directories to add */
433          item = new QTreeWidgetItem(parent);
434          item->setText(0, newdir.toUtf8().data());
435          item->setData(0, Qt::UserRole, QVariant(fullPath));
436          item->setCheckState(0, Qt::Unchecked);
437          /* Store the current state of the check status in column 1, which at
438           * this point has no text*/
439          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
440       } else {
441          ok = false;
442          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
443             QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
444                  .arg(m_cwd)
445                  .arg(newdir);
446             Pmsg0(000, msg.toUtf8().data());
447          }
448       }
449       /* insert into hash */
450       if (ok) {
451          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
452             Pmsg1(000, "Inserting %s\n", fullPath.toUtf8().data());
453          }
454          m_dirPaths.insert(fullPath, item);
455          added = true;
456       }
457    }
458    return added;
459 }
460
461 /*
462  * Virtual function which is called when this page is visible on the stack
463  */
464 void restoreTree::currentStackItem()
465 {
466    if(!m_populated) {
467       setupPage();
468       m_populated = true;
469    }
470 }
471
472 /*
473  * Populate the tree when refresh button pushed.
474  */
475 void restoreTree::refreshButtonPushed()
476 {
477    populateDirectoryTree();
478 }
479
480 /*
481  * Set the values of non-job combo boxes to the job defaults
482  */
483 void restoreTree::jobComboChanged(int)
484 {
485    if (jobCombo->currentText() == tr("Any")) {
486       fileSetCombo->setCurrentIndex(fileSetCombo->findText(tr("Any"), Qt::MatchExactly));
487       return;
488    }
489    job_defaults job_defs;
490
491    //(void)index;
492    job_defs.job_name = jobCombo->currentText();
493    if (m_console->get_job_defaults(job_defs)) {
494       fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
495       clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
496    }
497 }
498
499 /*
500  * Function to populate the file list table
501  */
502 void restoreTree::directoryCurrentItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
503 {
504    if (item == NULL)
505       return;
506
507    fileTable->clear();
508    /* Also clear the version table here */
509    versionTable->clear();
510    versionFileLabel->setText("");
511    versionTable->setRowCount(0);
512    versionTable->setColumnCount(0);
513
514    QStringList headerlist = (QStringList() << tr("File Name") << tr("Filename Id"));
515    fileTable->setColumnCount(headerlist.size());
516    fileTable->setHorizontalHeaderLabels(headerlist);
517    fileTable->setRowCount(0);
518    
519    m_fileCheckStateList.clear();
520    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
521            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
522    QBrush blackBrush(Qt::black);
523    QString directory = item->data(0, Qt::UserRole).toString();
524    directoryLabel->setText(tr("Present Working Directory: %1").arg(directory));
525    int pathid = m_directoryPathIdHash.value(directory, -1);
526    if (pathid != -1) {
527       QString cmd =
528          "SELECT DISTINCT Filename.Name AS FileName, Filename.FilenameId AS FilenameId"
529          " FROM File "
530          " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
531          " WHERE File.PathId=" + QString("%1").arg(pathid) +
532          " AND File.Jobid IN (" + m_checkedJobs + ")"
533          " AND Filename.Name!=''"
534          " ORDER BY FileName";
535
536       if (mainWin->m_sqlDebug) {
537          Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
538       }
539       QStringList results;
540       if (m_console->sql_cmd(cmd, results)) {
541       
542          QTableWidgetItem* tableItem;
543          QString field;
544          QStringList fieldlist;
545          fileTable->setRowCount(results.size());
546    
547          int row = 0;
548          /* Iterate through the record returned from the query */
549          foreach (QString resultline, results) {
550             /* Iterate through fields in the record */
551             int column = 0;
552             fieldlist = resultline.split("\t");
553             foreach (field, fieldlist) {
554                field = field.trimmed();  /* strip leading & trailing spaces */
555                tableItem = new QTableWidgetItem(field, 1);
556                /* Possible flags are Qt::ItemFlags flag = Qt::ItemIsSelectable | Qt::ItemIsEditablex
557                 *  | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable 
558                 *  | Qt::ItemIsEnabled | Qt::ItemIsTristate; */
559                tableItem->setForeground(blackBrush);
560                /* Just in case a column ever gets added */
561                if (column == 0) {
562                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
563                   tableItem->setFlags(flag);
564                   tableItem->setData(Qt::UserRole, QVariant(directory));
565                   fileTable->setItem(row, column, tableItem);
566                   m_fileCheckStateList.append(Qt::Unchecked);
567                   tableItem->setCheckState(Qt::Unchecked);
568                } else if (column == 1) {
569                   Qt::ItemFlags flag = Qt::ItemIsEnabled;
570                   tableItem->setFlags(flag);
571                   bool ok;
572                   int filenameid = field.toInt(&ok, 10);
573                   if (!ok) filenameid = -1;
574                   tableItem->setData(Qt::UserRole, QVariant(filenameid));
575                   fileTable->setItem(row, column, tableItem);
576                }
577                column++;
578             }
579             row++;
580          }
581          fileTable->setRowCount(row);
582       }
583       fileTable->resizeColumnsToContents();
584       fileTable->resizeRowsToContents();
585       fileTable->verticalHeader()->hide();
586       fileTable->hideColumn(1);
587       if (mainWin->m_rtDirCurICDebug) Pmsg0(000, "will update file table checks\n");
588       updateFileTableChecks();
589    } else if (mainWin->m_sqlDebug)
590       Pmsg1(000, "did not perform query, pathid=%i not found\n", pathid);
591    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
592           this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
593 }
594
595 /*
596  * Function to populate the version table
597  */
598 void restoreTree::fileCurrentItemChanged(QTableWidgetItem *currentFileTableItem, QTableWidgetItem *)
599 {
600    if (currentFileTableItem == NULL)
601       return;
602
603    int currentRow = fileTable->row(currentFileTableItem);
604    QTableWidgetItem *fileTableItem = fileTable->item(currentRow, 0);
605    QTableWidgetItem *fileNameIdTableItem = fileTable->item(currentRow, 1);
606    int fileNameId = fileNameIdTableItem->data(Qt::UserRole).toInt();
607
608    m_versionCheckStateList.clear();
609    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
610            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
611
612    QString file = fileTableItem->text();
613    versionFileLabel->setText(file);
614    QString directory = fileTableItem->data(Qt::UserRole).toString();
615
616    QBrush blackBrush(Qt::black);
617
618    QStringList headerlist = (QStringList() 
619       << tr("Job Id") << tr("Type") << tr("End Time") << tr("Hash") << tr("FileId") << tr("Job Type") << tr("First Volume"));
620    versionTable->clear();
621    versionTable->setColumnCount(headerlist.size());
622    versionTable->setHorizontalHeaderLabels(headerlist);
623    versionTable->setRowCount(0);
624    
625    int pathid = m_directoryPathIdHash.value(directory, -1);
626    if ((pathid != -1) && (fileNameId != -1)) {
627       QString cmd = 
628          "SELECT Job.JobId AS JobId, Job.Level AS Type,"
629            " Job.EndTime AS EndTime, File.MD5 AS MD5,"
630            " File.FileId AS FileId, Job.Type AS JobType,"
631            " (SELECT Media.VolumeName FROM JobMedia JOIN Media ON JobMedia.MediaId=Media.MediaId WHERE JobMedia.JobId=Job.JobId ORDER BY JobMediaId LIMIT 1) AS FirstVolume"
632          " FROM File"
633          " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
634          " INNER JOIN Path ON (Path.PathId=File.PathId)"
635          " INNER JOIN Job ON (File.JobId=Job.JobId)"
636          " WHERE Path.PathId=" + QString("%1").arg(pathid) +
637          //" AND Filename.Name='" + file + "'"
638          " AND Filename.FilenameId=" + QString("%1").arg(fileNameId) +
639          " AND Job.Jobid IN (" + m_checkedJobs + ")"
640          " ORDER BY Job.EndTime DESC";
641    
642       if (mainWin->m_sqlDebug)
643          Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
644       QStringList results;
645       if (m_console->sql_cmd(cmd, results)) {
646       
647          QTableWidgetItem* tableItem;
648          QString field;
649          QStringList fieldlist;
650          versionTable->setRowCount(results.size());
651    
652          int row = 0;
653          /* Iterate through the record returned from the query */
654          foreach (QString resultline, results) {
655             fieldlist = resultline.split("\t");
656             int column = 0;
657             /* remove directory */
658             if (fieldlist[0].trimmed() != "") {
659                /* Iterate through fields in the record */
660                foreach (field, fieldlist) {
661                   field = field.trimmed();  /* strip leading & trailing spaces */
662                   if (column == 5 ) {
663                      QByteArray jtype(field.trimmed().toAscii());
664                      if (jtype.size()) {
665                         field = job_type_to_str(jtype[0]);
666                      }
667                   }
668                   tableItem = new QTableWidgetItem(field, 1);
669                   tableItem->setFlags(0);
670                   tableItem->setForeground(blackBrush);
671                   tableItem->setData(Qt::UserRole, QVariant(directory));
672                   versionTable->setItem(row, column, tableItem);
673    
674                   if (column == 0) {
675                      Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
676                      tableItem->setFlags(flag);
677                      m_versionCheckStateList.append(Qt::Unchecked);
678                      tableItem->setCheckState(Qt::Unchecked);
679                   }
680                   column++;
681                }
682                row++;
683             }
684          }
685       }
686       versionTable->resizeColumnsToContents();
687       versionTable->resizeRowsToContents();
688       versionTable->verticalHeader()->hide();
689       updateVersionTableChecks();
690    } else {
691       if (mainWin->m_sqlDebug)
692          Pmsg2(000, "not querying : pathid=%i fileNameId=%i\n", pathid, fileNameId);
693    }
694    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
695            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
696 }
697
698 /*
699  * Save user settings associated with this page
700  */
701 void restoreTree::writeSettings()
702 {
703    QSettings settings(m_console->m_dir->name(), "bat");
704    settings.beginGroup(m_groupText);
705    settings.setValue(m_splitText1, m_splitter->saveState());
706    settings.setValue(m_splitText2, splitter->saveState());
707    settings.endGroup();
708 }
709
710 /*
711  * Read and restore user settings associated with this page
712  */
713 void restoreTree::readSettings()
714 {
715    m_groupText = tr("RestoreTreePage");
716    m_splitText1 = "splitterSizes1_3";
717    m_splitText2 = "splitterSizes2_3";
718    QSettings settings(m_console->m_dir->name(), "bat");
719    settings.beginGroup(m_groupText);
720    if (settings.contains(m_splitText1)) { m_splitter->restoreState(settings.value(m_splitText1).toByteArray()); }
721    if (settings.contains(m_splitText2)) { splitter->restoreState(settings.value(m_splitText2).toByteArray()); }
722    settings.endGroup();
723 }
724
725 /*
726  * This is a funcion to accomplish the one thing I struggled to figure out what
727  * was taking so long.  It add the icons, but after the tree is made.  Seemed to
728  * work fast after changing from png to png file for graphic.
729  */
730 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
731 {
732    int childCount = item->childCount();
733    for (int i=0; i<childCount; i++) {
734       QTreeWidgetItem *child = item->child(i);
735       if (child->icon(0).isNull())
736          child->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
737    }
738 }
739
740 /*
741  * Show what jobs meet the criteria and are being used to
742  * populate the directory tree and file and version tables.
743  */
744 void restoreTree::populateJobTable()
745 {
746    QBrush blackBrush(Qt::black);
747
748    if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating the Job Table\n");
749    QStringList headerlist = (QStringList() 
750       << tr("Job Id") << tr("End Time") << tr("Level") << tr("Type")
751       << tr("Name") << tr("Purged") << tr("TU") << tr("TD"));
752    m_toggleUpIndex = headerlist.indexOf(tr("TU"));
753    m_toggleDownIndex = headerlist.indexOf(tr("TD"));
754    int purgedIndex = headerlist.indexOf(tr("Purged"));
755    int typeIndex = headerlist.indexOf(tr("Type"));
756    jobTable->clear();
757    jobTable->setColumnCount(headerlist.size());
758    jobTable->setHorizontalHeaderLabels(headerlist);
759    QString jobQuery =
760       "SELECT Job.Jobid AS Id, Job.EndTime AS EndTime,"
761       " Job.Level AS Level, Job.Type AS Type,"
762       " Job.Name AS JobName, Job.purgedfiles AS Purged"
763       " FROM Job"
764       /* INNER JOIN FileSet eliminates all restore jobs */
765       " INNER JOIN Client ON (Job.ClientId=Client.ClientId)"
766       " INNER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)"
767       " WHERE"
768       " Client.Name='" + clientCombo->currentText() + "'";
769    if ((jobCombo->currentIndex() >= 0) && (jobCombo->currentText() != tr("Any"))) {
770       jobQuery += " AND Job.name = '" + jobCombo->currentText() + "'";
771    }
772    if ((fileSetCombo->currentIndex() >= 0) && (fileSetCombo->currentText() != tr("Any"))) {
773       jobQuery += " AND FileSet.FileSet='" + fileSetCombo->currentText() + "'";
774    }
775    /* If Limit check box For limit by days is checked  */
776    if (daysCheckBox->checkState() == Qt::Checked) {
777       QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
778       QString since = stamp.toString(Qt::ISODate);
779       jobQuery += " AND Job.Starttime>'" + since + "'";
780    }
781    //jobQuery += " AND Job.purgedfiles=0";
782    jobQuery += " ORDER BY Job.EndTime DESC";
783    /* If Limit check box for limit records returned is checked  */
784    if (limitCheckBox->checkState() == Qt::Checked) {
785       QString limit;
786       limit.setNum(limitSpinBox->value());
787       jobQuery += " LIMIT " + limit;
788    }
789    if (mainWin->m_sqlDebug)
790       Pmsg1(000, "Query cmd : %s\n", jobQuery.toUtf8().data());
791
792    QStringList results;
793    if (m_console->sql_cmd(jobQuery, results)) {
794    
795       QTableWidgetItem* tableItem;
796       QString field;
797       QStringList fieldlist;
798       jobTable->setRowCount(results.size());
799
800       int row = 0;
801       /* Iterate through the record returned from the query */
802       foreach (QString resultline, results) {
803          fieldlist = resultline.split("\t");
804          int column = 0;
805          /* remove directory */
806          if (fieldlist[0].trimmed() != "") {
807             /* Iterate through fields in the record */
808             foreach (field, fieldlist) {
809                field = field.trimmed();  /* strip leading & trailing spaces */
810                if (field != "") {
811                   if (column == typeIndex) {
812                      QByteArray jtype(field.trimmed().toAscii());
813                      if (jtype.size()) {
814                         field = job_type_to_str(jtype[0]);
815                      }
816                   }
817                   tableItem = new QTableWidgetItem(field, 1);
818                   tableItem->setFlags(0);
819                   tableItem->setForeground(blackBrush);
820                   jobTable->setItem(row, column, tableItem);
821                   if (column == 0) {
822                      bool ok;
823                      int purged = fieldlist[purgedIndex].toInt(&ok, 10); 
824                      if (!((ok) && (purged == 1))) {
825                         Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
826                         tableItem->setFlags(flag);
827                         tableItem->setCheckState(Qt::Checked);
828                         tableItem->setBackground(Qt::green);
829                      } else {
830                         tableItem->setFlags(0);
831                         tableItem->setCheckState(Qt::Unchecked);
832                      }
833                   }
834                   column++;
835                }
836             }
837             tableItem = new QTableWidgetItem(QIcon(QString::fromUtf8(":images/go-up.png")), "", 1);
838             tableItem->setFlags(0);
839             tableItem->setForeground(blackBrush);
840             jobTable->setItem(row, column, tableItem);
841             column++;
842             tableItem = new QTableWidgetItem(QIcon(QString::fromUtf8(":images/go-down.png")), "", 1);
843             tableItem->setFlags(0);
844             tableItem->setForeground(blackBrush);
845             jobTable->setItem(row, column, tableItem);
846             row++;
847          }
848       }
849    }
850    jobTable->resizeColumnsToContents();
851    jobTable->resizeRowsToContents();
852    jobTable->verticalHeader()->hide();
853    jobTable->hideColumn(purgedIndex);
854 }
855
856 void restoreTree::jobTableCellClicked(int row, int column)
857 {
858    if (column == m_toggleUpIndex){
859       int cnt;
860       for (cnt=0; cnt<row+1; cnt++) {
861          QTableWidgetItem *item = jobTable->item(cnt, 0);
862          if (item->flags()) {
863             Qt::CheckState state = item->checkState();
864             if (state == Qt::Checked)
865                item->setCheckState(Qt::Unchecked);
866             else if (state == Qt::Unchecked)
867                item->setCheckState(Qt::Checked);
868          }
869       }
870    }
871    if (column == m_toggleDownIndex){
872       int cnt, max = jobTable->rowCount();
873       for (cnt=row; cnt<max; cnt++) {
874          QTableWidgetItem *item = jobTable->item(cnt, 0);
875          if (item->flags()) {
876             Qt::CheckState state = item->checkState();
877             if (state == Qt::Checked)
878                item->setCheckState(Qt::Unchecked);
879             else if (state == Qt::Unchecked)
880                item->setCheckState(Qt::Checked);
881          }
882       }
883    }
884 }
885
886 /*
887  * When a directory item is "changed" check the state of the checkable item
888  * to see if it is different than what it was which is stored in Qt::UserRole
889  * of the 2nd column, column 1, of the tree widget.
890  */
891 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, int /*column*/)
892 {
893    Qt::CheckState prevState = (Qt::CheckState)item->data(1, Qt::UserRole).toInt();
894    Qt::CheckState curState = item->checkState(0);
895    QTreeWidgetItem* parent = item->parent();
896    Qt::CheckState parState;
897    if (parent) parState = parent->checkState(0);
898    else parState = (Qt::CheckState)3;
899    if (mainWin->m_rtDirICDebug) {
900       QString msg = QString("directory item OBJECT has changed prev=%1 cur=%2 par=%3 dir=%4\n")
901          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
902       Pmsg1(000, "%s", msg.toUtf8().data()); }
903    /* I only care when the check state changes */
904    if (prevState == curState) {
905       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning Early\n");
906       return;
907    }
908
909    if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (parState != Qt::Unchecked)) {
910       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::PartiallyChecked\n");
911       directoryTreeDisconnectedSet(item, Qt::PartiallyChecked);
912       curState = Qt::PartiallyChecked;
913    }
914    if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked)) {
915       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::Unchecked\n");
916       directoryTreeDisconnectedSet(item, Qt::Unchecked);
917       curState = Qt::Unchecked;
918    }
919    if (mainWin->m_rtDirICDebug) {
920       QString msg = QString("directory item CHECKSTATE has changed prev=%1 cur=%2 par=%3 dir=%4\n")
921          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
922       Pmsg1(000, "%s", msg.toUtf8().data()); }
923
924    item->setData(1, Qt::UserRole, QVariant(curState));
925    Qt::CheckState childState = curState;
926    if (childState == Qt::Checked)
927       childState = Qt::PartiallyChecked;
928    setCheckofChildren(item, childState);
929
930    /* Remove items from the exception lists.  The multi exception list is my index
931     * of what exceptions can be removed when the directory is known*/
932    QString directory = item->data(0, Qt::UserRole).toString();
933    QStringList fullPathList = m_fileExceptionMulti.values(directory);
934    int fullPathListCount = fullPathList.count();
935    if ((mainWin->m_rtDirICDebug) && fullPathListCount) Pmsg2(000, "Will attempt to remove file exceptions for %s count %i\n", directory.toUtf8().data(), fullPathListCount);
936    foreach (QString fullPath, fullPathList) {
937       /* If there is no value in the hash for the key fullPath a value of 3 will be returned
938        * which will match no Qt::xxx values */
939       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
940       if (mainWin->m_rtDirICDebug) Pmsg2(000, "hashState=%i childState=%i\n", hashState, childState);
941       if (hashState == Qt::Unchecked) {
942          fileExceptionRemove(fullPath, directory);
943          m_versionExceptionHash.remove(fullPath);
944          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal A\n");
945       }
946       if (hashState == Qt::Checked) {
947          fileExceptionRemove(fullPath, directory);
948          m_versionExceptionHash.remove(fullPath);
949          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal B\n");
950       }
951    }
952
953    if (item == directoryTree->currentItem()) {
954       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Will attempt to update File Table Checks\n");
955       updateFileTableChecks();
956       versionTable->clear();
957       versionTable->setRowCount(0);
958       versionTable->setColumnCount(0);
959    }
960    if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning At End\n");
961 }
962
963 /*
964  * When a directory item check state is changed, this function iterates through
965  * all subdirectories and sets all to the passed state, which is either partially
966  * checked or unchecked.
967  */
968 void restoreTree::setCheckofChildren(QTreeWidgetItem *item, Qt::CheckState state)
969 {
970    int childCount;
971    childCount = item->childCount();
972    for (int i=0; i<childCount; i++) {
973       QTreeWidgetItem *child = item->child(i);
974       child->setData(1, Qt::UserRole, QVariant(state));
975       child->setCheckState(0, state);
976       setCheckofChildren(child, state);
977    }
978 }
979
980 /*
981  * When a File Table Item is "changed" check to see if the state of the checkable
982  * item has changed which is stored in m_fileCheckStateList
983  * If changed store in a hash m_fileExceptionHash that whether this file should be
984  * restored or not.
985  * Called as a slot, connected after populated (after directory current changed called)
986  */
987 void restoreTree::fileTableItemChanged(QTableWidgetItem *item)
988 {
989    /* get the previous and current check states */
990    int row = fileTable->row(item);
991    Qt::CheckState prevState;
992    /* prevent a segfault */
993    prevState = m_fileCheckStateList[row];
994    Qt::CheckState curState = item->checkState();
995
996    /* deterimine the default state from the state of the directory */
997    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
998    Qt::CheckState dirState = (Qt::CheckState)dirTreeItem->data(1, Qt::UserRole).toInt();
999    Qt::CheckState defState = Qt::PartiallyChecked;
1000    if (dirState == Qt::Unchecked) defState = Qt::Unchecked;
1001
1002    /* determine if it is already in the m_fileExceptionHash */
1003    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
1004    QString file = item->text();
1005    QString fullPath = directory + file;
1006    Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1007    int verJobNum = m_versionExceptionHash.value(fullPath, 0);
1008
1009    if (mainWin->m_rtFileTabICDebug) {
1010       QString msg = QString("filerow=%1 prev=%2 cur=%3 def=%4 hash=%5 dir=%6 verJobNum=%7\n")
1011          .arg(row).arg(prevState).arg(curState).arg(defState).arg(hashState).arg(dirState).arg(verJobNum);
1012       Pmsg1(000, "%s", msg.toUtf8().data()); }
1013
1014    /* Remove the hash if currently checked previously unchecked and directory is checked or partial */
1015    if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (dirState == Qt::Unchecked)) {
1016       /* it can behave as defaulted so current of unchecked is fine */
1017       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
1018       fileExceptionRemove(fullPath, directory);
1019       m_versionExceptionHash.remove(fullPath);
1020    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
1021       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionInsert here\n");
1022       fileExceptionInsert(fullPath, directory, Qt::Unchecked);
1023    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0) && (defState == Qt::PartiallyChecked)) {
1024       /* filerow=2 prev=0 cur=2 def=1 hash=0 dir=2 verJobNum=0 */
1025       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove here\n");
1026       fileExceptionRemove(fullPath, directory);
1027    } else if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (defState == Qt::PartiallyChecked) && (verJobNum != 0) && (hashState == Qt::Checked)) {
1028       /* Check dir, check version, attempt uncheck in file
1029        * filerow=4 prev=2 cur=0 def=1 hash=2 dir=2 verJobNum=53 */
1030       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
1031       fileExceptionRemove(fullPath, directory);
1032       m_versionExceptionHash.remove(fullPath);
1033    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
1034       /* filerow=0 prev=0 cur=2 def=1 hash=0 dirState=2 verJobNum */
1035       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will Not remove here\n");
1036    } else if (prevState != curState) {
1037       if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  THE STATE OF THE Check has changed, Setting StateList[%i] to %i\n", row, curState);
1038       /* A user did not set the check state to Partially checked, ignore if so */
1039       if (curState != Qt::PartiallyChecked) {
1040          if ((defState == Qt::Unchecked) && (prevState == Qt::PartiallyChecked) && (curState == Qt::Unchecked)) {
1041             if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "  got here\n");
1042          } else {
1043             if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
1044             fileExceptionInsert(fullPath, directory, curState);
1045          }
1046       } else {
1047          if (mainWin->m_rtFileTabICDebug) Pmsg1(000, "Removing version hash for %s\n", fullPath.toUtf8().data());
1048          /* programattically been changed back to a default state of Qt::PartiallyChecked remove the version hash here */
1049          m_versionExceptionHash.remove(fullPath);
1050       }
1051    }
1052
1053    updateFileTableChecks();
1054    updateVersionTableChecks();
1055 }
1056
1057 /*
1058  * function to insert keys and values to both m_fileExceptionHash and m_fileExceptionMulti
1059  */
1060 void restoreTree::fileExceptionInsert(QString &fullPath, QString &direcotry, Qt::CheckState state)
1061 {
1062    m_fileExceptionHash.insert(fullPath, state);
1063    m_fileExceptionMulti.insert(direcotry, fullPath);
1064    directoryIconStateInsert(fullPath, state);
1065 }
1066
1067 /*
1068  * function to remove keys from both m_fileExceptionHash and m_fileExceptionMulti
1069  */
1070 void restoreTree::fileExceptionRemove(QString &fullPath, QString &directory)
1071 {
1072    m_fileExceptionHash.remove(fullPath);
1073    /* pull the list of values in the multi */
1074    QStringList fullPathList = m_fileExceptionMulti.values(directory);
1075    /* get the index of the fullpath to remove */
1076    int index = fullPathList.indexOf(fullPath);
1077    if (index != -1) {
1078       /* remove the desired item in the list */
1079       fullPathList.removeAt(index);
1080       /* remove the entire list from the multi */
1081       m_fileExceptionMulti.remove(directory);
1082       /* readd the remaining */
1083       foreach (QString fp, fullPathList) {
1084          m_fileExceptionMulti.insert(directory, fp);
1085       }
1086    }
1087    directoryIconStateRemove();
1088 }
1089
1090 /*
1091  * Overloaded function to be called from the slot and from other places to set the state
1092  * of the check marks in the version table
1093  */
1094 void restoreTree::versionTableItemChanged(QTableWidgetItem *item)
1095 {
1096    /* get the previous and current check states */
1097    int row = versionTable->row(item);
1098    QTableWidgetItem *colZeroItem = versionTable->item(row, 0);
1099    Qt::CheckState prevState = m_versionCheckStateList[row];
1100    Qt::CheckState curState = (Qt::CheckState)colZeroItem->checkState();
1101    m_versionCheckStateList[row] = curState;
1102
1103    /* deterimine the default state from the state of the file */
1104    QTableWidgetItem *fileTableItem = fileTable->currentItem();
1105    Qt::CheckState fileState = (Qt::CheckState)fileTableItem->checkState();
1106
1107    /* determine the default state */
1108    Qt::CheckState defState;
1109    if (row == 0) {
1110       defState = Qt::PartiallyChecked;
1111       if (fileState == Qt::Unchecked)
1112          defState = Qt::Unchecked;
1113    }
1114    else
1115       defState = Qt::Unchecked;
1116
1117    /* determine if it is already in the versionExceptionHash */
1118    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
1119    Qt::CheckState dirState = directoryTree->currentItem()->checkState(0);
1120    QString file = fileTableItem->text();
1121    QString fullPath = directory + file;
1122    int thisJobNum = colZeroItem->text().toInt();
1123    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1124
1125    if (mainWin->m_rtVerTabICDebug) {
1126       QString msg = QString("versrow=%1 prev=%2 cur=%3 def=%4 dir=%5 hashJobNum=%6 thisJobNum=%7 filestate=%8 fec=%9 vec=%10\n")
1127          .arg(row).arg(prevState).arg(curState).arg(defState).arg(dirState).arg(hashJobNum).arg(thisJobNum).arg(fileState)
1128          .arg(m_fileExceptionHash.count()).arg(m_versionExceptionHash.count());
1129       Pmsg1(000, "%s", msg.toUtf8().data()); }
1130    /* if changed from partially checked to checked, make it unchecked */
1131    if ((curState == Qt::Checked) && (row == 0) && (fileState == Qt::Unchecked)) {
1132       if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "Setting to Qt::Checked\n");
1133       fileTableItem->setCheckState(Qt::Checked);
1134    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (row == 0) && (fileState == Qt::Checked) && (dirState == Qt::Unchecked)) {
1135       //versrow=0 prev=1 cur=2 def=1 dir=0 hashJobNum=0 thisJobNum=64 filestate=2 fec=1 vec=0
1136       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "fileExceptionRemove %s, %i\n", fullPath.toUtf8().data());
1137       fileExceptionRemove(fullPath, directory);
1138    } else if ((curState == Qt::Checked) && (row == 0) && (hashJobNum != 0) && (dirState != Qt::Unchecked)) {
1139       //versrow=0 prev=0 cur=2 def=1 dir=2 hashJobNum=53 thisJobNum=64 filestate=2 fec=1 vec=1
1140       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
1141       m_versionExceptionHash.remove(fullPath);
1142       fileExceptionRemove(fullPath, directory);
1143    } else if ((curState == Qt::Checked) && (row == 0)) {
1144       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
1145       m_versionExceptionHash.remove(fullPath);
1146    } else if (prevState != curState) {
1147       if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "  THE STATE OF THE version Check has changed, Setting StateList[%i] to %i\n", row, curState);
1148       if ((curState == Qt::Checked) || (curState == Qt::PartiallyChecked && row != 0)) {
1149          if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_versionExceptionHash %s, %i\n", fullPath.toUtf8().data(), thisJobNum);
1150          m_versionExceptionHash.insert(fullPath, thisJobNum);
1151          if (fileState != Qt::Checked) {
1152             if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
1153             fileExceptionInsert(fullPath, directory, curState);
1154          }
1155       } else {
1156          if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "got here\n");
1157       }
1158    } else {
1159      if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "no conditions met\n");
1160    }
1161
1162    updateFileTableChecks();
1163    updateVersionTableChecks();
1164 }
1165
1166 /*
1167  * Simple function to set the check state in the file table by disconnecting the
1168  * signal/slot the setting then reconnecting the signal/slot
1169  */
1170 void restoreTree::fileTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state, bool color)
1171 {
1172    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1173            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
1174    item->setCheckState(state);
1175    if (color) item->setBackground(Qt::yellow);
1176    else item->setBackground(Qt::white);
1177    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1178            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
1179 }
1180
1181 /*
1182  * Simple function to set the check state in the version table by disconnecting the
1183  * signal/slot the setting then reconnecting the signal/slot
1184  */
1185 void restoreTree::versionTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state)
1186 {
1187    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1188            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
1189    item->setCheckState(state);
1190    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1191            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
1192 }
1193
1194 /*
1195  * Simple function to set the check state in the directory tree by disconnecting the
1196  * signal/slot the setting then reconnecting the signal/slot
1197  */
1198 void restoreTree::directoryTreeDisconnectedSet(QTreeWidgetItem *item, Qt::CheckState state)
1199 {
1200    disconnect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
1201            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
1202    item->setCheckState(0, state);
1203    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
1204            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
1205 }
1206
1207 /*
1208  * Simplify the updating of the check state in the File table by iterating through
1209  * each item in the file table to determine it's appropriate state.
1210  * !! Will probably want to concoct a way to do this without iterating for the possibility
1211  * of the very large directories.
1212  */
1213 void restoreTree::updateFileTableChecks()
1214 {
1215    /* deterimine the default state from the state of the directory */
1216    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
1217    Qt::CheckState dirState = dirTreeItem->checkState(0);
1218
1219    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
1220
1221    /* Update the items in the version table */
1222    int rcnt = fileTable->rowCount();
1223    for (int row=0; row<rcnt; row++) {
1224       QTableWidgetItem* item = fileTable->item(row, 0);
1225       if (!item) { return; }
1226
1227       Qt::CheckState curState = item->checkState();
1228       Qt::CheckState newState = Qt::PartiallyChecked;
1229       if (dirState == Qt::Unchecked) newState = Qt::Unchecked;
1230
1231       /* determine if it is already in the m_fileExceptionHash */
1232       QString file = item->text();
1233       QString fullPath = dirName + file;
1234       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1235       int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1236
1237       if (hashState != 3) newState = hashState;
1238
1239       if (mainWin->m_rtUpdateFTDebug) {
1240          QString msg = QString("file row=%1 cur=%2 hash=%3 new=%4 dirState=%5\n")
1241             .arg(row).arg(curState).arg(hashState).arg(newState).arg(dirState);
1242          Pmsg1(000, "%s", msg.toUtf8().data());
1243       }
1244
1245       bool docolor = false;
1246       if (hashJobNum != 0) docolor = true;
1247       bool isyellow = item->background().color() == QColor(Qt::yellow);
1248       if ((newState != curState) || (hashState == 3) || ((isyellow && !docolor) || (!isyellow && docolor)))
1249          fileTableDisconnectedSet(item, newState, docolor);
1250       m_fileCheckStateList[row] = newState;
1251    }
1252 }
1253
1254 /*
1255  * Simplify the updating of the check state in the Version table by iterating through
1256  * each item in the file table to determine it's appropriate state.
1257  */
1258 void restoreTree::updateVersionTableChecks()
1259 {
1260    /* deterimine the default state from the state of the directory */
1261    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
1262    Qt::CheckState dirState = dirTreeItem->checkState(0);
1263    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
1264
1265    /* deterimine the default state from the state of the file */
1266    QTableWidgetItem *fileTableItem = fileTable->item(fileTable->currentRow(), 0);
1267    if (!fileTableItem) { return; }
1268    Qt::CheckState fileState = fileTableItem->checkState();
1269    QString file = fileTableItem->text();
1270    QString fullPath = dirName + file;
1271    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1272
1273    /* Update the items in the version table */
1274    int cnt = versionTable->rowCount();
1275    for (int row=0; row<cnt; row++) {
1276       QTableWidgetItem* item = versionTable->item(row, 0);
1277       if (!item) { break; }
1278
1279       Qt::CheckState curState = item->checkState();
1280       Qt::CheckState newState = Qt::Unchecked;
1281
1282       if ((row == 0) && (fileState != Qt::Unchecked) && (hashJobNum == 0))
1283          newState = Qt::PartiallyChecked;
1284       /* determine if it is already in the versionExceptionHash */
1285       if (hashJobNum) {
1286          int thisJobNum = item->text().toInt();
1287          if (thisJobNum == hashJobNum)
1288             newState = Qt::Checked;
1289       }
1290       if (mainWin->m_rtChecksDebug) {
1291          QString msg = QString("ver row=%1 cur=%2 hashJobNum=%3 new=%4 dirState=%5\n")
1292             .arg(row).arg(curState).arg(hashJobNum).arg(newState).arg(dirState);
1293          Pmsg1(000, "%s", msg.toUtf8().data());
1294       }
1295       if (newState != curState)
1296          versionTableDisconnectedSet(item, newState);
1297       m_versionCheckStateList[row] = newState;
1298    }
1299 }
1300
1301 /*
1302  * Quick subroutine to "return via subPaths" a list of subpaths when passed a fullPath
1303  */
1304 void restoreTree::fullPathtoSubPaths(QStringList &subPaths, QString &fullPath_in)
1305 {
1306    int index;
1307    bool done = false;
1308    QString fullPath = fullPath_in;
1309    QString direct, path;
1310    while (((index = fullPath.lastIndexOf("/", -2)) != -1) && (!done)) {
1311       direct = path = fullPath;
1312       path.replace(index+1, fullPath.length()-index-1, "");
1313       direct.replace(0, index+1, "");
1314       if (false) {
1315          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1316                     .arg(fullPath.length()).arg(index).arg(path).arg(direct);
1317          Pmsg0(000, msg.toUtf8().data());
1318       }
1319       fullPath = path;
1320       subPaths.append(fullPath);
1321    }
1322 }
1323
1324 /*
1325  * A Function to set the icon state and insert a record into
1326  * m_directoryIconStateHash when an exception is added by the user
1327  */
1328 void restoreTree::directoryIconStateInsert(QString &fullPath, Qt::CheckState excpState)
1329 {
1330    QStringList paths;
1331    fullPathtoSubPaths(paths, fullPath);
1332    /* an exception that causes the item in the file table to be "Checked" has occured */
1333    if (excpState == Qt::Checked) {
1334       bool foundAsUnChecked = false;
1335       QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1336       if (firstItem) {
1337          if (firstItem->checkState(0) == Qt::Unchecked)
1338             foundAsUnChecked = true;
1339       }
1340       if (foundAsUnChecked) {
1341           /* as long as directory item is Unchecked, set icon state to "green check" */
1342          bool done = false;
1343          QListIterator<QString> siter(paths);
1344          while (siter.hasNext() && !done) {
1345             QString path = siter.next();
1346             QTreeWidgetItem *item = m_dirPaths.value(path);
1347             if (item) {
1348                if (item->checkState(0) != Qt::Unchecked)
1349                   done = true;
1350                else {
1351                   directorySetIcon(1, FolderGreenChecked, path, item);
1352                   if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1353                }
1354             }
1355          }
1356       } else {
1357          /* if it is partially checked or fully checked insert green Check until a unchecked is found in the path */
1358          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert Aqua %s\n", paths[0].toUtf8().data());
1359          bool done = false;
1360          QListIterator<QString> siter(paths);
1361          while (siter.hasNext() && !done) {
1362             QString path = siter.next();
1363             QTreeWidgetItem *item = m_dirPaths.value(path);
1364             if (item) {  /* if the directory item is checked, set icon state to unchecked "green check" */
1365                if (item->checkState(0) == Qt::Checked)
1366                   done = true;
1367                directorySetIcon(1, FolderGreenChecked, path, item);
1368                if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1369             }
1370          }
1371       }
1372    }
1373    /* an exception that causes the item in the file table to be "Unchecked" has occured */
1374    if (excpState == Qt::Unchecked) {
1375       bool done = false;
1376       QListIterator<QString> siter(paths);
1377       while (siter.hasNext() && !done) {
1378          QString path = siter.next();
1379          QTreeWidgetItem *item = m_dirPaths.value(path);
1380          if (item) {  /* if the directory item is checked, set icon state to unchecked "white check" */
1381             if (item->checkState(0) == Qt::Checked)
1382                done = true;
1383             directorySetIcon(1, FolderWhiteChecked, path, item);
1384             if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1385          }
1386       }
1387    }
1388 }
1389
1390 /*
1391  * A function to set the icon state back to "folder" and to remove a record from
1392  * m_directoryIconStateHash when an exception is removed by a user.
1393  */
1394 void restoreTree::directoryIconStateRemove()
1395 {
1396    QHash<QString, int> shouldBeIconStateHash;
1397    /* First determine all paths with icons that should be checked with m_fileExceptionHash */
1398    /* Use iterator tera to iterate through m_fileExceptionHash */
1399    QHashIterator<QString, Qt::CheckState> tera(m_fileExceptionHash);
1400    while (tera.hasNext()) {
1401       tera.next();
1402       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Alpha Key %s value %i\n", tera.key().toUtf8().data(), tera.value());
1403
1404       QString keyPath = tera.key();
1405       Qt::CheckState state = tera.value();
1406
1407       QStringList paths;
1408       fullPathtoSubPaths(paths, keyPath);
1409       /* if the state of the item in m_fileExceptionHash is checked 
1410        * each of the subpaths should be "Checked Green" */
1411       if (state == Qt::Checked) {
1412
1413          bool foundAsUnChecked = false;
1414          QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1415          if (firstItem) {
1416             if (firstItem->checkState(0) == Qt::Unchecked)
1417                foundAsUnChecked = true;
1418          }
1419          if (foundAsUnChecked) {
1420             /* The right most directory is Unchecked, iterate leftwards
1421              * as long as directory item is Unchecked, set icon state to "green check" */
1422             bool done = false;
1423             QListIterator<QString> siter(paths);
1424             while (siter.hasNext() && !done) {
1425                QString path = siter.next();
1426                QTreeWidgetItem *item = m_dirPaths.value(path);
1427                if (item) {
1428                   if (item->checkState(0) != Qt::Unchecked)
1429                      done = true;
1430                   else {
1431                      shouldBeIconStateHash.insert(path, FolderGreenChecked);
1432                      if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1433                   }
1434                }
1435             }
1436          }
1437          else {
1438             /* The right most directory is Unchecked, iterate leftwards
1439              * until directory item is Checked, set icon state to "green check" */
1440             bool done = false;
1441             QListIterator<QString> siter(paths);
1442             while (siter.hasNext() && !done) {
1443                QString path = siter.next();
1444                QTreeWidgetItem *item = m_dirPaths.value(path);
1445                if (item) {
1446                   if (item->checkState(0) == Qt::Checked)
1447                      done = true;
1448                   shouldBeIconStateHash.insert(path, FolderGreenChecked);
1449                }
1450             }
1451          }
1452       }
1453       /* if the state of the item in m_fileExceptionHash is UNChecked
1454        * each of the subpaths should be "Checked white" until the tree item
1455        * which represents that path is Qt::Checked */
1456       if (state == Qt::Unchecked) {
1457          bool done = false;
1458          QListIterator<QString> siter(paths);
1459          while (siter.hasNext() && !done) {
1460             QString path = siter.next();
1461             QTreeWidgetItem *item = m_dirPaths.value(path);
1462             if (item) {
1463                if (item->checkState(0) == Qt::Checked)
1464                   done = true;
1465                shouldBeIconStateHash.insert(path, FolderWhiteChecked);
1466             }
1467          }
1468       }
1469    }
1470    /* now iterate through m_directoryIconStateHash which are the items that are checked
1471     * and remove all of those that are not in shouldBeIconStateHash */
1472    QHashIterator<QString, int> iter(m_directoryIconStateHash);
1473    while (iter.hasNext()) {
1474       iter.next();
1475       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Beta Key %s value %i\n", iter.key().toUtf8().data(), iter.value());
1476
1477       QString keyPath = iter.key();
1478       if (shouldBeIconStateHash.value(keyPath)) {
1479          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "WAS found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1480          //newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1481          int newval = shouldBeIconStateHash.value(keyPath);
1482          newval = ~newval;
1483          newval = newval & FolderBothChecked;
1484          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1485          if (item)
1486             directorySetIcon(0, newval, keyPath, item);
1487       } else {
1488          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "NOT found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1489          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1490          if (item)
1491             directorySetIcon(0, FolderBothChecked, keyPath, item);
1492             //item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
1493             //m_directoryIconStateHash.remove(keyPath);
1494       }
1495    }
1496 }
1497
1498 void restoreTree::directorySetIcon(int operation, int change, QString &path, QTreeWidgetItem* item) {
1499    int newval;
1500    /* we are adding a check type white or green */
1501    if (operation > 0) {
1502       /* get the old val and "bitwise OR" with the change */
1503       newval = m_directoryIconStateHash.value(path, FolderUnchecked) | change;
1504       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1505       m_directoryIconStateHash.insert(path, newval);
1506    } else {
1507    /* we are removing a check type white or green */
1508       newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1509       if (newval == 0) {
1510          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Removing from m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1511          m_directoryIconStateHash.remove(path);
1512       }
1513       else {
1514          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1515          m_directoryIconStateHash.insert(path, newval);
1516       }
1517    }
1518    if (newval == FolderUnchecked)
1519       item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
1520    else if (newval == FolderGreenChecked)
1521       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderchecked.png")));
1522    else if (newval == FolderWhiteChecked)
1523       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderunchecked.png")));
1524    else if (newval == FolderBothChecked)
1525       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderbothchecked.png")));
1526 }
1527
1528 /*
1529  * Restore Button
1530  */
1531 void restoreTree::restoreButtonPushed()
1532 {
1533    /* Set progress bars and repaint */
1534    prLabel1->setVisible(true);
1535    prLabel1->setText(tr("Task 1 of 3"));
1536    prLabel2->setVisible(true);
1537    prLabel2->setText(tr("Processing Checked directories"));
1538    prBar1->setVisible(true);
1539    prBar1->setRange(0, 3);
1540    prBar1->setValue(0);
1541    prBar2->setVisible(true);
1542    prBar2->setRange(0, 0);
1543    repaint();
1544    QMultiHash<int, QString> versionFilesMulti;
1545    int vFMCounter = 0;
1546    QHash <QString, bool> fullPathDone;
1547    QHash <QString, int> fileIndexHash;
1548    if ((mainWin->m_rtRestore1Debug) || (mainWin->m_rtRestore2Debug) || (mainWin->m_rtRestore3Debug))
1549       Pmsg0(000, "In restoreTree::restoreButtonPushed\n");
1550    /* Use a tree widget item iterator to count directories for the progress bar */
1551    QTreeWidgetItemIterator diterc(directoryTree, QTreeWidgetItemIterator::Checked);
1552    int ditcount = 0;
1553    while (*diterc) {
1554       ditcount += 1;
1555       ++diterc;
1556    } /* while (*diterc) */
1557    prBar2->setRange(0, ditcount);
1558    prBar2->setValue(0);
1559    ditcount = 0;
1560    /* Use a tree widget item iterator filtering for Checked Items */
1561    QTreeWidgetItemIterator diter(directoryTree, QTreeWidgetItemIterator::Checked);
1562    while (*diter) {
1563       QString directory = (*diter)->data(0, Qt::UserRole).toString();
1564       int pathid = m_directoryPathIdHash.value(directory, -1);
1565       if (pathid != -1) {
1566          if (mainWin->m_rtRestore1Debug)
1567             Pmsg1(000, "Directory Checked=\"%s\"\n", directory.toUtf8().data());
1568          /* With a checked directory, query for the files in the directory */
1569    
1570          QString cmd =
1571             "SELECT Filename.Name AS Filename, t1.JobId AS JobId, File.FileIndex AS FileIndex"
1572             " FROM"
1573             " ( SELECT File.FilenameId AS FilenameId, MAX(Job.JobId) AS JobId"
1574               " FROM File"
1575               " INNER JOIN Job ON (Job.JobId=File.JobId)"
1576               " WHERE File.PathId=" + QString("%1").arg(pathid) +
1577               " AND Job.Jobid IN (" + m_checkedJobs + ")"
1578               " GROUP BY File.FilenameId"
1579             ") t1, File "
1580               " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1581               " INNER JOIN Job ON (Job.JobId=File.JobId)"
1582               " WHERE File.PathId=" + QString("%1").arg(pathid) +
1583               " AND File.FilenameId=t1.FilenameId"
1584               " AND Job.Jobid=t1.JobId"
1585             " ORDER BY Filename";
1586    
1587          if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1588          QStringList results;
1589          if (m_console->sql_cmd(cmd, results)) {
1590             QStringList fieldlist;
1591       
1592             int row = 0;
1593             /* Iterate through the record returned from the query */
1594             foreach (QString resultline, results) {
1595                /* Iterate through fields in the record */
1596                int column = 0;
1597                QString fullPath = "";
1598                Qt::CheckState fileExcpState = (Qt::CheckState)4;
1599                fieldlist = resultline.split("\t");
1600                int version = 0;
1601                int fileIndex = 0;
1602                foreach (QString field, fieldlist) {
1603                   if (column == 0) {
1604                      fullPath = directory + field;
1605                   }
1606                   if (column == 1) {
1607                      version = field.toInt();
1608                   }
1609                   if (column == 2) {
1610                      fileIndex = field.toInt();
1611                   }
1612                   column++;
1613                }
1614                fileExcpState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1615                
1616                int excpVersion = m_versionExceptionHash.value(fullPath, 0);
1617                if (fileExcpState != Qt::Unchecked) {
1618                   QString debugtext;
1619                   if (excpVersion != 0) {
1620                      debugtext = QString("*E* version=%1").arg(excpVersion);
1621                      version = excpVersion;
1622                      fileIndex = queryFileIndex(fullPath, excpVersion);
1623                   } else
1624                      debugtext = QString("___ version=%1").arg(version);
1625                   if (mainWin->m_rtRestore1Debug)
1626                      Pmsg2(000, "Restoring %s File %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1627                   fullPathDone.insert(fullPath, 1);
1628                   fileIndexHash.insert(fullPath, fileIndex);
1629                   versionFilesMulti.insert(version, fullPath);
1630                   vFMCounter += 1;
1631                }
1632                row++;
1633             }
1634          }
1635       }
1636       ditcount += 1;
1637       prBar2->setValue(ditcount);
1638       ++diter;
1639    } /* while (*diter) */
1640    prBar1->setValue(1);
1641    prLabel1->setText( tr("Task 2 of 3"));
1642    prLabel2->setText(tr("Processing Exceptions"));
1643    prBar2->setRange(0, 0);
1644    repaint();
1645
1646    /* There may be some exceptions not accounted for yet with fullPathDone */
1647    QHashIterator<QString, Qt::CheckState> ftera(m_fileExceptionHash);
1648    while (ftera.hasNext()) {
1649       ftera.next();
1650       QString fullPath = ftera.key();
1651       Qt::CheckState state = ftera.value();
1652       if (state != 0) {
1653          /* now we don't want the ones already done */
1654          if (fullPathDone.value(fullPath, 0) == 0) {
1655             int version = m_versionExceptionHash.value(fullPath, 0);
1656             int fileIndex = 0;
1657             QString debugtext = "";
1658             if (version != 0) {
1659                fileIndex = queryFileIndex(fullPath, version);
1660                debugtext = QString("E1* version=%1 fileid=%2").arg(version).arg(fileIndex);
1661             } else {
1662                version = mostRecentVersionfromFullPath(fullPath);
1663                if (version) {
1664                   fileIndex = queryFileIndex(fullPath, version);
1665                   debugtext = QString("E2* version=%1 fileid=%2").arg(version).arg(fileIndex);
1666                } else
1667                   debugtext = QString("Error det vers").arg(version);
1668             }
1669             if (mainWin->m_rtRestore1Debug)
1670                Pmsg2(000, "Restoring %s file %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1671             versionFilesMulti.insert(version, fullPath);
1672             vFMCounter += 1;
1673             fileIndexHash.insert(fullPath, fileIndex);
1674          } /* if fullPathDone.value(fullPath, 0) == 0 */
1675       } /* if state != 0 */
1676    } /* while ftera.hasNext */
1677    /* The progress bars for the next step */
1678    prBar1->setValue(2);
1679    prLabel1->setText(tr("Task 3 of 3"));
1680    prLabel2->setText(tr("Filling Database Table"));
1681    prBar2->setRange(0, vFMCounter);
1682    vFMCounter = 0;
1683    prBar2->setValue(vFMCounter);
1684    repaint();
1685
1686    /* now for the final spit out of the versions and lists of files for each version */
1687    QHash<int, int> doneKeys;
1688    QHashIterator<int, QString> vFMiter(versionFilesMulti);
1689    QString tempTable = "";
1690    QList<int> jobList;
1691    while (vFMiter.hasNext()) {
1692       vFMiter.next();
1693       int fversion = vFMiter.key();
1694       /* did not succeed in getting an iterator to work as expected on versionFilesMulti so use doneKeys */
1695       if (doneKeys.value(fversion, 0) == 0) {
1696          if (tempTable == "") {
1697             QSettings settings("www.bacula.org", "bat");
1698             settings.beginGroup("Restore");
1699             int counter = settings.value("Counter", 1).toInt();
1700             settings.setValue("Counter", counter+1);
1701             settings.endGroup();
1702             tempTable = "restore_" + QString("%1").arg(qrand()) + "_" + QString("%1").arg(counter);
1703             QString sqlcmd = "CREATE TEMPORARY TABLE " + tempTable + " (JobId INTEGER, FileIndex INTEGER)";
1704             if (mainWin->m_sqlDebug)
1705                Pmsg1(000, "Query cmd : %s ;\n", sqlcmd.toUtf8().data());
1706             QStringList results;
1707             if (!m_console->sql_cmd(sqlcmd, results))
1708                Pmsg1(000, "CREATE TABLE FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1709          }
1710
1711          if (mainWin->m_rtRestore2Debug) Pmsg1(000, "Version->%i\n", fversion);
1712          QStringList fullPathList = versionFilesMulti.values(fversion);
1713          /* create the command to perform the restore */
1714          foreach(QString ffullPath, fullPathList) {
1715             int fileIndex = fileIndexHash.value(ffullPath);
1716             if (mainWin->m_rtRestore2Debug) Pmsg2(000, "  file->%s id %i\n", ffullPath.toUtf8().data(), fileIndex);
1717             QString sqlcmd = "INSERT INTO " + tempTable + " (JobId, FileIndex) VALUES (" + QString("%1").arg(fversion) + ", " + QString("%1").arg(fileIndex) + ")";
1718             if (mainWin->m_rtRestore3Debug)
1719                Pmsg1(000, "Insert cmd : %s\n", sqlcmd.toUtf8().data());
1720             QStringList results;
1721             if (!m_console->sql_cmd(sqlcmd, results))
1722                Pmsg1(000, "INSERT INTO FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1723             prBar2->setValue(++vFMCounter);
1724          } /* foreach fullPathList */
1725          doneKeys.insert(fversion,1);
1726          jobList.append(fversion);
1727       } /*  if (doneKeys.value(fversion, 0) == 0) */
1728    } /* while (vFMiter.hasNext()) */
1729    if (tempTable != "") {
1730       /* a table was made, lets run the job */
1731       QString jobOption = " jobid=\"";
1732       bool first = true;
1733       /* create a list of jobs comma separated */
1734       foreach (int job, jobList) {
1735          if (first) first = false;
1736          else jobOption += ",";
1737          jobOption += QString("%1").arg(job);
1738       }
1739       jobOption += "\"";
1740       QString cmd = QString("restore");
1741       cmd += jobOption +
1742              " client=\"" + m_prevClientCombo + "\"" +
1743              " file=\"?" + tempTable + "\" done";
1744       if (mainWin->m_commandDebug)
1745          Pmsg1(000, "preRestore command \'%s\'\n", cmd.toUtf8().data());
1746       consoleCommand(cmd);
1747    }
1748    /* turn off the progress widgets */
1749    prBar1->setVisible(false);
1750    prBar2->setVisible(false);
1751    prLabel1->setVisible(false);
1752    prLabel2->setVisible(false);
1753 }
1754
1755 int restoreTree::mostRecentVersionfromFullPath(QString &fullPath)
1756 {
1757    int qversion = 0;
1758    QString directory, fileName;
1759    int index = fullPath.lastIndexOf("/", -2);
1760    if (index != -1) {
1761       directory = fileName = fullPath;
1762       directory.replace(index+1, fullPath.length()-index-1, "");
1763       fileName.replace(0, index+1, "");
1764       if (false) {
1765          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1766                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1767          Pmsg0(000, msg.toUtf8().data());
1768       }
1769       int pathid = m_directoryPathIdHash.value(directory, -1);
1770       if (pathid != -1) {
1771          /* so now we need the latest version from the database */
1772          QString cmd =
1773             "SELECT MAX(Job.JobId)"
1774             " FROM File "
1775             " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1776             " INNER JOIN Job ON (File.JobId=Job.JobId)"
1777             " WHERE File.PathId=" + QString("%1").arg(pathid) +
1778             " AND Job.Jobid IN (" + m_checkedJobs + ")"
1779             " AND Filename.Name='" + fileName + "'"
1780             " AND File.FilenameId!=" + QString("%1").arg(m_nullFileNameId) +
1781             " GROUP BY Filename.Name";
1782     
1783          if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1784          QStringList results;
1785          if (m_console->sql_cmd(cmd, results)) {
1786             QStringList fieldlist;
1787             int row = 0;
1788             /* Iterate through the record returned from the query */
1789             foreach (QString resultline, results) {
1790                /* Iterate through fields in the record */
1791                int column = 0;
1792                fieldlist = resultline.split("\t");
1793                foreach (QString field, fieldlist) {
1794                   if (column == 0) {
1795                      qversion = field.toInt();
1796                   }
1797                   column++;
1798                }
1799                row++;
1800             }
1801          }
1802       }
1803    } /* if (index != -1) */
1804    return qversion;
1805 }
1806
1807
1808 int restoreTree::queryFileIndex(QString &fullPath, int jobId)
1809 {
1810    int qfileIndex = 0;
1811    QString directory, fileName;
1812    int index = fullPath.lastIndexOf("/", -2);
1813    if (index != -1) {
1814       directory = fileName = fullPath;
1815       directory.replace(index+1, fullPath.length()-index-1, "");
1816       fileName.replace(0, index+1, "");
1817       if (false) {
1818          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1819                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1820          Pmsg0(000, msg.toUtf8().data());
1821       }
1822       int pathid = m_directoryPathIdHash.value(directory, -1);
1823       if (pathid != -1) {
1824          /* so now we need the latest version from the database */
1825          QString cmd =
1826             "SELECT"
1827              " File.FileIndex"
1828             " FROM File"
1829              " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1830              " INNER JOIN Job ON (File.JobId=Job.JobId)"
1831             " WHERE File.PathId=" + QString("%1").arg(pathid) +
1832              " AND Filename.Name='" + fileName + "'"
1833              " AND Job.Jobid='" + QString("%1").arg(jobId) + "'"
1834             " GROUP BY File.FileIndex";
1835     
1836          if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1837          QStringList results;
1838          if (m_console->sql_cmd(cmd, results)) {
1839             QStringList fieldlist;
1840             int row = 0;
1841             /* Iterate through the record returned from the query */
1842             foreach (QString resultline, results) {
1843                /* Iterate through fields in the record */
1844                int column = 0;
1845                fieldlist = resultline.split("\t");
1846                foreach (QString field, fieldlist) {
1847                   if (column == 0) {
1848                      qfileIndex = field.toInt();
1849                   }
1850                   column++;
1851                }
1852                row++;
1853             }
1854          }
1855       }
1856    } /* if (index != -1) */
1857    return qfileIndex;
1858 }
1859
1860
1861 void restoreTree::PgSeltreeWidgetClicked()
1862 {
1863    if (!isOnceDocked()) {
1864       dockPage();
1865    }
1866 }