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