]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/mainwin.cpp
Auto select console tree widget of current console when a command is entered in the
[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(QString::fromUtf8("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       printf("Should never get here\n");
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(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
370             QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
371             QBrush greyBrush(Qt::lightGray);
372             dirItem->setBackground(0, greyBrush);
373          }
374          /* make sure the close window and toggle dock options are removed */
375          treeWidget->removeAction(actionClosePage);
376          treeWidget->removeAction(actionToggleDock);
377          /* Is this a page that has been inserted into the hash  */
378          if (previousPage) {
379             foreach(QAction* pageaction, previousPage->m_contextActions) {
380                treeWidget->removeAction(pageaction);
381             }
382          } 
383       }
384    }
385
386    /* now process the next item */
387    
388    if ((nextPage) || (nextConsole)) {
389       if (nextConsole != previousConsole) {
390          /* make connections to the current console */
391          m_currentConsole = nextConsole;
392          connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect()));
393          connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
394          connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
395          /* Set director's tree widget background to magenta for ease of identification */
396          QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
397          QBrush magentaBrush(Qt::magenta);
398          dirItem->setBackground(0, magentaBrush);
399       }
400       /* set the value for the currently active console */
401       int stackindex = stackedWidget->indexOf(nextPage);
402    
403       /* Is this page currently on the stack or is it undocked */
404       if (stackindex >= 0) {
405          /* put this page on the top of the stack */
406          stackedWidget->setCurrentIndex(stackindex);
407       } else {
408          /* it is undocked, raise it to the front */
409          nextPage->raise();
410       }
411       /* for the page selectors menu action to dock or undock, set the text */
412       setContextMenuDockText(nextPage, currentitem);
413
414       treeWidget->addAction(actionToggleDock);
415       /* if this page is closeable, then add that action */
416       if (nextPage->isCloseable()) {
417          treeWidget->addAction(actionClosePage);
418       }
419
420       /* Add the actions to the Page Selectors tree widget that are part of the
421        * current items list of desired actions regardless of whether on top of stack*/
422       treeWidget->addActions(nextPage->m_contextActions);
423    }
424 }
425
426 void MainWin::labelDialogClicked() 
427 {
428    new labelDialog(m_currentConsole);
429 }
430
431 void MainWin::runDialogClicked() 
432 {
433    new runDialog(m_currentConsole);
434 }
435
436 void MainWin::restoreDialogClicked() 
437 {
438    new prerestoreDialog(m_currentConsole);
439 }
440
441 /*
442  * The user just finished typing a line in the command line edit box
443  */
444 void MainWin::input_line()
445 {
446    QString cmdStr = lineEdit->text();    /* Get the text */
447    lineEdit->clear();                    /* clear the lineEdit box */
448    if (m_currentConsole->is_connected()) {
449       m_currentConsole->display_text(cmdStr + "\n");
450       m_currentConsole->write_dir(cmdStr.toUtf8().data());         /* send to dir */
451    } else {
452       set_status("Director not connected. Click on connect button.");
453    }
454    m_cmd_history.append(cmdStr);
455    m_cmd_last = -1;
456    if (treeWidget->currentItem() != getFromHash(m_currentConsole)){
457       treeWidget->setCurrentItem(getFromHash(m_currentConsole));
458    }
459 }
460
461
462 void MainWin::about()
463 {
464    QMessageBox::about(this, tr("About bat"),
465             tr("<br><h2>bat 0.2, by Kern Sibbald</h2>"
466             "<p>Copyright &copy; " BYEAR " Free Software Foundation Europe e.V."
467             "<p>The <b>bat</b> is an administrative console"
468                " interface to the Director."));
469 }
470
471 void MainWin::set_statusf(const char *fmt, ...)
472 {
473    va_list arg_ptr;
474    char buf[1000];
475    int len;
476    va_start(arg_ptr, fmt);
477    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
478    va_end(arg_ptr);
479    set_status(buf);
480 }
481
482 void MainWin::set_status_ready()
483 {
484    set_status(" Ready");
485 }
486
487 void MainWin::set_status(const char *buf)
488 {
489    statusBar()->showMessage(buf);
490 }
491
492 /*
493  * Function to respond to the button bar button to undock
494  */
495 void MainWin::undockWindowButton()
496 {
497    Pages* page = (Pages*)stackedWidget->currentWidget();
498    page->togglePageDocking();
499    /* The window has been undocked, lets change the context menu */
500    setContextMenuDockText();
501 }
502
503 /*
504  * Function to respond to action on page selector context menu to toggle the 
505  * dock status of the window associated with the page selectors current
506  * tree widget item.
507  */
508 void MainWin::toggleDockContextWindow()
509 {
510    QTreeWidgetItem *currentitem = treeWidget->currentItem();
511    
512    /* Is this a page that has been inserted into the hash  */
513    if (getFromHash(currentitem)) {
514       Pages* page = getFromHash(currentitem);
515       page->togglePageDocking();
516       if (page->isDocked()) {
517          stackedWidget->setCurrentWidget(page);
518       }
519       /* Toggle the menu item.  The window's dock status has been toggled */
520       setContextMenuDockText(page, currentitem);
521    }
522 }
523
524 /*
525  * Function to set the text of the toggle dock context menu when page and
526  * widget item are NOT known.  This is an overoaded funciton.
527  * It is called from MainWin::undockWindowButton, it is not intended to change
528  * for the top pages tree widget, it is for the currently active tree widget
529  * item.  Which is why the page is not passed.
530  */
531 void MainWin::setContextMenuDockText()
532 {
533    QTreeWidgetItem *currentitem = treeWidget->currentItem();
534    
535    /* Is this a page that has been inserted into the hash  */
536    if (getFromHash(currentitem)) {
537       Pages* page = getFromHash(currentitem);
538       setContextMenuDockText(page, currentitem);
539    }
540 }
541
542 /*
543  * Function to set the text of the toggle dock context menu when page and
544  * widget item are known.  This is the more commonly used.
545  */
546 void MainWin::setContextMenuDockText(Pages* page, QTreeWidgetItem* item)
547 {
548    QString docktext("");
549    if (page->isDocked()) {
550       docktext += "UnDock ";
551    } else {
552       docktext += "ReDock ";
553    }
554    docktext += item->text(0) += " Window";
555    
556    actionToggleDock->setText(docktext);
557    setTreeWidgetItemDockColor(page, item);
558 }
559
560 /*
561  * Function to set the color of the tree widget item based on whether it is
562  * docked or not.
563  */
564 void MainWin::setTreeWidgetItemDockColor(Pages* page, QTreeWidgetItem* item)
565 {
566    if (item->text(0) != "Console") {
567       if (page->isDocked()) {
568       /* Set the brush to blue if undocked */
569          QBrush blackBrush(Qt::black);
570          item->setForeground(0, blackBrush);
571       } else {
572       /* Set the brush back to black if docked */
573          QBrush blueBrush(Qt::blue);
574          item->setForeground(0, blueBrush);
575       }
576    }
577 }
578
579 /*
580  *  Overload of previous function, use treeindex to get item from page
581  *  This is called when an undocked window is closed.
582  */
583 void MainWin::setTreeWidgetItemDockColor(Pages* page)
584 {
585    QTreeWidgetItem* item = getFromHash(page);
586    if (item) {
587      setTreeWidgetItemDockColor(page, item);
588    }
589 }
590
591 /*
592  * This function is called when the stack item is changed.  Call
593  * the virtual function here.  Avoids a window being undocked leaving
594  * a window at the top of the stack unpopulated.
595  */
596 void MainWin::stackItemChanged(int)
597 {
598    Pages* page = (Pages*)stackedWidget->currentWidget();
599    /* run the virtual function in case this class overrides it */
600    page->currentStackItem();
601 }
602
603 /*
604  * Function to simplify insertion of QTreeWidgetItem <-> Page association
605  * into a double direction hash.
606  */
607 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
608 {
609    m_pagehash.insert(item, page);
610    m_widgethash.insert(page, item);
611 }
612
613 /*
614  * Function to simplify removal of QTreeWidgetItem <-> Page association
615  * into a double direction hash.
616  */
617 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
618 {
619    /* I had all sorts of return status checking code here.  Do we have a log
620     * level capability in bat.  I would have left it in but it used printf's
621     * and it should really be some kind of log level facility ???
622     * ******FIXME********/
623    m_pagehash.remove(item);
624    m_widgethash.remove(page);
625 }
626
627 /*
628  * Function to retrieve a Page* when the item in the page selector's tree is
629  * known.
630  */
631 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
632 {
633    return m_pagehash.value(item);
634 }
635
636 /*
637  * Function to retrieve the page selectors tree widget item when the page is
638  * known.
639  */
640 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
641 {
642    return m_widgethash.value(page);
643 }
644
645 /*
646  * Function to respond to action on page selector context menu to close the
647  * current window.
648  */
649 void MainWin::closePage()
650 {
651    QTreeWidgetItem *currentitem = treeWidget->currentItem();
652    
653    /* Is this a page that has been inserted into the hash  */
654    if (getFromHash(currentitem)) {
655       Pages* page = getFromHash(currentitem);
656       if (page->isCloseable()) {
657          page->closeStackPage();
658       }
659    }
660 }