]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
Show purged jobs in the job table, just make them uncheckable and colored
[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    readSettings();
54    dockPage();
55    m_winRegExpDrive.setPattern("^[a-z]:/$");
56    m_winRegExpPath.setPattern("^[a-z]:/");
57    m_slashregex.setPattern("/");
58    m_debugCnt = 0;
59    m_debugTrap = true;
60
61    QGridLayout *m_gridLayout = new QGridLayout(this);
62    m_gridLayout->setSpacing(6);
63    m_gridLayout->setMargin(9);
64    m_gridLayout->setObjectName(QString::fromUtf8("m_gridLayout"));
65
66    QSplitter *splitter_2 = new QSplitter(Qt::Vertical, this);
67    QScrollArea *area = new QScrollArea();
68    area->setObjectName(QString::fromUtf8("area"));
69    area->setWidget(widget);
70    area->setWidgetResizable(true);
71    splitter_2->addWidget(splitter);
72    splitter_2->addWidget(area);
73
74    m_gridLayout->addWidget(splitter_2, 0, 0, 1, 1);
75
76    /* progress widgets */
77    prBar1->setVisible(false);
78    prBar2->setVisible(false);
79    prLabel1->setVisible(false);
80    prLabel2->setVisible(false);
81
82    /* Set Defaults for check and spin for limits */
83    limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
84    limitSpinBox->setValue(mainWin->m_recordLimitVal);
85    daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
86    daysSpinBox->setValue(mainWin->m_daysLimitVal);
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.endGroup();
624 }
625
626 /*
627  * Read and restore user settings associated with this page
628  */
629 void restoreTree::readSettings()
630 {
631    QSettings settings(m_console->m_dir->name(), "bat");
632    settings.beginGroup("RestoreTree");
633    splitter->restoreState(settings.value("splitterSizes").toByteArray());
634    settings.endGroup();
635 }
636
637 /*
638  * This is a funcion to accomplish the one thing I struggled to figure out what
639  * was taking so long.  It add the icons, but after the tree is made.  Seemed to
640  * work fast after changing from png to png file for graphic.
641  */
642 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
643 {
644    int childCount = item->childCount();
645    for (int i=0; i<childCount; i++) {
646       QTreeWidgetItem *child = item->child(i);
647       if (child->icon(0).isNull())
648          child->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
649    }
650 }
651
652 /*
653  * I wanted a table to show what jobs meet the criterion and are being used to
654  * populate the directory tree and file and version tables.
655  */
656 void restoreTree::populateJobTable()
657 {
658    QBrush blackBrush(Qt::black);
659
660    if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating the Job Table\n");
661    QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Level" << "Name" << "Purged" << "TU" << "TD");
662    m_toggleUpIndex = headerlist.indexOf("TU");
663    m_toggleDownIndex = headerlist.indexOf("TD");
664    int purgedIndex = headerlist.indexOf("Purged");
665    jobTable->clear();
666    jobTable->setColumnCount(headerlist.size());
667    jobTable->setHorizontalHeaderLabels(headerlist);
668    QString jobQuery =
669       "SELECT Job.Jobid AS Id, Job.EndTime AS EndTime, Job.Level AS Level, Job.Name AS JobName, Job.purgedfiles AS Purged"
670       " FROM Job"
671       /* INNER JOIN FileSet eliminates all restore jobs */
672       " INNER JOIN Client ON (Job.ClientId=Client.ClientId)"
673       " INNER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)"
674       " WHERE"
675       " Client.Name='" + clientCombo->currentText() + "'";
676    if ((jobCombo->currentIndex() >= 0) && (jobCombo->currentText() != "Any")) {
677       jobQuery += " AND Job.name = '" + jobCombo->currentText() + "'";
678    }
679    if ((fileSetCombo->currentIndex() >= 0) && (fileSetCombo->currentText() != "Any")) {
680       jobQuery += " AND FileSet.FileSet='" + fileSetCombo->currentText() + "'";
681    }
682    /* If Limit check box For limit by days is checked  */
683    if (daysCheckBox->checkState() == Qt::Checked) {
684       QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
685       QString since = stamp.toString(Qt::ISODate);
686       jobQuery += " AND Job.Starttime>'" + since + "'";
687    }
688    //jobQuery += " AND Job.purgedfiles=0";
689    jobQuery += " ORDER BY Job.EndTime DESC";
690    /* If Limit check box for limit records returned is checked  */
691    if (limitCheckBox->checkState() == Qt::Checked) {
692       QString limit;
693       limit.setNum(limitSpinBox->value());
694       jobQuery += " LIMIT " + limit;
695    }
696    if (mainWin->m_sqlDebug)
697       Pmsg1(000, "Query cmd : %s\n", jobQuery.toUtf8().data());
698
699    QStringList results;
700    if (m_console->sql_cmd(jobQuery, results)) {
701    
702       QTableWidgetItem* tableItem;
703       QString field;
704       QStringList fieldlist;
705       jobTable->setRowCount(results.size());
706
707       int row = 0;
708       /* Iterate through the record returned from the query */
709       foreach (QString resultline, results) {
710          fieldlist = resultline.split("\t");
711          int column = 0;
712          /* remove directory */
713          if (fieldlist[0].trimmed() != "") {
714             /* Iterate through fields in the record */
715             foreach (field, fieldlist) {
716                field = field.trimmed();  /* strip leading & trailing spaces */
717                if (field != "") {
718                   tableItem = new QTableWidgetItem(field, 1);
719                   tableItem->setFlags(0);
720                   tableItem->setForeground(blackBrush);
721                   jobTable->setItem(row, column, tableItem);
722                   if (column == 0) {
723                      bool ok;
724                      int purged = fieldlist[purgedIndex].toInt(&ok, 10); 
725                      if (!((ok) && (purged == 1))) {
726                         Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
727                         tableItem->setFlags(flag);
728                         tableItem->setCheckState(Qt::Checked);
729                         tableItem->setBackground(Qt::green);
730                      } else {
731                         tableItem->setFlags(0);
732                         tableItem->setCheckState(Qt::Unchecked);
733                      }
734                   }
735                   column++;
736                }
737             }
738             tableItem = new QTableWidgetItem(QIcon(QString::fromUtf8(":images/go-up.png")), "", 1);
739             tableItem->setFlags(0);
740             tableItem->setForeground(blackBrush);
741             jobTable->setItem(row, column, tableItem);
742             column++;
743             tableItem = new QTableWidgetItem(QIcon(QString::fromUtf8(":images/go-down.png")), "", 1);
744             tableItem->setFlags(0);
745             tableItem->setForeground(blackBrush);
746             jobTable->setItem(row, column, tableItem);
747             row++;
748          }
749       }
750    }
751    jobTable->resizeColumnsToContents();
752    jobTable->resizeRowsToContents();
753    jobTable->verticalHeader()->hide();
754    jobTable->hideColumn(purgedIndex);
755 }
756
757 void restoreTree::jobTableCellClicked(int row, int column)
758 {
759    if (column == m_toggleUpIndex){
760       int cnt;
761       for (cnt=0; cnt<row+1; cnt++) {
762          QTableWidgetItem *item = jobTable->item(cnt, 0);
763          if (item->flags()) {
764             Qt::CheckState state = item->checkState();
765             if (state == Qt::Checked)
766                item->setCheckState(Qt::Unchecked);
767             else if (state == Qt::Unchecked)
768                item->setCheckState(Qt::Checked);
769          }
770       }
771    }
772    if (column == m_toggleDownIndex){
773       int cnt, max = jobTable->rowCount();
774       for (cnt=row; cnt<max; cnt++) {
775          QTableWidgetItem *item = jobTable->item(cnt, 0);
776          if (item->flags()) {
777             Qt::CheckState state = item->checkState();
778             if (state == Qt::Checked)
779                item->setCheckState(Qt::Unchecked);
780             else if (state == Qt::Unchecked)
781                item->setCheckState(Qt::Checked);
782          }
783       }
784    }
785 }
786
787 /*
788  * When a directory item is "changed" check the state of the checkable item
789  * to see if it is different than what it was which is stored in Qt::UserRole
790  * of the 2nd column, column 1, of the tree widget.
791  */
792 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, int /*column*/)
793 {
794    Qt::CheckState prevState = (Qt::CheckState)item->data(1, Qt::UserRole).toInt();
795    Qt::CheckState curState = item->checkState(0);
796    QTreeWidgetItem* parent = item->parent();
797    Qt::CheckState parState;
798    if (parent) parState = parent->checkState(0);
799    else parState = (Qt::CheckState)3;
800    if (mainWin->m_rtDirICDebug) {
801       QString msg = QString("directory item OBJECT has changed prev=%1 cur=%2 par=%3 dir=%4\n")
802          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
803       Pmsg1(000, "%s", msg.toUtf8().data()); }
804    /* I only care when the check state changes */
805    if (prevState == curState) {
806       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning Early\n");
807       return;
808    }
809
810    if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (parState != Qt::Unchecked)) {
811       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::PartiallyChecked\n");
812       directoryTreeDisconnectedSet(item, Qt::PartiallyChecked);
813       curState = Qt::PartiallyChecked;
814    }
815    if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked)) {
816       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::Unchecked\n");
817       directoryTreeDisconnectedSet(item, Qt::Unchecked);
818       curState = Qt::Unchecked;
819    }
820    if (mainWin->m_rtDirICDebug) {
821       QString msg = QString("directory item CHECKSTATE has changed prev=%1 cur=%2 par=%3 dir=%4\n")
822          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
823       Pmsg1(000, "%s", msg.toUtf8().data()); }
824
825    item->setData(1, Qt::UserRole, QVariant(curState));
826    Qt::CheckState childState = curState;
827    if (childState == Qt::Checked)
828       childState = Qt::PartiallyChecked;
829    setCheckofChildren(item, childState);
830
831    /* Remove items from the exception lists.  The multi exception list is my index
832     * of what exceptions can be removed when the directory is known*/
833    QString directory = item->data(0, Qt::UserRole).toString();
834    QStringList fullPathList = m_fileExceptionMulti.values(directory);
835    int fullPathListCount = fullPathList.count();
836    if ((mainWin->m_rtDirICDebug) && fullPathListCount) Pmsg2(000, "Will attempt to remove file exceptions for %s count %i\n", directory.toUtf8().data(), fullPathListCount);
837    foreach (QString fullPath, fullPathList) {
838       /* If there is no value in the hash for the key fullPath a value of 3 will be returned
839        * which will match no Qt::xxx values */
840       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
841       if (mainWin->m_rtDirICDebug) Pmsg2(000, "hashState=%i childState=%i\n", hashState, childState);
842       if (hashState == Qt::Unchecked) {
843          fileExceptionRemove(fullPath, directory);
844          m_versionExceptionHash.remove(fullPath);
845          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal A\n");
846       }
847       if (hashState == Qt::Checked) {
848          fileExceptionRemove(fullPath, directory);
849          m_versionExceptionHash.remove(fullPath);
850          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal B\n");
851       }
852    }
853
854    if (item == directoryTree->currentItem()) {
855       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Will attempt to update File Table Checks\n");
856       updateFileTableChecks();
857       versionTable->clear();
858       versionTable->setRowCount(0);
859       versionTable->setColumnCount(0);
860    }
861    if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning At End\n");
862 }
863
864 /*
865  * When a directory item check state is changed, this function iterates through
866  * all subdirectories and sets all to the passed state, which is either partially
867  * checked or unchecked.
868  */
869 void restoreTree::setCheckofChildren(QTreeWidgetItem *item, Qt::CheckState state)
870 {
871    int childCount;
872    childCount = item->childCount();
873    for (int i=0; i<childCount; i++) {
874       QTreeWidgetItem *child = item->child(i);
875       child->setData(1, Qt::UserRole, QVariant(state));
876       child->setCheckState(0, state);
877       setCheckofChildren(child, state);
878    }
879 }
880
881 /*
882  * When a File Table Item is "changed" check to see if the state of the checkable
883  * item has changed which is stored in m_fileCheckStateList
884  * If changed store in a hash m_fileExceptionHash that whether this file should be
885  * restored or not.
886  * Called as a slot, connected after populated (after directory current changed called)
887  */
888 void restoreTree::fileTableItemChanged(QTableWidgetItem *item)
889 {
890    /* get the previous and current check states */
891    int row = fileTable->row(item);
892    Qt::CheckState prevState;
893    /* prevent a segfault */
894    prevState = m_fileCheckStateList[row];
895    Qt::CheckState curState = item->checkState();
896
897    /* deterimine the default state from the state of the directory */
898    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
899    Qt::CheckState dirState = (Qt::CheckState)dirTreeItem->data(1, Qt::UserRole).toInt();
900    Qt::CheckState defState = Qt::PartiallyChecked;
901    if (dirState == Qt::Unchecked) defState = Qt::Unchecked;
902
903    /* determine if it is already in the m_fileExceptionHash */
904    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
905    QString file = item->text();
906    QString fullPath = directory + file;
907    Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
908    int verJobNum = m_versionExceptionHash.value(fullPath, 0);
909
910    if (mainWin->m_rtFileTabICDebug) {
911       QString msg = QString("filerow=%1 prev=%2 cur=%3 def=%4 hash=%5 dir=%6 verJobNum=%7\n")
912          .arg(row).arg(prevState).arg(curState).arg(defState).arg(hashState).arg(dirState).arg(verJobNum);
913       Pmsg1(000, "%s", msg.toUtf8().data()); }
914
915    /* Remove the hash if currently checked previously unchecked and directory is checked or partial */
916    if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (dirState == Qt::Unchecked)) {
917       /* it can behave as defaulted so current of unchecked is fine */
918       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
919       fileExceptionRemove(fullPath, directory);
920       m_versionExceptionHash.remove(fullPath);
921    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
922       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionInsert here\n");
923       fileExceptionInsert(fullPath, directory, Qt::Unchecked);
924    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0) && (defState == Qt::PartiallyChecked)) {
925       /* filerow=2 prev=0 cur=2 def=1 hash=0 dir=2 verJobNum=0 */
926       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove here\n");
927       fileExceptionRemove(fullPath, directory);
928    } else if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (defState == Qt::PartiallyChecked) && (verJobNum != 0) && (hashState == Qt::Checked)) {
929       /* Check dir, check version, attempt uncheck in file
930        * filerow=4 prev=2 cur=0 def=1 hash=2 dir=2 verJobNum=53 */
931       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
932       fileExceptionRemove(fullPath, directory);
933       m_versionExceptionHash.remove(fullPath);
934    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
935       /* filerow=0 prev=0 cur=2 def=1 hash=0 dirState=2 verJobNum */
936       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will Not remove here\n");
937    } else if (prevState != curState) {
938       if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  THE STATE OF THE Check has changed, Setting StateList[%i] to %i\n", row, curState);
939       /* A user did not set the check state to Partially checked, ignore if so */
940       if (curState != Qt::PartiallyChecked) {
941          if ((defState == Qt::Unchecked) && (prevState == Qt::PartiallyChecked) && (curState == Qt::Unchecked)) {
942             if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "  got here\n");
943          } else {
944             if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
945             fileExceptionInsert(fullPath, directory, curState);
946          }
947       } else {
948          if (mainWin->m_rtFileTabICDebug) Pmsg1(000, "Removing version hash for %s\n", fullPath.toUtf8().data());
949          /* programattically been changed back to a default state of Qt::PartiallyChecked remove the version hash here */
950          m_versionExceptionHash.remove(fullPath);
951       }
952    }
953
954    updateFileTableChecks();
955    updateVersionTableChecks();
956 }
957
958 /*
959  * function to insert keys and values to both m_fileExceptionHash and m_fileExceptionMulti
960  */
961 void restoreTree::fileExceptionInsert(QString &fullPath, QString &direcotry, Qt::CheckState state)
962 {
963    m_fileExceptionHash.insert(fullPath, state);
964    m_fileExceptionMulti.insert(direcotry, fullPath);
965    directoryIconStateInsert(fullPath, state);
966 }
967
968 /*
969  * function to remove keys from both m_fileExceptionHash and m_fileExceptionMulti
970  */
971 void restoreTree::fileExceptionRemove(QString &fullPath, QString &directory)
972 {
973    m_fileExceptionHash.remove(fullPath);
974    /* pull the list of values in the multi */
975    QStringList fullPathList = m_fileExceptionMulti.values(directory);
976    /* get the index of the fullpath to remove */
977    int index = fullPathList.indexOf(fullPath);
978    if (index != -1) {
979       /* remove the desired item in the list */
980       fullPathList.removeAt(index);
981       /* remove the entire list from the multi */
982       m_fileExceptionMulti.remove(directory);
983       /* readd the remaining */
984       foreach (QString fp, fullPathList) {
985          m_fileExceptionMulti.insert(directory, fp);
986       }
987    }
988    directoryIconStateRemove();
989 }
990
991 /*
992  * Overloaded function to be called from the slot and from other places to set the state
993  * of the check marks in the version table
994  */
995 void restoreTree::versionTableItemChanged(QTableWidgetItem *item)
996 {
997    /* get the previous and current check states */
998    int row = versionTable->row(item);
999    QTableWidgetItem *colZeroItem = versionTable->item(row, 0);
1000    Qt::CheckState prevState = m_versionCheckStateList[row];
1001    Qt::CheckState curState = (Qt::CheckState)colZeroItem->checkState();
1002    m_versionCheckStateList[row] = curState;
1003
1004    /* deterimine the default state from the state of the file */
1005    QTableWidgetItem *fileTableItem = fileTable->currentItem();
1006    Qt::CheckState fileState = (Qt::CheckState)fileTableItem->checkState();
1007
1008    /* determine the default state */
1009    Qt::CheckState defState;
1010    if (row == 0) {
1011       defState = Qt::PartiallyChecked;
1012       if (fileState == Qt::Unchecked)
1013          defState = Qt::Unchecked;
1014    }
1015    else
1016       defState = Qt::Unchecked;
1017
1018    /* determine if it is already in the versionExceptionHash */
1019    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
1020    Qt::CheckState dirState = directoryTree->currentItem()->checkState(0);
1021    QString file = fileTableItem->text();
1022    QString fullPath = directory + file;
1023    int thisJobNum = colZeroItem->text().toInt();
1024    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1025
1026    if (mainWin->m_rtVerTabICDebug) {
1027       QString msg = QString("versrow=%1 prev=%2 cur=%3 def=%4 dir=%5 hashJobNum=%6 thisJobNum=%7 filestate=%8 fec=%9 vec=%10\n")
1028          .arg(row).arg(prevState).arg(curState).arg(defState).arg(dirState).arg(hashJobNum).arg(thisJobNum).arg(fileState)
1029          .arg(m_fileExceptionHash.count()).arg(m_versionExceptionHash.count());
1030       Pmsg1(000, "%s", msg.toUtf8().data()); }
1031    /* if changed from partially checked to checked, make it unchecked */
1032    if ((curState == Qt::Checked) && (row == 0) && (fileState == Qt::Unchecked)) {
1033       if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "Setting to Qt::Checked\n");
1034       fileTableItem->setCheckState(Qt::Checked);
1035    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (row == 0) && (fileState == Qt::Checked) && (dirState == Qt::Unchecked)) {
1036       //versrow=0 prev=1 cur=2 def=1 dir=0 hashJobNum=0 thisJobNum=64 filestate=2 fec=1 vec=0
1037       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "fileExceptionRemove %s, %i\n", fullPath.toUtf8().data());
1038       fileExceptionRemove(fullPath, directory);
1039    } else if ((curState == Qt::Checked) && (row == 0) && (hashJobNum != 0) && (dirState != Qt::Unchecked)) {
1040       //versrow=0 prev=0 cur=2 def=1 dir=2 hashJobNum=53 thisJobNum=64 filestate=2 fec=1 vec=1
1041       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
1042       m_versionExceptionHash.remove(fullPath);
1043       fileExceptionRemove(fullPath, directory);
1044    } else if ((curState == Qt::Checked) && (row == 0)) {
1045       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
1046       m_versionExceptionHash.remove(fullPath);
1047    } else if (prevState != curState) {
1048       if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "  THE STATE OF THE version Check has changed, Setting StateList[%i] to %i\n", row, curState);
1049       if ((curState == Qt::Checked) || (curState == Qt::PartiallyChecked) && (row != 0)) {
1050          if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_versionExceptionHash %s, %i\n", fullPath.toUtf8().data(), thisJobNum);
1051          m_versionExceptionHash.insert(fullPath, thisJobNum);
1052          if (fileState != Qt::Checked) {
1053             if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
1054             fileExceptionInsert(fullPath, directory, curState);
1055          }
1056       } else {
1057          if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "got here\n");
1058       }
1059    } else {
1060      if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "no conditions met\n");
1061    }
1062
1063    updateFileTableChecks();
1064    updateVersionTableChecks();
1065 }
1066
1067 /*
1068  * Simple function to set the check state in the file table by disconnecting the
1069  * signal/slot the setting then reconnecting the signal/slot
1070  */
1071 void restoreTree::fileTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state, bool color)
1072 {
1073    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1074            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
1075    item->setCheckState(state);
1076    if (color) item->setBackground(Qt::yellow);
1077    else item->setBackground(Qt::white);
1078    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1079            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
1080 }
1081
1082 /*
1083  * Simple function to set the check state in the version table by disconnecting the
1084  * signal/slot the setting then reconnecting the signal/slot
1085  */
1086 void restoreTree::versionTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state)
1087 {
1088    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1089            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
1090    item->setCheckState(state);
1091    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
1092            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
1093 }
1094
1095 /*
1096  * Simple function to set the check state in the directory tree by disconnecting the
1097  * signal/slot the setting then reconnecting the signal/slot
1098  */
1099 void restoreTree::directoryTreeDisconnectedSet(QTreeWidgetItem *item, Qt::CheckState state)
1100 {
1101    disconnect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
1102            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
1103    item->setCheckState(0, state);
1104    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
1105            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
1106 }
1107
1108 /*
1109  * Simplify the updating of the check state in the File table by iterating through
1110  * each item in the file table to determine it's appropriate state.
1111  * !! Will probably want to concoct a way to do this without iterating for the possibility
1112  * of the very large directories.
1113  */
1114 void restoreTree::updateFileTableChecks()
1115 {
1116    /* deterimine the default state from the state of the directory */
1117    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
1118    Qt::CheckState dirState = dirTreeItem->checkState(0);
1119
1120    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
1121
1122    /* Update the items in the version table */
1123    int rcnt = fileTable->rowCount();
1124    for (int row=0; row<rcnt; row++) {
1125       QTableWidgetItem* item = fileTable->item(row, 0);
1126
1127       Qt::CheckState curState = item->checkState();
1128       Qt::CheckState newState = Qt::PartiallyChecked;
1129       if (dirState == Qt::Unchecked) newState = Qt::Unchecked;
1130
1131       /* determine if it is already in the m_fileExceptionHash */
1132       QString file = item->text();
1133       QString fullPath = dirName + file;
1134       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1135       int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1136
1137       if (hashState != 3) newState = hashState;
1138
1139       if (mainWin->m_rtUpdateFTDebug) {
1140          QString msg = QString("file row=%1 cur=%2 hash=%3 new=%4 dirState=%5\n")
1141             .arg(row).arg(curState).arg(hashState).arg(newState).arg(dirState);
1142          Pmsg1(000, "%s", msg.toUtf8().data());
1143       }
1144
1145       bool docolor = false;
1146       if (hashJobNum != 0) docolor = true;
1147       bool isyellow = item->background().color() == QColor(Qt::yellow);
1148       if ((newState != curState) || (hashState == 3) || ((isyellow && !docolor) || (!isyellow && docolor)))
1149          fileTableDisconnectedSet(item, newState, docolor);
1150       m_fileCheckStateList[row] = newState;
1151    }
1152 }
1153
1154 /*
1155  * Simplify the updating of the check state in the Version table by iterating through
1156  * each item in the file table to determine it's appropriate state.
1157  */
1158 void restoreTree::updateVersionTableChecks()
1159 {
1160    /* deterimine the default state from the state of the directory */
1161    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
1162    Qt::CheckState dirState = dirTreeItem->checkState(0);
1163    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
1164
1165    /* deterimine the default state from the state of the file */
1166    QTableWidgetItem *fileTableItem = fileTable->item(fileTable->currentRow(), 0);
1167    Qt::CheckState fileState = fileTableItem->checkState();
1168    QString file = fileTableItem->text();
1169    QString fullPath = dirName + file;
1170    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1171
1172    /* Update the items in the version table */
1173    int cnt = versionTable->rowCount();
1174    for (int row=0; row<cnt; row++) {
1175       QTableWidgetItem* item = versionTable->item(row, 0);
1176
1177       Qt::CheckState curState = item->checkState();
1178       Qt::CheckState newState = Qt::Unchecked;
1179
1180       if ((row == 0) && (fileState != Qt::Unchecked) && (hashJobNum == 0))
1181          newState = Qt::PartiallyChecked;
1182       /* determine if it is already in the versionExceptionHash */
1183       if (hashJobNum) {
1184          int thisJobNum = item->text().toInt();
1185          if (thisJobNum == hashJobNum)
1186             newState = Qt::Checked;
1187       }
1188       if (mainWin->m_rtChecksDebug) {
1189          QString msg = QString("ver row=%1 cur=%2 hashJobNum=%3 new=%4 dirState=%5\n")
1190             .arg(row).arg(curState).arg(hashJobNum).arg(newState).arg(dirState);
1191          Pmsg1(000, "%s", msg.toUtf8().data());
1192       }
1193       if (newState != curState)
1194          versionTableDisconnectedSet(item, newState);
1195       m_versionCheckStateList[row] = newState;
1196    }
1197 }
1198
1199 /*
1200  * Quick subroutine to "return via subPaths" a list of subpaths when passed a fullPath
1201  */
1202 void restoreTree::fullPathtoSubPaths(QStringList &subPaths, QString &fullPath_in)
1203 {
1204    int index;
1205    bool done = false;
1206    QString fullPath = fullPath_in;
1207    QString direct, path;
1208    while (((index = m_slashregex.lastIndexIn(fullPath, -2)) != -1) && (!done)) {
1209       direct = path = fullPath;
1210       path.replace(index+1, fullPath.length()-index-1, "");
1211       direct.replace(0, index+1, "");
1212       if (false) {
1213          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1214                     .arg(fullPath.length()).arg(index).arg(path).arg(direct);
1215          Pmsg0(000, msg.toUtf8().data());
1216       }
1217       fullPath = path;
1218       subPaths.append(fullPath);
1219    }
1220 }
1221
1222 /*
1223  * A Function to set the icon state and insert a record into
1224  * m_directoryIconStateHash when an exception is added by the user
1225  */
1226 void restoreTree::directoryIconStateInsert(QString &fullPath, Qt::CheckState excpState)
1227 {
1228    QStringList paths;
1229    fullPathtoSubPaths(paths, fullPath);
1230    /* an exception that causes the item in the file table to be "Checked" has occured */
1231    if (excpState == Qt::Checked) {
1232       bool foundAsUnChecked = false;
1233       QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1234       if (firstItem) {
1235          if (firstItem->checkState(0) == Qt::Unchecked)
1236             foundAsUnChecked = true;
1237       }
1238       if (foundAsUnChecked) {
1239           /* as long as directory item is Unchecked, set icon state to "green check" */
1240          bool done = false;
1241          QListIterator<QString> siter(paths);
1242          while (siter.hasNext() && !done) {
1243             QString path = siter.next();
1244             QTreeWidgetItem *item = m_dirPaths.value(path);
1245             if (item) {
1246                if (item->checkState(0) != Qt::Unchecked)
1247                   done = true;
1248                else {
1249                   directorySetIcon(1, FolderGreenChecked, path, item);
1250                   if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1251                }
1252             }
1253          }
1254       } else {
1255          /* if it is partially checked or fully checked insert green Check until a unchecked is found in the path */
1256          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert Aqua %s\n", paths[0].toUtf8().data());
1257          bool done = false;
1258          QListIterator<QString> siter(paths);
1259          while (siter.hasNext() && !done) {
1260             QString path = siter.next();
1261             QTreeWidgetItem *item = m_dirPaths.value(path);
1262             if (item) {  /* if the directory item is checked, set icon state to unchecked "green check" */
1263                if (item->checkState(0) == Qt::Checked)
1264                   done = true;
1265                directorySetIcon(1, FolderGreenChecked, path, item);
1266                if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1267             }
1268          }
1269       }
1270    }
1271    /* an exception that causes the item in the file table to be "Unchecked" has occured */
1272    if (excpState == Qt::Unchecked) {
1273       bool done = false;
1274       QListIterator<QString> siter(paths);
1275       while (siter.hasNext() && !done) {
1276          QString path = siter.next();
1277          QTreeWidgetItem *item = m_dirPaths.value(path);
1278          if (item) {  /* if the directory item is checked, set icon state to unchecked "white check" */
1279             if (item->checkState(0) == Qt::Checked)
1280                done = true;
1281             directorySetIcon(1, FolderWhiteChecked, path, item);
1282             if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1283          }
1284       }
1285    }
1286 }
1287
1288 /*
1289  * A function to set the icon state back to "folder" and to remove a record from
1290  * m_directoryIconStateHash when an exception is removed by a user.
1291  */
1292 void restoreTree::directoryIconStateRemove()
1293 {
1294    QHash<QString, int> shouldBeIconStateHash;
1295    /* First determine all paths with icons that should be checked with m_fileExceptionHash */
1296    /* Use iterator tera to iterate through m_fileExceptionHash */
1297    QHashIterator<QString, Qt::CheckState> tera(m_fileExceptionHash);
1298    while (tera.hasNext()) {
1299       tera.next();
1300       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Alpha Key %s value %i\n", tera.key().toUtf8().data(), tera.value());
1301
1302       QString keyPath = tera.key();
1303       Qt::CheckState state = tera.value();
1304
1305       QStringList paths;
1306       fullPathtoSubPaths(paths, keyPath);
1307       /* if the state of the item in m_fileExceptionHash is checked 
1308        * each of the subpaths should be "Checked Green" */
1309       if (state == Qt::Checked) {
1310
1311          bool foundAsUnChecked = false;
1312          QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1313          if (firstItem) {
1314             if (firstItem->checkState(0) == Qt::Unchecked)
1315                foundAsUnChecked = true;
1316          }
1317          if (foundAsUnChecked) {
1318             /* The right most directory is Unchecked, iterate leftwards
1319              * as long as directory item is Unchecked, set icon state to "green check" */
1320             bool done = false;
1321             QListIterator<QString> siter(paths);
1322             while (siter.hasNext() && !done) {
1323                QString path = siter.next();
1324                QTreeWidgetItem *item = m_dirPaths.value(path);
1325                if (item) {
1326                   if (item->checkState(0) != Qt::Unchecked)
1327                      done = true;
1328                   else {
1329                      shouldBeIconStateHash.insert(path, FolderGreenChecked);
1330                      if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1331                   }
1332                }
1333             }
1334          }
1335          else {
1336             /* The right most directory is Unchecked, iterate leftwards
1337              * until directory item is Checked, set icon state to "green check" */
1338             bool done = false;
1339             QListIterator<QString> siter(paths);
1340             while (siter.hasNext() && !done) {
1341                QString path = siter.next();
1342                QTreeWidgetItem *item = m_dirPaths.value(path);
1343                if (item) {
1344                   if (item->checkState(0) == Qt::Checked)
1345                      done = true;
1346                   shouldBeIconStateHash.insert(path, FolderGreenChecked);
1347                }
1348             }
1349          }
1350       }
1351       /* if the state of the item in m_fileExceptionHash is UNChecked
1352        * each of the subpaths should be "Checked white" until the tree item
1353        * which represents that path is Qt::Checked */
1354       if (state == Qt::Unchecked) {
1355          bool done = false;
1356          QListIterator<QString> siter(paths);
1357          while (siter.hasNext() && !done) {
1358             QString path = siter.next();
1359             QTreeWidgetItem *item = m_dirPaths.value(path);
1360             if (item) {
1361                if (item->checkState(0) == Qt::Checked)
1362                   done = true;
1363                shouldBeIconStateHash.insert(path, FolderWhiteChecked);
1364             }
1365          }
1366       }
1367    }
1368    /* now iterate through m_directoryIconStateHash which are the items that are checked
1369     * and remove all of those that are not in shouldBeIconStateHash */
1370    QHashIterator<QString, int> iter(m_directoryIconStateHash);
1371    while (iter.hasNext()) {
1372       iter.next();
1373       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Beta Key %s value %i\n", iter.key().toUtf8().data(), iter.value());
1374
1375       QString keyPath = iter.key();
1376       if (shouldBeIconStateHash.value(keyPath)) {
1377          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "WAS found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1378          //newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1379          int newval = shouldBeIconStateHash.value(keyPath);
1380          newval = ~newval;
1381          newval = newval & FolderBothChecked;
1382          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1383          if (item)
1384             directorySetIcon(0, newval, keyPath, item);
1385       } else {
1386          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "NOT found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1387          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1388          if (item)
1389             directorySetIcon(0, FolderBothChecked, keyPath, item);
1390             //item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
1391             //m_directoryIconStateHash.remove(keyPath);
1392       }
1393    }
1394 }
1395
1396 void restoreTree::directorySetIcon(int operation, int change, QString &path, QTreeWidgetItem* item) {
1397    int newval;
1398    /* we are adding a check type white or green */
1399    if (operation > 0) {
1400       /* get the old val and "bitwise OR" with the change */
1401       newval = m_directoryIconStateHash.value(path, FolderUnchecked) | change;
1402       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1403       m_directoryIconStateHash.insert(path, newval);
1404    } else {
1405    /* we are removing a check type white or green */
1406       newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1407       if (newval == 0) {
1408          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Removing from m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1409          m_directoryIconStateHash.remove(path);
1410       }
1411       else {
1412          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1413          m_directoryIconStateHash.insert(path, newval);
1414       }
1415    }
1416    if (newval == FolderUnchecked)
1417       item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
1418    else if (newval == FolderGreenChecked)
1419       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderchecked.png")));
1420    else if (newval == FolderWhiteChecked)
1421       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderunchecked.png")));
1422    else if (newval == FolderBothChecked)
1423       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderbothchecked.png")));
1424 }
1425
1426 /*
1427  * Test Button
1428  */
1429 void restoreTree::restoreButtonPushed()
1430 {
1431    /* Set progress bars and repaint */
1432    prLabel1->setVisible(true);
1433    prLabel1->setText("Task 1 of 3");
1434    prLabel2->setVisible(true);
1435    prLabel2->setText("Processing Checked directories");
1436    prBar1->setVisible(true);
1437    prBar1->setRange(0, 3);
1438    prBar1->setValue(0);
1439    prBar2->setVisible(true);
1440    prBar2->setRange(0, 0);
1441    repaint();
1442    QMultiHash<int, QString> versionFilesMulti;
1443    int vFMCounter = 0;
1444    QHash <QString, bool> fullPathDone;
1445    QHash <QString, int> fileIndexHash;
1446    if ((mainWin->m_rtRestore1Debug) || (mainWin->m_rtRestore2Debug) || (mainWin->m_rtRestore3Debug))
1447       Pmsg0(000, "In restoreTree::restoreButtonPushed\n");
1448    /* Use a tree widget item iterator to count directories for the progress bar */
1449    QTreeWidgetItemIterator diterc(directoryTree, QTreeWidgetItemIterator::Checked);
1450    int ditcount = 0;
1451    while (*diterc) {
1452       ditcount += 1;
1453       ++diterc;
1454    } /* while (*diterc) */
1455    prBar2->setRange(0, ditcount);
1456    prBar2->setValue(0);
1457    ditcount = 0;
1458    /* Use a tree widget item iterator filtering for Checked Items */
1459    QTreeWidgetItemIterator diter(directoryTree, QTreeWidgetItemIterator::Checked);
1460    while (*diter) {
1461       QString directory = (*diter)->data(0, Qt::UserRole).toString();
1462       if (mainWin->m_rtRestore1Debug)
1463       Pmsg1(000, "Directory Checked=\"%s\"\n", directory.toUtf8().data());
1464       /* With a checked directory, query for the files in the directory */
1465
1466       QString cmd =
1467          "SELECT t1.Filename AS Filename, t1.JobId AS JobId, File.FileIndex AS FileIndex"
1468          " FROM"
1469          " ( SELECT Filename.Name AS Filename, MAX(Job.JobId) AS JobId"
1470            " FROM File"
1471              " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1472              " INNER JOIN Path ON (Path.PathId=File.PathId)"
1473              " INNER JOIN Job ON (Job.JobId=File.JobId)"
1474            " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1475            "  AND Job.Jobid IN (" + m_checkedJobs + ")"
1476            " GROUP BY Filename.Name"
1477          ") t1, File "
1478            " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1479            " INNER JOIN Path ON (Path.PathId=File.PathId)"
1480            " INNER JOIN Job ON (Job.JobId=File.JobId)"
1481          " WHERE"
1482            " Path.Path='" + directory + "'"
1483            " AND Filename.Name=t1.Filename"
1484            " AND Job.Jobid=t1.JobId"
1485          " ORDER BY Filename";
1486
1487       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1488       QStringList results;
1489       if (m_console->sql_cmd(cmd, results)) {
1490          QStringList fieldlist;
1491    
1492          int row = 0;
1493          /* Iterate through the record returned from the query */
1494          foreach (QString resultline, results) {
1495             /* Iterate through fields in the record */
1496             int column = 0;
1497             QString fullPath = "";
1498             Qt::CheckState fileExcpState = (Qt::CheckState)4;
1499             fieldlist = resultline.split("\t");
1500             int version = 0;
1501             int fileIndex = 0;
1502             foreach (QString field, fieldlist) {
1503                if (column == 0) {
1504                   fullPath = directory + field;
1505                }
1506                if (column == 1) {
1507                   version = field.toInt();
1508                }
1509                if (column == 2) {
1510                   fileIndex = field.toInt();
1511                }
1512                column++;
1513             }
1514             fileExcpState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1515             
1516             int excpVersion = m_versionExceptionHash.value(fullPath, 0);
1517             if (fileExcpState != Qt::Unchecked) {
1518                QString debugtext;
1519                if (excpVersion != 0) {
1520                   debugtext = QString("*E* version=%1").arg(excpVersion);
1521                   version = excpVersion;
1522                   fileIndex = queryFileIndex(fullPath, excpVersion);
1523                } else
1524                   debugtext = QString("___ version=%1").arg(version);
1525                if (mainWin->m_rtRestore1Debug)
1526                   Pmsg2(000, "Restoring %s File %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1527                fullPathDone.insert(fullPath, 1);
1528                fileIndexHash.insert(fullPath, fileIndex);
1529                versionFilesMulti.insert(version, fullPath);
1530                vFMCounter += 1;
1531             }
1532             row++;
1533          }
1534       }
1535       ditcount += 1;
1536       prBar2->setValue(ditcount);
1537       ++diter;
1538    } /* while (*diter) */
1539    prBar1->setValue(1);
1540    prLabel1->setText("Task 2 of 3");
1541    prLabel2->setText("Processing Exceptions");
1542    prBar2->setRange(0, 0);
1543    repaint();
1544
1545    /* There may be some exceptions not accounted for yet with fullPathDone */
1546    QHashIterator<QString, Qt::CheckState> ftera(m_fileExceptionHash);
1547    while (ftera.hasNext()) {
1548       ftera.next();
1549       QString fullPath = ftera.key();
1550       Qt::CheckState state = ftera.value();
1551       if (state != 0) {
1552          /* now we don't want the ones already done */
1553          if (fullPathDone.value(fullPath, 0) == 0) {
1554             int version = m_versionExceptionHash.value(fullPath, 0);
1555             int fileIndex = 0;
1556             QString debugtext = "";
1557             if (version != 0) {
1558                fileIndex = queryFileIndex(fullPath, version);
1559                debugtext = QString("E1* version=%1 fileid=%2").arg(version).arg(fileIndex);
1560             } else {
1561                version = mostRecentVersionfromFullPath(fullPath);
1562                if (version) {
1563                   fileIndex = queryFileIndex(fullPath, version);
1564                   debugtext = QString("E2* version=%1 fileid=%2").arg(version).arg(fileIndex);
1565                } else
1566                   debugtext = QString("Error det vers").arg(version);
1567             }
1568             if (mainWin->m_rtRestore1Debug)
1569                Pmsg2(000, "Restoring %s file %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1570             versionFilesMulti.insert(version, fullPath);
1571             vFMCounter += 1;
1572             fileIndexHash.insert(fullPath, fileIndex);
1573          } /* if fullPathDone.value(fullPath, 0) == 0 */
1574       } /* if state != 0 */
1575    } /* while ftera.hasNext */
1576    /* The progress bars for the next step */
1577    prBar1->setValue(2);
1578    prLabel1->setText("Task 3 of 3");
1579    prLabel2->setText("Filling Database Table");
1580    prBar2->setRange(0, vFMCounter);
1581    vFMCounter = 0;
1582    prBar2->setValue(vFMCounter);
1583    repaint();
1584
1585    /* now for the final spit out of the versions and lists of files for each version */
1586    QHash<int, int> doneKeys;
1587    QHashIterator<int, QString> vFMiter(versionFilesMulti);
1588    QString tempTable = "";
1589    QList<int> jobList;
1590    while (vFMiter.hasNext()) {
1591       vFMiter.next();
1592       int fversion = vFMiter.key();
1593       /* did not succeed in getting an iterator to work as expected on versionFilesMulti so use doneKeys */
1594       if (doneKeys.value(fversion, 0) == 0) {
1595          if (tempTable == "") {
1596             QSettings settings("www.bacula.org", "bat");
1597             settings.beginGroup("Restore");
1598             int counter = settings.value("Counter", 1).toInt();
1599             settings.setValue("Counter", counter+1);
1600             settings.endGroup();
1601             tempTable = "restore_" + QString("%1").arg(qrand()) + "_" + QString("%1").arg(counter);
1602             QString sqlcmd = "CREATE TEMPORARY TABLE " + tempTable + " (JobId INTEGER, FileIndex INTEGER)";
1603             if (mainWin->m_sqlDebug)
1604                Pmsg1(000, "Query cmd : %s ;\n", sqlcmd.toUtf8().data());
1605             QStringList results;
1606             if (!m_console->sql_cmd(sqlcmd, results))
1607                Pmsg1(000, "CREATE TABLE FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1608          }
1609
1610          if (mainWin->m_rtRestore2Debug) Pmsg1(000, "Version->%i\n", fversion);
1611          QStringList fullPathList = versionFilesMulti.values(fversion);
1612          /* create the command to perform the restore */
1613          foreach(QString ffullPath, fullPathList) {
1614             int fileIndex = fileIndexHash.value(ffullPath);
1615             if (mainWin->m_rtRestore2Debug) Pmsg2(000, "  file->%s id %i\n", ffullPath.toUtf8().data(), fileIndex);
1616             QString sqlcmd = "INSERT INTO " + tempTable + " (JobId, FileIndex) VALUES (" + QString("%1").arg(fversion) + ", " + QString("%1").arg(fileIndex) + ")";
1617             if (mainWin->m_rtRestore3Debug)
1618                Pmsg1(000, "Insert cmd : %s\n", sqlcmd.toUtf8().data());
1619             QStringList results;
1620             if (!m_console->sql_cmd(sqlcmd, results))
1621                Pmsg1(000, "INSERT INTO FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1622             prBar2->setValue(++vFMCounter);
1623          } /* foreach fullPathList */
1624          doneKeys.insert(fversion,1);
1625          jobList.append(fversion);
1626       } /*  if (doneKeys.value(fversion, 0) == 0) */
1627    } /* while (vFMiter.hasNext()) */
1628    if (tempTable != "") {
1629       /* a table was made, lets run the job */
1630       QString jobOption = " jobid=\"";
1631       bool first = true;
1632       /* create a list of jobs comma separated */
1633       foreach (int job, jobList) {
1634          if (first) first = false;
1635          else jobOption += ",";
1636          jobOption += QString("%1").arg(job);
1637       }
1638       jobOption += "\"";
1639       QString cmd = QString("restore");
1640       cmd += jobOption +
1641              " client=\"" + m_prevClientCombo + "\"" +
1642              " file=\"?" + tempTable + "\" done";
1643       if (mainWin->m_commandDebug)
1644          Pmsg1(000, "preRestore command \'%s\'\n", cmd.toUtf8().data());
1645       consoleCommand(cmd);
1646    }
1647    /* turn off the progress widgets */
1648    prBar1->setVisible(false);
1649    prBar2->setVisible(false);
1650    prLabel1->setVisible(false);
1651    prLabel2->setVisible(false);
1652 }
1653
1654 int restoreTree::mostRecentVersionfromFullPath(QString &fullPath)
1655 {
1656    int qversion = 0;
1657    QString directory, fileName;
1658    int index = m_slashregex.lastIndexIn(fullPath, -2);
1659    if (index != -1) {
1660       directory = fileName = fullPath;
1661       directory.replace(index+1, fullPath.length()-index-1, "");
1662       fileName.replace(0, index+1, "");
1663       if (false) {
1664          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1665                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1666          Pmsg0(000, msg.toUtf8().data());
1667       }
1668       /* so now we need the latest version from the database */
1669       QString cmd =
1670          "SELECT MAX(Job.JobId)"
1671          " FROM File "
1672          " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1673          " INNER JOIN Path ON (Path.PathId=File.PathId)"
1674          " INNER JOIN Job ON (File.JobId=Job.JobId)"
1675          " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1676          " AND Job.Jobid IN (" + m_checkedJobs + ")"
1677          " AND Filename.Name='" + fileName + "'"
1678          " GROUP BY Filename.Name";
1679  
1680       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1681       QStringList results;
1682       if (m_console->sql_cmd(cmd, results)) {
1683          QStringList fieldlist;
1684          int row = 0;
1685          /* Iterate through the record returned from the query */
1686          foreach (QString resultline, results) {
1687             /* Iterate through fields in the record */
1688             int column = 0;
1689             fieldlist = resultline.split("\t");
1690             foreach (QString field, fieldlist) {
1691                if (column == 0) {
1692                   qversion = field.toInt();
1693                }
1694                column++;
1695             }
1696             row++;
1697          }
1698       }
1699    } /* if (index != -1) */
1700    return qversion;
1701 }
1702
1703
1704 int restoreTree::queryFileIndex(QString &fullPath, int jobId)
1705 {
1706    int qfileIndex = 0;
1707    QString directory, fileName;
1708    int index = m_slashregex.lastIndexIn(fullPath, -2);
1709    if (index != -1) {
1710       directory = fileName = fullPath;
1711       directory.replace(index+1, fullPath.length()-index-1, "");
1712       fileName.replace(0, index+1, "");
1713       if (false) {
1714          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1715                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1716          Pmsg0(000, msg.toUtf8().data());
1717       }
1718       /* so now we need the latest version from the database */
1719       QString cmd =
1720          "SELECT"
1721           " File.FileIndex"
1722          " FROM File"
1723           " INNER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1724           " INNER JOIN Path ON (Path.PathId=File.PathId)"
1725           " INNER JOIN Job ON (File.JobId=Job.JobId)"
1726          " WHERE"
1727           " Path.Path='" + directory + "'"
1728           " AND Filename.Name='" + fileName + "'"
1729           " AND Job.Jobid='" + QString("%1").arg(jobId) + "'"
1730          " GROUP BY File.FileIndex";
1731  
1732       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1733       QStringList results;
1734       if (m_console->sql_cmd(cmd, results)) {
1735          QStringList fieldlist;
1736          int row = 0;
1737          /* Iterate through the record returned from the query */
1738          foreach (QString resultline, results) {
1739             /* Iterate through fields in the record */
1740             int column = 0;
1741             fieldlist = resultline.split("\t");
1742             foreach (QString field, fieldlist) {
1743                if (column == 0) {
1744                   qfileIndex = field.toInt();
1745                }
1746                column++;
1747             }
1748             row++;
1749          }
1750       }
1751    } /* if (index != -1) */
1752    return qfileIndex;
1753 }