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