]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
Enhance restore messages
[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    }
202    setJobsCheckedList();
203    if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating from checks in Job Table\n");
204
205    if (m_checkedJobs != "") {
206       /* First get the filenameid of where the nae is null.  These will be the directories
207        * This could be done in a subquery but postgres's query analyzer won't do the right
208        * thing like I want */
209       if (m_nullFileNameId == -1) {
210          QString cmd = "SELECT FilenameId FROM Filename WHERE name=''";
211          if (mainWin->m_sqlDebug)
212             Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
213          QStringList qres;
214          if (m_console->sql_cmd(cmd, qres)) {
215             if (qres.count()) {
216                QStringList fieldlist = qres[0].split("\t");
217                QString field = fieldlist[0];
218                bool ok;
219                int val = field.toInt(&ok, 10);
220                if (ok) m_nullFileNameId = val;
221             }
222          }
223       }
224       /* now create the query to get the list of paths */
225       QString cmd =
226          "SELECT DISTINCT Path.Path AS Path, File.PathId AS PathId"
227          " FROM File"
228          " INNER JOIN Path ON (File.PathId=Path.PathId)";
229       if (m_nullFileNameId != -1)
230          cmd += " WHERE File.FilenameId=" + QString("%1").arg(m_nullFileNameId);
231       else
232          cmd += " WHERE File.FilenameId IN (SELECT FilenameId FROM Filename WHERE Name='')";
233       cmd += " AND File.Jobid IN (" + m_checkedJobs + ")"
234          " ORDER BY Path";
235       if (mainWin->m_sqlDebug)
236          Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
237       prBar1->setValue(ontask++);
238       prLabel1->setText("Task " + QString("%1").arg(ontask) + " of " + QString("%1").arg(taskcount));
239       prBar2->setValue(0);
240       prBar2->setRange(0,0);
241       prLabel2->setText("Querying for Directories");
242       repaint();
243       QStringList results;
244       m_directoryPathIdHash.clear();
245       bool querydone = false;
246       if (m_console->sql_cmd(cmd, results)) {
247          if (!querydone) {
248             querydone = true;
249             prLabel2->setText("Processing Directories");
250             prBar2->setRange(0,results.count());
251             repaint();
252          }
253          if (mainWin->m_miscDebug)
254             Pmsg1(000, "Done with query %i results\n", results.count());
255          QStringList fieldlist;
256          foreach(QString resultline, results) {
257             /* Update progress bar periodically */
258             if ((++m_debugCnt && 0x3FF) == 0) {
259                prBar2->setValue(m_debugCnt);
260             }
261             fieldlist = resultline.split("\t");
262             int fieldcnt = 0;
263             QString field;
264             /* Iterate through fields in the record */
265             foreach (field, fieldlist) {
266                if (fieldcnt == 0 ) {
267                   parseDirectory(field);
268                } else if (fieldcnt == 1) {
269                   bool ok;
270                   int pathid = field.toInt(&ok, 10);
271                   if (ok)
272                      m_directoryPathIdHash.insert(fieldlist[0], pathid);
273                }
274                fieldcnt += 1;
275             }
276          }
277       }
278    } else {
279      QMessageBox::warning(this, tr("Bat"),
280         tr("No jobs were selected in the job query !!!.\n"
281       "Press OK to continue?"),
282       QMessageBox::Ok );
283    }
284    prBar1->setVisible(false);
285    prBar2->setVisible(false);
286    prLabel1->setVisible(false);
287    prLabel2->setVisible(false);
288 }
289
290 /*
291  *  Function to set m_checkedJobs from the jobs that are checked in the table
292  *  of jobs
293  */     
294 void restoreTree::setJobsCheckedList()
295 {
296    m_JobsCheckedList = "";
297    bool first = true;
298    /* Update the items in the version table */
299    int cnt = jobTable->rowCount();
300    for (int row=0; row<cnt; row++) {
301       QTableWidgetItem* jobItem = jobTable->item(row, 0);
302       if (jobItem->checkState() == Qt::Checked) {
303          if (!first)
304             m_JobsCheckedList += ",";
305          m_JobsCheckedList += jobItem->text();
306          first = false;
307          jobItem->setBackground(Qt::green);
308       } else {
309          if (jobItem->flags())
310             jobItem->setBackground(Qt::gray);
311          else
312             jobItem->setBackground(Qt::darkYellow);
313       }
314    }
315    m_checkedJobs = m_JobsCheckedList;
316 }
317
318 /*
319  * Function to parse a directory into all possible subdirectories, then add to
320  * The tree.
321  */
322 void restoreTree::parseDirectory(QString &dir_in)
323 {
324    /* m_debugTrap is to only print debugs for a few occurennces of calling parseDirectory
325     * instead of printing out what could potentially a whole bunch */
326    if (m_debugCnt > 2)
327       m_debugTrap = false;
328    /* Truncate everything after the last / */
329    if (dir_in.right(1) != "/") {
330       dir_in.truncate(dir_in.lastIndexOf("/") + 1);
331    }
332    if ((mainWin->m_miscDebug) && (m_debugTrap))
333       Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
334
335    /* split and add if not in yet */
336    QString direct, path;
337    int index;
338    bool done = false;
339    QStringList pathAfter, dirAfter;
340    /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/ 
341     * if not added into tree, then try /etc/ and somedir/ if not added, then try
342     * / and etc/ .  That should succeed, then add the ones that failed in reverse */
343    while (((index = dir_in.lastIndexOf("/", -2)) != -1) && (!done)) {
344       direct = path = dir_in;
345       path.replace(index+1, dir_in.length()-index-1,"");
346       direct.replace(0, index+1, "");
347       if ((mainWin->m_miscDebug) && (m_debugTrap)) {
348          QString msg = QString("length = \"%1\" index = \"%2\" Adding \"%3\" \"%4\"\n")
349                     .arg(dir_in.length()).arg(index).arg(path).arg(direct);
350          Pmsg0(000, msg.toUtf8().data());
351       }
352       if (addDirectory(path, direct)) done = true;
353       else {
354          if ((mainWin->m_miscDebug) && (m_debugTrap))
355             Pmsg0(000, "Saving for later\n");
356          pathAfter.prepend(path);
357          dirAfter.prepend(direct);
358       }
359       dir_in = path;
360    }
361
362    for (int k=0; k<pathAfter.count(); k++) {
363       if (addDirectory(pathAfter[k], dirAfter[k]))
364          if ((mainWin->m_miscDebug) && (m_debugTrap))
365             Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
366       else
367          if ((mainWin->m_miscDebug) && (m_debugTrap))
368             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
369    }
370 }
371
372
373 /*
374  * Function called from fill directory when a directory is found to see if this
375  * directory exists in the directory pane and then add it to the directory pane
376  */
377 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
378 {
379    QString newdir = newdirr;
380    QString fullPath = m_cwd + newdirr;
381    bool ok = true, added = false;
382
383    if ((mainWin->m_miscDebug) && (m_debugTrap)) {
384       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
385                     .arg(m_cwd)
386                     .arg(newdir);
387       Pmsg0(000, msg.toUtf8().data());
388    }
389
390    if (!m_slashTrap) {
391       /* add unix '/' directory first */
392       if (m_dirPaths.empty() && !isWin32Path(fullPath)) {
393          m_slashTrap = true;
394          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
395          QString text("/");
396          item->setText(0, text.toUtf8().data());
397          item->setData(0, Qt::UserRole, QVariant(text));
398          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
399          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
400          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
401             Pmsg1(000, "Pre Inserting %s\n", text.toUtf8().data());
402          }
403          m_dirPaths.insert(text, item);
404       }
405       /* no need to check for windows drive if unix */
406       if (isWin32Path(m_cwd)) {
407          if (!m_dirPaths.contains(m_cwd)) {
408             /* this is a windows drive add the base widget */
409             QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
410             item->setText(0, m_cwd);
411             item->setData(0, Qt::UserRole, QVariant(fullPath));
412             item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
413             item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
414             if ((mainWin->m_miscDebug) && (m_debugTrap)) {
415                Pmsg0(000, "Added Base \"letter\":/\n");
416             }
417             m_dirPaths.insert(m_cwd, item);
418          }
419       }
420    }
421  
422    /* is it already existent ?? */
423    if (!m_dirPaths.contains(fullPath)) {
424       QTreeWidgetItem *item = NULL;
425       QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
426       if (parent) {
427          /* new directories to add */
428          item = new QTreeWidgetItem(parent);
429          item->setText(0, newdir.toUtf8().data());
430          item->setData(0, Qt::UserRole, QVariant(fullPath));
431          item->setCheckState(0, Qt::Unchecked);
432          /* Store the current state of the check status in column 1, which at
433           * this point has no text*/
434          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
435       } else {
436          ok = false;
437          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
438             QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
439                  .arg(m_cwd)
440                  .arg(newdir);
441             Pmsg0(000, msg.toUtf8().data());
442          }
443       }
444       /* insert into hash */
445       if (ok) {
446          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
447             Pmsg1(000, "Inserting %s\n", fullPath.toUtf8().data());
448          }
449          m_dirPaths.insert(fullPath, item);
450          added = true;
451       }
452    }
453    return added;
454 }
455
456 /*
457  * Virtual function which is called when this page is visible on the stack
458  */
459 void restoreTree::currentStackItem()
460 {
461    if(!m_populated) {
462       if (!m_console->preventInUseConnect())
463          return;
464       setupPage();
465       m_populated = true;
466    }
467 }
468
469 /*
470  * Populate the tree when refresh button pushed.
471  */
472 void restoreTree::refreshButtonPushed()
473 {
474    populateDirectoryTree();
475 }
476
477 /*
478  * Set the values of non-job combo boxes to the job defaults
479  */
480 void restoreTree::jobComboChanged(int)
481 {
482    if (jobCombo->currentText() == "Any") {
483       fileSetCombo->setCurrentIndex(fileSetCombo->findText("Any", Qt::MatchExactly));
484       return;
485    }
486    job_defaults job_defs;
487
488    (void)index;
489    job_defs.job_name = jobCombo->currentText();
490    if (m_console->get_job_defaults(job_defs)) {
491       fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
492       clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
493    }
494 }
495
496 /*
497  * Function to populate the file list table
498  */
499 void restoreTree::directoryCurrentItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
500 {
501    if (item == NULL)
502       return;
503
504    fileTable->clear();
505    /* Also clear the version table here */
506    versionTable->clear();
507    versionFileLabel->setText("");
508    versionTable->setRowCount(0);
509    versionTable->setColumnCount(0);
510
511    QStringList headerlist = (QStringList() << "File Name" << "Filename Id");
512    fileTable->setColumnCount(headerlist.size());
513    fileTable->setHorizontalHeaderLabels(headerlist);
514    fileTable->setRowCount(0);
515    
516    m_fileCheckStateList.clear();
517    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
518            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
519    QBrush blackBrush(Qt::black);
520    QString directory = item->data(0, Qt::UserRole).toString();
521    directoryLabel->setText("Present Working Directory : " + directory);
522    int pathid = m_directoryPathIdHash.value(directory, -1);
523    if (pathid != -1) {
524       QString cmd =
525          "SELECT DISTINCT Filename.Name AS FileName, Filename.FilenameId AS FilenameId"
526          " FROM File "
527          " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
528          " WHERE File.PathId=" + QString("%1").arg(pathid) +
529          " AND File.Jobid IN (" + m_checkedJobs + ")"
530          " AND Filename.Name!=''"
531          " ORDER BY FileName";
532
533       if (mainWin->m_sqlDebug) {
534          Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
535       }
536       QStringList results;
537       if (m_console->sql_cmd(cmd, results)) {
538       
539          QTableWidgetItem* tableItem;
540          QString field;
541          QStringList fieldlist;
542          fileTable->setRowCount(results.size());
543    
544          int row = 0;
545          /* Iterate through the record returned from the query */
546          foreach (QString resultline, results) {
547             /* Iterate through fields in the record */
548             int column = 0;
549             fieldlist = resultline.split("\t");
550             foreach (field, fieldlist) {
551                field = field.trimmed();  /* strip leading & trailing spaces */
552                tableItem = new QTableWidgetItem(field, 1);
553                /* Possible flags are Qt::ItemFlags flag = Qt::ItemIsSelectable | Qt::ItemIsEditablex
554                 *  | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable 
555                 *  | Qt::ItemIsEnabled | Qt::ItemIsTristate; */
556                tableItem->setForeground(blackBrush);
557                /* Just in case a column ever gets added */
558                if (column == 0) {
559                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
560                   tableItem->setFlags(flag);
561                   tableItem->setData(Qt::UserRole, QVariant(directory));
562                   fileTable->setItem(row, column, tableItem);
563                   m_fileCheckStateList.append(Qt::Unchecked);
564                   tableItem->setCheckState(Qt::Unchecked);
565                } else if (column == 1) {
566                   Qt::ItemFlags flag = Qt::ItemIsEnabled;
567                   tableItem->setFlags(flag);
568                   bool ok;
569                   int filenameid = field.toInt(&ok, 10);
570                   if (!ok) filenameid = -1;
571                   tableItem->setData(Qt::UserRole, QVariant(filenameid));
572                   fileTable->setItem(row, column, tableItem);
573                }
574                column++;
575             }
576             row++;
577          }
578          fileTable->setRowCount(row);
579       }
580       fileTable->resizeColumnsToContents();
581       fileTable->resizeRowsToContents();
582       fileTable->verticalHeader()->hide();
583       fileTable->hideColumn(1);
584       if (mainWin->m_rtDirCurICDebug) Pmsg0(000, "will update file table checks\n");
585       updateFileTableChecks();
586    } else if (mainWin->m_sqlDebug)
587       Pmsg1(000, "did not perform query, pathid=%i not found\n", pathid);
588    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
589           this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
590 }
591
592 /*
593  * Function to populate the version table
594  */
595 void restoreTree::fileCurrentItemChanged(QTableWidgetItem *currentFileTableItem, QTableWidgetItem *)
596 {
597    if (currentFileTableItem == NULL)
598       return;
599
600    int currentRow = fileTable->row(currentFileTableItem);
601    QTableWidgetItem *fileTableItem = fileTable->item(currentRow, 0);
602    QTableWidgetItem *fileNameIdTableItem = fileTable->item(currentRow, 1);
603    int fileNameId = fileNameIdTableItem->data(Qt::UserRole).toInt();
604
605    m_versionCheckStateList.clear();
606    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
607            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
608
609    QString file = fileTableItem->text();
610    versionFileLabel->setText(file);
611    QString directory = fileTableItem->data(Qt::UserRole).toString();
612
613    QBrush blackBrush(Qt::black);
614
615    QStringList headerlist = (QStringList() << "Job Id" << "Type" << "End Time" << "Hash" << "FileId");
616    versionTable->clear();
617    versionTable->setColumnCount(headerlist.size());
618    versionTable->setHorizontalHeaderLabels(headerlist);
619    versionTable->setRowCount(0);
620    
621    int pathid = m_directoryPathIdHash.value(directory, -1);
622    if ((pathid != -1) && (fileNameId != -1)) {
623       QString cmd = 
624          "SELECT Job.JobId AS JobId,Job.Level AS Type,Job.EndTime AS EndTime,File.MD5 AS MD5,File.FileId AS FileId"
625          " FROM File"
626          " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
627          " INNER JOIN Path ON (Path.PathId=File.PathId)"
628          " INNER JOIN Job ON (File.JobId=Job.JobId)"
629          " WHERE Path.PathId=" + QString("%1").arg(pathid) +
630          //" AND Filename.Name='" + file + "'"
631          " AND Filename.FilenameId=" + QString("%1").arg(fileNameId) +
632          " AND Job.Jobid IN (" + m_checkedJobs + ")"
633          " ORDER BY Job.EndTime DESC";
634    
635       if (mainWin->m_sqlDebug)
636          Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
637       QStringList results;
638       if (m_console->sql_cmd(cmd, results)) {
639       
640          QTableWidgetItem* tableItem;
641          QString field;
642          QStringList fieldlist;
643          versionTable->setRowCount(results.size());
644    
645          int row = 0;
646          /* Iterate through the record returned from the query */
647          foreach (QString resultline, results) {
648             fieldlist = resultline.split("\t");
649             int column = 0;
650             /* remove directory */
651             if (fieldlist[0].trimmed() != "") {
652                /* Iterate through fields in the record */
653                foreach (field, fieldlist) {
654                   field = field.trimmed();  /* strip leading & trailing spaces */
655                   tableItem = new QTableWidgetItem(field, 1);
656                   tableItem->setFlags(0);
657                   tableItem->setForeground(blackBrush);
658                   tableItem->setData(Qt::UserRole, QVariant(directory));
659                   versionTable->setItem(row, column, tableItem);
660    
661                   if (column == 0) {
662                      Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
663                      tableItem->setFlags(flag);
664                      m_versionCheckStateList.append(Qt::Unchecked);
665                      tableItem->setCheckState(Qt::Unchecked);
666                   }
667                   column++;
668                }
669                row++;
670             }
671          }
672       }
673       versionTable->resizeColumnsToContents();
674       versionTable->resizeRowsToContents();
675       versionTable->verticalHeader()->hide();
676       updateVersionTableChecks();
677    } else {
678       if (mainWin->m_sqlDebug)
679          Pmsg2(000, "not querying : pathid=%i fileNameId=%i\n", pathid, fileNameId);
680    }
681    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
682            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
683 }
684
685 /*
686  * Save user settings associated with this page
687  */
688 void restoreTree::writeSettings()
689 {
690    QSettings settings(m_console->m_dir->name(), "bat");
691    settings.beginGroup(m_groupText);
692    settings.setValue(m_splitText, m_splitter->saveState());
693    settings.endGroup();
694 }
695
696 /*
697  * Read and restore user settings associated with this page
698  */
699 void restoreTree::readSettings()
700 {
701    m_groupText = "RestoreTreePage";
702    m_splitText = "splitterSizes_1";
703    QSettings settings(m_console->m_dir->name(), "bat");
704    settings.beginGroup(m_groupText);
705    m_splitter->restoreState(settings.value(m_splitText).toByteArray());
706    settings.endGroup();
707 }
708
709 /*
710  * This is a funcion to accomplish the one thing I struggled to figure out what
711  * was taking so long.  It add the icons, but after the tree is made.  Seemed to
712  * work fast after changing from png to png file for graphic.
713  */
714 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
715 {
716    int childCount = item->childCount();
717    for (int i=0; i<childCount; i++) {
718       QTreeWidgetItem *child = item->child(i);
719       if (child->icon(0).isNull())
720          child->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
721    }
722 }
723
724 /*
725  * Show what jobs meet the criteria and are being used to
726  * populate the directory tree and file and version tables.
727  */
728 void restoreTree::populateJobTable()
729 {
730    QBrush blackBrush(Qt::black);
731
732    if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating the Job Table\n");
733    QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Level" << 
734                                               "Name" << "Purged" << "TU" << "TD");
735    m_toggleUpIndex = headerlist.indexOf("TU");
736    m_toggleDownIndex = headerlist.indexOf("TD");
737    int purgedIndex = headerlist.indexOf("Purged");
738    jobTable->clear();
739    jobTable->setColumnCount(headerlist.size());
740    jobTable->setHorizontalHeaderLabels(headerlist);
741    QString jobQuery =
742       "SELECT Job.Jobid AS Id,Job.EndTime AS EndTime,Job.Level AS Level,"
743       "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 }