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