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