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