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