2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
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
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.
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
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.
31 * Dirk Bartley, March 2007
35 #include <QAbstractEventDispatcher>
36 #include <QTableWidgetItem>
40 #include "joblog/joblog.h"
42 #include "jobgraphs/jobplot.h"
44 #include "util/fmtwidgetitem.h"
45 #include "util/comboutil.h"
48 * Constructor for the class
50 JobList::JobList(const QString &mediaName, const QString &clientName,
51 const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
54 m_name = "Jobs Run"; /* treeWidgetName has a virtual override in this class */
55 m_mediaName = mediaName;
56 m_clientName = clientName;
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")));
67 if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != "")) {
70 m_checkCurrentWidget = true;
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);
78 QGridLayout *gridLayout = new QGridLayout(this);
79 gridLayout->setSpacing(6);
80 gridLayout->setMargin(9);
81 gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
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);
91 gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
94 if (m_closeable) { dockPage(); }
98 * Write the m_splitter settings in the destructor
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.
111 void JobList::populateTable()
113 /* Can't do this in constructor because not neccesarily conected in constructor */
114 prepareFilterWidgets();
117 Freeze frz(*mp_tableWidget); /* disable updating*/
121 fillQueryString(query);
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"));
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"));
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 */
148 if (mainWin->m_sqlDebug) {
149 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
153 if (m_console->sql_cmd(query, results)) {
154 m_resultCount = results.count();
156 QStringList fieldlist;
157 mp_tableWidget->setRowCount(results.size());
160 /* Iterate through the record returned from the query */
162 foreach (resultline, results) {
163 fieldlist = resultline.split("\t");
164 if (fieldlist.size() < 13)
165 continue; /* some fields missing, ignore row */
167 TableItemFormatter jobitem(*mp_tableWidget, row);
169 /* Iterate through fields in the record */
170 QStringListIterator fld(fieldlist);
174 jobitem.setNumericFld(col++, fld.next());
177 jobitem.setTextFld(col++, fld.next());
180 jobitem.setTextFld(col++, fld.next());
183 jobitem.setTextFld(col++, fld.next(), true);
186 jobitem.setJobTypeFld(col++, fld.next());
189 jobitem.setJobLevelFld(col++, fld.next());
192 jobitem.setNumericFld(col++, fld.next());
195 jobitem.setBytesFld(col++, fld.next());
198 jobitem.setJobStatusFld(col++, fld.next());
201 jobitem.setBoolFld(col++, fld.next());
204 jobitem.setTextFld(col++, fld.next());
207 jobitem.setTextFld(col++, fld.next());
210 jobitem.setTextFld(col++, fld.next());
213 jobitem.setNumericFld(col++, fld.next());
217 /* set default sorting */
218 mp_tableWidget->sortByColumn(m_jobIdIndex, Qt::DescendingOrder);
219 mp_tableWidget->setSortingEnabled(true);
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
228 QMessageBox::warning(this, "Bat",
229 tr("The Jobs query returned no results.\n"
230 "Press OK to continue?"), QMessageBox::Ok );
234 int rcnt = mp_tableWidget->rowCount();
235 int ccnt = mp_tableWidget->columnCount();
236 for(int r=0; r < rcnt; r++) {
237 for(int c=0; c < ccnt; c++) {
238 QTableWidgetItem* item = mp_tableWidget->item(r, c);
240 item->setFlags(Qt::ItemFlags(item->flags() & (~Qt::ItemIsEditable)));
246 void JobList::prepareFilterWidgets()
249 clientComboBox->addItem(tr("Any"));
250 clientComboBox->addItems(m_console->client_list);
251 comboSel(clientComboBox, m_clientName);
253 QStringList volumeList;
254 getVolumeList(volumeList);
255 volumeComboBox->addItem(tr("Any"));
256 volumeComboBox->addItems(volumeList);
257 comboSel(volumeComboBox, m_mediaName);
259 jobComboBox->addItem(tr("Any"));
260 jobComboBox->addItems(m_console->job_list);
261 comboSel(jobComboBox, m_jobName);
263 levelComboFill(levelComboBox);
265 boolComboFill(purgedComboBox);
267 fileSetComboBox->addItem(tr("Any"));
268 fileSetComboBox->addItems(m_console->fileset_list);
269 comboSel(fileSetComboBox, m_filesetName);
271 poolComboBox->addItem(tr("Any"));
272 poolComboBox->addItems(m_console->pool_list);
274 jobStatusComboFill(statusComboBox);
278 void JobList::fillQueryString(QString &query)
281 int volumeIndex = volumeComboBox->currentIndex();
282 if (volumeIndex != -1)
283 m_mediaName = volumeComboBox->itemText(volumeIndex);
284 QString distinct = "";
285 if (m_mediaName != tr("Any")) { distinct = "DISTINCT "; }
286 query += "SELECT " + distinct + "Job.JobId AS JobId, Job.Name AS JobName, "
287 " Client.Name AS Client,"
288 " Job.Starttime AS JobStart, Job.Type AS JobType,"
289 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
290 " Job.JobBytes AS Bytes, Job.JobStatus AS Status,"
291 " Job.PurgedFiles AS Purged, FileSet.FileSet,"
292 " Pool.Name AS Pool,"
293 " (SELECT Media.VolumeName FROM JobMedia JOIN Media ON JobMedia.MediaId=Media.MediaId WHERE JobMedia.JobId=Job.JobId ORDER BY JobMediaId LIMIT 1) AS FirstVolume,"
294 " (SELECT count(DISTINCT MediaId) FROM JobMedia WHERE JobMedia.JobId=Job.JobId) AS Volumes"
296 " JOIN Client ON (Client.ClientId=Job.ClientId)"
297 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId) "
298 " LEFT OUTER JOIN Pool ON Job.PoolId = Pool.PoolId ";
299 QStringList conditions;
300 if (m_mediaName != tr("Any")) {
301 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId) "
302 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId) ";
303 conditions.append("Media.VolumeName='" + m_mediaName + "'");
306 comboCond(conditions, clientComboBox, "Client.Name");
307 comboCond(conditions, jobComboBox, "Job.Name");
308 levelComboCond(conditions, levelComboBox, "Job.Level");
309 jobStatusComboCond(conditions, statusComboBox, "Job.JobStatus");
310 boolComboCond(conditions, purgedComboBox, "Job.PurgedFiles");
311 comboCond(conditions, fileSetComboBox, "FileSet.FileSet");
312 comboCond(conditions, poolComboBox, "Pool.Name");
314 /* If Limit check box For limit by days is checked */
315 if (daysCheckBox->checkState() == Qt::Checked) {
316 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
317 QString since = stamp.toString(Qt::ISODate);
318 conditions.append("Job.Starttime > '" + since + "'");
320 if (filterCopyCheckBox->checkState() == Qt::Checked) {
321 conditions.append("Job.Type != 'c'" );
323 if (filterMigrationCheckBox->checkState() == Qt::Checked) {
324 conditions.append("Job.Type != 'g'" );
327 foreach (QString condition, conditions) {
329 query += " WHERE " + condition;
332 query += " AND " + condition;
336 query += " ORDER BY Job.JobId DESC";
337 /* If Limit check box for limit records returned is checked */
338 if (limitCheckBox->checkState() == Qt::Checked) {
340 limit.setNum(limitSpinBox->value());
341 query += " LIMIT " + limit;
346 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
347 * The tree has been populated.
349 void JobList::PgSeltreeWidgetClicked()
353 /* Lets make sure the splitter is not all the way to size index 0 == 0 */
354 QList<int> sizes = m_splitter->sizes();
356 int frameMax = frame->maximumHeight();
358 foreach(int size, sizes) { sizeSum += size; }
359 int tabHeight = mainWin->tabWidget->geometry().height();
361 sizes[1] = tabHeight - frameMax;
362 m_splitter->setSizes(sizes);
369 * Virtual function override of pages function which is called when this page
370 * is visible on the stack
372 void JobList::currentStackItem()
374 /* if (!m_populated) populate every time user comes back to this object */
379 * Virtual Function to return the name for the medialist tree widget
381 void JobList::treeWidgetName(QString &desc)
383 if (m_mediaName != "" ) {
384 desc = tr("Jobs Run on Volume %1").arg(m_mediaName);
385 } else if (m_clientName != "" ) {
386 desc = tr("Jobs Run from Client %1").arg(m_clientName);
387 } else if (m_jobName != "" ) {
388 desc = tr("Jobs Run of Job %1").arg(m_jobName);
389 } else if (m_filesetName != "" ) {
390 desc = tr("Jobs Run with fileset %1").arg(m_filesetName);
392 desc = tr("Jobs Run");
397 * Function to create connections for context sensitive menu for this and
400 void JobList::createConnections()
402 /* connect to the action specific to this pages class that shows up in the
403 * page selector tree */
404 connect(actionRefreshJobList, SIGNAL(triggered()), this, SLOT(populateTable()));
405 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
407 connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
409 graphButton->setEnabled(false);
410 graphButton->setVisible(false);
412 /* for the selectionChanged to maintain m_currentJob and a delete selection */
413 connect(mp_tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
414 connect(mp_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(showInfoForJob()));
416 /* Do what is required for the local context sensitive menu */
419 /* setContextMenuPolicy is required */
420 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
422 connect(actionListFilesOnJob, SIGNAL(triggered()), this, SLOT(consoleListFilesOnJob()));
423 connect(actionListJobMedia, SIGNAL(triggered()), this, SLOT(consoleListJobMedia()));
424 connect(actionListVolumes, SIGNAL(triggered()), this, SLOT(consoleListVolumes()));
425 connect(actionDeleteJob, SIGNAL(triggered()), this, SLOT(consoleDeleteJob()));
426 connect(actionPurgeFiles, SIGNAL(triggered()), this, SLOT(consolePurgeFiles()));
427 connect(actionRestoreFromJob, SIGNAL(triggered()), this, SLOT(preRestoreFromJob()));
428 connect(actionRestoreFromTime, SIGNAL(triggered()), this, SLOT(preRestoreFromTime()));
429 connect(actionShowLogForJob, SIGNAL(triggered()), this, SLOT(showLogForJob()));
430 connect(actionShowInfoForJob, SIGNAL(triggered()), this, SLOT(showInfoForJob()));
431 connect(actionCancelJob, SIGNAL(triggered()), this, SLOT(consoleCancelJob()));
432 connect(actionListJobTotals, SIGNAL(triggered()), this, SLOT(consoleListJobTotals()));
433 connect(m_splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(splitterMoved(int, int)));
435 m_contextActions.append(actionRefreshJobList);
436 m_contextActions.append(actionListJobTotals);
440 * Functions to respond to local context sensitive menu sending console commands
441 * If I could figure out how to make these one function passing a string, Yaaaaaa
443 void JobList::consoleListFilesOnJob()
445 QString cmd("list files jobid=");
447 if (mainWin->m_longList) { cmd.prepend("l"); }
450 void JobList::consoleListJobMedia()
452 QString cmd("list jobmedia jobid=");
454 if (mainWin->m_longList) { cmd.prepend("l"); }
457 void JobList::consoleListVolumes()
459 QString cmd("list volumes jobid=");
461 if (mainWin->m_longList) { cmd.prepend("l"); }
464 void JobList::consoleListJobTotals()
466 QString cmd("list jobtotals");
467 if (mainWin->m_longList) { cmd.prepend("l"); }
470 void JobList::consoleDeleteJob()
472 if (QMessageBox::warning(this, "Bat",
473 tr("Are you sure you want to delete?? !!!.\n"
474 "This delete command is used to delete a Job record and all associated catalog"
475 " records that were created. This command operates only on the Catalog"
476 " database and has no effect on the actual data written to a Volume. This"
477 " command can be dangerous and we strongly recommend that you do not use"
478 " it unless you know what you are doing. The Job and all its associated"
479 " records (File and JobMedia) will be deleted from the catalog."
480 "Press OK to proceed with delete operation.?"),
481 QMessageBox::Ok | QMessageBox::Cancel)
482 == QMessageBox::Cancel) { return; }
484 QString cmd("delete job jobid=");
485 cmd += m_selectedJobs;
486 consoleCommand(cmd, false);
489 void JobList::consolePurgeFiles()
491 if (QMessageBox::warning(this, "Bat",
492 tr("Are you sure you want to purge ?? !!!.\n"
493 "The Purge command will delete associated Catalog database records from Jobs and"
494 " Volumes without considering the retention period. Purge works only on the"
495 " Catalog database and does not affect data written to Volumes. This command can"
496 " be dangerous because you can delete catalog records associated with current"
497 " backups of files, and we recommend that you do not use it unless you know what"
499 "Press OK to proceed with the purge operation?"),
500 QMessageBox::Ok | QMessageBox::Cancel)
501 == QMessageBox::Cancel) { return; }
503 m_console->m_warningPrevent = true;
504 foreach(QString job, m_selectedJobsList) {
505 QString cmd("purge files jobid=");
507 consoleCommand(cmd, false);
509 m_console->m_warningPrevent = false;
514 * Subroutine to call preRestore to restore from a select job
516 void JobList::preRestoreFromJob()
518 new prerestorePage(m_currentJob, R_JOBIDLIST);
522 * Subroutine to call preRestore to restore from a select job
524 void JobList::preRestoreFromTime()
526 new prerestorePage(m_currentJob, R_JOBDATETIME);
530 * Subroutine to call class to show the log in the database from that job
532 void JobList::showLogForJob()
534 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
535 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
539 * Subroutine to call class to show the log in the database from that job
541 void JobList::showInfoForJob()
543 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
544 new Job(m_currentJob, pageSelectorTreeWidgetItem);
548 * Cancel a running job
550 void JobList::consoleCancelJob()
552 QString cmd("cancel jobid=");
560 void JobList::graphTable()
564 pass.recordLimitCheck = limitCheckBox->checkState();
565 pass.daysLimitCheck = daysCheckBox->checkState();
566 pass.recordLimitSpin = limitSpinBox->value();
567 pass.daysLimitSpin = daysSpinBox->value();
568 pass.jobCombo = jobComboBox->currentText();
569 pass.clientCombo = clientComboBox->currentText();
570 pass.volumeCombo = volumeComboBox->currentText();
571 pass.fileSetCombo = fileSetComboBox->currentText();
572 pass.purgedCombo = purgedComboBox->currentText();
573 pass.levelCombo = levelComboBox->currentText();
574 pass.statusCombo = statusComboBox->currentText();
576 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
577 new JobPlot(pageSelectorTreeWidgetItem, pass);
582 * Save user settings associated with this page
584 void JobList::writeSettings()
586 QSettings settings(m_console->m_dir->name(), "bat");
587 settings.beginGroup(m_groupText);
588 settings.setValue(m_splitText, m_splitter->saveState());
589 settings.setValue("FilterCopyCheckState", filterCopyCheckBox->checkState());
590 settings.setValue("FilterMigrationCheckState", filterMigrationCheckBox->checkState());
595 * Read and restore user settings associated with this page
597 void JobList::readSettings()
599 m_groupText = "JobListPage";
600 m_splitText = "splitterSizes_2";
601 QSettings settings(m_console->m_dir->name(), "bat");
602 settings.beginGroup(m_groupText);
603 if (settings.contains(m_splitText)) {
604 m_splitter->restoreState(settings.value(m_splitText).toByteArray());
606 filterCopyCheckBox->setCheckState((Qt::CheckState)settings.value("FilterCopyCheckState").toInt());
607 filterMigrationCheckBox->setCheckState((Qt::CheckState)settings.value("FilterMigrationCheckState").toInt());
612 * Function to fill m_selectedJobsCount and m_selectedJobs with selected values
614 void JobList::selectionChanged()
617 QList<QTableWidgetItem *> sitems = mp_tableWidget->selectedItems();
618 foreach (QTableWidgetItem *sitem, sitems) {
619 int row = sitem->row();
620 if (!rowList.contains(row)) {
626 m_selectedJobsList.clear();
628 foreach(int row, rowList) {
629 QTableWidgetItem * sitem = mp_tableWidget->item(row, m_jobIdIndex);
630 if (!first) m_selectedJobs.append(",");
632 m_selectedJobs.append(sitem->text());
633 m_selectedJobsList.append(sitem->text());
635 m_selectedJobsCount = rowList.count();
636 if (m_selectedJobsCount > 1) {
637 QString text = QString( tr("Delete list of %1 Jobs")).arg(m_selectedJobsCount);
638 actionDeleteJob->setText(text);
639 text = QString( tr("Purge Files from list of %1 Jobs")).arg(m_selectedJobsCount);
640 actionPurgeFiles->setText(text);
642 actionDeleteJob->setText(tr("Delete Single Job"));
643 actionPurgeFiles->setText(tr("Purge Files from single job"));
646 /* remove all actions */
647 foreach(QAction* mediaAction, mp_tableWidget->actions()) {
648 mp_tableWidget->removeAction(mediaAction);
652 mp_tableWidget->addAction(actionRefreshJobList);
653 if (m_selectedJobsCount == 1) {
654 mp_tableWidget->addAction(actionListFilesOnJob);
655 mp_tableWidget->addAction(actionListJobMedia);
656 mp_tableWidget->addAction(actionListVolumes);
657 mp_tableWidget->addAction(actionRestoreFromJob);
658 mp_tableWidget->addAction(actionRestoreFromTime);
659 mp_tableWidget->addAction(actionShowLogForJob);
660 mp_tableWidget->addAction(actionShowInfoForJob);
662 if (m_selectedJobsCount >= 1) {
663 mp_tableWidget->addAction(actionDeleteJob);
664 mp_tableWidget->addAction(actionPurgeFiles);
667 /* Make Connections */
668 if (m_checkCurrentWidget) {
669 int row = mp_tableWidget->currentRow();
670 QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
671 m_currentJob = jobitem->text();
673 /* include purged action or not */
674 jobitem = mp_tableWidget->item(row, m_purgedIndex);
675 QString purged = jobitem->text();
676 /* mp_tableWidget->removeAction(actionPurgeFiles);
677 if (purged == tr("No") ) {
678 mp_tableWidget->addAction(actionPurgeFiles);
680 /* include restore from time and job action or not */
681 jobitem = mp_tableWidget->item(row, m_typeIndex);
682 QString type = jobitem->text();
683 if (m_selectedJobsCount == 1) {
684 mp_tableWidget->removeAction(actionRestoreFromJob);
685 mp_tableWidget->removeAction(actionRestoreFromTime);
686 if (type == tr("Backup")) {
687 mp_tableWidget->addAction(actionRestoreFromJob);
688 mp_tableWidget->addAction(actionRestoreFromTime);
691 /* include cancel action or not */
692 jobitem = mp_tableWidget->item(row, m_statusIndex);
693 QString status = jobitem->text();
694 mp_tableWidget->removeAction(actionCancelJob);
695 if (status == tr("Running") || status == tr("Created, not yet running")) {
696 mp_tableWidget->addAction(actionCancelJob);
702 * Function to prevent the splitter from making index 0 of the size larger than it
705 void JobList::splitterMoved(int /*pos*/, int /*index*/)
707 int frameMax = frame->maximumHeight();
708 QList<int> sizes = m_splitter->sizes();
710 foreach(int size, sizes) { sizeSum += size; }
711 if (sizes[0] > frameMax) {
713 sizes[1] = sizeSum - frameMax;
714 m_splitter->setSizes(sizes);