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