]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/joblist/joblist.cpp
Fix some missed copyright changes
[bacula/bacula] / bacula / src / qt-console / joblist / joblist.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Version $Id$
30  *
31  *   Dirk Bartley, March 2007
32  */
33  
34 #include "bat.h"
35 #include <QAbstractEventDispatcher>
36 #include <QTableWidgetItem>
37 #include "joblist.h"
38 #include "restore.h"
39 #include "job/job.h"
40 #include "joblog/joblog.h"
41 #ifdef HAVE_QWT
42 #include "jobgraphs/jobplot.h"
43 #endif
44 #include "util/fmtwidgetitem.h"
45 #include "util/comboutil.h"
46
47 /*
48  * Constructor for the class
49  */
50 JobList::JobList(const QString &mediaName, const QString &clientName,
51           const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
52 {
53    setupUi(this);
54    m_name = "Jobs Run"; /* treeWidgetName has a virtual override in this class */
55    m_mediaName = mediaName;
56    m_clientName = clientName;
57    m_jobName = jobName;
58    m_filesetName = filesetName;
59    m_filesetName = filesetName;
60    pgInitialize("", parentTreeWidgetItem);
61    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
62    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.png")));
63
64    m_resultCount = 0;
65    m_populated = false;
66    m_closeable = false;
67    if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != "")) {
68       m_closeable=true;
69    }
70    m_checkCurrentWidget = true;
71
72    /* Set Defaults for check and spin for limits */
73    limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
74    limitSpinBox->setValue(mainWin->m_recordLimitVal);
75    daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
76    daysSpinBox->setValue(mainWin->m_daysLimitVal);
77
78    QGridLayout *gridLayout = new QGridLayout(this);
79    gridLayout->setSpacing(6);
80    gridLayout->setMargin(9);
81    gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
82
83    m_splitter = new QSplitter(Qt::Vertical, this);
84    QScrollArea *area = new QScrollArea();
85    area->setObjectName(QString::fromUtf8("area"));
86    area->setWidget(frame);
87    area->setWidgetResizable(true);
88    m_splitter->addWidget(area);
89    m_splitter->addWidget(mp_tableWidget);
90
91    gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
92    createConnections();
93    readSettings();
94    if (m_closeable) { dockPage(); }
95 }
96
97 /*
98  * Write the m_splitter settings in the destructor
99  */
100 JobList::~JobList()
101 {
102    writeSettings();
103 }
104
105 /*
106  * The Meat of the class.
107  * This function will populate the QTableWidget, mp_tablewidget, with
108  * QTableWidgetItems representing the results of a query for what jobs exist on
109  * the media name passed from the constructor stored in m_mediaName.
110  */
111 void JobList::populateTable()
112 {
113    /* Can't do this in constructor because not neccesarily conected in constructor */
114    prepareFilterWidgets();
115    m_populated = true;
116
117    Freeze frz(*mp_tableWidget); /* disable updating*/
118
119    /* Set up query */
120    QString query;
121    fillQueryString(query);
122
123    /* Set up the Header for the table */
124    QStringList headerlist = (QStringList()
125       << tr("Job Id") << tr("Job Name") << tr("Client") << tr("Job Starttime") 
126       << tr("Job Type") << tr("Job Level") << tr("Job Files") 
127       << tr("Job Bytes") << tr("Job Status")  << tr("Purged") << tr("File Set")
128       << tr("Pool Name") << tr("First Volume") << tr("VolCount"));
129
130    m_jobIdIndex = headerlist.indexOf(tr("Job Id"));
131    m_purgedIndex = headerlist.indexOf(tr("Purged"));
132    m_typeIndex = headerlist.indexOf(tr("Job Type"));
133    m_statusIndex = headerlist.indexOf(tr("Job Status"));
134    m_startIndex = headerlist.indexOf(tr("Job Starttime"));
135    m_filesIndex = headerlist.indexOf(tr("Job Files"));
136    m_bytesIndex = headerlist.indexOf(tr("Job Bytes"));
137
138    /* Initialize the QTableWidget */
139    m_checkCurrentWidget = false;
140    mp_tableWidget->clear();
141    m_checkCurrentWidget = true;
142    mp_tableWidget->setColumnCount(headerlist.size());
143    mp_tableWidget->setHorizontalHeaderLabels(headerlist);
144    mp_tableWidget->horizontalHeader()->setHighlightSections(false);
145    mp_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
146    mp_tableWidget->setSortingEnabled(false); /* rows move on insert if sorting enabled */
147
148    if (mainWin->m_sqlDebug) {
149       Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
150    }
151
152    QStringList results;
153    if (m_console->sql_cmd(query, results)) {
154       m_resultCount = results.count();
155
156       QStringList fieldlist;
157       mp_tableWidget->setRowCount(results.size());
158
159       int row = 0;
160       /* Iterate through the record returned from the query */
161       QString resultline;
162       foreach (resultline, results) {
163          fieldlist = resultline.split("\t");
164          if (fieldlist.size() < 13)
165             continue; /* some fields missing, ignore row */
166
167          TableItemFormatter jobitem(*mp_tableWidget, row);
168   
169          /* Iterate through fields in the record */
170          QStringListIterator fld(fieldlist);
171          int col = 0;
172
173          /* job id */
174          jobitem.setNumericFld(col++, fld.next());
175
176          /* job name */
177          jobitem.setTextFld(col++, fld.next());
178
179          /* client */
180          jobitem.setTextFld(col++, fld.next());
181
182          /* job starttime */
183          jobitem.setTextFld(col++, fld.next(), true);
184
185          /* job type */
186          jobitem.setJobTypeFld(col++, fld.next());
187
188          /* job level */
189          jobitem.setJobLevelFld(col++, fld.next());
190
191          /* job files */
192          jobitem.setNumericFld(col++, fld.next());
193
194          /* job bytes */
195          jobitem.setBytesFld(col++, fld.next());
196
197          /* job status */
198          jobitem.setJobStatusFld(col++, fld.next());
199
200          /* purged */
201          jobitem.setBoolFld(col++, fld.next());
202
203          /* fileset */
204          jobitem.setTextFld(col++, fld.next());
205
206          /* pool name */
207          jobitem.setTextFld(col++, fld.next());
208
209          /* First Media */
210          jobitem.setTextFld(col++, fld.next());
211
212          /* Medias count */
213          jobitem.setNumericFld(col++, fld.next());
214          row++;
215       }
216    } 
217    /* set default sorting */
218    mp_tableWidget->sortByColumn(m_jobIdIndex, Qt::DescendingOrder);
219    mp_tableWidget->setSortingEnabled(true);
220    
221    /* Resize the columns */
222    mp_tableWidget->resizeColumnsToContents();
223    mp_tableWidget->resizeRowsToContents();
224    mp_tableWidget->verticalHeader()->hide();
225    if ((m_mediaName != tr("Any")) && (m_resultCount == 0)){
226       /* for context sensitive searches, let the user know if there were no
227        * results */
228       QMessageBox::warning(this, "Bat",
229           tr("The Jobs query returned no results.\n"
230          "Press OK to continue?"), QMessageBox::Ok );
231    }
232
233    /* make read only */
234    mp_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
235 }
236
237 void JobList::prepareFilterWidgets()
238 {
239    if (!m_populated) {
240       clientComboBox->addItem(tr("Any"));
241       clientComboBox->addItems(m_console->client_list);
242       comboSel(clientComboBox, m_clientName);
243
244       QStringList volumeList;
245       getVolumeList(volumeList);
246       volumeComboBox->addItem(tr("Any"));
247       volumeComboBox->addItems(volumeList);
248       comboSel(volumeComboBox, m_mediaName);
249
250       jobComboBox->addItem(tr("Any"));
251       jobComboBox->addItems(m_console->job_list);
252       comboSel(jobComboBox, m_jobName);
253
254       levelComboFill(levelComboBox);
255
256       boolComboFill(purgedComboBox);
257
258       fileSetComboBox->addItem(tr("Any"));
259       fileSetComboBox->addItems(m_console->fileset_list);
260       comboSel(fileSetComboBox, m_filesetName);
261
262       poolComboBox->addItem(tr("Any"));
263       poolComboBox->addItems(m_console->pool_list);
264
265       jobStatusComboFill(statusComboBox);
266    }
267 }
268
269 void JobList::fillQueryString(QString &query)
270 {
271    query = "";
272    int volumeIndex = volumeComboBox->currentIndex();
273    if (volumeIndex != -1)
274       m_mediaName = volumeComboBox->itemText(volumeIndex);
275    QString distinct = "";
276    if (m_mediaName != tr("Any")) { distinct = "DISTINCT "; }
277    query += "SELECT " + distinct + "Job.JobId AS JobId, Job.Name AS JobName, " 
278             " Client.Name AS Client,"
279             " Job.Starttime AS JobStart, Job.Type AS JobType,"
280             " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
281             " Job.JobBytes AS Bytes, Job.JobStatus AS Status,"
282             " Job.PurgedFiles AS Purged, FileSet.FileSet,"
283             " Pool.Name AS Pool,"
284             " (SELECT Media.VolumeName FROM JobMedia JOIN Media ON JobMedia.MediaId=Media.MediaId WHERE JobMedia.JobId=Job.JobId ORDER BY JobMediaId LIMIT 1) AS FirstVolume,"
285             " (SELECT count(DISTINCT MediaId) FROM JobMedia WHERE JobMedia.JobId=Job.JobId) AS Volumes"
286             " FROM Job"
287             " JOIN Client ON (Client.ClientId=Job.ClientId)"
288             " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId) "
289             " LEFT OUTER JOIN Pool ON Job.PoolId = Pool.PoolId ";
290    QStringList conditions;
291    if (m_mediaName != tr("Any")) {
292       query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId) "
293                " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId) ";
294       conditions.append("Media.VolumeName='" + m_mediaName + "'");
295    }
296
297    comboCond(conditions, clientComboBox, "Client.Name");
298    comboCond(conditions, jobComboBox, "Job.Name");
299    levelComboCond(conditions, levelComboBox, "Job.Level");
300    jobStatusComboCond(conditions, statusComboBox, "Job.JobStatus");
301    boolComboCond(conditions, purgedComboBox, "Job.PurgedFiles");
302    comboCond(conditions, fileSetComboBox, "FileSet.FileSet");
303    comboCond(conditions, poolComboBox, "Pool.Name");
304
305    /* If Limit check box For limit by days is checked  */
306    if (daysCheckBox->checkState() == Qt::Checked) {
307       QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
308       QString since = stamp.toString(Qt::ISODate);
309       conditions.append("Job.Starttime > '" + since + "'");
310    }
311    if (filterCopyCheckBox->checkState() == Qt::Checked) {
312       conditions.append("Job.Type != 'c'" );
313    }
314    if (filterMigrationCheckBox->checkState() == Qt::Checked) {
315       conditions.append("Job.Type != 'g'" );
316    }
317    bool first = true;
318    foreach (QString condition, conditions) {
319       if (first) {
320          query += " WHERE " + condition;
321          first = false;
322       } else {
323          query += " AND " + condition;
324       }
325    }
326    /* Descending */
327    query += " ORDER BY Job.JobId DESC";
328    /* If Limit check box for limit records returned is checked  */
329    if (limitCheckBox->checkState() == Qt::Checked) {
330       QString limit;
331       limit.setNum(limitSpinBox->value());
332       query += " LIMIT " + limit;
333    }
334 }
335
336 /*
337  * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
338  * The tree has been populated.
339  */
340 void JobList::PgSeltreeWidgetClicked()
341 {
342    if (!m_populated) {
343       populateTable();
344       /* Lets make sure the splitter is not all the way to size index 0 == 0 */
345       QList<int> sizes = m_splitter->sizes();
346       if (sizes[0] == 0) {
347          int frameMax = frame->maximumHeight();
348          int sizeSum = 0;
349          foreach(int size, sizes) { sizeSum += size; }
350          int tabHeight = mainWin->tabWidget->geometry().height();
351          sizes[0] = frameMax;
352          sizes[1] = tabHeight - frameMax;
353          m_splitter->setSizes(sizes);
354       }
355    }
356    if (!isOnceDocked()) {
357       dockPage();
358    }
359 }
360
361 /*
362  *  Virtual function override of pages function which is called when this page
363  *  is visible on the stack
364  */
365 void JobList::currentStackItem()
366 {
367 /*   if (!m_populated) populate every time user comes back to this object */
368       populateTable();
369 }
370
371 /*
372  * Virtual Function to return the name for the medialist tree widget
373  */
374 void JobList::treeWidgetName(QString &desc)
375 {
376    if (m_mediaName != "" ) {
377      desc = tr("Jobs Run on Volume %1").arg(m_mediaName);
378    } else if (m_clientName != "" ) {
379      desc = tr("Jobs Run from Client %1").arg(m_clientName);
380    } else if (m_jobName != "" ) {
381      desc = tr("Jobs Run of Job %1").arg(m_jobName);
382    } else if (m_filesetName != "" ) {
383      desc = tr("Jobs Run with fileset %1").arg(m_filesetName);
384    } else {
385      desc = tr("Jobs Run");
386    }
387 }
388
389 /*
390  * Function to create connections for context sensitive menu for this and
391  * the page selector
392  */
393 void JobList::createConnections()
394 {
395    /* connect to the action specific to this pages class that shows up in the 
396     * page selector tree */
397    connect(actionRefreshJobList, SIGNAL(triggered()), this, SLOT(populateTable()));
398    connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
399 #ifdef HAVE_QWT
400    connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
401 #else
402    graphButton->setEnabled(false);
403    graphButton->setVisible(false);
404 #endif
405    /* for the selectionChanged to maintain m_currentJob and a delete selection */
406    connect(mp_tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
407    connect(mp_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(showInfoForJob()));
408
409    /* Do what is required for the local context sensitive menu */
410
411
412    /* setContextMenuPolicy is required */
413    mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
414
415    connect(actionListFilesOnJob, SIGNAL(triggered()), this, SLOT(consoleListFilesOnJob()));
416    connect(actionListJobMedia, SIGNAL(triggered()), this, SLOT(consoleListJobMedia()));
417    connect(actionDeleteJob, SIGNAL(triggered()), this, SLOT(consoleDeleteJob()));
418    connect(actionPurgeFiles, SIGNAL(triggered()), this, SLOT(consolePurgeFiles()));
419    connect(actionRestoreFromJob, SIGNAL(triggered()), this, SLOT(preRestoreFromJob()));
420    connect(actionRestoreFromTime, SIGNAL(triggered()), this, SLOT(preRestoreFromTime()));
421    connect(actionShowLogForJob, SIGNAL(triggered()), this, SLOT(showLogForJob()));
422    connect(actionShowInfoForJob, SIGNAL(triggered()), this, SLOT(showInfoForJob()));
423    connect(actionCancelJob, SIGNAL(triggered()), this, SLOT(consoleCancelJob()));
424    connect(actionListJobTotals, SIGNAL(triggered()), this, SLOT(consoleListJobTotals()));
425    connect(m_splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(splitterMoved(int, int)));
426
427    m_contextActions.append(actionRefreshJobList);
428    m_contextActions.append(actionListJobTotals);
429 }
430
431 /*
432  * Functions to respond to local context sensitive menu sending console commands
433  * If I could figure out how to make these one function passing a string, Yaaaaaa
434  */
435 void JobList::consoleListFilesOnJob()
436 {
437    QString cmd("list files jobid=");
438    cmd += m_currentJob;
439    if (mainWin->m_longList) { cmd.prepend("l"); }
440    consoleCommand(cmd);
441 }
442 void JobList::consoleListJobMedia()
443 {
444    QString cmd("list jobmedia jobid=");
445    cmd += m_currentJob;
446    if (mainWin->m_longList) { cmd.prepend("l"); }
447    consoleCommand(cmd);
448 }
449
450 void JobList::consoleListJobTotals()
451 {
452    QString cmd("list jobtotals");
453    if (mainWin->m_longList) { cmd.prepend("l"); }
454    consoleCommand(cmd);
455 }
456 void JobList::consoleDeleteJob()
457 {
458    if (QMessageBox::warning(this, "Bat",
459       tr("Are you sure you want to delete??  !!!.\n"
460 "This delete command is used to delete a Job record and all associated catalog"
461 " records that were created. This command operates only on the Catalog"
462 " database and has no effect on the actual data written to a Volume. This"
463 " command can be dangerous and we strongly recommend that you do not use"
464 " it unless you know what you are doing.  The Job and all its associated"
465 " records (File and JobMedia) will be deleted from the catalog."
466       "Press OK to proceed with delete operation.?"),
467       QMessageBox::Ok | QMessageBox::Cancel)
468       == QMessageBox::Cancel) { return; }
469
470    QString cmd("delete job jobid=");
471    cmd += m_selectedJobs;
472    consoleCommand(cmd, false);
473    populateTable();
474 }
475 void JobList::consolePurgeFiles()
476 {
477    if (QMessageBox::warning(this, "Bat",
478       tr("Are you sure you want to purge ??  !!!.\n"
479 "The Purge command will delete associated Catalog database records from Jobs and"
480 " Volumes without considering the retention period. Purge  works only on the"
481 " Catalog database and does not affect data written to Volumes. This command can"
482 " be dangerous because you can delete catalog records associated with current"
483 " backups of files, and we recommend that you do not use it unless you know what"
484 " you are doing.\n"
485       "Press OK to proceed with the purge operation?"),
486       QMessageBox::Ok | QMessageBox::Cancel)
487       == QMessageBox::Cancel) { return; }
488
489    m_console->m_warningPrevent = true;
490    foreach(QString job, m_selectedJobsList) {
491       QString cmd("purge files jobid=");
492       cmd += job;
493       consoleCommand(cmd, false);
494    }
495    m_console->m_warningPrevent = false;
496    populateTable();
497 }
498
499 /*
500  * Subroutine to call preRestore to restore from a select job
501  */
502 void JobList::preRestoreFromJob()
503 {
504    new prerestorePage(m_currentJob, R_JOBIDLIST);
505 }
506
507 /*
508  * Subroutine to call preRestore to restore from a select job
509  */
510 void JobList::preRestoreFromTime()
511 {
512    new prerestorePage(m_currentJob, R_JOBDATETIME);
513 }
514
515 /*
516  * Subroutine to call class to show the log in the database from that job
517  */
518 void JobList::showLogForJob()
519 {
520    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
521    new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
522 }
523
524 /*
525  * Subroutine to call class to show the log in the database from that job
526  */
527 void JobList::showInfoForJob(QTableWidgetItem * /*item*/)
528 {
529    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
530    new Job(m_currentJob, pageSelectorTreeWidgetItem);
531 }
532
533 /*
534  * Cancel a running job
535  */
536 void JobList::consoleCancelJob()
537 {
538    QString cmd("cancel jobid=");
539    cmd += m_currentJob;
540    consoleCommand(cmd);
541 }
542
543 /*
544  * Graph this table
545  */
546 void JobList::graphTable()
547 {
548 #ifdef HAVE_QWT
549    JobPlotPass pass;
550    pass.recordLimitCheck = limitCheckBox->checkState();
551    pass.daysLimitCheck = daysCheckBox->checkState();
552    pass.recordLimitSpin = limitSpinBox->value();
553    pass.daysLimitSpin = daysSpinBox->value();
554    pass.jobCombo = jobComboBox->currentText();
555    pass.clientCombo = clientComboBox->currentText();
556    pass.volumeCombo = volumeComboBox->currentText();
557    pass.fileSetCombo = fileSetComboBox->currentText();
558    pass.purgedCombo = purgedComboBox->currentText();
559    pass.levelCombo = levelComboBox->currentText();
560    pass.statusCombo = statusComboBox->currentText();
561    pass.use = true;
562    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
563    new JobPlot(pageSelectorTreeWidgetItem, pass);
564 #endif
565 }
566
567 /*
568  * Save user settings associated with this page
569  */
570 void JobList::writeSettings()
571 {
572    QSettings settings(m_console->m_dir->name(), "bat");
573    settings.beginGroup(m_groupText);
574    settings.setValue(m_splitText, m_splitter->saveState());
575    settings.setValue("FilterCopyCheckState", filterCopyCheckBox->checkState());
576    settings.setValue("FilterMigrationCheckState", filterMigrationCheckBox->checkState());
577    settings.endGroup();
578 }
579
580 /*
581  * Read and restore user settings associated with this page
582  */
583 void JobList::readSettings()
584 {
585    m_groupText = "JobListPage";
586    m_splitText = "splitterSizes_2";
587    QSettings settings(m_console->m_dir->name(), "bat");
588    settings.beginGroup(m_groupText);
589    if (settings.contains(m_splitText)) {
590       m_splitter->restoreState(settings.value(m_splitText).toByteArray());
591    }
592    filterCopyCheckBox->setCheckState((Qt::CheckState)settings.value("FilterCopyCheckState").toInt());
593    filterMigrationCheckBox->setCheckState((Qt::CheckState)settings.value("FilterMigrationCheckState").toInt());
594    settings.endGroup();
595 }
596
597 /*
598  * Function to fill m_selectedJobsCount and m_selectedJobs with selected values
599  */
600 void JobList::selectionChanged()
601 {
602    QList<int> rowList;
603    QList<QTableWidgetItem *> sitems = mp_tableWidget->selectedItems();
604    foreach (QTableWidgetItem *sitem, sitems) {
605       int row = sitem->row();
606       if (!rowList.contains(row)) {
607          rowList.append(row);
608       }
609    }
610
611    m_selectedJobs = "";
612    m_selectedJobsList.clear();
613    bool first = true;
614    foreach(int row, rowList) {
615       QTableWidgetItem * sitem = mp_tableWidget->item(row, m_jobIdIndex);
616       if (!first) m_selectedJobs.append(",");
617       else first = false;
618       m_selectedJobs.append(sitem->text());
619       m_selectedJobsList.append(sitem->text());
620    }
621    m_selectedJobsCount = rowList.count();
622    if (m_selectedJobsCount > 1) {
623       QString text = QString( tr("Delete list of %1 Jobs")).arg(m_selectedJobsCount);
624       actionDeleteJob->setText(text);
625       text = QString( tr("Purge Files from list of %1 Jobs")).arg(m_selectedJobsCount);
626       actionPurgeFiles->setText(text);
627    } else {
628       actionDeleteJob->setText(tr("Delete Single Job"));
629       actionPurgeFiles->setText(tr("Purge Files from single job"));
630    }
631
632    /* remove all actions */
633    foreach(QAction* mediaAction, mp_tableWidget->actions()) {
634       mp_tableWidget->removeAction(mediaAction);
635    }
636
637    /* Add Actions */
638    mp_tableWidget->addAction(actionRefreshJobList);
639    if (m_selectedJobsCount == 1) {
640       mp_tableWidget->addAction(actionListFilesOnJob);
641       mp_tableWidget->addAction(actionListJobMedia);
642       mp_tableWidget->addAction(actionRestoreFromJob);
643       mp_tableWidget->addAction(actionRestoreFromTime);
644       mp_tableWidget->addAction(actionShowLogForJob);
645       mp_tableWidget->addAction(actionShowInfoForJob);
646    }
647    if (m_selectedJobsCount >= 1) {
648       mp_tableWidget->addAction(actionDeleteJob);
649       mp_tableWidget->addAction(actionPurgeFiles);
650    }
651
652    /* Make Connections */
653    if (m_checkCurrentWidget) {
654       int row = mp_tableWidget->currentRow();
655       QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
656       m_currentJob = jobitem->text();
657
658       /* include purged action or not */
659       jobitem = mp_tableWidget->item(row, m_purgedIndex);
660       QString purged = jobitem->text();
661 /*      mp_tableWidget->removeAction(actionPurgeFiles);
662       if (purged == tr("No") ) {
663          mp_tableWidget->addAction(actionPurgeFiles);
664       }*/
665       /* include restore from time and job action or not */
666       jobitem = mp_tableWidget->item(row, m_typeIndex);
667       QString type = jobitem->text();
668       if (m_selectedJobsCount == 1) {
669          mp_tableWidget->removeAction(actionRestoreFromJob);
670          mp_tableWidget->removeAction(actionRestoreFromTime);
671          if (type == tr("Backup")) {
672             mp_tableWidget->addAction(actionRestoreFromJob);
673             mp_tableWidget->addAction(actionRestoreFromTime);
674          }
675       }
676       /* include cancel action or not */
677       jobitem = mp_tableWidget->item(row, m_statusIndex);
678       QString status = jobitem->text();
679       mp_tableWidget->removeAction(actionCancelJob);
680       if (status == tr("Running") || status == tr("Created, not yet running")) {
681          mp_tableWidget->addAction(actionCancelJob);
682       }
683    }
684 }
685
686 /*
687  *  Function to prevent the splitter from making index 0 of the size larger than it
688  *  needs to be
689  */
690 void JobList::splitterMoved(int /*pos*/, int /*index*/)
691 {
692    int frameMax = frame->maximumHeight();
693    QList<int> sizes = m_splitter->sizes();
694    int sizeSum = 0;
695    foreach(int size, sizes) { sizeSum += size; }
696    if (sizes[0] > frameMax) {
697       sizes[0] = frameMax;
698       sizes[1] = sizeSum - frameMax;
699       m_splitter->setSizes(sizes);
700    }
701 }