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