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 /* Use consoleInput to allow typing anything */
426 m_currentConsole->consoleInput(cmdStr);
428 set_status("Director not connected. Click on connect button.");
430 m_cmd_history.append(cmdStr);
432 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
433 m_currentConsole->setCurrent();
437 void MainWin::about()
439 QMessageBox::about(this, tr("About bat"),
440 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
441 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
442 "<p>The <b>bat</b> is an administrative console"
443 " interface to the Director."));
448 Help::displayFile("index.html");
451 void MainWin::set_statusf(const char *fmt, ...)
456 va_start(arg_ptr, fmt);
457 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
462 void MainWin::set_status_ready()
464 set_status(" Ready");
467 void MainWin::set_status(const char *buf)
469 statusBar()->showMessage(buf);
473 * Function to respond to the button bar button to undock
475 void MainWin::undockWindowButton()
477 Pages* page = (Pages*)stackedWidget->currentWidget();
478 page->togglePageDocking();
482 * Function to respond to action on page selector context menu to toggle the
483 * dock status of the window associated with the page selectors current
486 void MainWin::toggleDockContextWindow()
488 QTreeWidgetItem *currentitem = treeWidget->currentItem();
490 /* Is this a page that has been inserted into the hash */
491 if (getFromHash(currentitem)) {
492 Pages* page = getFromHash(currentitem);
493 page->togglePageDocking();
498 * This function is called when the stack item is changed. Call
499 * the virtual function here. Avoids a window being undocked leaving
500 * a window at the top of the stack unpopulated.
502 void MainWin::stackItemChanged(int)
504 Pages* page = (Pages*)stackedWidget->currentWidget();
505 /* run the virtual function in case this class overrides it */
506 page->currentStackItem();
510 * Function to simplify insertion of QTreeWidgetItem <-> Page association
511 * into a double direction hash.
513 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
515 m_pagehash.insert(item, page);
516 m_widgethash.insert(page, item);
520 * Function to simplify removal of QTreeWidgetItem <-> Page association
521 * into a double direction hash.
523 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
525 /* I had all sorts of return status checking code here. Do we have a log
526 * level capability in bat. I would have left it in but it used printf's
527 * and it should really be some kind of log level facility ???
528 * ******FIXME********/
529 m_pagehash.remove(item);
530 m_widgethash.remove(page);
534 * Function to retrieve a Page* when the item in the page selector's tree is
537 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
539 return m_pagehash.value(item);
543 * Function to retrieve the page selectors tree widget item when the page is
546 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
548 return m_widgethash.value(page);
552 * Function to respond to action on page selector context menu to close the
555 void MainWin::closePage()
557 QTreeWidgetItem *currentitem = treeWidget->currentItem();
559 /* Is this a page that has been inserted into the hash */
560 if (getFromHash(currentitem)) {
561 Pages* page = getFromHash(currentitem);
562 if (page->isCloseable()) {
563 page->closeStackPage();
568 /* Quick function to return the current console */
569 Console *MainWin::currentConsole()
571 return m_currentConsole;
573 /* Quick function to return the tree item for the director */
574 QTreeWidgetItem *MainWin::currentTopItem()
576 return m_currentConsole->directorTreeItem();
579 /* Preferences menu item clicked */
580 void MainWin::setPreferences()
583 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
584 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
585 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
586 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
587 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
588 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
589 prefs.recordSpinBox->setValue(m_recordLimitVal);
590 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
591 prefs.daysSpinBox->setValue(m_daysLimitVal);
592 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
593 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
594 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
599 /* Preferences dialog */
600 prefsDialog::prefsDialog()
605 void prefsDialog::accept()
608 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
609 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
610 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
611 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
612 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
613 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
614 mainWin->m_recordLimitVal = this->recordSpinBox->value();
615 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
616 mainWin->m_daysLimitVal = this->daysSpinBox->value();
617 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
618 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
619 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
620 QSettings settings("www.bacula.org", "bat");
621 settings.beginGroup("Debug");
622 settings.setValue("commDebug", mainWin->m_commDebug);
623 settings.setValue("displayAll", mainWin->m_displayAll);
624 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
625 settings.setValue("commandDebug", mainWin->m_commandDebug);
626 settings.setValue("miscDebug", mainWin->m_miscDebug);
628 settings.beginGroup("JobList");
629 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
630 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
631 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
632 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
634 settings.beginGroup("Messages");
635 settings.setValue("checkMessages", mainWin->m_checkMessages);
636 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
638 settings.beginGroup("Misc");
639 settings.setValue("longList", mainWin->m_longList);
641 foreach(Console *console, mainWin->m_consoleHash) {
642 console->startTimer();
646 void prefsDialog::reject()
649 mainWin->set_status("Canceled");
652 /* read preferences for the prefences dialog box */
653 void MainWin::readPreferences()
655 QSettings settings("www.bacula.org", "bat");
656 settings.beginGroup("Debug");
657 m_commDebug = settings.value("commDebug", false).toBool();
658 m_displayAll = settings.value("displayAll", false).toBool();
659 m_sqlDebug = settings.value("sqlDebug", false).toBool();
660 m_commandDebug = settings.value("commandDebug", false).toBool();
661 m_miscDebug = settings.value("miscDebug", false).toBool();
663 settings.beginGroup("JobList");
664 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
665 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
666 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
667 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
669 settings.beginGroup("Messages");
670 m_checkMessages = settings.value("checkMessages", false).toBool();
671 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
673 settings.beginGroup("Misc");
674 m_longList = settings.value("longList", false).toBool();