]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/wxbmainframe.cpp
- wxbRestorePanel : Corrected a problem when you double-clicked an a folder in the...
[bacula/bacula] / bacula / src / wx-console / wxbmainframe.cpp
1 /*
2  *
3  *   Main frame
4  *
5  *    Nicolas Boichat, April 2004
6  *
7  */
8 /*
9    Copyright (C) 2004 Kern Sibbald and John Walker
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License
13    as published by the Free Software Foundation; either version 2
14    of the License, or (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #include "wxbmainframe.h" // class's header file
27
28 #include "wxbrestorepanel.h"
29
30 #include "csprint.h"
31
32 #include "wxwin16x16.xpm"
33
34 #include <wx/arrimpl.cpp>
35
36 #include <wx/stattext.h>
37
38 // ----------------------------------------------------------------------------
39 // event tables and other macros for wxWindows
40 // ----------------------------------------------------------------------------
41
42 // ----------------------------------------------------------------------------
43 // constants
44 // ----------------------------------------------------------------------------
45
46 // IDs for the controls and the menu commands
47 enum
48 {
49    // menu items
50    Minimal_Quit = 1,
51
52    // it is important for the id corresponding to the "About" command to have
53    // this standard value as otherwise it won't be handled properly under Mac
54    // (where it is special and put into the "Apple" menu)
55    Minimal_About = wxID_ABOUT,
56    TypeText = 2,
57    SendButton = 3,
58    Thread = 4
59 };
60
61 /*
62  *   wxbTHREAD_EVENT declaration, used by csprint
63  */
64 BEGIN_DECLARE_EVENT_TYPES()
65    DECLARE_EVENT_TYPE(wxbTHREAD_EVENT,       1)
66 END_DECLARE_EVENT_TYPES()
67
68 DEFINE_EVENT_TYPE(wxbTHREAD_EVENT)
69
70 // the event tables connect the wxWindows events with the functions (event
71 // handlers) which process them. It can be also done at run-time, but for the
72 // simple menu events like this the static method is much simpler.
73 BEGIN_EVENT_TABLE(wxbMainFrame, wxFrame)
74    EVT_MENU(Minimal_Quit,  wxbMainFrame::OnQuit)
75    EVT_MENU(Minimal_About, wxbMainFrame::OnAbout)
76    EVT_TEXT_ENTER(TypeText, wxbMainFrame::OnEnter)
77    EVT_BUTTON(SendButton, wxbMainFrame::OnEnter)
78    EVT_CUSTOM(wxbTHREAD_EVENT, Thread, wxbMainFrame::OnPrint)
79 END_EVENT_TABLE()
80
81 // ----------------------------------------------------------------------------
82 // wxbThreadEvent
83 // ----------------------------------------------------------------------------
84
85 /*
86  *  wxbThreadEvent constructor
87  */
88 wxbThreadEvent::wxbThreadEvent(int id): wxEvent(id, wxbTHREAD_EVENT) {
89    m_eventObject = NULL;
90 }
91
92 /*
93  *  wxbThreadEvent destructor
94  */
95 wxbThreadEvent::~wxbThreadEvent()
96 {
97    if (m_eventObject != NULL) {
98       delete m_eventObject;
99    }
100 }
101
102 /*
103  *  wxbThreadEvent copy constructor
104  */
105 wxbThreadEvent::wxbThreadEvent(const wxbThreadEvent& te)
106 {
107    this->m_eventType = te.m_eventType;
108    this->m_id = te.m_id;
109    if (te.m_eventObject != NULL) {
110       this->m_eventObject = new wxbPrintObject(*((wxbPrintObject*)te.m_eventObject));
111    }
112    else {
113       this->m_eventObject = NULL;
114    }
115    this->m_skipped = te.m_skipped;
116    this->m_timeStamp = te.m_timeStamp;
117 }
118
119 /*
120  *  Must be implemented (abstract in wxEvent)
121  */
122 wxEvent* wxbThreadEvent::Clone() const
123 {
124    return new wxbThreadEvent(*this);
125 }
126
127 /*
128  *  Gets the wxbPrintObject attached to this event, containing data sent by director
129  */
130 wxbPrintObject* wxbThreadEvent::GetEventPrintObject()
131 {
132    return (wxbPrintObject*)m_eventObject;
133 }
134
135 /*
136  *  Sets the wxbPrintObject attached to this event
137  */
138 void wxbThreadEvent::SetEventPrintObject(wxbPrintObject* object)
139 {
140    m_eventObject = (wxObject*)object;
141 }
142
143 // ----------------------------------------------------------------------------
144 // main frame
145 // ----------------------------------------------------------------------------
146
147 wxbMainFrame *wxbMainFrame::frame = NULL;
148
149 /*
150  *  Singleton constructor
151  */
152 wxbMainFrame* wxbMainFrame::CreateInstance(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
153 {
154    frame = new wxbMainFrame(title, pos, size, style);
155    return frame;
156 }
157
158 /*
159  *  Returns singleton instance
160  */
161 wxbMainFrame* wxbMainFrame::GetInstance()
162 {
163    return frame;
164 }
165
166 /*
167  *  Private destructor
168  */
169 wxbMainFrame::~wxbMainFrame()
170 {
171    if (ct != NULL) { // && (!ct->IsRunning())
172       ct->Delete();
173    }
174    frame = NULL;
175 }
176
177 /*
178  *  Private constructor
179  */
180 wxbMainFrame::wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
181       : wxFrame(NULL, -1, title, pos, size, style)
182 {
183    ct = NULL;
184
185    // set the frame icon
186    SetIcon(wxIcon(wxwin16x16_xpm));
187
188 #if wxUSE_MENUS
189    // create a menu bar
190    wxMenu *menuFile = new wxMenu;
191
192    // the "About" item should be in the help menu
193    wxMenu *helpMenu = new wxMenu;
194    helpMenu->Append(Minimal_About, _T("&About...\tF1"), _T("Show about dialog"));
195
196    menuFile->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
197
198    // now append the freshly created menu to the menu bar...
199    wxMenuBar *menuBar = new wxMenuBar();
200    menuBar->Append(menuFile, _T("&File"));
201    menuBar->Append(helpMenu, _T("&Help"));
202
203    // ... and attach this menu bar to the frame
204    SetMenuBar(menuBar);
205 #endif // wxUSE_MENUS
206
207    CreateStatusBar(1);
208    SetStatusText(wxString("Welcome to bacula wx-console ") << VERSION << " (" << BDATE << ")!\n");
209
210    wxPanel* global = new wxPanel(this, -1);
211
212    notebook = new wxNotebook(global, -1);
213
214    /* Console */
215
216    wxPanel* consolePanel = new wxPanel(notebook, -1);
217    notebook->AddPage(consolePanel, "Console");
218
219    consoleCtrl = new wxTextCtrl(consolePanel,-1,"",wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH);
220    consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)));
221
222    wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(2, 1, 0, 0);
223    consoleSizer->AddGrowableCol(0);
224    consoleSizer->AddGrowableRow(0);
225
226    typeCtrl = new wxTextCtrl(consolePanel,TypeText,"",wxDefaultPosition,wxSize(200,20), wxTE_PROCESS_ENTER);
227    sendButton = new wxButton(consolePanel, SendButton, "Send");
228    
229    wxFlexGridSizer *typeSizer = new wxFlexGridSizer(1, 3, 0, 0);
230    typeSizer->AddGrowableCol(1);
231    typeSizer->AddGrowableRow(0);
232
233    typeSizer->Add(new wxStaticText(consolePanel, -1, "Command: "), 0, wxALIGN_CENTER | wxALL, 0);
234    typeSizer->Add(typeCtrl, 1, wxEXPAND | wxALL, 0);
235    typeSizer->Add(sendButton, 1, wxEXPAND | wxLEFT, 5);
236
237    consoleSizer->Add(consoleCtrl, 1, wxEXPAND | wxALL, 0);
238    consoleSizer->Add(typeSizer, 0, wxEXPAND | wxALL, 0);
239
240    consolePanel->SetAutoLayout( TRUE );
241    consolePanel->SetSizer( consoleSizer );
242    consoleSizer->SetSizeHints( consolePanel );
243
244    // Creates the list of panels which are included in notebook, and that need to receive director information
245
246    panels = new wxbPanel* [2];
247    panels[0] = new wxbRestorePanel(notebook);
248    panels[1] = NULL;
249
250    for (int i = 0; panels[i] != NULL; i++) {
251       notebook->AddPage(panels[i], panels[i]->GetTitle());
252    }
253
254    wxBoxSizer* globalSizer = new wxBoxSizer(wxHORIZONTAL);
255
256    globalSizer->Add(new wxNotebookSizer(notebook), 1, wxEXPAND, 0);
257
258    global->SetSizer( globalSizer );
259    globalSizer->SetSizeHints( global );
260
261    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
262
263    sizer->Add(global, 1, wxEXPAND | wxALL, 0);
264    SetAutoLayout(true);
265    SetSizer( sizer );
266    sizer->SetSizeHints( this );
267    this->SetSize(size);
268    EnableConsole(false);
269    
270    lockedbyconsole = false;
271    
272    consoleBuffer = "";
273 }
274
275 /*
276  *  Starts the thread interacting with the director
277  */
278 void wxbMainFrame::StartConsoleThread()
279 {
280    if (ct != NULL) {
281       ct->Delete();
282    }
283    else {
284       promptparser = new wxbPromptParser();      
285    }
286    ct = new console_thread();
287    ct->Create();
288    ct->Run();
289    SetStatusText("Connecting to the director...");
290 }
291
292 /* Register a new wxbDataParser */
293 void wxbMainFrame::Register(wxbDataParser* dp) {
294    parsers.Add(dp);
295 }
296    
297 /* Unregister a wxbDataParser */
298 void wxbMainFrame::Unregister(wxbDataParser* dp) {
299    int index;
300    if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
301       parsers.RemoveAt(index);
302    }
303    else {
304       Print("Failed to unregister a data parser !", CS_DEBUG);
305    }
306 }
307
308 // event handlers
309
310 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
311 {
312    Print("Quitting.\n", CS_DEBUG);
313    if (ct != NULL) {
314       ct->Delete();
315       ct = NULL;
316    }
317    frame = NULL;
318    Close(TRUE);
319 }
320
321 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
322 {
323    wxString msg;
324    msg.Printf( _T("Welcome to Bacula wx-console.\nWritten by Nicolas Boichat <nicolas@boichat.ch>\n(C) 2004 Kern Sibbald and John Walker\n"));
325
326    wxMessageBox(msg, _T("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
327 }
328
329 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
330 {
331    lockedbyconsole = true;
332    DisablePanels();
333    wxString str = typeCtrl->GetValue() + "\n";
334    Send(str);
335 }
336
337 /*
338  *  Called when data is arriving from director
339  */
340 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
341    wxbPrintObject* po = event.GetEventPrintObject();
342
343    Print(po->str, po->status);
344 }
345
346 /*
347  *  Prints data received from director to the console, and forwards it to the panels
348  */
349 void wxbMainFrame::Print(wxString str, int status)
350 {
351    if (lockedbyconsole) {
352       EnableConsole(false);
353    }
354    
355    if (status == CS_TERMINATED) {
356       consoleCtrl->AppendText(consoleBuffer);
357       consoleBuffer = "";
358       SetStatusText("Console thread terminated.");
359       ct = NULL;
360       DisablePanels();
361       int answer = wxMessageBox("Connection to the director lost. Quit program?", "Connection lost",
362                         wxYES_NO | wxICON_EXCLAMATION, this);
363       if (answer == wxYES) {
364          frame = NULL;
365          Close(true);
366       }
367       return;
368    }
369    
370    if (status == CS_CONNECTED) {
371       SetStatusText("Connected to the director.");
372       EnablePanels();
373       return;
374    }
375    if (status == CS_DISCONNECTED) {
376       consoleCtrl->AppendText(consoleBuffer);
377       consoleBuffer = "";
378       SetStatusText("Disconnected of the director.");
379       DisablePanels();
380       return;
381    }
382       
383    // CS_DEBUG is often sent by panels, 
384    // and resend it to them would sometimes cause infinite loops
385    
386    /* One promptcaught is normal, so we must have two true Print values to be
387     * sure that the prompt has effectively been caught.
388     */
389    int promptcaught = -1;
390    
391    if (status != CS_DEBUG) {
392       for (unsigned int i = 0; i < parsers.GetCount(); i++) {
393          promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
394       }
395          
396       if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
397          Print("Unexpected question has been received.\n", CS_DEBUG);
398 //         Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
399          
400          wxString message;
401          if (promptparser->getIntroString() != "") {
402             message << promptparser->getIntroString() << "\n";
403          }
404          message << promptparser->getQuestionString();
405          
406          if (promptparser->getChoices()) {
407             wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
408             int *numbers = new int[promptparser->getChoices()->GetCount()];
409             int n = 0;
410             
411             for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
412                if ((*promptparser->getChoices())[i] != "") {
413                   choices[n] = (*promptparser->getChoices())[i];
414                   numbers[n] = i;
415                   n++;
416                }
417             }
418             
419             int res = ::wxGetSingleChoiceIndex(message,
420                "wx-console: unexpected director's question.", n, choices, this);
421             if (res == -1) {
422                Send("\n");
423             }
424             else {
425                Send(wxString() << numbers[res] << "\n");
426             }
427          }
428          else {
429             Send(::wxGetTextFromUser(message,
430                "wx-console: unexpected director's question.", "", this) + "\n");
431          }
432       }
433    }
434       
435    if (status == CS_END) {
436       if (lockedbyconsole) {
437          EnablePanels();
438          lockedbyconsole = false;
439       }
440       str = "#";
441    }
442
443    if (status == CS_DEBUG) {
444       consoleCtrl->AppendText(consoleBuffer);
445       consoleBuffer = "";
446       consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
447    }
448    else {
449       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
450    }
451    consoleBuffer << str;
452    if (status == CS_PROMPT) {
453       if (lockedbyconsole) {
454          EnableConsole(true);
455       }
456       consoleBuffer << "<P>";
457    }
458    
459    if ((status == CS_END) || (status == CS_PROMPT) || (str.Find("\n") > -1)) {
460       consoleCtrl->AppendText(consoleBuffer);
461       consoleBuffer = "";
462    
463       consoleCtrl->ScrollLines(3);
464    }
465    
466 //   consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
467    
468    /*if (status != CS_DEBUG) {
469       consoleCtrl->AppendText("@");
470    }*/
471    //consoleCtrl->SetInsertionPointEnd();
472    
473 /*   if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
474       nlines = consoleCtrl->GetNumberOfLines()-1;
475    }
476    
477    if (status == CS_END) {
478       consoleCtrl->ShowPosition(nlines);
479    }*/
480 }
481
482 /*
483  *  Sends data to the director
484  */
485 void wxbMainFrame::Send(wxString str)
486 {
487    if (ct != NULL) {
488       ct->Write((const char*)str);
489       typeCtrl->SetValue("");
490       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
491       consoleCtrl->AppendText(str);
492       consoleCtrl->ScrollLines(3);
493    }
494    
495 /*   if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
496       nlines = consoleCtrl->GetNumberOfLines()-1;
497    }
498    
499    consoleCtrl->ShowPosition(nlines);*/
500 }
501
502 /* Enable panels */
503 void wxbMainFrame::EnablePanels() {
504    for (int i = 0; panels[i] != NULL; i++) {
505       panels[i]->EnablePanel(true);
506    }
507    EnableConsole(true);
508 }
509
510 /* Disable panels, except the one passed as parameter */
511 void wxbMainFrame::DisablePanels(void* except) {
512    for (int i = 0; panels[i] != NULL; i++) {
513       if (panels[i] != except) {
514          panels[i]->EnablePanel(false);
515       }
516       else {
517          panels[i]->EnablePanel(true);
518       }
519    }
520    if (this != except) {
521       EnableConsole(false);
522    }
523 }
524
525 /* Enable or disable console typing */
526 void wxbMainFrame::EnableConsole(bool enable) {
527    typeCtrl->Enable(enable);
528    sendButton->Enable(enable);
529    if (enable) {
530       typeCtrl->SetFocus();
531    }
532 }
533
534 /*
535  *  Used by csprint, which is called by console thread.
536  *
537  *  In GTK and perhaps X11, only the main thread is allowed to interact with
538  *  graphical components, so by firing an event, the main loop will call OnPrint.
539  *
540  *  Calling OnPrint directly from console thread produces "unexpected async replies".
541  */
542 void firePrintEvent(wxString str, int status)
543 {
544    wxbPrintObject* po = new wxbPrintObject(str, status);
545
546    wxbThreadEvent evt(Thread);
547    evt.SetEventPrintObject(po);
548    
549    if (wxbMainFrame::GetInstance()) {
550       wxbMainFrame::GetInstance()->AddPendingEvent(evt);
551    }
552 }
553
554 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
555
556 /*
557  *  Called by console thread, this function forwards data line by line and end
558  *  signals to the GUI.
559  */
560 void csprint(const char* str, int status)
561 {
562    if (str != 0) {
563       firePrintEvent(wxString(str), status);
564    }
565    else {
566       firePrintEvent("", status);
567    }
568 }
569