2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2007 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 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.
32 * Main Window control for bat (qt-console)
34 * Kern Sibbald, January MMVII
39 #include "joblist/joblist.h"
40 #include "storage/storage.h"
41 #include "fileset/fileset.h"
42 #include "label/label.h"
45 #include "restore/restore.h"
46 #include "medialist/medialist.h"
47 #include "joblist/joblist.h"
48 #include "clients/clients.h"
49 #include "restore/restoretree.h"
50 #include "help/help.h"
51 #include "jobs/jobs.h"
52 #include "jobgraphs/jobplot.h"
55 * Daemon message callback
57 void message_callback(int /* type */, char *msg)
59 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
62 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
65 m_dtformat = "yyyy-MM-dd HH:mm:ss";
67 setupUi(this); /* Setup UI defined by main.ui (designer) */
68 register_message_callback(message_callback);
71 treeWidget->setColumnCount(1);
72 treeWidget->setHeaderLabel("Select Page");
73 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
85 foreach(Console *console, m_consoleHash) {
86 console->connect_dir();
88 m_currentConsole = (Console*)getFromHash(m_firstItem);
89 m_currentConsole->setCurrent();
91 QString directoryResourceName;
92 m_currentConsole->getDirResName(directoryResourceName);
93 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
97 void MainWin::createPages()
100 QTreeWidgetItem *item, *topItem;
104 foreach_res(dir, R_DIRECTOR) {
106 /* Create console tree stacked widget item */
107 m_currentConsole = new Console(stackedWidget);
108 m_currentConsole->setDirRes(dir);
109 m_currentConsole->readSettings();
111 /* The top tree item representing the director */
112 topItem = new QTreeWidgetItem(treeWidget);
113 topItem->setText(0, dir->name());
114 topItem->setIcon(0, QIcon(":images/server.png"));
115 /* Set background to grey for ease of identification of inactive Director */
116 QBrush greyBrush(Qt::lightGray);
117 topItem->setBackground(0, greyBrush);
118 m_currentConsole->setDirectorTreeItem(topItem);
119 m_consoleHash.insert(topItem, m_currentConsole);
121 /* Create Tree Widget Item */
122 item = new QTreeWidgetItem(topItem);
123 item->setText(0, "Console");
124 if (!m_firstItem){ m_firstItem = item; }
125 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
127 /* insert the cosole and tree widget item into the hashes */
128 hashInsert(item, m_currentConsole);
130 /* Set Color of treeWidgetItem for the console
131 * It will be set to green in the console class if the connection is made.
133 QBrush redBrush(Qt::red);
134 item->setForeground(0, redBrush);
135 m_currentConsole->dockPage();
138 * Create instances in alphabetic order of the rest
139 * of the classes that will by default exist under each Director.
145 createPageJobList("", "", "", "", NULL);
151 new JobPlot(NULL, pass);
153 treeWidget->expandItem(topItem);
154 stackedWidget->setCurrentWidget(m_currentConsole);
160 * create an instance of the the joblist class on the stack
162 void MainWin::createPageJobList(const QString &media, const QString &client,
163 const QString &job, const QString &fileset, QTreeWidgetItem *parentTreeWidgetItem)
165 QTreeWidgetItem *holdItem;
167 /* save current tree widget item in case query produces no results */
168 holdItem = treeWidget->currentItem();
169 JobList* joblist = new JobList(media, client, job, fileset, parentTreeWidgetItem);
170 /* If this is a query of jobs on a specific media */
171 if ((media != "") || (client != "") || (job != "") || (fileset != "")) {
172 joblist->setCurrent();
173 /* did query produce results, if not close window and set back to hold */
174 if (joblist->m_resultCount == 0) {
175 joblist->closeStackPage();
176 treeWidget->setCurrentItem(holdItem);
182 * Handle up and down arrow keys for the command line
185 void MainWin::keyPressEvent(QKeyEvent *event)
187 if (m_cmd_history.size() == 0) {
191 switch (event->key()) {
193 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
200 if (m_cmd_last == 0) {
204 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
205 m_cmd_last = m_cmd_history.size() - 1;
214 lineEdit->setText(m_cmd_history[m_cmd_last]);
217 void MainWin::createConnections()
219 /* Connect signals to slots */
220 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
221 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
222 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
223 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
224 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
225 connect(treeWidget, SIGNAL(
226 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
227 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
228 connect(stackedWidget, SIGNAL(currentChanged(int)),
229 this, SLOT(stackItemChanged(int)));
230 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
231 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
232 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
233 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
234 connect(actionBrowse, SIGNAL(triggered()), this, SLOT(browseButtonClicked()));
235 connect(actionJobPlot, SIGNAL(triggered()), this, SLOT(jobPlotButtonClicked()));
236 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
237 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
238 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
239 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
240 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
244 * Reimplementation of QWidget closeEvent virtual function
246 void MainWin::closeEvent(QCloseEvent *event)
250 /* close all non console pages, this will call settings in destructors */
251 foreach(Pages *page, m_pagehash) {
252 if (page != page->console()) {
253 page->console()->setCurrent();
254 page->closeStackPage();
257 /* close the console pages and terminate connection */
258 foreach(Console *console, m_consoleHash){
259 console->writeSettings();
260 console->terminate();
261 console->closeStackPage();
266 void MainWin::writeSettings()
268 QSettings settings("bacula.org", "bat");
270 settings.beginGroup("MainWin");
271 settings.setValue("winSize", size());
272 settings.setValue("winPos", pos());
273 settings.setValue("state", saveState());
277 void MainWin::readSettings()
279 QSettings settings("bacula.org", "bat");
281 settings.beginGroup("MainWin");
282 resize(settings.value("winSize", QSize(1041, 801)).toSize());
283 move(settings.value("winPos", QPoint(200, 150)).toPoint());
284 restoreState(settings.value("state").toByteArray());
289 * This subroutine is called with an item in the Page Selection window
292 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
294 /* Is this a page that has been inserted into the hash */
295 if (getFromHash(item)) {
296 Pages* page = getFromHash(item);
297 int stackindex=stackedWidget->indexOf(page);
299 if (stackindex >= 0) {
300 stackedWidget->setCurrentWidget(page);
302 /* run the virtual function in case this class overrides it */
303 page->PgSeltreeWidgetClicked();
308 * Called with a change of the highlighed tree widget item in the page selector.
310 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
312 if (m_isClosing) return; /* if closing the application, do nothing here */
314 Pages *previousPage, *nextPage;
315 Console *previousConsole, *nextConsole;
317 /* remove all actions before adding actions appropriate for new page */
318 foreach(QAction* pageAction, treeWidget->actions()) {
319 treeWidget->removeAction(pageAction);
322 /* first determine the next item */
324 /* knowing the treeWidgetItem, get the page from the hash */
325 nextPage = getFromHash(currentitem);
326 nextConsole = m_consoleHash.value(currentitem);
327 /* Is this a page that has been inserted into the hash */
329 nextConsole = nextPage->console();
330 /* then is it a treeWidgetItem representing a director */
331 } else if (nextConsole) {
332 /* let the next page BE the console */
333 nextPage = nextConsole;
335 /* Should never get here */
340 /* The Previous item */
342 /* this condition prevents a segfault. The first time there is no previousitem*/
344 /* knowing the treeWidgetItem, get the page from the hash */
345 previousPage = getFromHash(previousitem);
346 previousConsole = m_consoleHash.value(previousitem);
348 previousConsole = previousPage->console();
349 } else if (previousConsole) {
350 previousPage = previousConsole;
352 if ((previousPage) || (previousConsole)) {
353 if (nextConsole != previousConsole) {
354 /* remove connections to the current console */
355 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
356 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
357 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
358 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
359 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
360 QBrush greyBrush(Qt::lightGray);
361 dirItem->setBackground(0, greyBrush);
366 /* process the current (next) item */
368 if ((nextPage) || (nextConsole)) {
369 if (nextConsole != previousConsole) {
370 /* make connections to the current console */
371 m_currentConsole = nextConsole;
372 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
373 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
374 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
375 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
376 /* Set director's tree widget background to magenta for ease of identification */
377 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
378 QBrush magentaBrush(Qt::magenta);
379 dirItem->setBackground(0, magentaBrush);
381 /* set the value for the currently active console */
382 int stackindex = stackedWidget->indexOf(nextPage);
384 /* Is this page currently on the stack or is it undocked */
385 if (stackindex >= 0) {
386 /* put this page on the top of the stack */
387 stackedWidget->setCurrentIndex(stackindex);
389 /* it is undocked, raise it to the front */
392 /* for the page selectors menu action to dock or undock, set the text */
393 nextPage->setContextMenuDockText();
395 treeWidget->addAction(actionToggleDock);
396 /* if this page is closeable, and it has no childern, then add that action */
397 if ((nextPage->isCloseable()) && (currentitem->child(0) == NULL))
398 treeWidget->addAction(actionClosePage);
400 /* Add the actions to the Page Selectors tree widget that are part of the
401 * current items list of desired actions regardless of whether on top of stack*/
402 treeWidget->addActions(nextPage->m_contextActions);
406 void MainWin::labelButtonClicked()
411 void MainWin::runButtonClicked()
416 void MainWin::estimateButtonClicked()
421 void MainWin::browseButtonClicked()
426 void MainWin::restoreButtonClicked()
428 new prerestorePage();
431 void MainWin::jobPlotButtonClicked()
435 new JobPlot(NULL, pass);
439 * The user just finished typing a line in the command line edit box
441 void MainWin::input_line()
443 QString cmdStr = lineEdit->text(); /* Get the text */
444 lineEdit->clear(); /* clear the lineEdit box */
445 if (m_currentConsole->is_connected()) {
446 /* Use consoleInput to allow typing anything */
447 m_currentConsole->consoleInput(cmdStr);
449 set_status("Director not connected. Click on connect button.");
451 m_cmd_history.append(cmdStr);
453 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
454 m_currentConsole->setCurrent();
458 void MainWin::about()
460 QMessageBox::about(this, tr("About bat"),
461 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
462 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
463 "<p>The <b>bat</b> is an administrative console"
464 " interface to the Director."));
469 Help::displayFile("index.html");
472 void MainWin::set_statusf(const char *fmt, ...)
477 va_start(arg_ptr, fmt);
478 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
483 void MainWin::set_status_ready()
485 set_status(" Ready");
488 void MainWin::set_status(const char *buf)
490 statusBar()->showMessage(buf);
494 * Function to respond to the button bar button to undock
496 void MainWin::undockWindowButton()
498 Pages* page = (Pages*)stackedWidget->currentWidget();
499 page->togglePageDocking();
503 * Function to respond to action on page selector context menu to toggle the
504 * dock status of the window associated with the page selectors current
507 void MainWin::toggleDockContextWindow()
509 QTreeWidgetItem *currentitem = treeWidget->currentItem();
511 /* Is this a page that has been inserted into the hash */
512 if (getFromHash(currentitem)) {
513 Pages* page = getFromHash(currentitem);
514 page->togglePageDocking();
519 * This function is called when the stack item is changed. Call
520 * the virtual function here. Avoids a window being undocked leaving
521 * a window at the top of the stack unpopulated.
523 void MainWin::stackItemChanged(int)
525 if (m_isClosing) return; /* if closing the application, do nothing here */
526 Pages* page = (Pages*)stackedWidget->currentWidget();
527 /* run the virtual function in case this class overrides it */
528 page->currentStackItem();
532 * Function to simplify insertion of QTreeWidgetItem <-> Page association
533 * into a double direction hash.
535 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
537 m_pagehash.insert(item, page);
538 m_widgethash.insert(page, item);
542 * Function to simplify removal of QTreeWidgetItem <-> Page association
543 * into a double direction hash.
545 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
547 /* I had all sorts of return status checking code here. Do we have a log
548 * level capability in bat. I would have left it in but it used printf's
549 * and it should really be some kind of log level facility ???
550 * ******FIXME********/
551 m_pagehash.remove(item);
552 m_widgethash.remove(page);
556 * Function to retrieve a Page* when the item in the page selector's tree is
559 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
561 return m_pagehash.value(item);
565 * Function to retrieve the page selectors tree widget item when the page is
568 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
570 return m_widgethash.value(page);
574 * Function to respond to action on page selector context menu to close the
577 void MainWin::closePage()
579 QTreeWidgetItem *currentitem = treeWidget->currentItem();
581 /* Is this a page that has been inserted into the hash */
582 if (getFromHash(currentitem)) {
583 Pages* page = getFromHash(currentitem);
584 if (page->isCloseable()) {
585 page->closeStackPage();
590 /* Quick function to return the current console */
591 Console *MainWin::currentConsole()
593 return m_currentConsole;
595 /* Quick function to return the tree item for the director */
596 QTreeWidgetItem *MainWin::currentTopItem()
598 return m_currentConsole->directorTreeItem();
601 /* Preferences menu item clicked */
602 void MainWin::setPreferences()
605 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
606 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
607 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
608 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
609 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
610 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
611 prefs.recordSpinBox->setValue(m_recordLimitVal);
612 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
613 prefs.daysSpinBox->setValue(m_daysLimitVal);
614 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
615 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
616 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
621 /* Preferences dialog */
622 prefsDialog::prefsDialog()
627 void prefsDialog::accept()
630 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
631 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
632 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
633 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
634 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
635 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
636 mainWin->m_recordLimitVal = this->recordSpinBox->value();
637 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
638 mainWin->m_daysLimitVal = this->daysSpinBox->value();
639 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
640 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
641 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
642 QSettings settings("www.bacula.org", "bat");
643 settings.beginGroup("Debug");
644 settings.setValue("commDebug", mainWin->m_commDebug);
645 settings.setValue("displayAll", mainWin->m_displayAll);
646 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
647 settings.setValue("commandDebug", mainWin->m_commandDebug);
648 settings.setValue("miscDebug", mainWin->m_miscDebug);
650 settings.beginGroup("JobList");
651 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
652 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
653 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
654 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
656 settings.beginGroup("Messages");
657 settings.setValue("checkMessages", mainWin->m_checkMessages);
658 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
660 settings.beginGroup("Misc");
661 settings.setValue("longList", mainWin->m_longList);
663 foreach(Console *console, mainWin->m_consoleHash) {
664 console->startTimer();
668 void prefsDialog::reject()
671 mainWin->set_status("Canceled");
674 /* read preferences for the prefences dialog box */
675 void MainWin::readPreferences()
677 QSettings settings("www.bacula.org", "bat");
678 settings.beginGroup("Debug");
679 m_commDebug = settings.value("commDebug", false).toBool();
680 m_displayAll = settings.value("displayAll", false).toBool();
681 m_sqlDebug = settings.value("sqlDebug", false).toBool();
682 m_commandDebug = settings.value("commandDebug", false).toBool();
683 m_miscDebug = settings.value("miscDebug", false).toBool();
685 settings.beginGroup("JobList");
686 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
687 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
688 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
689 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
691 settings.beginGroup("Messages");
692 m_checkMessages = settings.value("checkMessages", false).toBool();
693 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
695 settings.beginGroup("Misc");
696 m_longList = settings.value("longList", false).toBool();