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