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