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