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