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"
54 * Daemon message callback
56 void message_callback(int /* type */, char *msg)
58 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
61 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
63 m_dtformat = "yyyy-MM-dd HH:mm:ss";
65 setupUi(this); /* Setup UI defined by main.ui (designer) */
66 register_message_callback(message_callback);
69 treeWidget->setColumnCount(1);
70 treeWidget->setHeaderLabel("Select Page");
71 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
83 foreach(Console *console, m_consoleHash) {
84 console->connect_dir();
86 m_currentConsole = (Console*)getFromHash(m_firstItem);
87 m_currentConsole->setCurrent();
89 QString directoryResourceName;
90 m_currentConsole->getDirResName(directoryResourceName);
91 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
95 void MainWin::createPages()
98 QTreeWidgetItem *item, *topItem;
102 foreach_res(dir, R_DIRECTOR) {
104 /* Create console tree stacked widget item */
105 m_currentConsole = new Console(stackedWidget);
106 m_currentConsole->setDirRes(dir);
107 m_currentConsole->readSettings();
109 /* The top tree item representing the director */
110 topItem = new QTreeWidgetItem(treeWidget);
111 topItem->setText(0, dir->name());
112 topItem->setIcon(0, QIcon(":images/server.png"));
113 /* Set background to grey for ease of identification of inactive Director */
114 QBrush greyBrush(Qt::lightGray);
115 topItem->setBackground(0, greyBrush);
116 m_currentConsole->setDirectorTreeItem(topItem);
117 m_consoleHash.insert(topItem, m_currentConsole);
119 /* Create Tree Widget Item */
120 item = new QTreeWidgetItem(topItem);
121 item->setText(0, "Console");
122 if (!m_firstItem){ m_firstItem = item; }
123 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
125 /* insert the cosole and tree widget item into the hashes */
126 hashInsert(item, m_currentConsole);
128 /* Set Color of treeWidgetItem for the console
129 * It will be set to green in the console class if the connection is made.
131 QBrush redBrush(Qt::red);
132 item->setForeground(0, redBrush);
133 m_currentConsole->dockPage();
136 * Create instances in alphabetic order of the rest
137 * of the classes that will by default exist under each Director.
143 createPageJobList("", "", "", "", NULL);
148 treeWidget->expandItem(topItem);
149 stackedWidget->setCurrentWidget(m_currentConsole);
155 * create an instance of the the joblist class on the stack
157 void MainWin::createPageJobList(const QString &media, const QString &client,
158 const QString &job, const QString &fileset, QTreeWidgetItem *parentTreeWidgetItem)
160 QTreeWidgetItem *holdItem;
162 /* save current tree widget item in case query produces no results */
163 holdItem = treeWidget->currentItem();
164 JobList* joblist = new JobList(media, client, job, fileset, parentTreeWidgetItem);
165 /* If this is a query of jobs on a specific media */
166 if ((media != "") || (client != "") || (job != "") || (fileset != "")) {
167 joblist->setCurrent();
168 /* did query produce results, if not close window and set back to hold */
169 if (joblist->m_resultCount == 0) {
170 joblist->closeStackPage();
171 treeWidget->setCurrentItem(holdItem);
177 * Handle up and down arrow keys for the command line
180 void MainWin::keyPressEvent(QKeyEvent *event)
182 if (m_cmd_history.size() == 0) {
186 switch (event->key()) {
188 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
195 if (m_cmd_last == 0) {
199 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
200 m_cmd_last = m_cmd_history.size() - 1;
209 lineEdit->setText(m_cmd_history[m_cmd_last]);
212 void MainWin::createConnections()
214 /* Connect signals to slots */
215 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
216 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
217 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
218 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
219 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
220 connect(treeWidget, SIGNAL(
221 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
222 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
223 connect(stackedWidget, SIGNAL(currentChanged(int)),
224 this, SLOT(stackItemChanged(int)));
225 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
226 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
227 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
228 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
229 connect(actionBrowse, SIGNAL(triggered()), this, SLOT(browseButtonClicked()));
230 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
231 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
232 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
233 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
234 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
238 * Reimplementation of QWidget closeEvent virtual function
240 void MainWin::closeEvent(QCloseEvent *event)
243 foreach(Console *console, m_consoleHash){
244 console->writeSettings();
245 console->terminate();
248 foreach(Pages *page, m_pagehash) {
249 if (!page->isDocked())
254 void MainWin::writeSettings()
256 QSettings settings("bacula.org", "bat");
258 settings.beginGroup("MainWin");
259 settings.setValue("winSize", size());
260 settings.setValue("winPos", pos());
261 settings.setValue("state", saveState());
265 void MainWin::readSettings()
267 QSettings settings("bacula.org", "bat");
269 settings.beginGroup("MainWin");
270 resize(settings.value("winSize", QSize(1041, 801)).toSize());
271 move(settings.value("winPos", QPoint(200, 150)).toPoint());
272 restoreState(settings.value("state").toByteArray());
277 * This subroutine is called with an item in the Page Selection window
280 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
282 /* Is this a page that has been inserted into the hash */
283 if (getFromHash(item)) {
284 Pages* page = getFromHash(item);
285 int stackindex=stackedWidget->indexOf(page);
287 if (stackindex >= 0) {
288 stackedWidget->setCurrentWidget(page);
290 /* run the virtual function in case this class overrides it */
291 page->PgSeltreeWidgetClicked();
296 * Called with a change of the highlighed tree widget item in the page selector.
298 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
300 Pages *previousPage, *nextPage;
301 Console *previousConsole, *nextConsole;
303 /* remove all actions before adding actions appropriate for new page */
304 foreach(QAction* pageAction, treeWidget->actions()) {
305 treeWidget->removeAction(pageAction);
308 /* first determine the next item */
310 /* knowing the treeWidgetItem, get the page from the hash */
311 nextPage = getFromHash(currentitem);
312 nextConsole = m_consoleHash.value(currentitem);
313 /* Is this a page that has been inserted into the hash */
315 nextConsole = nextPage->console();
316 /* then is it a treeWidgetItem representing a director */
317 } else if (nextConsole) {
318 /* let the next page BE the console */
319 nextPage = nextConsole;
321 /* Should never get here */
326 /* The Previous item */
328 /* this condition prevents a segfault. The first time there is no previousitem*/
330 /* knowing the treeWidgetItem, get the page from the hash */
331 previousPage = getFromHash(previousitem);
332 previousConsole = m_consoleHash.value(previousitem);
334 previousConsole = previousPage->console();
335 } else if (previousConsole) {
336 previousPage = previousConsole;
338 if ((previousPage) || (previousConsole)) {
339 if (nextConsole != previousConsole) {
340 /* remove connections to the current console */
341 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
342 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
343 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
344 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
345 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
346 QBrush greyBrush(Qt::lightGray);
347 dirItem->setBackground(0, greyBrush);
352 /* process the current (next) item */
354 if ((nextPage) || (nextConsole)) {
355 if (nextConsole != previousConsole) {
356 /* make connections to the current console */
357 m_currentConsole = nextConsole;
358 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
359 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
360 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
361 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
362 /* Set director's tree widget background to magenta for ease of identification */
363 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
364 QBrush magentaBrush(Qt::magenta);
365 dirItem->setBackground(0, magentaBrush);
367 /* set the value for the currently active console */
368 int stackindex = stackedWidget->indexOf(nextPage);
370 /* Is this page currently on the stack or is it undocked */
371 if (stackindex >= 0) {
372 /* put this page on the top of the stack */
373 stackedWidget->setCurrentIndex(stackindex);
375 /* it is undocked, raise it to the front */
378 /* for the page selectors menu action to dock or undock, set the text */
379 nextPage->setContextMenuDockText();
381 treeWidget->addAction(actionToggleDock);
382 /* if this page is closeable, and it has no childern, then add that action */
383 if ((nextPage->isCloseable()) && (currentitem->child(0) == NULL))
384 treeWidget->addAction(actionClosePage);
386 /* Add the actions to the Page Selectors tree widget that are part of the
387 * current items list of desired actions regardless of whether on top of stack*/
388 treeWidget->addActions(nextPage->m_contextActions);
392 void MainWin::labelButtonClicked()
397 void MainWin::runButtonClicked()
402 void MainWin::estimateButtonClicked()
407 void MainWin::browseButtonClicked()
412 void MainWin::restoreButtonClicked()
414 new prerestorePage();
418 * The user just finished typing a line in the command line edit box
420 void MainWin::input_line()
422 QString cmdStr = lineEdit->text(); /* Get the text */
423 lineEdit->clear(); /* clear the lineEdit box */
424 if (m_currentConsole->is_connected()) {
425 m_currentConsole->consoleCommand(cmdStr);
427 set_status("Director not connected. Click on connect button.");
429 m_cmd_history.append(cmdStr);
431 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
432 m_currentConsole->setCurrent();
436 void MainWin::about()
438 QMessageBox::about(this, tr("About bat"),
439 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
440 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
441 "<p>The <b>bat</b> is an administrative console"
442 " interface to the Director."));
447 Help::displayFile("index.html");
450 void MainWin::set_statusf(const char *fmt, ...)
455 va_start(arg_ptr, fmt);
456 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
461 void MainWin::set_status_ready()
463 set_status(" Ready");
466 void MainWin::set_status(const char *buf)
468 statusBar()->showMessage(buf);
472 * Function to respond to the button bar button to undock
474 void MainWin::undockWindowButton()
476 Pages* page = (Pages*)stackedWidget->currentWidget();
477 page->togglePageDocking();
481 * Function to respond to action on page selector context menu to toggle the
482 * dock status of the window associated with the page selectors current
485 void MainWin::toggleDockContextWindow()
487 QTreeWidgetItem *currentitem = treeWidget->currentItem();
489 /* Is this a page that has been inserted into the hash */
490 if (getFromHash(currentitem)) {
491 Pages* page = getFromHash(currentitem);
492 page->togglePageDocking();
497 * This function is called when the stack item is changed. Call
498 * the virtual function here. Avoids a window being undocked leaving
499 * a window at the top of the stack unpopulated.
501 void MainWin::stackItemChanged(int)
503 Pages* page = (Pages*)stackedWidget->currentWidget();
504 /* run the virtual function in case this class overrides it */
505 page->currentStackItem();
509 * Function to simplify insertion of QTreeWidgetItem <-> Page association
510 * into a double direction hash.
512 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
514 m_pagehash.insert(item, page);
515 m_widgethash.insert(page, item);
519 * Function to simplify removal of QTreeWidgetItem <-> Page association
520 * into a double direction hash.
522 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
524 /* I had all sorts of return status checking code here. Do we have a log
525 * level capability in bat. I would have left it in but it used printf's
526 * and it should really be some kind of log level facility ???
527 * ******FIXME********/
528 m_pagehash.remove(item);
529 m_widgethash.remove(page);
533 * Function to retrieve a Page* when the item in the page selector's tree is
536 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
538 return m_pagehash.value(item);
542 * Function to retrieve the page selectors tree widget item when the page is
545 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
547 return m_widgethash.value(page);
551 * Function to respond to action on page selector context menu to close the
554 void MainWin::closePage()
556 QTreeWidgetItem *currentitem = treeWidget->currentItem();
558 /* Is this a page that has been inserted into the hash */
559 if (getFromHash(currentitem)) {
560 Pages* page = getFromHash(currentitem);
561 if (page->isCloseable()) {
562 page->closeStackPage();
567 /* Quick function to return the current console */
568 Console *MainWin::currentConsole()
570 return m_currentConsole;
572 /* Quick function to return the tree item for the director */
573 QTreeWidgetItem *MainWin::currentTopItem()
575 return m_currentConsole->directorTreeItem();
578 /* Preferences menu item clicked */
579 void MainWin::setPreferences()
582 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
583 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
584 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
585 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
586 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
587 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
588 prefs.recordSpinBox->setValue(m_recordLimitVal);
589 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
590 prefs.daysSpinBox->setValue(m_daysLimitVal);
591 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
592 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
593 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
598 /* Preferences dialog */
599 prefsDialog::prefsDialog()
604 void prefsDialog::accept()
607 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
608 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
609 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
610 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
611 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
612 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
613 mainWin->m_recordLimitVal = this->recordSpinBox->value();
614 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
615 mainWin->m_daysLimitVal = this->daysSpinBox->value();
616 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
617 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
618 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
619 QSettings settings("www.bacula.org", "bat");
620 settings.beginGroup("Debug");
621 settings.setValue("commDebug", mainWin->m_commDebug);
622 settings.setValue("displayAll", mainWin->m_displayAll);
623 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
624 settings.setValue("commandDebug", mainWin->m_commandDebug);
625 settings.setValue("miscDebug", mainWin->m_miscDebug);
627 settings.beginGroup("JobList");
628 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
629 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
630 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
631 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
633 settings.beginGroup("Messages");
634 settings.setValue("checkMessages", mainWin->m_checkMessages);
635 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
637 settings.beginGroup("Misc");
638 settings.setValue("longList", mainWin->m_longList);
640 foreach(Console *console, mainWin->m_consoleHash) {
641 console->startTimer();
645 void prefsDialog::reject()
648 mainWin->set_status("Canceled");
651 /* read preferences for the prefences dialog box */
652 void MainWin::readPreferences()
654 QSettings settings("www.bacula.org", "bat");
655 settings.beginGroup("Debug");
656 m_commDebug = settings.value("commDebug", false).toBool();
657 m_displayAll = settings.value("displayAll", false).toBool();
658 m_sqlDebug = settings.value("sqlDebug", false).toBool();
659 m_commandDebug = settings.value("commandDebug", false).toBool();
660 m_miscDebug = settings.value("miscDebug", false).toBool();
662 settings.beginGroup("JobList");
663 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
664 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
665 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
666 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
668 settings.beginGroup("Messages");
669 m_checkMessages = settings.value("checkMessages", false).toBool();
670 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
672 settings.beginGroup("Misc");
673 m_longList = settings.value("longList", false).toBool();