1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19
20 /*
21 *
22 * Main Window control for bat (qt-console)
23 *
24 * Kern Sibbald, January MMVII
25 *
26 */
27
28 #include "bat.h"
29 #include "version.h"
30 #include "joblist/joblist.h"
31 #include "storage/storage.h"
32 #include "fileset/fileset.h"
33 #include "label/label.h"
34 #include "run/run.h"
35 #include "pages.h"
36 #include "restore/restore.h"
37 #include "medialist/medialist.h"
38 #include "joblist/joblist.h"
39 #include "clients/clients.h"
40 #include "restore/restoretree.h"
41 #include "help/help.h"
42 #include "jobs/jobs.h"
43 #include "medialist/mediaview.h"
44 #ifdef HAVE_QWT
45 #include "jobgraphs/jobplot.h"
46 #endif
47 #include "status/dirstat.h"
48 #include "util/fmtwidgetitem.h"
49
50 /*
51 * Daemon message callback
52 */
message_callback(int,char * msg)53 void message_callback(int /* type */, char *msg)
54 {
55 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
56 }
57
MainWin(QWidget * parent)58 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
59 {
60 app->setOverrideCursor(QCursor(Qt::WaitCursor));
61 m_isClosing = false;
62 m_waitState = false;
63 m_doConnect = false;
64 m_treeStackTrap = false;
65 m_dtformat = "yyyy-MM-dd HH:mm:ss";
66 mainWin = this;
67 setupUi(this); /* Setup UI defined by main.ui (designer) */
68 register_message_callback(message_callback);
69 readPreferences();
70 treeWidget->clear();
71 treeWidget->setColumnCount(1);
72 treeWidget->setHeaderLabel( tr("Select Page") );
73 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
74 tabWidget->setTabsClosable(true); /* wait for QT 4.5 */
75 createPages();
76
77 resetFocus(); /* lineEdit->setFocus() */
78
79 #ifndef HAVE_QWT
80 actionJobPlot->setEnabled(false);
81 actionJobPlot->setVisible(false);
82 #endif
83
84 this->show();
85
86 readSettings();
87
88 foreach(Console *console, m_consoleHash) {
89 console->connect_dir();
90 }
91 /*
92 * Note, the notifier is now a global flag, although each notifier
93 * can be individually turned on and off at a socket level. Once
94 * the notifier is turned off, we don't accept anything from anyone
95 * this prevents unwanted messages from getting into the input
96 * dialogs such as restore that read from the director and "know"
97 * what to expect.
98 */
99 m_notify = true;
100 m_currentConsole = (Console*)getFromHash(m_firstItem);
101 QTimer::singleShot(2000, this, SLOT(popLists()));
102 if (m_miscDebug) {
103 QString directoryResourceName;
104 m_currentConsole->getDirResName(directoryResourceName);
105 Pmsg1(100, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
106 }
107 app->restoreOverrideCursor();
108 }
109
popLists()110 void MainWin::popLists()
111 {
112 foreach(Console *console, m_consoleHash) {
113 console->populateLists(true);
114 }
115 m_doConnect = true;
116 connectConsoleSignals();
117 connectSignals();
118 app->restoreOverrideCursor();
119 m_currentConsole->setCurrent();
120 }
121
createPages()122 void MainWin::createPages()
123 {
124 DIRRES *dir;
125 QTreeWidgetItem *item, *topItem;
126 m_firstItem = NULL;
127
128 LockRes();
129 foreach_res(dir, R_DIRECTOR) {
130
131 /* Create console tree stacked widget item */
132 m_currentConsole = new Console(tabWidget);
133 m_currentConsole->setDirRes(dir);
134 m_currentConsole->readSettings();
135
136 /* The top tree item representing the director */
137 topItem = new QTreeWidgetItem(treeWidget);
138 topItem->setText(0, dir->name());
139 topItem->setIcon(0, QIcon(":images/server.png"));
140 /* Set background to grey for ease of identification of inactive Director */
141 QBrush greyBrush(Qt::lightGray);
142 topItem->setBackground(0, greyBrush);
143 m_currentConsole->setDirectorTreeItem(topItem);
144 m_consoleHash.insert(topItem, m_currentConsole);
145
146 /* Create Tree Widget Item */
147 item = new QTreeWidgetItem(topItem);
148 item->setText(0, tr("Console"));
149 if (!m_firstItem){ m_firstItem = item; }
150 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.png")));
151
152 /* insert the cosole and tree widget item into the hashes */
153 hashInsert(item, m_currentConsole);
154 m_currentConsole->dockPage();
155
156 /* Set Color of treeWidgetItem for the console
157 * It will be set to green in the console class if the connection is made.
158 */
159 QBrush redBrush(Qt::red);
160 item->setForeground(0, redBrush);
161
162 /*
163 * Create instances in alphabetic order of the rest
164 * of the classes that will by default exist under each Director.
165 */
166 new bRestore();
167 new Clients();
168 new FileSet();
169 new Jobs();
170 createPageJobList("", "", "", "", NULL);
171 #ifdef HAVE_QWT
172 JobPlotPass pass;
173 pass.use = false;
174 if (m_openPlot)
175 new JobPlot(NULL, pass);
176 #endif
177 new MediaList();
178 new MediaView();
179 new Storage();
180 // if (m_openBrowser) {
181 // new restoreTree();
182 // }
183 if (m_openDirStat) {
184 new DirStat();
185 }
186 treeWidget->expandItem(topItem);
187 tabWidget->setCurrentWidget(m_currentConsole);
188 }
189 UnlockRes();
190 }
191
192 /*
193 * create an instance of the the joblist class on the stack
194 */
createPageJobList(const QString & media,const QString & client,const QString & job,const QString & fileset,QTreeWidgetItem * parentTreeWidgetItem)195 void MainWin::createPageJobList(const QString &media, const QString &client,
196 const QString &job, const QString &fileset, QTreeWidgetItem *parentTreeWidgetItem)
197 {
198 QTreeWidgetItem *holdItem;
199
200 /* save current tree widget item in case query produces no results */
201 holdItem = treeWidget->currentItem();
202 JobList* joblist = new JobList(media, client, job, fileset, parentTreeWidgetItem);
203 /* If this is a query of jobs on a specific media */
204 if ((media != "") || (client != "") || (job != "") || (fileset != "")) {
205 joblist->setCurrent();
206 /* did query produce results, if not close window and set back to hold */
207 if (joblist->m_resultCount == 0) {
208 joblist->closeStackPage();
209 treeWidget->setCurrentItem(holdItem);
210 }
211 }
212 }
213
214 /*
215 * Handle up and down arrow keys for the command line
216 * history.
217 */
keyPressEvent(QKeyEvent * event)218 void MainWin::keyPressEvent(QKeyEvent *event)
219 {
220 if (m_cmd_history.size() == 0) {
221 event->ignore();
222 return;
223 }
224 switch (event->key()) {
225 case Qt::Key_Down:
226 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
227 event->ignore();
228 return;
229 }
230 m_cmd_last++;
231 break;
232 case Qt::Key_Up:
233 if (m_cmd_last == 0) {
234 event->ignore();
235 return;
236 }
237 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
238 m_cmd_last = m_cmd_history.size() - 1;
239 } else {
240 m_cmd_last--;
241 }
242 break;
243 default:
244 event->ignore();
245 return;
246 }
247 lineEdit->setText(m_cmd_history[m_cmd_last]);
248 }
249
connectSignals()250 void MainWin::connectSignals()
251 {
252 /* Connect signals to slots */
253 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
254 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
255 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
256 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(treeItemClicked(QTreeWidgetItem *, int)));
257 connect(treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
258 connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(stackItemChanged(int)));
259 connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closePage(int)));
260 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
261 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
262 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
263 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
264 connect(actionBrowse, SIGNAL(triggered()), this, SLOT(browseButtonClicked()));
265 connect(actionStatusDirPage, SIGNAL(triggered()), this, SLOT(statusPageButtonClicked()));
266 #ifdef HAVE_QWT
267 connect(actionJobPlot, SIGNAL(triggered()), this, SLOT(jobPlotButtonClicked()));
268 #endif
269 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
270 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
271 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
272 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closeCurrentPage()));
273 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
274 connect(actionRepopLists, SIGNAL(triggered()), this, SLOT(repopLists()));
275 connect(actionReloadRepop, SIGNAL(triggered()), this, SLOT(reloadRepopLists()));
276 }
277
disconnectSignals()278 void MainWin::disconnectSignals()
279 {
280 /* Connect signals to slots */
281 disconnect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
282 disconnect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
283 disconnect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
284 disconnect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(treeItemClicked(QTreeWidgetItem *, int)));
285 disconnect(treeWidget, SIGNAL( currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
286 disconnect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(stackItemChanged(int)));
287 disconnect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closePage(int)));
288 disconnect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
289 disconnect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
290 disconnect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
291 disconnect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
292 disconnect(actionBrowse, SIGNAL(triggered()), this, SLOT(browseButtonClicked()));
293 disconnect(actionStatusDirPage, SIGNAL(triggered()), this, SLOT(statusPageButtonClicked()));
294 #ifdef HAVE_QWT
295 disconnect(actionJobPlot, SIGNAL(triggered()), this, SLOT(jobPlotButtonClicked()));
296 #endif
297 disconnect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
298 disconnect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
299 disconnect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
300 disconnect(actionClosePage, SIGNAL(triggered()), this, SLOT(closeCurrentPage()));
301 disconnect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
302 disconnect(actionRepopLists, SIGNAL(triggered()), this, SLOT(repopLists()));
303 disconnect(actionReloadRepop, SIGNAL(triggered()), this, SLOT(reloadRepopLists()));
304 }
305
306 /*
307 * Enter wait state
308 */
waitEnter()309 void MainWin::waitEnter()
310 {
311 if (m_waitState || m_isClosing) {
312 return;
313 }
314 m_waitState = true;
315 if (mainWin->m_connDebug) Pmsg0(000, "Entering Wait State\n");
316 app->setOverrideCursor(QCursor(Qt::WaitCursor));
317 disconnectSignals();
318 disconnectConsoleSignals(m_currentConsole);
319 m_waitTreeItem = treeWidget->currentItem();
320 }
321
322 /*
323 * Leave wait state
324 */
waitExit()325 void MainWin::waitExit()
326 {
327 if (!m_waitState || m_isClosing) {
328 return;
329 }
330 if (mainWin->m_connDebug) Pmsg0(000, "Exiting Wait State\n");
331 if (m_waitTreeItem && (m_waitTreeItem != treeWidget->currentItem())) {
332 treeWidget->setCurrentItem(m_waitTreeItem);
333 }
334 if (m_doConnect) {
335 connectSignals();
336 connectConsoleSignals();
337 }
338 app->restoreOverrideCursor();
339 m_waitState = false;
340 }
341
connectConsoleSignals()342 void MainWin::connectConsoleSignals()
343 {
344 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
345 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
346 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
347 }
348
disconnectConsoleSignals(Console * console)349 void MainWin::disconnectConsoleSignals(Console *console)
350 {
351 disconnect(actionConnect, SIGNAL(triggered()), console, SLOT(connect_dir()));
352 disconnect(actionMessages, SIGNAL(triggered()), console, SLOT(messages()));
353 disconnect(actionSelectFont, SIGNAL(triggered()), console, SLOT(set_font()));
354 }
355
356
357 /*
358 * Two functions to respond to menu items to repop lists and execute reload and repopulate
359 * the lists for jobs, clients, filesets .. ..
360 */
repopLists()361 void MainWin::repopLists()
362 {
363 m_currentConsole->populateLists(false);
364 }
reloadRepopLists()365 void MainWin::reloadRepopLists()
366 {
367 QString cmd = "reload";
368 m_currentConsole->consoleCommand(cmd);
369 m_currentConsole->populateLists(false);
370 }
371
372 /*
373 * Reimplementation of QWidget closeEvent virtual function
374 */
closeEvent(QCloseEvent * event)375 void MainWin::closeEvent(QCloseEvent *event)
376 {
377 m_isClosing = true;
378 writeSettings();
379 /* Remove all groups from settings for OpenOnExit so that we can start some of the status windows */
380 foreach(Console *console, m_consoleHash){
381 QSettings settings(console->m_dir->name(), "bat");
382 settings.beginGroup("OpenOnExit");
383 settings.remove("");
384 settings.endGroup();
385 }
386 /* close all non console pages, this will call settings in destructors */
387 while (m_consoleHash.count() < m_pagehash.count()) {
388 foreach(Pages *page, m_pagehash) {
389 if (page != page->console()) {
390 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(page);
391 if (pageSelectorTreeWidgetItem->childCount() == 0) {
392 page->console()->setCurrent();
393 page->closeStackPage();
394 }
395 }
396 }
397 }
398 foreach(Console *console, m_consoleHash){
399 console->writeSettings();
400 console->terminate();
401 console->closeStackPage();
402 }
403 event->accept();
404 }
405
writeSettings()406 void MainWin::writeSettings()
407 {
408 QSettings settings("bacula.org", "bat");
409
410 settings.beginGroup("MainWin");
411 settings.setValue("winSize", size());
412 settings.setValue("winPos", pos());
413 settings.setValue("state", saveState());
414 settings.endGroup();
415
416 }
417
readSettings()418 void MainWin::readSettings()
419 {
420 QSettings settings("bacula.org", "bat");
421
422 settings.beginGroup("MainWin");
423 resize(settings.value("winSize", QSize(1041, 801)).toSize());
424 move(settings.value("winPos", QPoint(200, 150)).toPoint());
425 restoreState(settings.value("state").toByteArray());
426 settings.endGroup();
427 }
428
429 /*
430 * This subroutine is called with an item in the Page Selection window
431 * is clicked
432 */
treeItemClicked(QTreeWidgetItem * item,int)433 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
434 {
435 /* Is this a page that has been inserted into the hash */
436 Pages* page = getFromHash(item);
437 if (page) {
438 int stackindex = tabWidget->indexOf(page);
439
440 if (stackindex >= 0) {
441 tabWidget->setCurrentWidget(page);
442 }
443 page->dockPage();
444 /* run the virtual function in case this class overrides it */
445 page->PgSeltreeWidgetClicked();
446 } else {
447 Dmsg0(000, "Page not in hash");
448 }
449 }
450
451 /*
452 * Called with a change of the highlighed tree widget item in the page selector.
453 */
treeItemChanged(QTreeWidgetItem * currentitem,QTreeWidgetItem * previousitem)454 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
455 {
456 if (m_isClosing) return; /* if closing the application, do nothing here */
457
458 Pages *previousPage, *nextPage;
459 Console *previousConsole = NULL;
460 Console *nextConsole;
461
462 /* remove all actions before adding actions appropriate for new page */
463 foreach(QAction* pageAction, treeWidget->actions()) {
464 treeWidget->removeAction(pageAction);
465 }
466
467 /* first determine the next item */
468
469 /* knowing the treeWidgetItem, get the page from the hash */
470 nextPage = getFromHash(currentitem);
471 nextConsole = m_consoleHash.value(currentitem);
472 /* Is this a page that has been inserted into the hash */
473 if (nextPage) {
474 nextConsole = nextPage->console();
475 /* then is it a treeWidgetItem representing a director */
476 } else if (nextConsole) {
477 /* let the next page BE the console */
478 nextPage = nextConsole;
479 } else {
480 /* Should never get here */
481 nextPage = NULL;
482 nextConsole = NULL;
483 }
484
485 /* The Previous item */
486
487 /* this condition prevents a segfault. The first time there is no previousitem*/
488 if (previousitem) {
489 if (m_treeStackTrap == false) { /* keep track of previous items for going Back */
490 m_treeWidgetStack.append(previousitem);
491 }
492 /* knowing the treeWidgetItem, get the page from the hash */
493 previousPage = getFromHash(previousitem);
494 previousConsole = m_consoleHash.value(previousitem);
495 if (previousPage) {
496 previousConsole = previousPage->console();
497 } else if (previousConsole) {
498 previousPage = previousConsole;
499 }
500 if ((previousPage) || (previousConsole)) {
501 if (nextConsole != previousConsole) {
502 /* remove connections to the current console */
503 disconnectConsoleSignals(previousConsole);
504 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
505 QBrush greyBrush(Qt::lightGray);
506 dirItem->setBackground(0, greyBrush);
507 }
508 }
509 }
510
511 /* process the current (next) item */
512
513 if ((nextPage) || (nextConsole)) {
514 if (nextConsole != previousConsole) {
515 /* make connections to the current console */
516 m_currentConsole = nextConsole;
517 connectConsoleSignals();
518 setMessageIcon();
519 /* Set director's tree widget background to magenta for ease of identification */
520 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
521 QBrush magentaBrush(Qt::magenta);
522 dirItem->setBackground(0, magentaBrush);
523 }
524 /* set the value for the currently active console */
525 int stackindex = tabWidget->indexOf(nextPage);
526 nextPage->firstUseDock();
527
528 /* Is this page currently on the stack or is it undocked */
529 if (stackindex >= 0) {
530 /* put this page on the top of the stack */
531 tabWidget->setCurrentIndex(stackindex);
532 } else {
533 /* it is undocked, raise it to the front */
534 nextPage->raise();
535 }
536 /* for the page selectors menu action to dock or undock, set the text */
537 nextPage->setContextMenuDockText();
538
539 treeWidget->addAction(actionToggleDock);
540 /* if this page is closeable, and it has no childern, then add that action */
541 if ((nextPage->isCloseable()) && (currentitem->child(0) == NULL))
542 treeWidget->addAction(actionClosePage);
543
544 /* Add the actions to the Page Selectors tree widget that are part of the
545 * current items list of desired actions regardless of whether on top of stack*/
546 treeWidget->addActions(nextPage->m_contextActions);
547 }
548 }
549
labelButtonClicked()550 void MainWin::labelButtonClicked()
551 {
552 new labelPage();
553 }
554
runButtonClicked()555 void MainWin::runButtonClicked()
556 {
557 new runPage("");
558 }
559
estimateButtonClicked()560 void MainWin::estimateButtonClicked()
561 {
562 new estimatePage();
563 }
564
browseButtonClicked()565 void MainWin::browseButtonClicked()
566 {
567 // new restoreTree();
568 }
569
statusPageButtonClicked()570 void MainWin::statusPageButtonClicked()
571 {
572 /* if one exists, then just set it current */
573 bool found = false;
574 foreach(Pages *page, m_pagehash) {
575 if (m_currentConsole == page->console()) {
576 if (page->name() == tr("Director Status")) {
577 found = true;
578 page->setCurrent();
579 }
580 }
581 }
582 if (!found) {
583 new DirStat();
584 }
585 }
586
restoreButtonClicked()587 void MainWin::restoreButtonClicked()
588 {
589 new prerestorePage();
590 if (mainWin->m_miscDebug) Pmsg0(000, "in restoreButtonClicked after prerestorePage\n");
591 }
592
jobPlotButtonClicked()593 void MainWin::jobPlotButtonClicked()
594 {
595 #ifdef HAVE_QWT
596 JobPlotPass pass;
597 pass.use = false;
598 new JobPlot(NULL, pass);
599 #endif
600 }
601
602 /*
603 * The user just finished typing a line in the command line edit box
604 */
input_line()605 void MainWin::input_line()
606 {
607 int conn;
608 QString cmdStr = lineEdit->text(); /* Get the text */
609 lineEdit->clear(); /* clear the lineEdit box */
610 if (m_currentConsole->is_connected()) {
611 if (m_currentConsole->findDirComm(conn)) {
612 m_currentConsole->consoleCommand(cmdStr, conn);
613 } else {
614 /* Use consoleCommand to allow typing anything */
615 m_currentConsole->consoleCommand(cmdStr);
616 }
617 } else {
618 set_status(tr("Director not connected. Click on connect button."));
619 }
620 m_cmd_history.append(cmdStr);
621 m_cmd_last = -1;
622 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
623 m_currentConsole->setCurrent();
624 }
625
626
about()627 void MainWin::about()
628 {
629 QMessageBox::about(this, tr("About bat"),
630 tr("<br><h2>Bacula Bat %1 (%2)</h2>"
631 "<p>Copyright © 2007-%3 Kern Sibbald"
632 "<p>The <b>bat</b> is an administrative console"
633 " interface to the Director.").arg(VERSION).arg(BDATE).arg(BYEAR));
634 }
635
help()636 void MainWin::help()
637 {
638 Help::displayFile("index.html");
639 }
640
set_statusf(const char * fmt,...)641 void MainWin::set_statusf(const char *fmt, ...)
642 {
643 va_list arg_ptr;
644 char buf[1000];
645 va_start(arg_ptr, fmt);
646 bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
647 va_end(arg_ptr);
648 set_status(buf);
649 }
650
set_status_ready()651 void MainWin::set_status_ready()
652 {
653 set_status(tr(" Ready"));
654 }
655
set_status(const QString & str)656 void MainWin::set_status(const QString &str)
657 {
658 statusBar()->showMessage(str);
659 }
660
set_status(const char * buf)661 void MainWin::set_status(const char *buf)
662 {
663 statusBar()->showMessage(buf);
664 }
665
666 /*
667 * Function to respond to the button bar button to undock
668 */
undockWindowButton()669 void MainWin::undockWindowButton()
670 {
671 Pages* page = (Pages*)tabWidget->currentWidget();
672 if (page) {
673 page->togglePageDocking();
674 }
675 }
676
677 /*
678 * Function to respond to action on page selector context menu to toggle the
679 * dock status of the window associated with the page selectors current
680 * tree widget item.
681 */
toggleDockContextWindow()682 void MainWin::toggleDockContextWindow()
683 {
684 QTreeWidgetItem *currentitem = treeWidget->currentItem();
685
686 /* Is this a page that has been inserted into the hash */
687 if (getFromHash(currentitem)) {
688 Pages* page = getFromHash(currentitem);
689 if (page) {
690 page->togglePageDocking();
691 }
692 }
693 }
694
695 /*
696 * This function is called when the stack item is changed. Call
697 * the virtual function here. Avoids a window being undocked leaving
698 * a window at the top of the stack unpopulated.
699 */
stackItemChanged(int)700 void MainWin::stackItemChanged(int)
701 {
702 if (m_isClosing) return; /* if closing the application, do nothing here */
703 Pages* page = (Pages*)tabWidget->currentWidget();
704 /* run the virtual function in case this class overrides it */
705 if (page) {
706 page->currentStackItem();
707 }
708 if (!m_waitState) {
709 disconnect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(treeItemClicked(QTreeWidgetItem *, int)));
710 disconnect(treeWidget, SIGNAL( currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
711 treeWidget->setCurrentItem(getFromHash(page));
712 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(treeItemClicked(QTreeWidgetItem *, int)));
713 connect(treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
714 }
715 }
716
717 /*
718 * Function to simplify insertion of QTreeWidgetItem <-> Page association
719 * into a double direction hash.
720 */
hashInsert(QTreeWidgetItem * item,Pages * page)721 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
722 {
723 m_pagehash.insert(item, page);
724 m_widgethash.insert(page, item);
725 }
726
727 /*
728 * Function to simplify removal of QTreeWidgetItem <-> Page association
729 * into a double direction hash.
730 */
hashRemove(QTreeWidgetItem * item,Pages * page)731 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
732 {
733 /* I had all sorts of return status checking code here. Do we have a log
734 * level capability in bat. I would have left it in but it used printf's
735 * and it should really be some kind of log level facility ???
736 * ******FIXME********/
737 m_pagehash.remove(item);
738 m_widgethash.remove(page);
739 }
740
741 /*
742 * Function to retrieve a Page* when the item in the page selector's tree is
743 * known.
744 */
getFromHash(QTreeWidgetItem * item)745 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
746 {
747 return m_pagehash.value(item);
748 }
749
750 /*
751 * Function to retrieve the page selectors tree widget item when the page is
752 * known.
753 */
getFromHash(Pages * page)754 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
755 {
756 return m_widgethash.value(page);
757 }
758
closeCurrentPage()759 void MainWin::closeCurrentPage()
760 {
761 closePage(-1);
762 }
763
764 /*
765 * Function to respond to action on page selector context menu to close the
766 * current window.
767 */
closePage(int item)768 void MainWin::closePage(int item)
769 {
770 QTreeWidgetItem *currentitem;
771 Pages *page = NULL;
772
773 if (item >= 0) {
774 page = (Pages *)tabWidget->widget(item);
775 } else {
776 currentitem = treeWidget->currentItem();
777 /* Is this a page that has been inserted into the hash */
778 if (getFromHash(currentitem)) {
779 page = getFromHash(currentitem);
780 }
781 }
782
783 if (page) {
784 if (page->isCloseable()) {
785 page->closeStackPage();
786 } else {
787 page->hidePage();
788 }
789 }
790 }
791
792 /* Quick function to return the current console */
currentConsole()793 Console *MainWin::currentConsole()
794 {
795 return m_currentConsole;
796 }
797
798 /* Quick function to return the tree item for the director */
currentTopItem()799 QTreeWidgetItem *MainWin::currentTopItem()
800 {
801 return m_currentConsole->directorTreeItem();
802 }
803
804 /* Preferences menu item clicked */
setPreferences()805 void MainWin::setPreferences()
806 {
807 prefsDialog prefs;
808 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
809 prefs.connDebug->setCheckState(m_connDebug ? Qt::Checked : Qt::Unchecked);
810 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
811 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
812 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
813 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
814 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
815 prefs.recordSpinBox->setValue(m_recordLimitVal);
816 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
817 prefs.daysSpinBox->setValue(m_daysLimitVal);
818 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
819 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
820 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
821 prefs.rtPopDirCheckBox->setCheckState(m_rtPopDirDebug ? Qt::Checked : Qt::Unchecked);
822 prefs.rtDirCurICCheckBox->setCheckState(m_rtDirCurICDebug ? Qt::Checked : Qt::Unchecked);
823 prefs.rtDirICCheckBox->setCheckState(m_rtDirICDebug ? Qt::Checked : Qt::Unchecked);
824 prefs.rtFileTabICCheckBox->setCheckState(m_rtFileTabICDebug ? Qt::Checked : Qt::Unchecked);
825 prefs.rtVerTabICCheckBox->setCheckState(m_rtVerTabICDebug ? Qt::Checked : Qt::Unchecked);
826 prefs.rtUpdateFTCheckBox->setCheckState(m_rtUpdateFTDebug ? Qt::Checked : Qt::Unchecked);
827 prefs.rtUpdateVTCheckBox->setCheckState(m_rtUpdateVTDebug ? Qt::Checked : Qt::Unchecked);
828 prefs.rtChecksCheckBox->setCheckState(m_rtChecksDebug ? Qt::Checked : Qt::Unchecked);
829 prefs.rtIconStateCheckBox->setCheckState(m_rtIconStateDebug ? Qt::Checked : Qt::Unchecked);
830 prefs.rtRestore1CheckBox->setCheckState(m_rtRestore1Debug ? Qt::Checked : Qt::Unchecked);
831 prefs.rtRestore2CheckBox->setCheckState(m_rtRestore2Debug ? Qt::Checked : Qt::Unchecked);
832 prefs.rtRestore3CheckBox->setCheckState(m_rtRestore3Debug ? Qt::Checked : Qt::Unchecked);
833 switch (ItemFormatterBase::getBytesConversion()) {
834 case ItemFormatterBase::BYTES_CONVERSION_NONE:
835 prefs.radioConvertOff->setChecked(true);
836 break;
837 case ItemFormatterBase::BYTES_CONVERSION_IEC:
838 prefs.radioConvertIEC->setChecked(true);
839 break;
840 default:
841 prefs.radioConvertStandard->setChecked(true);
842 break;
843 }
844 prefs.openPlotCheckBox->setCheckState(m_openPlot ? Qt::Checked : Qt::Unchecked);
845 #ifndef HAVE_QWT
846 prefs.openPlotCheckBox->setVisible(false);
847 #endif
848 prefs.openBrowserCheckBox->setCheckState(m_openBrowser ? Qt::Checked : Qt::Unchecked);
849 prefs.openDirStatCheckBox->setCheckState(m_openDirStat ? Qt::Checked : Qt::Unchecked);
850 prefs.exec();
851 }
852
853 /* Preferences dialog */
prefsDialog()854 prefsDialog::prefsDialog() : QDialog()
855 {
856 setupUi(this);
857 }
858
accept()859 void prefsDialog::accept()
860 {
861 this->hide();
862 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
863 mainWin->m_connDebug = this->connDebug->checkState() == Qt::Checked;
864 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
865 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
866 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
867 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
868 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
869 mainWin->m_recordLimitVal = this->recordSpinBox->value();
870 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
871 mainWin->m_daysLimitVal = this->daysSpinBox->value();
872 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
873 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
874 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
875
876 mainWin->m_rtPopDirDebug = this->rtPopDirCheckBox->checkState() == Qt::Checked;
877 mainWin->m_rtDirCurICDebug = this->rtDirCurICCheckBox->checkState() == Qt::Checked;
878 mainWin->m_rtDirICDebug = this->rtDirICCheckBox->checkState() == Qt::Checked;
879 mainWin->m_rtFileTabICDebug = this->rtFileTabICCheckBox->checkState() == Qt::Checked;
880 mainWin->m_rtVerTabICDebug = this->rtVerTabICCheckBox->checkState() == Qt::Checked;
881 mainWin->m_rtUpdateFTDebug = this->rtUpdateFTCheckBox->checkState() == Qt::Checked;
882 mainWin->m_rtUpdateVTDebug = this->rtUpdateVTCheckBox->checkState() == Qt::Checked;
883 mainWin->m_rtChecksDebug = this->rtChecksCheckBox->checkState() == Qt::Checked;
884 mainWin->m_rtIconStateDebug = this->rtIconStateCheckBox->checkState() == Qt::Checked;
885 mainWin->m_rtRestore1Debug = this->rtRestore1CheckBox->checkState() == Qt::Checked;
886 mainWin->m_rtRestore2Debug = this->rtRestore2CheckBox->checkState() == Qt::Checked;
887 mainWin->m_rtRestore3Debug = this->rtRestore3CheckBox->checkState() == Qt::Checked;
888 if (this->radioConvertOff->isChecked()) {
889 ItemFormatterBase::setBytesConversion(ItemFormatterBase::BYTES_CONVERSION_NONE);
890 } else if (this->radioConvertIEC->isChecked()){
891 ItemFormatterBase::setBytesConversion(ItemFormatterBase::BYTES_CONVERSION_IEC);
892 } else {
893 ItemFormatterBase::setBytesConversion(ItemFormatterBase::BYTES_CONVERSION_SI);
894 }
895 mainWin->m_openPlot = this->openPlotCheckBox->checkState() == Qt::Checked;
896 mainWin->m_openBrowser = this->openBrowserCheckBox->checkState() == Qt::Checked;
897 mainWin->m_openDirStat = this->openDirStatCheckBox->checkState() == Qt::Checked;
898
899 QSettings settings("www.bacula.org", "bat");
900 settings.beginGroup("Debug");
901 settings.setValue("commDebug", mainWin->m_commDebug);
902 settings.setValue("connDebug", mainWin->m_connDebug);
903 settings.setValue("displayAll", mainWin->m_displayAll);
904 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
905 settings.setValue("commandDebug", mainWin->m_commandDebug);
906 settings.setValue("miscDebug", mainWin->m_miscDebug);
907 settings.endGroup();
908 settings.beginGroup("JobList");
909 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
910 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
911 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
912 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
913 settings.endGroup();
914 settings.beginGroup("Timers");
915 settings.setValue("checkMessages", mainWin->m_checkMessages);
916 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
917 settings.endGroup();
918 settings.beginGroup("Misc");
919 settings.setValue("longList", mainWin->m_longList);
920 settings.setValue("byteConvert", ItemFormatterBase::getBytesConversion());
921 settings.setValue("openplot", mainWin->m_openPlot);
922 settings.setValue("openbrowser", mainWin->m_openBrowser);
923 settings.setValue("opendirstat", mainWin->m_openDirStat);
924 settings.endGroup();
925 settings.beginGroup("RestoreTree");
926 settings.setValue("rtPopDirDebug", mainWin->m_rtPopDirDebug);
927 settings.setValue("rtDirCurICDebug", mainWin->m_rtDirCurICDebug);
928 settings.setValue("rtDirCurICRetDebug", mainWin->m_rtDirICDebug);
929 settings.setValue("rtFileTabICDebug", mainWin->m_rtFileTabICDebug);
930 settings.setValue("rtVerTabICDebug", mainWin->m_rtVerTabICDebug);
931 settings.setValue("rtUpdateFTDebug", mainWin->m_rtUpdateFTDebug);
932 settings.setValue("rtUpdateVTDebug", mainWin->m_rtUpdateVTDebug);
933 settings.setValue("rtChecksDebug", mainWin->m_rtChecksDebug);
934 settings.setValue("rtIconStateDebug", mainWin->m_rtIconStateDebug);
935 settings.setValue("rtRestore1Debug", mainWin->m_rtRestore1Debug);
936 settings.setValue("rtRestore2Debug", mainWin->m_rtRestore2Debug);
937 settings.setValue("rtRestore3Debug", mainWin->m_rtRestore3Debug);
938 settings.endGroup();
939 }
940
reject()941 void prefsDialog::reject()
942 {
943 this->hide();
944 mainWin->set_status(tr("Canceled"));
945 }
946
947 /* read preferences for the prefences dialog box */
readPreferences()948 void MainWin::readPreferences()
949 {
950 QSettings settings("www.bacula.org", "bat");
951 settings.beginGroup("Debug");
952 m_commDebug = settings.value("commDebug", false).toBool();
953 m_connDebug = settings.value("connDebug", false).toBool();
954 m_displayAll = settings.value("displayAll", false).toBool();
955 m_sqlDebug = settings.value("sqlDebug", false).toBool();
956 m_commandDebug = settings.value("commandDebug", false).toBool();
957 m_miscDebug = settings.value("miscDebug", false).toBool();
958 settings.endGroup();
959 settings.beginGroup("JobList");
960 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
961 m_recordLimitVal = settings.value("recordLimitVal", 50).toInt();
962 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
963 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
964 settings.endGroup();
965 settings.beginGroup("Timers");
966 m_checkMessages = settings.value("checkMessages", false).toBool();
967 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
968 settings.endGroup();
969 settings.beginGroup("Misc");
970 m_longList = settings.value("longList", false).toBool();
971 ItemFormatterBase::setBytesConversion(
972 (ItemFormatterBase::BYTES_CONVERSION) settings.value("byteConvert",
973 ItemFormatterBase::BYTES_CONVERSION_IEC).toInt());
974 m_openPlot = settings.value("openplot", false).toBool();
975 m_openBrowser = settings.value("openbrowser", false).toBool();
976 m_openDirStat = settings.value("opendirstat", false).toBool();
977 settings.endGroup();
978 settings.beginGroup("RestoreTree");
979 m_rtPopDirDebug = settings.value("rtPopDirDebug", false).toBool();
980 m_rtDirCurICDebug = settings.value("rtDirCurICDebug", false).toBool();
981 m_rtDirICDebug = settings.value("rtDirCurICRetDebug", false).toBool();
982 m_rtFileTabICDebug = settings.value("rtFileTabICDebug", false).toBool();
983 m_rtVerTabICDebug = settings.value("rtVerTabICDebug", false).toBool();
984 m_rtUpdateFTDebug = settings.value("rtUpdateFTDebug", false).toBool();
985 m_rtUpdateVTDebug = settings.value("rtUpdateVTDebug", false).toBool();
986 m_rtChecksDebug = settings.value("rtChecksDebug", false).toBool();
987 m_rtIconStateDebug = settings.value("rtIconStateDebug", false).toBool();
988 m_rtRestore1Debug = settings.value("rtRestore1Debug", false).toBool();
989 m_rtRestore2Debug = settings.value("rtRestore2Debug", false).toBool();
990 m_rtRestore3Debug = settings.value("rtRestore3Debug", false).toBool();
991 settings.endGroup();
992 }
993
setMessageIcon()994 void MainWin::setMessageIcon()
995 {
996 if (m_currentConsole->is_messagesPending())
997 actionMessages->setIcon(QIcon(QString::fromUtf8(":/images/mail-message-pending.png")));
998 else
999 actionMessages->setIcon(QIcon(QString::fromUtf8(":/images/mail-message-new.png")));
1000 }
1001
goToPreviousPage()1002 void MainWin::goToPreviousPage()
1003 {
1004 m_treeStackTrap = true;
1005 bool done = false;
1006 while (!done) {
1007 /* If stack list is emtpty, then done */
1008 if (m_treeWidgetStack.isEmpty()) {
1009 done = true;
1010 } else {
1011 QTreeWidgetItem* testItem = m_treeWidgetStack.takeLast();
1012 QTreeWidgetItemIterator it(treeWidget);
1013 /* lets avoid a segfault by setting an item current that no longer exists */
1014 while (*it) {
1015 if (*it == testItem) {
1016 if (testItem != treeWidget->currentItem()) {
1017 treeWidget->setCurrentItem(testItem);
1018 done = true;
1019 }
1020 break;
1021 }
1022 ++it;
1023 }
1024 }
1025 }
1026 m_treeStackTrap = false;
1027 }
1028