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