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 plus additions
11 that are listed in the file LICENSE.
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 "help/help.h"
50 #include "jobs/jobs.h"
53 * Daemon message callback
55 void message_callback(int /* type */, char *msg)
57 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
60 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
62 m_dtformat = "yyyy-MM-dd HH:mm:ss";
64 setupUi(this); /* Setup UI defined by main.ui (designer) */
65 register_message_callback(message_callback);
68 treeWidget->setColumnCount(1);
69 treeWidget->setHeaderLabel("Select Page");
70 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
82 foreach(Console *console, m_consoleHash) {
83 console->connect_dir();
85 m_currentConsole = (Console*)getFromHash(m_firstItem);
86 m_currentConsole->setCurrent();
88 QString directoryResourceName;
89 m_currentConsole->getDirResName(directoryResourceName);
90 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
94 void MainWin::createPages()
97 QTreeWidgetItem *item, *topItem;
101 foreach_res(dir, R_DIRECTOR) {
103 /* Create console tree stacked widget item */
104 m_currentConsole = new Console(stackedWidget);
105 m_currentConsole->setDirRes(dir);
106 m_currentConsole->readSettings();
108 /* The top tree item representing the director */
109 topItem = createTopPage(dir->name());
110 topItem->setIcon(0, QIcon(":images/server.png"));
111 /* Set background to grey for ease of identification of inactive Director */
112 QBrush greyBrush(Qt::lightGray);
113 topItem->setBackground(0, greyBrush);
114 m_currentConsole->setDirectorTreeItem(topItem);
115 m_consoleHash.insert(topItem, m_currentConsole);
117 /* Create Tree Widget Item */
118 item = createPage("Console", topItem);
119 if (!m_firstItem){ m_firstItem = item; }
120 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
122 /* insert the cosole and tree widget item into the hashes */
123 hashInsert(item, m_currentConsole);
125 /* Set Color of treeWidgetItem for the console
126 * It will be set to green in the console class if the connection is made.
128 QBrush redBrush(Qt::red);
129 item->setForeground(0, redBrush);
130 m_currentConsole->dockPage();
133 * Create instances in alphabetic order of the rest
134 * of the classes that will by default exist under each Director.
136 // new new bRestore();
140 QString emptymedia(""), emptyclient("");
141 createPageJobList(emptymedia, emptyclient, NULL);
145 treeWidget->expandItem(topItem);
146 stackedWidget->setCurrentWidget(m_currentConsole);
152 * create an instance of the the joblist class on the stack
154 void MainWin::createPageJobList(QString &media, QString &client,
155 QTreeWidgetItem *parentTreeWidgetItem)
157 QTreeWidgetItem *holdItem;
159 /* save current tree widget item in case query produces no results */
160 holdItem = treeWidget->currentItem();
161 JobList* joblist = new JobList(media, client, parentTreeWidgetItem);
162 /* If this is a query of jobs on a specific media */
163 if ((media != "") || (client != "")) {
164 joblist->setCurrent();
165 /* did query produce results, if not close window and set back to hold */
166 if (joblist->m_resultCount == 0) {
167 joblist->closeStackPage();
168 treeWidget->setCurrentItem(holdItem);
173 /* Create a root Tree Widget */
174 QTreeWidgetItem *MainWin::createTopPage(char *name)
176 QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
177 item->setText(0, name);
181 /* Create A Tree Widget Item which will be associated with a Page in the stacked widget */
182 QTreeWidgetItem *MainWin::createPage(char *name, QTreeWidgetItem *parent)
184 QTreeWidgetItem *item = new QTreeWidgetItem(parent);
185 item->setText(0, name);
190 * Handle up and down arrow keys for the command line
193 void MainWin::keyPressEvent(QKeyEvent *event)
195 if (m_cmd_history.size() == 0) {
199 switch (event->key()) {
201 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
208 if (m_cmd_last == 0) {
212 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
213 m_cmd_last = m_cmd_history.size() - 1;
222 lineEdit->setText(m_cmd_history[m_cmd_last]);
225 void MainWin::createConnections()
227 /* Connect signals to slots */
228 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
229 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
230 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
231 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
232 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
233 connect(treeWidget, SIGNAL(
234 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
235 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
236 connect(stackedWidget, SIGNAL(currentChanged(int)),
237 this, SLOT(stackItemChanged(int)));
238 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
239 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
240 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
241 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
242 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
243 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
244 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
245 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
246 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
250 * Reimplementation of QWidget closeEvent virtual function
252 void MainWin::closeEvent(QCloseEvent *event)
255 foreach(Console *console, m_consoleHash){
256 console->writeSettings();
257 console->terminate();
260 foreach(Pages *page, m_pagehash) {
261 if (!page->isDocked())
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 Pages *previousPage, *nextPage;
313 Console *previousConsole, *nextConsole;
315 /* first determine the next item */
317 /* knowing the treeWidgetItem, get the page from the hash */
318 nextPage = getFromHash(currentitem);
319 nextConsole = m_consoleHash.value(currentitem);
320 /* Is this a page that has been inserted into the hash */
322 nextConsole = nextPage->console();
323 /* then is it a treeWidgetItem representing a director */
324 } else if (nextConsole) {
325 /* let the next page BE the console */
326 nextPage = nextConsole;
328 /* Should never get here */
333 /* The Previous item */
335 /* this condition prevents a segfault. The first time there is no previousitem*/
337 /* knowing the treeWidgetItem, get the page from the hash */
338 previousPage = getFromHash(previousitem);
339 previousConsole = m_consoleHash.value(previousitem);
341 previousConsole = previousPage->console();
342 } else if (previousConsole) {
343 previousPage = previousConsole;
345 if ((previousPage) || (previousConsole)) {
346 if (nextConsole != previousConsole) {
347 /* remove connections to the current console */
348 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
349 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
350 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
351 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
352 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
353 QBrush greyBrush(Qt::lightGray);
354 dirItem->setBackground(0, greyBrush);
356 /* make sure the close window and toggle dock options are removed */
357 treeWidget->removeAction(actionClosePage);
358 treeWidget->removeAction(actionToggleDock);
359 /* Is this a page that has been inserted into the hash */
361 foreach(QAction* pageaction, previousPage->m_contextActions) {
362 treeWidget->removeAction(pageaction);
368 /* process the current (next) item */
370 if ((nextPage) || (nextConsole)) {
371 if (nextConsole != previousConsole) {
372 /* make connections to the current console */
373 m_currentConsole = nextConsole;
374 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
375 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
376 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
377 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
378 /* Set director's tree widget background to magenta for ease of identification */
379 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
380 QBrush magentaBrush(Qt::magenta);
381 dirItem->setBackground(0, magentaBrush);
383 /* set the value for the currently active console */
384 int stackindex = stackedWidget->indexOf(nextPage);
386 /* Is this page currently on the stack or is it undocked */
387 if (stackindex >= 0) {
388 /* put this page on the top of the stack */
389 stackedWidget->setCurrentIndex(stackindex);
391 /* it is undocked, raise it to the front */
394 /* for the page selectors menu action to dock or undock, set the text */
395 nextPage->setContextMenuDockText();
397 treeWidget->addAction(actionToggleDock);
398 /* if this page is closeable, then add that action */
399 if (nextPage->isCloseable()) {
400 treeWidget->addAction(actionClosePage);
403 /* Add the actions to the Page Selectors tree widget that are part of the
404 * current items list of desired actions regardless of whether on top of stack*/
405 treeWidget->addActions(nextPage->m_contextActions);
409 void MainWin::labelButtonClicked()
414 void MainWin::runButtonClicked()
419 void MainWin::estimateButtonClicked()
424 void MainWin::restoreButtonClicked()
426 new prerestorePage();
430 * The user just finished typing a line in the command line edit box
432 void MainWin::input_line()
434 QString cmdStr = lineEdit->text(); /* Get the text */
435 lineEdit->clear(); /* clear the lineEdit box */
436 if (m_currentConsole->is_connected()) {
437 m_currentConsole->consoleCommand(cmdStr);
439 set_status("Director not connected. Click on connect button.");
441 m_cmd_history.append(cmdStr);
443 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
444 m_currentConsole->setCurrent();
448 void MainWin::about()
450 QMessageBox::about(this, tr("About bat"),
451 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
452 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
453 "<p>The <b>bat</b> is an administrative console"
454 " interface to the Director."));
459 Help::displayFile("index.html");
462 void MainWin::set_statusf(const char *fmt, ...)
467 va_start(arg_ptr, fmt);
468 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
473 void MainWin::set_status_ready()
475 set_status(" Ready");
478 void MainWin::set_status(const char *buf)
480 statusBar()->showMessage(buf);
484 * Function to respond to the button bar button to undock
486 void MainWin::undockWindowButton()
488 Pages* page = (Pages*)stackedWidget->currentWidget();
489 page->togglePageDocking();
493 * Function to respond to action on page selector context menu to toggle the
494 * dock status of the window associated with the page selectors current
497 void MainWin::toggleDockContextWindow()
499 QTreeWidgetItem *currentitem = treeWidget->currentItem();
501 /* Is this a page that has been inserted into the hash */
502 if (getFromHash(currentitem)) {
503 Pages* page = getFromHash(currentitem);
504 page->togglePageDocking();
509 * This function is called when the stack item is changed. Call
510 * the virtual function here. Avoids a window being undocked leaving
511 * a window at the top of the stack unpopulated.
513 void MainWin::stackItemChanged(int)
515 Pages* page = (Pages*)stackedWidget->currentWidget();
516 /* run the virtual function in case this class overrides it */
517 page->currentStackItem();
521 * Function to simplify insertion of QTreeWidgetItem <-> Page association
522 * into a double direction hash.
524 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
526 m_pagehash.insert(item, page);
527 m_widgethash.insert(page, item);
531 * Function to simplify removal of QTreeWidgetItem <-> Page association
532 * into a double direction hash.
534 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
536 /* I had all sorts of return status checking code here. Do we have a log
537 * level capability in bat. I would have left it in but it used printf's
538 * and it should really be some kind of log level facility ???
539 * ******FIXME********/
540 m_pagehash.remove(item);
541 m_widgethash.remove(page);
545 * Function to retrieve a Page* when the item in the page selector's tree is
548 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
550 return m_pagehash.value(item);
554 * Function to retrieve the page selectors tree widget item when the page is
557 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
559 return m_widgethash.value(page);
563 * Function to respond to action on page selector context menu to close the
566 void MainWin::closePage()
568 QTreeWidgetItem *currentitem = treeWidget->currentItem();
570 /* Is this a page that has been inserted into the hash */
571 if (getFromHash(currentitem)) {
572 Pages* page = getFromHash(currentitem);
573 if (page->isCloseable()) {
574 page->closeStackPage();
579 /* Quick function to return the current console */
580 Console *MainWin::currentConsole()
582 return m_currentConsole;
584 /* Quick function to return the tree item for the director */
585 QTreeWidgetItem *MainWin::currentTopItem()
587 return m_currentConsole->directorTreeItem();
590 /* Preferences menu item clicked */
591 void MainWin::setPreferences()
594 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
595 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
596 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
597 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
598 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
599 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
600 prefs.recordSpinBox->setValue(m_recordLimitVal);
601 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
602 prefs.daysSpinBox->setValue(m_daysLimitVal);
603 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
604 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
605 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
610 /* Preferences dialog */
611 prefsDialog::prefsDialog()
616 void prefsDialog::accept()
619 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
620 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
621 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
622 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
623 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
624 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
625 mainWin->m_recordLimitVal = this->recordSpinBox->value();
626 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
627 mainWin->m_daysLimitVal = this->daysSpinBox->value();
628 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
629 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
630 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
631 QSettings settings("www.bacula.org", "bat");
632 settings.beginGroup("Debug");
633 settings.setValue("commDebug", mainWin->m_commDebug);
634 settings.setValue("displayAll", mainWin->m_displayAll);
635 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
636 settings.setValue("commandDebug", mainWin->m_commandDebug);
637 settings.setValue("miscDebug", mainWin->m_miscDebug);
639 settings.beginGroup("JobList");
640 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
641 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
642 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
643 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
645 settings.beginGroup("Messages");
646 settings.setValue("checkMessages", mainWin->m_checkMessages);
647 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
649 settings.beginGroup("Misc");
650 settings.setValue("longList", mainWin->m_longList);
652 foreach(Console *console, mainWin->m_consoleHash) {
653 console->startTimer();
657 void prefsDialog::reject()
660 mainWin->set_status("Canceled");
663 /* read preferences for the prefences dialog box */
664 void MainWin::readPreferences()
666 QSettings settings("www.bacula.org", "bat");
667 settings.beginGroup("Debug");
668 m_commDebug = settings.value("commDebug", false).toBool();
669 m_displayAll = settings.value("displayAll", false).toBool();
670 m_sqlDebug = settings.value("sqlDebug", false).toBool();
671 m_commandDebug = settings.value("commandDebug", false).toBool();
672 m_miscDebug = settings.value("miscDebug", false).toBool();
674 settings.beginGroup("JobList");
675 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
676 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
677 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
678 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
680 settings.beginGroup("Messages");
681 m_checkMessages = settings.value("checkMessages", false).toBool();
682 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
684 settings.beginGroup("Misc");
685 m_longList = settings.value("longList", false).toBool();