]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/mainwin.cpp
Add a button to the button bar for .messages
[bacula/bacula] / bacula / src / qt-console / mainwin.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
5
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.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28
29 /*
30  *   Version $Id$
31  *
32  *  Main Window control for bat (qt-console)
33  *
34  *   Kern Sibbald, January MMVII
35  *
36  */ 
37
38 #include "bat.h"
39 #include "joblist/joblist.h"
40 #include "storage/storage.h"
41 #include "fileset/fileset.h"
42
43 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
44 {
45
46    mainWin = this;
47    setupUi(this);                     /* Setup UI defined by main.ui (designer) */
48    treeWidget->clear();
49    treeWidget->setColumnCount(1);
50    treeWidget->setHeaderLabel("Select Page");
51    treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
52
53    createPages();
54
55    resetFocus();
56
57    createConnections();
58
59    this->show();
60
61    readSettings();
62
63    foreach(Console *console, m_consoleHash){
64       console->connect();
65    }
66    m_currentConsole = (Console*)getFromHash(m_firstItem);
67    treeWidget->setCurrentItem(getFromHash(m_currentConsole));
68    /*  FIXME
69     *  I'd like to turn this into a debug item
70     *  DIRRES* dirres = m_currentConsole->getDirRes();
71     *  printf("Setting initial window to %s\n", dirres->name());
72     */
73 }
74
75 void MainWin::createPages()
76 {
77    DIRRES *dir;
78    QTreeWidgetItem *item, *topItem;
79    m_firstItem = NULL;
80
81    LockRes();
82    foreach_res(dir, R_DIRECTOR) {
83
84       /* Create console tree stacked widget item */
85       m_currentConsole = new Console(stackedWidget);
86       m_currentConsole->setDirRes(dir);
87       m_currentConsole->readSettings();
88
89       /* The top tree item representing the director */
90       topItem = createTopPage(dir->name());
91       topItem->setIcon(0, QIcon(":images/server.png"));
92       /* Set background to grey for ease of identification of inactive dirfector */
93       QBrush greyBrush(Qt::lightGray);
94       topItem->setBackground(0, greyBrush);
95       m_currentConsole->setDirectorTreeItem(topItem);
96       m_consoleHash.insert(topItem, m_currentConsole);
97
98       /* Create Tree Widget Item */
99       item = createPage("Console", topItem);
100       if (!m_firstItem){ m_firstItem = item; }
101
102       /* insert the cosole and tree widget item into the hashes */
103       hashInsert(item, m_currentConsole);
104
105       /* Set Color of treeWidgetItem for the console
106       * It will be set to green in the console class if the connection is made.
107       */
108       QBrush redBrush(Qt::red);
109       item->setForeground(0, redBrush);
110       m_currentConsole->dockPage();
111
112       /* create instances of the rest of the classes that will by default exist
113       * under each director */
114 //    createPagebRestore();
115       createPageMediaList();
116       QString emptymedia(""), emptyclient("");
117       createPageJobList(emptymedia, emptyclient, NULL);
118       createPageClients();
119       createPageStorage();
120       createPageFileSet();
121
122       treeWidget->expandItem(topItem);
123       stackedWidget->setCurrentWidget(m_currentConsole);
124    }
125    UnlockRes();
126 }
127
128 /*
129  * create an instance of the the brestore class on the stack
130  */
131 void MainWin::createPagebRestore()
132 {
133    bRestore* brestore = new bRestore();
134    brestore->dockPage();
135 }
136
137 /*
138  * create an instance of the the medialist class on the stack
139  */
140 void MainWin::createPageMediaList()
141 {
142    MediaList* medialist = new MediaList();
143    medialist->dockPage();
144 }
145
146 /*
147  * create an instance of the the joblist class on the stack
148  */
149 void MainWin::createPageJobList(QString &media, QString &client,
150               QTreeWidgetItem *parentTreeWidgetItem)
151 {
152    QTreeWidgetItem *item, *holdItem;
153
154    /* save current tree widget item in case query produces no results */
155    holdItem = treeWidget->currentItem();
156    JobList* joblist = new JobList(media, client, parentTreeWidgetItem);
157    joblist->dockPage();
158    /* If this is a query of jobs on a specific media */
159    if ((media != "") || (client != "")) {
160       item = getFromHash(joblist);
161       treeWidget->setCurrentItem(item);
162       /* did query produce results, if not close window and set back to hold */
163       if (joblist->m_resultCount == 0) {
164          joblist->closeStackPage();
165          treeWidget->setCurrentItem(holdItem);
166       }
167    }
168 }
169
170 /*
171  * create an instance of the the Clients class on the stack
172  */
173 void MainWin::createPageClients()
174 {
175    Clients* clients = new Clients();
176    clients->dockPage();
177 }
178
179 /*
180  * create an instance of the the storage class on the stack
181  */
182 void MainWin::createPageStorage()
183 {
184    Storage* storage = new Storage();
185    storage->dockPage();
186 }
187
188 /*
189  * create an instance of the the fileset class on the stack
190  */
191 void MainWin::createPageFileSet()
192 {
193    FileSet* fileset = new FileSet();
194    fileset->dockPage();
195 }
196
197 /* Create a root Tree Widget */
198 QTreeWidgetItem *MainWin::createTopPage(char *name)
199 {
200    QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
201    item->setText(0, name);
202    return item;
203 }
204
205 /* Create A Tree Widget Item which will be associated with a Page in the stacked widget */
206 QTreeWidgetItem *MainWin::createPage(char *name, QTreeWidgetItem *parent)
207 {
208    QTreeWidgetItem *item = new QTreeWidgetItem(parent);
209    item->setText(0, name);
210    return item;
211 }
212
213 /*
214  * Handle up and down arrow keys for the command line
215  *  history.
216  */
217 void MainWin::keyPressEvent(QKeyEvent *event)
218 {
219    if (m_cmd_history.size() == 0) {
220       event->ignore();
221       return;
222    }
223    switch (event->key()) {
224    case Qt::Key_Down:
225       if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
226          event->ignore();
227          return;
228       }
229       m_cmd_last++;
230       break;
231    case Qt::Key_Up:
232       if (m_cmd_last == 0) {
233          event->ignore();
234          return;
235       }
236       if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
237          m_cmd_last = m_cmd_history.size() - 1;
238       } else {
239          m_cmd_last--;
240       }
241       break;
242    default:
243       event->ignore();
244       return;
245    }
246    lineEdit->setText(m_cmd_history[m_cmd_last]);
247 }
248
249 void MainWin::createConnections()
250 {
251    /* Connect signals to slots */
252    connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
253    connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
254    connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, 
255            SLOT(treeItemClicked(QTreeWidgetItem *, int)));
256    connect(treeWidget, SIGNAL(
257            currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
258            this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
259    connect(stackedWidget, SIGNAL(currentChanged(int)),
260            this, SLOT(stackItemChanged(int)));
261    connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
262    connect(actionLabel, SIGNAL(triggered()), this,  SLOT(labelDialogClicked()));
263    connect(actionRun, SIGNAL(triggered()), this,  SLOT(runDialogClicked()));
264    connect(actionRestore, SIGNAL(triggered()), this,  SLOT(restoreDialogClicked()));
265    connect(actionUndock, SIGNAL(triggered()), this,  SLOT(undockWindowButton()));
266    connect(actionToggleDock, SIGNAL(triggered()), this,  SLOT(toggleDockContextWindow()));
267    connect(actionClosePage, SIGNAL(triggered()), this,  SLOT(closePage()));
268 }
269
270 /* 
271  * Reimplementation of QWidget closeEvent virtual function   
272  */
273 void MainWin::closeEvent(QCloseEvent *event)
274 {
275    writeSettings();
276    foreach(Console *console, m_consoleHash){
277       console->writeSettings();
278       console->terminate();
279    }
280    event->accept();
281    foreach(Pages *page, m_pagehash) {
282       if (!page->isDocked())
283          page->close();
284    }
285 }
286
287 void MainWin::writeSettings()
288 {
289    QSettings settings("bacula.org", "bat");
290
291    settings.beginGroup("MainWin");
292    settings.setValue("winSize", size());
293    settings.setValue("winPos", pos());
294    settings.endGroup();
295 }
296
297 void MainWin::readSettings()
298
299    QSettings settings("bacula.org", "bat");
300
301    settings.beginGroup("MainWin");
302    resize(settings.value("winSize", QSize(1041, 801)).toSize());
303    move(settings.value("winPos", QPoint(200, 150)).toPoint());
304    settings.endGroup();
305 }
306
307 /*
308  * This subroutine is called with an item in the Page Selection window
309  *   is clicked 
310  */
311 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
312 {
313    /* Is this a page that has been inserted into the hash  */
314    if (getFromHash(item)) {
315       Pages* page = getFromHash(item);
316       int stackindex=stackedWidget->indexOf(page);
317
318       if (stackindex >= 0) {
319          stackedWidget->setCurrentWidget(page);
320       }
321       /* run the virtual function in case this class overrides it */
322       page->PgSeltreeWidgetClicked();
323    }
324 }
325
326 /*
327  * Called with a change of the highlighed tree widget item in the page selector.
328  */
329 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
330 {
331    Pages *previousPage, *nextPage;
332    Console *previousConsole, *nextConsole;
333
334    /* first determine the next item */
335
336    /* knowing the treeWidgetItem, get the page from the hash */
337    nextPage = getFromHash(currentitem);
338    nextConsole = m_consoleHash.value(currentitem);
339    /* Is this a page that has been inserted into the hash  */
340    if (nextPage) {
341       nextConsole = nextPage->console();
342       /* then is it a treeWidgetItem representing a director */
343    } else if (nextConsole) {
344       /* let the next page BE the console */
345       nextPage = nextConsole;
346    } else {
347       /* Should never get here */
348       nextPage = NULL;
349       nextConsole = NULL;
350    }
351           
352    /* The Previous item */
353
354    /* this condition prevents a segfault.  The first time there is no previousitem*/
355    if (previousitem) {
356       /* knowing the treeWidgetItem, get the page from the hash */
357       previousPage = getFromHash(previousitem);
358       previousConsole = m_consoleHash.value(previousitem);
359       if (previousPage) {
360          previousConsole = previousPage->console();
361       } else if (previousConsole) {
362          previousPage = previousConsole;
363       }
364       if ((previousPage) || (previousConsole)) {
365          if (nextConsole != previousConsole) {
366             /* remove connections to the current console */
367             disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect()));
368             disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
369             disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
370             disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
371             QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
372             QBrush greyBrush(Qt::lightGray);
373             dirItem->setBackground(0, greyBrush);
374          }
375          /* make sure the close window and toggle dock options are removed */
376          treeWidget->removeAction(actionClosePage);
377          treeWidget->removeAction(actionToggleDock);
378          /* Is this a page that has been inserted into the hash  */
379          if (previousPage) {
380             foreach(QAction* pageaction, previousPage->m_contextActions) {
381                treeWidget->removeAction(pageaction);
382             }
383          } 
384       }
385    }
386
387    /* process the current (next) item */
388    
389    if ((nextPage) || (nextConsole)) {
390       if (nextConsole != previousConsole) {
391          /* make connections to the current console */
392          m_currentConsole = nextConsole;
393          connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect()));
394          connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
395          connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
396          connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
397          /* Set director's tree widget background to magenta for ease of identification */
398          QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
399          QBrush magentaBrush(Qt::magenta);
400          dirItem->setBackground(0, magentaBrush);
401       }
402       /* set the value for the currently active console */
403       int stackindex = stackedWidget->indexOf(nextPage);
404    
405       /* Is this page currently on the stack or is it undocked */
406       if (stackindex >= 0) {
407          /* put this page on the top of the stack */
408          stackedWidget->setCurrentIndex(stackindex);
409       } else {
410          /* it is undocked, raise it to the front */
411          nextPage->raise();
412       }
413       /* for the page selectors menu action to dock or undock, set the text */
414       setContextMenuDockText(nextPage, currentitem);
415
416       treeWidget->addAction(actionToggleDock);
417       /* if this page is closeable, then add that action */
418       if (nextPage->isCloseable()) {
419          treeWidget->addAction(actionClosePage);
420       }
421
422       /* Add the actions to the Page Selectors tree widget that are part of the
423        * current items list of desired actions regardless of whether on top of stack*/
424       treeWidget->addActions(nextPage->m_contextActions);
425    }
426 }
427
428 void MainWin::labelDialogClicked() 
429 {
430    new labelDialog(m_currentConsole);
431 }
432
433 void MainWin::runDialogClicked() 
434 {
435    new runDialog(m_currentConsole);
436 }
437
438 void MainWin::restoreDialogClicked() 
439 {
440    new prerestoreDialog(m_currentConsole);
441 }
442
443 /*
444  * The user just finished typing a line in the command line edit box
445  */
446 void MainWin::input_line()
447 {
448    QString cmdStr = lineEdit->text();    /* Get the text */
449    lineEdit->clear();                    /* clear the lineEdit box */
450    if (m_currentConsole->is_connected()) {
451       m_currentConsole->display_text(cmdStr + "\n");
452       m_currentConsole->write_dir(cmdStr.toUtf8().data());         /* send to dir */
453    } else {
454       set_status("Director not connected. Click on connect button.");
455    }
456    m_cmd_history.append(cmdStr);
457    m_cmd_last = -1;
458    if (treeWidget->currentItem() != getFromHash(m_currentConsole)){
459       treeWidget->setCurrentItem(getFromHash(m_currentConsole));
460    }
461 }
462
463
464 void MainWin::about()
465 {
466    QMessageBox::about(this, tr("About bat"),
467             tr("<br><h2>bat 0.2, by Kern Sibbald</h2>"
468             "<p>Copyright &copy; " BYEAR " Free Software Foundation Europe e.V."
469             "<p>The <b>bat</b> is an administrative console"
470                " interface to the Director."));
471 }
472
473 void MainWin::set_statusf(const char *fmt, ...)
474 {
475    va_list arg_ptr;
476    char buf[1000];
477    int len;
478    va_start(arg_ptr, fmt);
479    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
480    va_end(arg_ptr);
481    set_status(buf);
482 }
483
484 void MainWin::set_status_ready()
485 {
486    set_status(" Ready");
487 }
488
489 void MainWin::set_status(const char *buf)
490 {
491    statusBar()->showMessage(buf);
492 }
493
494 /*
495  * Function to respond to the button bar button to undock
496  */
497 void MainWin::undockWindowButton()
498 {
499    Pages* page = (Pages*)stackedWidget->currentWidget();
500    page->togglePageDocking();
501    /* The window has been undocked, lets change the context menu */
502    setContextMenuDockText();
503 }
504
505 /*
506  * Function to respond to action on page selector context menu to toggle the 
507  * dock status of the window associated with the page selectors current
508  * tree widget item.
509  */
510 void MainWin::toggleDockContextWindow()
511 {
512    QTreeWidgetItem *currentitem = treeWidget->currentItem();
513    
514    /* Is this a page that has been inserted into the hash  */
515    if (getFromHash(currentitem)) {
516       Pages* page = getFromHash(currentitem);
517       page->togglePageDocking();
518       if (page->isDocked()) {
519          stackedWidget->setCurrentWidget(page);
520       }
521       /* Toggle the menu item.  The window's dock status has been toggled */
522       setContextMenuDockText(page, currentitem);
523    }
524 }
525
526 /*
527  * Function to set the text of the toggle dock context menu when page and
528  * widget item are NOT known.  This is an overoaded funciton.
529  * It is called from MainWin::undockWindowButton, it is not intended to change
530  * for the top pages tree widget, it is for the currently active tree widget
531  * item.  Which is why the page is not passed.
532  */
533 void MainWin::setContextMenuDockText()
534 {
535    QTreeWidgetItem *currentitem = treeWidget->currentItem();
536    
537    /* Is this a page that has been inserted into the hash  */
538    if (getFromHash(currentitem)) {
539       Pages* page = getFromHash(currentitem);
540       setContextMenuDockText(page, currentitem);
541    }
542 }
543
544 /*
545  * Function to set the text of the toggle dock context menu when page and
546  * widget item are known.  This is the more commonly used.
547  */
548 void MainWin::setContextMenuDockText(Pages* page, QTreeWidgetItem* item)
549 {
550    QString docktext("");
551    if (page->isDocked()) {
552       docktext += "UnDock ";
553    } else {
554       docktext += "ReDock ";
555    }
556    docktext += item->text(0) += " Window";
557    
558    actionToggleDock->setText(docktext);
559    setTreeWidgetItemDockColor(page, item);
560 }
561
562 /*
563  * Function to set the color of the tree widget item based on whether it is
564  * docked or not.
565  */
566 void MainWin::setTreeWidgetItemDockColor(Pages* page, QTreeWidgetItem* item)
567 {
568    if (item->text(0) != "Console") {
569       if (page->isDocked()) {
570       /* Set the brush to blue if undocked */
571          QBrush blackBrush(Qt::black);
572          item->setForeground(0, blackBrush);
573       } else {
574       /* Set the brush back to black if docked */
575          QBrush blueBrush(Qt::blue);
576          item->setForeground(0, blueBrush);
577       }
578    }
579 }
580
581 /*
582  *  Overload of previous function, use treeindex to get item from page
583  *  This is called when an undocked window is closed.
584  */
585 void MainWin::setTreeWidgetItemDockColor(Pages* page)
586 {
587    QTreeWidgetItem* item = getFromHash(page);
588    if (item) {
589      setTreeWidgetItemDockColor(page, item);
590    }
591 }
592
593 /*
594  * This function is called when the stack item is changed.  Call
595  * the virtual function here.  Avoids a window being undocked leaving
596  * a window at the top of the stack unpopulated.
597  */
598 void MainWin::stackItemChanged(int)
599 {
600    Pages* page = (Pages*)stackedWidget->currentWidget();
601    /* run the virtual function in case this class overrides it */
602    page->currentStackItem();
603 }
604
605 /*
606  * Function to simplify insertion of QTreeWidgetItem <-> Page association
607  * into a double direction hash.
608  */
609 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
610 {
611    m_pagehash.insert(item, page);
612    m_widgethash.insert(page, item);
613 }
614
615 /*
616  * Function to simplify removal of QTreeWidgetItem <-> Page association
617  * into a double direction hash.
618  */
619 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
620 {
621    /* I had all sorts of return status checking code here.  Do we have a log
622     * level capability in bat.  I would have left it in but it used printf's
623     * and it should really be some kind of log level facility ???
624     * ******FIXME********/
625    m_pagehash.remove(item);
626    m_widgethash.remove(page);
627 }
628
629 /*
630  * Function to retrieve a Page* when the item in the page selector's tree is
631  * known.
632  */
633 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
634 {
635    return m_pagehash.value(item);
636 }
637
638 /*
639  * Function to retrieve the page selectors tree widget item when the page is
640  * known.
641  */
642 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
643 {
644    return m_widgethash.value(page);
645 }
646
647 /*
648  * Function to respond to action on page selector context menu to close the
649  * current window.
650  */
651 void MainWin::closePage()
652 {
653    QTreeWidgetItem *currentitem = treeWidget->currentItem();
654    
655    /* Is this a page that has been inserted into the hash  */
656    if (getFromHash(currentitem)) {
657       Pages* page = getFromHash(currentitem);
658       if (page->isCloseable()) {
659          page->closeStackPage();
660       }
661    }
662 }