]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
The result of the following merge: This merges the work in the branch into the head.
[bacula/bacula] / bacula / src / qt-console / restore / restoretree.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28  
29 /*
30  *   Version $Id: restore.cpp 4945 2007-05-31 01:24:28Z bartleyd2 $
31  *
32  *  Restore Class 
33  *
34  *   Kern Sibbald, February MMVII
35  *
36  */ 
37
38 #include "bat.h"
39 #include "restoretree.h"
40 #include "pages.h"
41
42 restoreTree::restoreTree()
43 {
44    setupUi(this);
45    m_name = "Version Browser";
46    pgInitialize();
47    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48    thisitem->setIcon(0, QIcon(QString::fromUtf8(":images/browse.png")));
49
50    m_closeable = true;
51    m_populated = false;
52
53    readSettings();
54    dockPage();
55    m_winRegExpDrive.setPattern("^[a-z]:/$");
56    m_winRegExpPath.setPattern("^[a-z]:/");
57    m_slashregex.setPattern("/");
58    m_debugCnt = 0;
59    m_debugTrap = true;
60 }
61
62 restoreTree::~restoreTree()
63 {
64    writeSettings();
65 }
66
67 /*
68  * Called from the constructor to set up the page widgets and connections.
69  */
70 void restoreTree::setupPage()
71 {
72    connect(refreshButton, SIGNAL(pressed()), this, SLOT(refreshButtonPushed()));
73    connect(testButton, SIGNAL(pressed()), this, SLOT(testButtonPushed()));
74    connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(jobComboChanged(int)));
75    connect(directoryTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
76            this, SLOT(directoryCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
77    connect(directoryTree, SIGNAL(itemExpanded(QTreeWidgetItem *)),
78            this, SLOT(directoryItemExpanded(QTreeWidgetItem *)));
79    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
80            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
81    connect(fileTable, SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
82            this, SLOT(fileCurrentItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
83
84    QStringList titles = QStringList() << "Directories";
85    directoryTree->setHeaderLabels(titles);
86    clientCombo->addItems(m_console->client_list);
87    fileSetCombo->addItem("Any");
88    fileSetCombo->addItems(m_console->fileset_list);
89    jobCombo->addItems(m_console->job_list);
90
91    directoryTree->setContextMenuPolicy(Qt::ActionsContextMenu);
92 }
93
94 /*
95  * When refresh button is pushed, perform a query getting the directories and
96  * use parseDirectory and addDirectory to populate the directory tree with items.
97  */
98 void restoreTree::populateDirectoryTree()
99 {
100    m_slashTrap = false;
101    m_dirPaths.clear();
102    directoryTree->clear();
103    fileTable->clear();
104    fileTable->setRowCount(0);
105    fileTable->setColumnCount(0);
106    versionTable->clear();
107    versionTable->setRowCount(0);
108    versionTable->setColumnCount(0);
109    m_fileExceptionHash.clear();
110    m_fileExceptionMulti.clear();
111    m_versionExceptionHash.clear();
112    m_directoryIconStateHash.clear();
113
114    
115    int clientIndex = clientCombo->currentIndex();
116    int fileSetIndex = fileSetCombo->currentIndex();
117    QString jobComboText = jobCombo->itemText(jobCombo->currentIndex());
118    QString clientComboText = clientCombo->itemText(clientIndex);
119    QString fileSetComboText = fileSetCombo->itemText(fileSetIndex);
120    if ((m_prevJobCombo != jobComboText) || (m_prevClientCombo != clientComboText) || (m_prevFileSetCombo != fileSetComboText)) {
121       m_prevJobCombo =  jobComboText;
122       m_prevClientCombo = clientComboText;
123       m_prevFileSetCombo = fileSetComboText;
124       if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating the Job Table\n");
125
126       m_condition = " Job.name = '" + jobCombo->itemText(jobCombo->currentIndex()) + "'";
127       if ((clientIndex >= 0) && (clientCombo->itemText(clientIndex) != "Any")) {
128          m_condition.append(" AND Client.Name='" + clientCombo->itemText(clientIndex) + "'");
129       }
130       if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
131          m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
132       }
133       m_jobQueryPart =
134          " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
135          " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)"
136          " WHERE" + m_condition +
137          " AND Job.purgedfiles=0";
138       m_jobQuery =
139          "SELECT Job.Jobid"
140          " From Job" + m_jobQueryPart;
141       if (mainWin->m_sqlDebug) {
142          Pmsg1(000, "Query cmd : %s\n", m_jobQuery.toUtf8().data());
143       }
144       populateJobTable();
145    } else {
146       m_JobsCheckedList = "";
147       bool first = true;
148       /* Update the items in the version table */
149       int cnt = jobTable->rowCount();
150       for (int row=0; row<cnt; row++) {
151          QTableWidgetItem* jobItem = jobTable->item(row, 0);
152          if (jobItem->checkState() == Qt::Checked) {
153             if (!first)
154                m_JobsCheckedList += ",";
155             m_JobsCheckedList += jobItem->text();
156             first = false;
157             jobItem->setBackground(Qt::green);
158          } else
159             jobItem->setBackground(Qt::gray);
160       }
161       m_jobQuery = m_JobsCheckedList;
162    }
163
164    QString cmd =
165       "SELECT DISTINCT Path.Path"
166       " FROM Path"
167       " LEFT OUTER JOIN File ON (File.PathId=Path.PathId)"
168       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
169       " WHERE Job.Jobid IN (" + m_jobQuery + ")";
170    if (mainWin->m_sqlDebug) {
171       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
172    }
173    QStringList directories;
174    if (m_console->sql_cmd(cmd, directories)) {
175       if (mainWin->m_miscDebug) {
176          Pmsg1(000, "Done with query %i directories\n", directories.count());
177       }
178       foreach(QString directory, directories) {
179          m_debugCnt += 1;
180          parseDirectory(directory);
181       }
182    }
183 }
184
185 /*
186  * Function to parse a directory into all possible subdirectories, then add to
187  * The tree.
188  */
189 void restoreTree::parseDirectory(QString &dir_in)
190 {
191    /* m_debugTrap is to only print debugs for a few occurances of calling parseDirectory
192     * instead of printing out what could potentially a whole bunch */
193    if (m_debugCnt > 2)
194       m_debugTrap = false;
195    /* Clean up the directory string remove some funny char after last '/' */
196    QRegExp rgx("[^/]$");
197    int lastslash = rgx.indexIn(dir_in);
198    dir_in.replace(lastslash, dir_in.length()-lastslash, "");
199    if ((mainWin->m_miscDebug) && (m_debugTrap))
200       Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
201
202    /* split and add if not in yet */
203    QString direct, path;
204    int index;
205    bool done = false;
206    QStringList pathAfter, dirAfter;
207    /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/ 
208     * if not added into tree, then try /etc/ and somedir/ if not added, then try
209     * / and etc/ .  That should succeed, then add the ones that failed in reverse */
210    while (((index = m_slashregex.lastIndexIn(dir_in, -2)) != -1) && (!done)) {
211       direct = path = dir_in;
212       path.replace(index+1, dir_in.length()-index-1,"");
213       direct.replace(0, index+1, "");
214       if ((mainWin->m_miscDebug) && (m_debugTrap)) {
215          QString msg = QString("length = \"%1\" index = \"%2\" Adding \"%3\" \"%4\"\n")
216                     .arg(dir_in.length()).arg(index).arg(path).arg(direct);
217          Pmsg0(000, msg.toUtf8().data());
218       }
219       if (addDirectory(path, direct)) done = true;
220       else {
221          if ((mainWin->m_miscDebug) && (m_debugTrap))
222             Pmsg0(000, "Saving for later\n");
223          pathAfter.prepend(path);
224          dirAfter.prepend(direct);
225       }
226       dir_in = path;
227    }
228
229    for (int k=0; k<pathAfter.count(); k++) {
230       if (addDirectory(pathAfter[k], dirAfter[k]))
231          if ((mainWin->m_miscDebug) && (m_debugTrap))
232             Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
233       else
234          if ((mainWin->m_miscDebug) && (m_debugTrap))
235             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
236    }
237 }
238
239 /*
240  * Function called from fill directory when a directory is found to see if this
241  * directory exists in the directory pane and then add it to the directory pane
242  */
243 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
244 {
245    QString newdir = newdirr;
246    QString fullPath = m_cwd + newdirr;
247    bool ok = true, added = false;
248
249    if ((mainWin->m_miscDebug) && (m_debugTrap)) {
250       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
251                     .arg(m_cwd)
252                     .arg(newdir);
253       Pmsg0(000, msg.toUtf8().data());
254    }
255
256    if (!m_slashTrap) {
257       /* add unix '/' directory first */
258       if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullPath, 0) == -1)) {
259          m_slashTrap = true;
260          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
261          QString text("/");
262          item->setText(0, text.toUtf8().data());
263          item->setData(0, Qt::UserRole, QVariant(text));
264          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
265          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
266          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
267             Pmsg1(000, "Pre Inserting %s\n", text.toUtf8().data());
268          }
269          m_dirPaths.insert(text, item);
270       }
271       /* no need to check for windows drive if unix */
272       if (m_winRegExpDrive.indexIn(m_cwd, 0) == 0) {
273          /* this is a windows drive add the base widget */
274          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
275          item->setText(0, m_cwd);
276          item->setData(0, Qt::UserRole, QVariant(fullPath));
277          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
278          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
279          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
280             Pmsg0(000, "Added Base \"letter\":/\n");
281          }
282          m_dirPaths.insert(m_cwd, item);
283       }
284    }
285  
286    /* is it already existent ?? */
287    if (!m_dirPaths.contains(fullPath)) {
288       QTreeWidgetItem *item = NULL;
289       QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
290       if (parent) {
291          /* new directories to add */
292          item = new QTreeWidgetItem(parent);
293          item->setText(0, newdir.toUtf8().data());
294          item->setData(0, Qt::UserRole, QVariant(fullPath));
295          item->setCheckState(0, Qt::Unchecked);
296          /* Store the current state of the check status in column 1, which at
297           * this point has no text*/
298          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
299       } else {
300          ok = false;
301          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
302             QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
303                  .arg(m_cwd)
304                  .arg(newdir);
305             Pmsg0(000, msg.toUtf8().data());
306          }
307       }
308       /* insert into hash */
309       if (ok) {
310          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
311             Pmsg1(000, "Inserting %s\n", fullPath.toUtf8().data());
312          }
313          m_dirPaths.insert(fullPath, item);
314          added = true;
315       }
316    }
317    return added;
318 }
319
320 /*
321  * Virtual function which is called when this page is visible on the stack
322  */
323 void restoreTree::currentStackItem()
324 {
325    if(!m_populated) {
326       if (!m_console->preventInUseConnect())
327          return;
328       setupPage();
329       m_populated=true;
330    }
331 }
332
333 /*
334  * Populate the tree when refresh button pushed.
335  */
336 void restoreTree::refreshButtonPushed()
337 {
338    populateDirectoryTree();
339 }
340
341 /*
342  * Set the values of non-job combo boxes to the job defaults
343  */
344 void restoreTree::jobComboChanged(int)
345 {
346    job_defaults job_defs;
347
348    (void)index;
349    job_defs.job_name = jobCombo->currentText();
350    if (m_console->get_job_defaults(job_defs)) {
351       fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
352       clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
353    }
354 }
355
356 /*
357  * Function to populate the file list table
358  */
359 void restoreTree::directoryCurrentItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
360 {
361    if (item == NULL)
362       return;
363
364    m_fileCheckStateList.clear();
365    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
366            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
367    QBrush blackBrush(Qt::black);
368    QString directory = item->data(0, Qt::UserRole).toString();
369    directoryLabel->setText("Present Working Directory : " + directory);
370    QString cmd =
371       "SELECT DISTINCT Filename.Name"
372       " FROM File "
373       " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
374       " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
375       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
376       " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
377       " AND Job.Jobid IN (" + m_jobQuery + ")";
378  
379    QStringList headerlist = (QStringList() << "File Name");
380    fileTable->clear();
381    /* Also clear the version table here */
382    versionTable->clear();
383    versionFileLabel->setText("");
384    versionTable->setRowCount(0);
385    versionTable->setColumnCount(0);
386    fileTable->setColumnCount(headerlist.size());
387    fileTable->setHorizontalHeaderLabels(headerlist);
388
389    if (mainWin->m_sqlDebug) {
390       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
391    }
392    QStringList results;
393    if (m_console->sql_cmd(cmd, results)) {
394    
395       QTableWidgetItem* tableItem;
396       QString field;
397       QStringList fieldlist;
398       fileTable->setRowCount(results.size());
399
400       int row = 0;
401       /* Iterate through the record returned from the query */
402       foreach (QString resultline, results) {
403          /* Iterate through fields in the record */
404          int column = 0;
405          fieldlist = resultline.split("\t");
406          foreach (field, fieldlist) {
407             field = field.trimmed();  /* strip leading & trailing spaces */
408             tableItem = new QTableWidgetItem(field, 1);
409             /* Possible flags are Qt::ItemFlags flag = Qt::ItemIsSelectable | Qt::ItemIsEditablex
410              *  | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable 
411              *  | Qt::ItemIsEnabled | Qt::ItemIsTristate; */
412             tableItem->setForeground(blackBrush);
413             /* Just in case a column ever gets added */
414             if (column == 0) {
415                Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
416                tableItem->setFlags(flag);
417                tableItem->setData(Qt::UserRole, QVariant(directory));
418                fileTable->setItem(row, column, tableItem);
419                m_fileCheckStateList.append(Qt::Unchecked);
420                tableItem->setCheckState(Qt::Unchecked);
421             }
422             column++;
423          }
424          row++;
425       }
426       fileTable->setRowCount(row);
427    }
428    fileTable->resizeColumnsToContents();
429    fileTable->resizeRowsToContents();
430    fileTable->verticalHeader()->hide();
431    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
432            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
433    if (mainWin->m_rtDirCurICDebug) Pmsg0(000, "will update file table checks\n");
434    updateFileTableChecks();
435 }
436
437 /*
438  * Function to populate the version table
439  */
440 void restoreTree::fileCurrentItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
441 {
442    if (fileTableItem == NULL)
443       return;
444
445    m_versionCheckStateList.clear();
446    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
447            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
448
449    QString file = fileTableItem->text();
450    versionFileLabel->setText(file);
451    QString directory = fileTableItem->data(Qt::UserRole).toString();
452
453    QBrush blackBrush(Qt::black);
454    QString cmd = 
455       "SELECT Job.JobId AS JobId, Job.Level AS Type, Job.EndTime AS EndTime, File.Md5 AS MD5"
456       " FROM File"
457       " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
458       " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
459       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
460       " WHERE Filename.Name='" + file + "' AND Path.Path='" + directory + "'"
461       " AND Job.Jobid IN (" + m_jobQuery + ")"
462       " ORDER BY Job.EndTime DESC";
463
464    QStringList headerlist = (QStringList() << "Job Id" << "Type" << "End Time" << "Md5");
465    versionTable->clear();
466    versionTable->setColumnCount(headerlist.size());
467    versionTable->setHorizontalHeaderLabels(headerlist);
468
469    if (mainWin->m_sqlDebug) {
470       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
471    }
472    QStringList results;
473    if (m_console->sql_cmd(cmd, results)) {
474    
475       QTableWidgetItem* tableItem;
476       QString field;
477       QStringList fieldlist;
478       versionTable->setRowCount(results.size());
479
480       int row = 0;
481       /* Iterate through the record returned from the query */
482       foreach (QString resultline, results) {
483          fieldlist = resultline.split("\t");
484          int column = 0;
485          /* remove directory */
486          if (fieldlist[0].trimmed() != "") {
487             /* Iterate through fields in the record */
488             foreach (field, fieldlist) {
489                field = field.trimmed();  /* strip leading & trailing spaces */
490                tableItem = new QTableWidgetItem(field, 1);
491                tableItem->setFlags(0);
492                tableItem->setForeground(blackBrush);
493                tableItem->setData(Qt::UserRole, QVariant(directory));
494                versionTable->setItem(row, column, tableItem);
495
496                if (column == 0) {
497                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
498                   tableItem->setFlags(flag);
499                   m_versionCheckStateList.append(Qt::Unchecked);
500                   tableItem->setCheckState(Qt::Unchecked);
501                }
502                column++;
503             }
504             row++;
505          }
506       }
507    }
508    versionTable->resizeColumnsToContents();
509    versionTable->resizeRowsToContents();
510    versionTable->verticalHeader()->hide();
511    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
512            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
513    updateVersionTableChecks();
514 }
515
516 /*
517  * Save user settings associated with this page
518  */
519 void restoreTree::writeSettings()
520 {
521    QSettings settings(m_console->m_dir->name(), "bat");
522    settings.beginGroup("RestoreTree");
523    settings.setValue("splitterSizes", splitter->saveState());
524    settings.endGroup();
525 }
526
527 /*
528  * Read and restore user settings associated with this page
529  */
530 void restoreTree::readSettings()
531 {
532    QSettings settings(m_console->m_dir->name(), "bat");
533    settings.beginGroup("RestoreTree");
534    splitter->restoreState(settings.value("splitterSizes").toByteArray());
535    settings.endGroup();
536 }
537
538 /*
539  * This is a funcion to accomplish the one thing I struggled to figure out what
540  * was taking so long.  It add the icons, but after the tree is made.  Seemed to
541  * work fast after changing from svg to png file for graphic.
542  */
543 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
544 {
545    int childCount = item->childCount();
546    for (int i=0; i<childCount; i++) {
547       QTreeWidgetItem *child = item->child(i);
548       if (child->icon(0).isNull())
549          child->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
550    }
551 }
552
553 /*
554  * I wanted a table to show what jobs meet the criterion and are being used to
555  * populate the directory tree and file and version tables.
556  */
557 void restoreTree::populateJobTable()
558 {
559    QBrush blackBrush(Qt::black);
560    QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Type");
561    jobTable->clear();
562    jobTable->setColumnCount(headerlist.size());
563    jobTable->setHorizontalHeaderLabels(headerlist);
564    QString jobQuery =
565       "SELECT Job.Jobid AS Id, Job.EndTime AS EndTime, Job.Level AS Level"
566       " FROM Job" + m_jobQueryPart +
567       " ORDER BY Job.EndTime DESC";
568    if (mainWin->m_sqlDebug) {
569       Pmsg1(000, "Query cmd : %s\n", jobQuery.toUtf8().data());
570    }
571
572    QStringList results;
573    if (m_console->sql_cmd(jobQuery, results)) {
574    
575       QTableWidgetItem* tableItem;
576       QString field;
577       QStringList fieldlist;
578       jobTable->setRowCount(results.size());
579
580       int row = 0;
581       /* Iterate through the record returned from the query */
582       foreach (QString resultline, results) {
583          fieldlist = resultline.split("\t");
584          int column = 0;
585          /* remove directory */
586          if (fieldlist[0].trimmed() != "") {
587             /* Iterate through fields in the record */
588             foreach (field, fieldlist) {
589                field = field.trimmed();  /* strip leading & trailing spaces */
590                tableItem = new QTableWidgetItem(field, 1);
591                tableItem->setFlags(0);
592                tableItem->setForeground(blackBrush);
593                jobTable->setItem(row, column, tableItem);
594                if (column == 0) {
595                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
596                   tableItem->setFlags(flag);
597                   tableItem->setCheckState(Qt::Checked);
598                   tableItem->setBackground(Qt::green);
599                }
600                column++;
601             }
602             row++;
603          }
604       }
605    }
606    jobTable->resizeColumnsToContents();
607    jobTable->resizeRowsToContents();
608    jobTable->verticalHeader()->hide();
609 }
610
611 /*
612  * When a directory item is "changed" check the state of the checkable item
613  * to see if it is different than what it was which is stored in Qt::UserRole
614  * of the 2nd column, column 1, of the tree widget.
615  */
616 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, int /*column*/)
617 {
618    Qt::CheckState prevState = (Qt::CheckState)item->data(1, Qt::UserRole).toInt();
619    Qt::CheckState curState = item->checkState(0);
620    QTreeWidgetItem* parent = item->parent();
621    Qt::CheckState parState;
622    if (parent) parState = parent->checkState(0);
623    else parState = (Qt::CheckState)3;
624    if (mainWin->m_rtDirICDebug) {
625       QString msg = QString("directory item OBJECT has changed prev=%1 cur=%2 par=%3 dir=%4\n")
626          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
627       Pmsg1(000, "%s", msg.toUtf8().data()); }
628    /* I only care when the check state changes */
629    if (prevState == curState) {
630       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning Early\n");
631       return;
632    }
633
634    if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (parState != Qt::Unchecked)) {
635       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::PartiallyChecked\n");
636       directoryTreeDisconnectedSet(item, Qt::PartiallyChecked);
637       curState = Qt::PartiallyChecked;
638    }
639    if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked)) {
640       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::Unchecked\n");
641       directoryTreeDisconnectedSet(item, Qt::Unchecked);
642       curState = Qt::Unchecked;
643    }
644    if (mainWin->m_rtDirICDebug) {
645       QString msg = QString("directory item CHECKSTATE has changed prev=%1 cur=%2 par=%3 dir=%4\n")
646          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
647       Pmsg1(000, "%s", msg.toUtf8().data()); }
648
649    item->setData(1, Qt::UserRole, QVariant(curState));
650    Qt::CheckState childState = curState;
651    if (childState == Qt::Checked)
652       childState = Qt::PartiallyChecked;
653    setCheckofChildren(item, childState);
654
655    /* Remove items from the exception lists.  The multi exception list is my index
656     * of what exceptions can be removed when the directory is known*/
657    QString directory = item->data(0, Qt::UserRole).toString();
658    QStringList fullPathList = m_fileExceptionMulti.values(directory);
659    int fullPathListCount = fullPathList.count();
660    if ((mainWin->m_rtDirICDebug) && fullPathListCount) Pmsg2(000, "Will attempt to remove file exceptions for %s count %i\n", directory.toUtf8().data(), fullPathListCount);
661    foreach (QString fullPath, fullPathList) {
662       /* If there is no value in the hash for the key fullPath a value of 3 will be returned
663        * which will match no Qt::xxx values */
664       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
665       if (mainWin->m_rtDirICDebug) Pmsg2(000, "hashState=%i childState=%i\n", hashState, childState);
666       if (hashState == Qt::Unchecked) {
667          fileExceptionRemove(fullPath, directory);
668          m_versionExceptionHash.remove(fullPath);
669          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal A\n");
670       }
671       if (hashState == Qt::Checked) {
672          fileExceptionRemove(fullPath, directory);
673          m_versionExceptionHash.remove(fullPath);
674          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal B\n");
675       }
676    }
677
678    if (item == directoryTree->currentItem()) {
679       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Will attempt to update File Table Checks\n");
680       updateFileTableChecks();
681       versionTable->clear();
682       versionTable->setRowCount(0);
683       versionTable->setColumnCount(0);
684    }
685    if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning At End\n");
686 }
687
688 /*
689  * When a directory item check state is changed, this function iterates through
690  * all subdirectories and sets all to the passed state, which is either partially
691  * checked or unchecked.
692  */
693 void restoreTree::setCheckofChildren(QTreeWidgetItem *item, Qt::CheckState state)
694 {
695    int childCount;
696    childCount = item->childCount();
697    for (int i=0; i<childCount; i++) {
698       QTreeWidgetItem *child = item->child(i);
699       child->setData(1, Qt::UserRole, QVariant(state));
700       child->setCheckState(0, state);
701       setCheckofChildren(child, state);
702    }
703 }
704
705 /*
706  * When a File Table Item is "changed" check to see if the state of the checkable
707  * item has changed which is stored in m_fileCheckStateList
708  * If changed store in a hash m_fileExceptionHash that whether this file should be
709  * restored or not.
710  * Called as a slot, connected after populated (after directory current changed called)
711  */
712 void restoreTree::fileTableItemChanged(QTableWidgetItem *item)
713 {
714    /* get the previous and current check states */
715    int row = fileTable->row(item);
716    Qt::CheckState prevState;
717    /* prevent a segfault */
718    prevState = m_fileCheckStateList[row];
719    Qt::CheckState curState = item->checkState();
720
721    /* deterimine the default state from the state of the directory */
722    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
723    Qt::CheckState dirState = (Qt::CheckState)dirTreeItem->data(1, Qt::UserRole).toInt();
724    Qt::CheckState defState = Qt::PartiallyChecked;
725    if (dirState == Qt::Unchecked) defState = Qt::Unchecked;
726
727    /* determine if it is already in the m_fileExceptionHash */
728    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
729    QString file = item->text();
730    QString fullPath = directory + file;
731    Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
732    int verJobNum = m_versionExceptionHash.value(fullPath, 0);
733
734    if (mainWin->m_rtFileTabICDebug) {
735       QString msg = QString("filerow=%1 prev=%2 cur=%3 def=%4 hash=%5 dir=%6 verJobNum=%7\n")
736          .arg(row).arg(prevState).arg(curState).arg(defState).arg(hashState).arg(dirState).arg(verJobNum);
737       Pmsg1(000, "%s", msg.toUtf8().data()); }
738
739    /* Remove the hash if currently checked previously unchecked and directory is checked or partial */
740    if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (dirState == Qt::Unchecked)) {
741       /* it can behave as defaulted so current of unchecked is fine */
742       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
743       fileExceptionRemove(fullPath, directory);
744       m_versionExceptionHash.remove(fullPath);
745    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
746       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionInsert here\n");
747       fileExceptionInsert(fullPath, directory, Qt::Unchecked);
748    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0) && (defState == Qt::PartiallyChecked)) {
749       /* filerow=2 prev=0 cur=2 def=1 hash=0 dir=2 verJobNum=0 */
750       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove here\n");
751       fileExceptionRemove(fullPath, directory);
752    } else if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (defState == Qt::PartiallyChecked) && (verJobNum != 0) && (hashState == Qt::Checked)) {
753       /* Check dir, check version, attempt uncheck in file
754        * filerow=4 prev=2 cur=0 def=1 hash=2 dir=2 verJobNum=53 */
755       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
756       fileExceptionRemove(fullPath, directory);
757       m_versionExceptionHash.remove(fullPath);
758    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
759       /* filerow=0 prev=0 cur=2 def=1 hash=0 dirState=2 verJobNum */
760       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will Not remove here\n");
761    } else if (prevState != curState) {
762       if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  THE STATE OF THE Check has changed, Setting StateList[%i] to %i\n", row, curState);
763       /* A user did not set the check state to Partially checked, ignore if so */
764       if (curState != Qt::PartiallyChecked) {
765          if ((defState == Qt::Unchecked) && (prevState == Qt::PartiallyChecked) && (curState == Qt::Unchecked)) {
766             if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "  got here\n");
767          } else {
768             if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
769             fileExceptionInsert(fullPath, directory, curState);
770          }
771       } else {
772          if (mainWin->m_rtFileTabICDebug) Pmsg1(000, "Removing version hash for %s\n", fullPath.toUtf8().data());
773          /* programattically been changed back to a default state of Qt::PartiallyChecked remove the version hash here */
774          m_versionExceptionHash.remove(fullPath);
775       }
776    }
777
778    updateFileTableChecks();
779    updateVersionTableChecks();
780 }
781
782 /*
783  * function to insert keys and values to both m_fileExceptionHash and m_fileExceptionMulti
784  */
785 void restoreTree::fileExceptionInsert(QString &fullPath, QString &direcotry, Qt::CheckState state)
786 {
787    m_fileExceptionHash.insert(fullPath, state);
788    m_fileExceptionMulti.insert(direcotry, fullPath);
789    directoryIconStateInsert(fullPath, state);
790 }
791
792 /*
793  * function to remove keys from both m_fileExceptionHash and m_fileExceptionMulti
794  */
795 void restoreTree::fileExceptionRemove(QString &fullPath, QString &directory)
796 {
797    m_fileExceptionHash.remove(fullPath);
798    /* pull the list of values in the multi */
799    QStringList fullPathList = m_fileExceptionMulti.values(directory);
800    /* get the index of the fullpath to remove */
801    int index = fullPathList.indexOf(fullPath);
802    if (index != -1) {
803       /* remove the desired item in the list */
804       fullPathList.removeAt(index);
805       /* remove the entire list from the multi */
806       m_fileExceptionMulti.remove(directory);
807       /* readd the remaining */
808       foreach (QString fp, fullPathList) {
809          m_fileExceptionMulti.insert(directory, fp);
810       }
811    }
812    directoryIconStateRemove();
813 }
814
815 /*
816  * Overloaded function to be called from the slot and from other places to set the state
817  * of the check marks in the version table
818  */
819 void restoreTree::versionTableItemChanged(QTableWidgetItem *item)
820 {
821    /* get the previous and current check states */
822    int row = versionTable->row(item);
823    QTableWidgetItem *colZeroItem = versionTable->item(row, 0);
824    Qt::CheckState prevState = m_versionCheckStateList[row];
825    Qt::CheckState curState = (Qt::CheckState)colZeroItem->checkState();
826    m_versionCheckStateList[row] = curState;
827
828    /* deterimine the default state from the state of the file */
829    QTableWidgetItem *fileTableItem = fileTable->currentItem();
830    Qt::CheckState fileState = (Qt::CheckState)fileTableItem->checkState();
831
832    /* determine the default state */
833    Qt::CheckState defState;
834    if (row == 0) {
835       defState = Qt::PartiallyChecked;
836       if (fileState == Qt::Unchecked)
837          defState = Qt::Unchecked;
838    }
839    else
840       defState = Qt::Unchecked;
841
842    /* determine if it is already in the versionExceptionHash */
843    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
844    Qt::CheckState dirState = directoryTree->currentItem()->checkState(0);
845    QString file = fileTableItem->text();
846    QString fullPath = directory + file;
847    int thisJobNum = colZeroItem->text().toInt();
848    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
849
850    if (mainWin->m_rtVerTabICDebug) {
851       QString msg = QString("versrow=%1 prev=%2 cur=%3 def=%4 dir=%5 hashJobNum=%6 thisJobNum=%7 filestate=%8 fec=%9 vec=%10\n")
852          .arg(row).arg(prevState).arg(curState).arg(defState).arg(dirState).arg(hashJobNum).arg(thisJobNum).arg(fileState)
853          .arg(m_fileExceptionHash.count()).arg(m_versionExceptionHash.count());
854       Pmsg1(000, "%s", msg.toUtf8().data()); }
855    /* if changed from partially checked to checked, make it unchecked */
856    if ((curState == Qt::Checked) && (row == 0) && (fileState == Qt::Unchecked)) {
857       if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "Setting to Qt::Checked\n");
858       fileTableItem->setCheckState(Qt::Checked);
859    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (row == 0) && (fileState == Qt::Checked) && (dirState == Qt::Unchecked)) {
860       //versrow=0 prev=1 cur=2 def=1 dir=0 hashJobNum=0 thisJobNum=64 filestate=2 fec=1 vec=0
861       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "fileExceptionRemove %s, %i\n", fullPath.toUtf8().data());
862       fileExceptionRemove(fullPath, directory);
863    } else if ((curState == Qt::Checked) && (row == 0) && (hashJobNum != 0) && (dirState != Qt::Unchecked)) {
864       //versrow=0 prev=0 cur=2 def=1 dir=2 hashJobNum=53 thisJobNum=64 filestate=2 fec=1 vec=1
865       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
866       m_versionExceptionHash.remove(fullPath);
867       fileExceptionRemove(fullPath, directory);
868    } else if ((curState == Qt::Checked) && (row == 0)) {
869       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
870       m_versionExceptionHash.remove(fullPath);
871    } else if (prevState != curState) {
872       if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "  THE STATE OF THE version Check has changed, Setting StateList[%i] to %i\n", row, curState);
873       if ((curState == Qt::Checked) || (curState == Qt::PartiallyChecked) && (row != 0)) {
874          if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_versionExceptionHash %s, %i\n", fullPath.toUtf8().data(), thisJobNum);
875          m_versionExceptionHash.insert(fullPath, thisJobNum);
876          if (fileState != Qt::Checked) {
877             if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
878             fileExceptionInsert(fullPath, directory, curState);
879          }
880       } else {
881          if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "got here\n");
882       }
883    } else {
884      if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "no conditions met\n");
885    }
886
887    updateFileTableChecks();
888    updateVersionTableChecks();
889 }
890
891 /*
892  * Simple function to set the check state in the file table by disconnecting the
893  * signal/slot the setting then reconnecting the signal/slot
894  */
895 void restoreTree::fileTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state, bool color)
896 {
897    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
898            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
899    item->setCheckState(state);
900    if (color) item->setBackground(Qt::yellow);
901    else item->setBackground(Qt::white);
902    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
903            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
904 }
905
906 /*
907  * Simple function to set the check state in the version table by disconnecting the
908  * signal/slot the setting then reconnecting the signal/slot
909  */
910 void restoreTree::versionTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state)
911 {
912    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
913            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
914    item->setCheckState(state);
915    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
916            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
917 }
918
919 /*
920  * Simple function to set the check state in the directory tree by disconnecting the
921  * signal/slot the setting then reconnecting the signal/slot
922  */
923 void restoreTree::directoryTreeDisconnectedSet(QTreeWidgetItem *item, Qt::CheckState state)
924 {
925    disconnect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
926            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
927    item->setCheckState(0, state);
928    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
929            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
930 }
931
932 /*
933  * Simplify the updating of the check state in the File table by iterating through
934  * each item in the file table to determine it's appropriate state.
935  * !! Will probably want to concoct a way to do this without iterating for the possibility
936  * of the very large directories.
937  */
938 void restoreTree::updateFileTableChecks()
939 {
940    /* deterimine the default state from the state of the directory */
941    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
942    Qt::CheckState dirState = dirTreeItem->checkState(0);
943
944    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
945
946    /* Update the items in the version table */
947    int rcnt = fileTable->rowCount();
948    for (int row=0; row<rcnt; row++) {
949       QTableWidgetItem* item = fileTable->item(row, 0);
950
951       Qt::CheckState curState = item->checkState();
952       Qt::CheckState newState = Qt::PartiallyChecked;
953       if (dirState == Qt::Unchecked) newState = Qt::Unchecked;
954
955       /* determine if it is already in the m_fileExceptionHash */
956       QString file = item->text();
957       QString fullPath = dirName + file;
958       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
959       int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
960
961       if (hashState != 3) newState = hashState;
962
963       if (mainWin->m_rtUpdateFTDebug) {
964          QString msg = QString("file row=%1 cur=%2 hash=%3 new=%4 dirState=%5\n")
965             .arg(row).arg(curState).arg(hashState).arg(newState).arg(dirState);
966          Pmsg1(000, "%s", msg.toUtf8().data());
967       }
968
969       bool docolor = false;
970       if (hashJobNum != 0) docolor = true;
971       bool isyellow = item->background().color() == QColor(Qt::yellow);
972       if ((newState != curState) || (hashState == 3) || ((isyellow && !docolor) || (!isyellow && docolor)))
973          fileTableDisconnectedSet(item, newState, docolor);
974       m_fileCheckStateList[row] = newState;
975    }
976 }
977
978 /*
979  * Simplify the updating of the check state in the Version table by iterating through
980  * each item in the file table to determine it's appropriate state.
981  */
982 void restoreTree::updateVersionTableChecks()
983 {
984    /* deterimine the default state from the state of the directory */
985    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
986    Qt::CheckState dirState = dirTreeItem->checkState(0);
987    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
988
989    /* deterimine the default state from the state of the file */
990    QTableWidgetItem *fileTableItem = fileTable->item(fileTable->currentRow(), 0);
991    Qt::CheckState fileState = fileTableItem->checkState();
992    QString file = fileTableItem->text();
993    QString fullPath = dirName + file;
994    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
995
996    /* Update the items in the version table */
997    int cnt = versionTable->rowCount();
998    for (int row=0; row<cnt; row++) {
999       QTableWidgetItem* item = versionTable->item(row, 0);
1000
1001       Qt::CheckState curState = item->checkState();
1002       Qt::CheckState newState = Qt::Unchecked;
1003
1004       if ((row == 0) && (fileState != Qt::Unchecked) && (hashJobNum == 0))
1005          newState = Qt::PartiallyChecked;
1006       /* determine if it is already in the versionExceptionHash */
1007       if (hashJobNum) {
1008          int thisJobNum = item->text().toInt();
1009          if (thisJobNum == hashJobNum)
1010             newState = Qt::Checked;
1011       }
1012       if (mainWin->m_rtChecksDebug) {
1013          QString msg = QString("ver row=%1 cur=%2 hashJobNum=%3 new=%4 dirState=%5\n")
1014             .arg(row).arg(curState).arg(hashJobNum).arg(newState).arg(dirState);
1015          Pmsg1(000, "%s", msg.toUtf8().data());
1016       }
1017       if (newState != curState)
1018          versionTableDisconnectedSet(item, newState);
1019       m_versionCheckStateList[row] = newState;
1020    }
1021 }
1022
1023 /*
1024  * Quick subroutine to "return via subPaths" a list of subpaths when passed a fullPath
1025  */
1026 void restoreTree::fullPathtoSubPaths(QStringList &subPaths, QString &fullPath_in)
1027 {
1028    int index;
1029    bool done = false;
1030    QString fullPath = fullPath_in;
1031    QString direct, path;
1032    while (((index = m_slashregex.lastIndexIn(fullPath, -2)) != -1) && (!done)) {
1033       direct = path = fullPath;
1034       path.replace(index+1, fullPath.length()-index-1, "");
1035       direct.replace(0, index+1, "");
1036       if (false) {
1037          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1038                     .arg(fullPath.length()).arg(index).arg(path).arg(direct);
1039          Pmsg0(000, msg.toUtf8().data());
1040       }
1041       fullPath = path;
1042       subPaths.append(fullPath);
1043    }
1044 }
1045
1046 /*
1047  * A Function to set the icon state and insert a record into
1048  * m_directoryIconStateHash when an exception is added by the user
1049  */
1050 void restoreTree::directoryIconStateInsert(QString &fullPath, Qt::CheckState excpState)
1051 {
1052    QStringList paths;
1053    fullPathtoSubPaths(paths, fullPath);
1054    /* an exception that causes the item in the file table to be "Checked" has occured */
1055    if (excpState == Qt::Checked) {
1056       bool foundAsUnChecked = false;
1057       QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1058       if (firstItem) {
1059          if (firstItem->checkState(0) == Qt::Unchecked)
1060             foundAsUnChecked = true;
1061       }
1062       if (foundAsUnChecked) {
1063           /* as long as directory item is Unchecked, set icon state to "green check" */
1064          bool done = false;
1065          QListIterator<QString> siter(paths);
1066          while (siter.hasNext() && !done) {
1067             QString path = siter.next();
1068             QTreeWidgetItem *item = m_dirPaths.value(path);
1069             if (item) {
1070                if (item->checkState(0) != Qt::Unchecked)
1071                   done = true;
1072                else {
1073                   directorySetIcon(1, FolderGreenChecked, path, item);
1074                   if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1075                }
1076             }
1077          }
1078       } else {
1079          /* if it is partially checked or fully checked insert green Check until a unchecked is found in the path */
1080          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert Aqua %s\n", paths[0].toUtf8().data());
1081          bool done = false;
1082          QListIterator<QString> siter(paths);
1083          while (siter.hasNext() && !done) {
1084             QString path = siter.next();
1085             QTreeWidgetItem *item = m_dirPaths.value(path);
1086             if (item) {  /* if the directory item is checked, set icon state to unchecked "green check" */
1087                if (item->checkState(0) == Qt::Checked)
1088                   done = true;
1089                directorySetIcon(1, FolderGreenChecked, path, item);
1090                if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1091             }
1092          }
1093       }
1094    }
1095    /* an exception that causes the item in the file table to be "Unchecked" has occured */
1096    if (excpState == Qt::Unchecked) {
1097       bool done = false;
1098       QListIterator<QString> siter(paths);
1099       while (siter.hasNext() && !done) {
1100          QString path = siter.next();
1101          QTreeWidgetItem *item = m_dirPaths.value(path);
1102          if (item) {  /* if the directory item is checked, set icon state to unchecked "white check" */
1103             if (item->checkState(0) == Qt::Checked)
1104                done = true;
1105             directorySetIcon(1, FolderWhiteChecked, path, item);
1106             if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1107          }
1108       }
1109    }
1110 }
1111
1112 /*
1113  * A function to set the icon state back to "folder" and to remove a record from
1114  * m_directoryIconStateHash when an exception is removed by a user.
1115  */
1116 void restoreTree::directoryIconStateRemove()
1117 {
1118    QHash<QString, int> shouldBeIconStateHash;
1119    /* First determine all paths with icons that should be checked with m_fileExceptionHash */
1120    /* Use iterator tera to iterate through m_fileExceptionHash */
1121    QHashIterator<QString, Qt::CheckState> tera(m_fileExceptionHash);
1122    while (tera.hasNext()) {
1123       tera.next();
1124       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Alpha Key %s value %i\n", tera.key().toUtf8().data(), tera.value());
1125
1126       QString keyPath = tera.key();
1127       Qt::CheckState state = tera.value();
1128
1129       QStringList paths;
1130       fullPathtoSubPaths(paths, keyPath);
1131       /* if the state of the item in m_fileExceptionHash is checked 
1132        * each of the subpaths should be "Checked Green" */
1133       if (state == Qt::Checked) {
1134
1135          bool foundAsUnChecked = false;
1136          QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1137          if (firstItem) {
1138             if (firstItem->checkState(0) == Qt::Unchecked)
1139                foundAsUnChecked = true;
1140          }
1141          if (foundAsUnChecked) {
1142             /* The right most directory is Unchecked, iterate leftwards
1143              * as long as directory item is Unchecked, set icon state to "green check" */
1144             bool done = false;
1145             QListIterator<QString> siter(paths);
1146             while (siter.hasNext() && !done) {
1147                QString path = siter.next();
1148                QTreeWidgetItem *item = m_dirPaths.value(path);
1149                if (item) {
1150                   if (item->checkState(0) != Qt::Unchecked)
1151                      done = true;
1152                   else {
1153                      shouldBeIconStateHash.insert(path, FolderGreenChecked);
1154                      if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1155                   }
1156                }
1157             }
1158          }
1159          else {
1160             /* The right most directory is Unchecked, iterate leftwards
1161              * until directory item is Checked, set icon state to "green check" */
1162             bool done = false;
1163             QListIterator<QString> siter(paths);
1164             while (siter.hasNext() && !done) {
1165                QString path = siter.next();
1166                QTreeWidgetItem *item = m_dirPaths.value(path);
1167                if (item) {
1168                   if (item->checkState(0) == Qt::Checked)
1169                      done = true;
1170                   shouldBeIconStateHash.insert(path, FolderGreenChecked);
1171                }
1172             }
1173          }
1174       }
1175       /* if the state of the item in m_fileExceptionHash is UNChecked
1176        * each of the subpaths should be "Checked white" until the tree item
1177        * which represents that path is Qt::Checked */
1178       if (state == Qt::Unchecked) {
1179          bool done = false;
1180          QListIterator<QString> siter(paths);
1181          while (siter.hasNext() && !done) {
1182             QString path = siter.next();
1183             QTreeWidgetItem *item = m_dirPaths.value(path);
1184             if (item) {
1185                if (item->checkState(0) == Qt::Checked)
1186                   done = true;
1187                shouldBeIconStateHash.insert(path, FolderWhiteChecked);
1188             }
1189          }
1190       }
1191    }
1192    /* now iterate through m_directoryIconStateHash which are the items that are checked
1193     * and remove all of those that are not in shouldBeIconStateHash */
1194    QHashIterator<QString, int> iter(m_directoryIconStateHash);
1195    while (iter.hasNext()) {
1196       iter.next();
1197       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Beta Key %s value %i\n", iter.key().toUtf8().data(), iter.value());
1198
1199       QString keyPath = iter.key();
1200       if (shouldBeIconStateHash.value(keyPath)) {
1201          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "WAS found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1202          //newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1203          int newval = shouldBeIconStateHash.value(keyPath);
1204          newval = ~newval;
1205          newval = newval & FolderBothChecked;
1206          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1207          if (item)
1208             directorySetIcon(0, newval, keyPath, item);
1209       } else {
1210          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "NOT found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1211          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1212          if (item)
1213             directorySetIcon(0, FolderBothChecked, keyPath, item);
1214             //item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
1215             //m_directoryIconStateHash.remove(keyPath);
1216       }
1217    }
1218 }
1219
1220 void restoreTree::directorySetIcon(int operation, int change, QString &path, QTreeWidgetItem* item) {
1221    int newval;
1222    /* we are adding a check type white or green */
1223    if (operation > 0) {
1224       /* get the old val and "bitwise OR" with the change */
1225       newval = m_directoryIconStateHash.value(path, FolderUnchecked) | change;
1226       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1227       m_directoryIconStateHash.insert(path, newval);
1228    } else {
1229    /* we are removing a check type white or green */
1230       newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1231       if (newval == 0) {
1232          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Removing from m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1233          m_directoryIconStateHash.remove(path);
1234       }
1235       else {
1236          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1237          m_directoryIconStateHash.insert(path, newval);
1238       }
1239    }
1240    if (newval == FolderUnchecked)
1241       item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
1242    else if (newval == FolderGreenChecked)
1243       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderchecked.png")));
1244    else if (newval == FolderWhiteChecked)
1245       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderunchecked.png")));
1246    else if (newval == FolderBothChecked)
1247       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderbothchecked.png")));
1248 }
1249
1250 /*
1251  * Test Button
1252  */
1253 void restoreTree::testButtonPushed()
1254 {
1255    QMultiHash<int, QString> versionFilesMulti;
1256    QHash <QString, bool> fullPathDone;
1257    if ((mainWin->m_rtRestore1Debug) || (mainWin->m_rtRestore2Debug) || (mainWin->m_rtRestore3Debug))
1258       Pmsg0(000, "In restoreTree::testButtonPushed\n");
1259    /* Use a tree widget item iterator filtering for Checked Items */
1260    QTreeWidgetItemIterator diter(directoryTree, QTreeWidgetItemIterator::Checked);
1261    while (*diter) {
1262       QString directory = (*diter)->data(0, Qt::UserRole).toString();
1263       if (mainWin->m_rtRestore1Debug)
1264       Pmsg1(000, "Directory Checked=\"%s\"\n", directory.toUtf8().data());
1265       /* With a checked directory, query for the files in the directory */
1266       QString cmd =
1267          "SELECT DISTINCT Filename.Name, MAX(Job.JobId)"
1268          " FROM File "
1269          " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1270          " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1271          " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
1272          " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1273          " AND Job.Jobid IN (" + m_jobQuery + ")"
1274          " GROUP BY Filename.Name";
1275  
1276       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1277       QStringList results;
1278       if (m_console->sql_cmd(cmd, results)) {
1279       
1280          QStringList fieldlist;
1281    
1282          int row = 0;
1283          /* Iterate through the record returned from the query */
1284          foreach (QString resultline, results) {
1285             /* Iterate through fields in the record */
1286             int column = 0;
1287             QString fullPath = "";
1288             Qt::CheckState fileExcpState = (Qt::CheckState)4;
1289             fieldlist = resultline.split("\t");
1290             int version = 0;
1291             foreach (QString field, fieldlist) {
1292                if (column == 0) {
1293                   fullPath = directory + field;
1294                }
1295                if (column == 1) {
1296                   version = field.toInt();
1297                }
1298                column++;
1299             }
1300             fileExcpState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1301             
1302             int excpVersion = m_versionExceptionHash.value(fullPath, 0);
1303             if (fileExcpState != Qt::Unchecked) {
1304                QString debugtext;
1305                if (excpVersion != 0) {
1306                   debugtext = QString("*E* version=%1").arg(excpVersion);
1307                   version = excpVersion;
1308                } else
1309                   debugtext = QString("___ version=%1").arg(version);
1310                if (mainWin->m_rtRestore1Debug)
1311                   Pmsg2(000, "Restoring %s File %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1312                fullPathDone.insert(fullPath, 1);
1313                versionFilesMulti.insert(version, fullPath);
1314             }
1315             row++;
1316          }
1317       }
1318       ++diter;
1319    } /* while (*diter) */
1320
1321    /* There may be some exceptions not accounted for yet with fullPathDone */
1322    QHashIterator<QString, Qt::CheckState> ftera(m_fileExceptionHash);
1323    while (ftera.hasNext()) {
1324       ftera.next();
1325       QString fullPath = ftera.key();
1326       Qt::CheckState state = ftera.value();
1327       if (state != 0) {
1328          /* now we don't want the ones already done */
1329          if (fullPathDone.value(fullPath, 0) == 0) {
1330             int version = m_versionExceptionHash.value(fullPath, 0);
1331             QString debugtext = "";
1332             if (version != 0)
1333                debugtext = QString("E1* version=%1").arg(version);
1334             else {
1335                version = mostRecentVersionfromFullPath(fullPath);
1336                if (version)
1337                   debugtext = QString("E2* version=%1").arg(version);
1338                else
1339                   debugtext = QString("Error det vers").arg(version);
1340             }
1341             if (mainWin->m_rtRestore1Debug)
1342                Pmsg2(000, "Restoring %s file %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1343             versionFilesMulti.insert(version, fullPath);
1344          } /* if fullPathDone.value(fullPath, 0) == 0 */
1345       } /* if state != 0 */
1346    } /* while ftera.hasNext */
1347
1348    /* now for the final spit out of the versions and lists of files for each version */
1349    QHash<int, int> doneKeys;
1350    QHashIterator<int, QString> miter(versionFilesMulti);
1351    while (miter.hasNext()) {
1352       miter.next();
1353       int fversion = miter.key();
1354       /* did not succeed in getting an iterator to work as expected on versionFilesMulti so use doneKeys */
1355       if (doneKeys.value(fversion, 0) == 0) {
1356          if (mainWin->m_rtRestore2Debug) Pmsg1(000, "Version->%i\n", fversion);
1357          QStringList fullPathList = versionFilesMulti.values(fversion);
1358          /* create the command to perform the restore */
1359          QString cmd = QString("restore");
1360          cmd += " client=\"" + m_prevClientCombo + "\""
1361                 " jobid=\"" + QString("%1").arg(fversion) + "\" yes";
1362          foreach(QString ffullPath, fullPathList) {
1363             if (mainWin->m_rtRestore2Debug) Pmsg1(000, "  file->%s\n", ffullPath.toUtf8().data());
1364             cmd += " file=\"" + ffullPath + "\"";
1365          }
1366          cmd += " yes";
1367          if (mainWin->m_commandDebug)
1368             Pmsg1(000, "preRestore command \'%s\'\n", cmd.toUtf8().data());
1369          consoleCommand(cmd);
1370          mainWin->resetFocus();
1371          doneKeys.insert(fversion,1);
1372       }
1373    }
1374 }
1375
1376 int restoreTree::mostRecentVersionfromFullPath(QString &fullPath)
1377 {
1378    int qversion = 0;
1379    QString directory, fileName;
1380    int index = m_slashregex.lastIndexIn(fullPath, -2);
1381    if (index != -1) {
1382       directory = fileName = fullPath;
1383       directory.replace(index+1, fullPath.length()-index-1, "");
1384       fileName.replace(0, index+1, "");
1385       if (false) {
1386          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1387                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1388          Pmsg0(000, msg.toUtf8().data());
1389       }
1390       /* so now we need the latest version from the database */
1391       QString cmd =
1392          "SELECT MAX(Job.JobId)"
1393          " FROM File "
1394          " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1395          " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1396          " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
1397          " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1398          " AND Job.Jobid IN (" + m_jobQuery + ")"
1399          " AND Filename.Name='" + fileName + "'"
1400          " GROUP BY Filename.Name";
1401  
1402       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1403       QStringList results;
1404       if (m_console->sql_cmd(cmd, results)) {
1405          QStringList fieldlist;
1406          int row = 0;
1407          /* Iterate through the record returned from the query */
1408          foreach (QString resultline, results) {
1409             /* Iterate through fields in the record */
1410             int column = 0;
1411             fieldlist = resultline.split("\t");
1412             foreach (QString field, fieldlist) {
1413                if (column == 0) {
1414                   qversion = field.toInt();
1415                }
1416                column++;
1417             }
1418             row++;
1419          }
1420       }
1421    } /* if (index != -1) */
1422    return qversion;
1423 }