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