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