]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/wxbmainframe.cpp
Did not intend to leave those debugging lines there.
[bacula/bacula] / bacula / src / wx-console / wxbmainframe.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-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 and included
11    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 Kern Sibbald.
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  *   Main frame
31  *
32  *    Nicolas Boichat, July 2004
33  *
34  *    Version $Id$
35  */
36
37 /*  Windows debug builds set _DEBUG which is used by wxWidgets to select their
38  *  debug memory allocator.  Unfortunately it conflicts with Bacula's SmartAlloc.
39  * So we turn _DEBUG off since we aren't interested in things it enables.
40  */
41
42 #undef _DEBUG
43
44 #include "bacula.h"
45
46 #include "wxbmainframe.h" // class's header file
47
48 #include "wxbrestorepanel.h"
49
50 #include "wxbconfigfileeditor.h"
51
52 #include "csprint.h"
53
54 #include "wxwin16x16.xpm"
55
56 #include <wx/arrimpl.cpp>
57
58 #include <wx/stattext.h>
59 #include <wx/statline.h>
60 #include <wx/config.h>
61
62 #include <wx/filename.h>
63
64 #undef Yield /* MinGW defines Yield */
65
66 // ----------------------------------------------------------------------------
67 // event tables and other macros for wxWindows
68 // ----------------------------------------------------------------------------
69
70 // ----------------------------------------------------------------------------
71 // constants
72 // ----------------------------------------------------------------------------
73
74 // IDs for the controls and the menu commands
75 enum
76 {
77    // menu items
78    Minimal_Quit = 1,
79
80    // it is important for the id corresponding to the "About" command to have
81    // this standard value as otherwise it won't be handled properly under Mac
82    // (where it is special and put into the "Apple" menu)
83    Minimal_About = wxID_ABOUT,
84    
85    ChangeConfigFile = 2,
86    EditConfigFile = 3,
87    MenuConnect = 4,
88    MenuDisconnect = 5,
89    TypeText = 6,
90    SendButton = 7,
91    Thread = 8
92 };
93
94 /*
95  *   wxbTHREAD_EVENT declaration, used by csprint
96  */
97
98 DEFINE_EVENT_TYPE(wxbTHREAD_EVENT)
99
100 typedef void (wxEvtHandler::*wxThreadEventFunction)(wxbThreadEvent&);
101
102 #define EVT_THREAD_EVENT(id, fn) \
103     DECLARE_EVENT_TABLE_ENTRY( \
104         wxbTHREAD_EVENT, id, wxID_ANY, \
105         (wxObjectEventFunction)(wxEventFunction)(wxThreadEventFunction)&fn, \
106         (wxObject *) NULL \
107     ),
108
109 // the event tables connect the wxWindows events with the functions (event
110 // handlers) which process them. It can be also done at run-time, but for the
111 // simple menu events like this the static method is much simpler.
112 BEGIN_EVENT_TABLE(wxbMainFrame, wxFrame)
113    EVT_MENU(Minimal_Quit,  wxbMainFrame::OnQuit)
114    EVT_MENU(Minimal_About, wxbMainFrame::OnAbout)
115    EVT_MENU(ChangeConfigFile, wxbMainFrame::OnChangeConfig)
116    EVT_MENU(EditConfigFile, wxbMainFrame::OnEditConfig)
117    EVT_MENU(MenuConnect, wxbMainFrame::OnConnect)
118    EVT_MENU(MenuDisconnect, wxbMainFrame::OnDisconnect)
119    EVT_TEXT_ENTER(TypeText, wxbMainFrame::OnEnter)
120    EVT_THREAD_EVENT(Thread, wxbMainFrame::OnPrint)
121    EVT_BUTTON(SendButton, wxbMainFrame::OnEnter)
122 END_EVENT_TABLE()
123
124 // ----------------------------------------------------------------------------
125 // wxbThreadEvent
126 // ----------------------------------------------------------------------------
127
128 /*
129  *  wxbThreadEvent constructor
130  */
131 wxbThreadEvent::wxbThreadEvent(int id): wxEvent(id, wxbTHREAD_EVENT) {
132    m_eventObject = NULL;
133 }
134
135 /*
136  *  wxbThreadEvent destructor
137  */
138 wxbThreadEvent::~wxbThreadEvent()
139 {
140    if (m_eventObject != NULL) {
141       delete m_eventObject;
142    }
143 }
144
145 /*
146  *  wxbThreadEvent copy constructor
147  */
148 wxbThreadEvent::wxbThreadEvent(const wxbThreadEvent& te)
149 {
150    this->m_eventType = te.m_eventType;
151    this->m_id = te.m_id;
152    if (te.m_eventObject != NULL) {
153       this->m_eventObject = new wxbPrintObject(*((wxbPrintObject*)te.m_eventObject));
154    }
155    else {
156       this->m_eventObject = NULL;
157    }
158    this->m_skipped = te.m_skipped;
159    this->m_timeStamp = te.m_timeStamp;
160 }
161
162 /*
163  *  Must be implemented (abstract in wxEvent)
164  */
165 wxEvent* wxbThreadEvent::Clone() const
166 {
167    return new wxbThreadEvent(*this);
168 }
169
170 /*
171  *  Gets the wxbPrintObject attached to this event, containing data sent by director
172  */
173 wxbPrintObject* wxbThreadEvent::GetEventPrintObject()
174 {
175    return (wxbPrintObject*)m_eventObject;
176 }
177
178 /*
179  *  Sets the wxbPrintObject attached to this event
180  */
181 void wxbThreadEvent::SetEventPrintObject(wxbPrintObject* object)
182 {
183    m_eventObject = (wxObject*)object;
184 }
185
186 // ----------------------------------------------------------------------------
187 // main frame
188 // ----------------------------------------------------------------------------
189
190 wxbMainFrame *wxbMainFrame::frame = NULL;
191
192 /*
193  *  Singleton constructor
194  */
195 wxbMainFrame* wxbMainFrame::CreateInstance(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
196 {
197    frame = new wxbMainFrame(title, pos, size, style);
198    return frame;
199 }
200
201 /*
202  *  Returns singleton instance
203  */
204 wxbMainFrame* wxbMainFrame::GetInstance()
205 {
206    return frame;
207 }
208
209 /*
210  *  Private destructor
211  */
212 wxbMainFrame::~wxbMainFrame()
213 {
214    wxConfig::Get()->Write(wxT("/Position/X"), (long)GetPosition().x);
215    wxConfig::Get()->Write(wxT("/Position/Y"), (long)GetPosition().y);
216    wxConfig::Get()->Write(wxT("/Size/Width"), (long)GetSize().GetWidth());
217    wxConfig::Get()->Write(wxT("/Size/Height"), (long)GetSize().GetHeight());
218
219    if (ct != NULL) { // && (!ct->IsRunning())
220       ct->Delete();
221    }
222    frame = NULL;
223 }
224
225 /*
226  *  Private constructor
227  */
228 wxbMainFrame::wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
229       : wxFrame(NULL, -1, title, pos, size, style)
230 {
231    lockedbyconsole = false;
232    
233    ct = NULL;
234    
235    promptparser = NULL;
236
237    // set the frame icon
238    SetIcon(wxIcon(wxwin16x16_xpm));
239
240 #if wxUSE_MENUS
241    // create a menu bar
242    menuFile = new wxMenu;
243
244    // the "About" item should be in the help menu
245    wxMenu *helpMenu = new wxMenu;
246    helpMenu->Append(Minimal_About, _("&About...\tF1"), _("Show about dialog"));
247
248    menuFile->Append(MenuConnect, _("Connect"), _("Connect to the director"));
249    menuFile->Append(MenuDisconnect, _("Disconnect"), _("Disconnect of the director"));
250    menuFile->AppendSeparator();
251    menuFile->Append(ChangeConfigFile, _("Change of configuration file"), _("Change your default configuration file"));
252    menuFile->Append(EditConfigFile, _("Edit your configuration file"), _("Edit your configuration file"));
253    menuFile->AppendSeparator();
254    menuFile->Append(Minimal_Quit, _("E&xit\tAlt-X"), _("Quit this program"));
255
256    // now append the freshly created menu to the menu bar...
257    wxMenuBar *menuBar = new wxMenuBar();
258    menuBar->Append(menuFile, _("&File"));
259    menuBar->Append(helpMenu, _("&Help"));
260
261    // ... and attach this menu bar to the frame
262    SetMenuBar(menuBar);
263 #endif // wxUSE_MENUS
264
265    CreateStatusBar(1);
266    
267    SetStatusText(wxString::Format(_("Welcome to bacula bwx-console %s (%s)!\n"), wxT(VERSION), wxT(BDATE)));
268
269    wxPanel* global = new wxPanel(this, -1);
270
271    notebook = new wxNotebook(global, -1);
272
273    /* Console */
274
275    wxPanel* consolePanel = new wxPanel(notebook, -1);
276    notebook->AddPage(consolePanel, _("Console"));
277
278    consoleCtrl = new wxTextCtrl(consolePanel,-1,wxT(""),wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH);
279    wxFont font(10, wxMODERN, wxNORMAL, wxNORMAL);
280 #if defined __WXGTK12__ && !defined __WXGTK20__ // Fix for "chinese" fonts under gtk+ 1.2
281    font.SetDefaultEncoding(wxFONTENCODING_ISO8859_1);
282    consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
283    Print(_("Warning : Unicode is disabled because you are using wxWidgets for GTK+ 1.2.\n"), CS_DEBUG);
284 #else 
285    consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
286 #if (wxUSE_UNICODE == 0) && __WXGTK20__
287    Print(_("Warning : There is a problem with wxWidgets for GTK+ 2.0 without Unicode support when handling non-ASCII filenames: Every non-ASCII character in such filenames will be replaced by an interrogation mark.\nIf this behaviour disturbs you, please build bwx-console against a Unicode version of wxWidgets for GTK+ 2.0.\n---\n"), CS_DEBUG);   
288 #endif
289 #endif
290
291    helpCtrl = new wxStaticText(consolePanel, -1, _("Type your command below:"));
292
293    wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(4, 1, 0, 0);
294    consoleSizer->AddGrowableCol(0);
295    consoleSizer->AddGrowableRow(0);
296
297    typeCtrl = new wxbHistoryTextCtrl(helpCtrl, consolePanel,TypeText,wxT(""),wxDefaultPosition,wxSize(200,20));
298    sendButton = new wxButton(consolePanel, SendButton, _("Send"));
299    
300    wxFlexGridSizer *typeSizer = new wxFlexGridSizer(1, 2, 0, 0);
301    typeSizer->AddGrowableCol(0);
302    typeSizer->AddGrowableRow(0);
303
304    //typeSizer->Add(new wxStaticText(consolePanel, -1, _("Command: ")), 0, wxALIGN_CENTER | wxALL, 0);
305    typeSizer->Add(typeCtrl, 1, wxEXPAND | wxALL, 0);
306    typeSizer->Add(sendButton, 1, wxEXPAND | wxLEFT, 5);
307
308    consoleSizer->Add(consoleCtrl, 1, wxEXPAND | wxALL, 0);
309    consoleSizer->Add(new wxStaticLine(consolePanel, -1), 0, wxEXPAND | wxALL, 0);
310    consoleSizer->Add(helpCtrl, 1, wxEXPAND | wxALL, 2);
311    consoleSizer->Add(typeSizer, 0, wxEXPAND | wxALL, 2);
312
313    consolePanel->SetAutoLayout( TRUE );
314    consolePanel->SetSizer( consoleSizer );
315    consoleSizer->SetSizeHints( consolePanel );
316
317    // Creates the list of panels which are included in notebook, and that need to receive director information
318
319    panels = new wxbPanel* [2];
320    panels[0] = new wxbRestorePanel(notebook);
321    panels[1] = NULL;
322
323    for (int i = 0; panels[i] != NULL; i++) {
324       notebook->AddPage(panels[i], panels[i]->GetTitle());
325    }
326
327    wxBoxSizer* globalSizer = new wxBoxSizer(wxHORIZONTAL);
328
329 #if wxCHECK_VERSION(2, 6, 0)
330    globalSizer->Add(notebook, 1, wxEXPAND, 0);
331 #else
332    globalSizer->Add(new wxNotebookSizer(notebook), 1, wxEXPAND, 0);
333 #endif
334
335    global->SetSizer( globalSizer );
336    globalSizer->SetSizeHints( global );
337
338    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
339
340    sizer->Add(global, 1, wxEXPAND | wxALL, 0);
341    SetAutoLayout(true);
342    SetSizer( sizer );
343    sizer->SetSizeHints( this );
344    this->SetSize(size);
345    EnableConsole(false);
346    
347    consoleBuffer = wxT("");
348    
349    configfile = wxT("");
350 }
351
352 /*
353  *  Starts the thread interacting with the director
354  *  If config is not empty, uses this config file.
355  */
356 void wxbMainFrame::StartConsoleThread(const wxString& config) {
357    menuFile->Enable(MenuConnect, false);
358    menuFile->Enable(MenuDisconnect, false);
359    menuFile->Enable(ChangeConfigFile, false);
360    menuFile->Enable(EditConfigFile, false);
361
362    if (ct != NULL) {
363       ct->Delete();
364       ct = NULL;
365       wxTheApp->Yield();
366    }
367    if (promptparser == NULL) {
368       promptparser = new wxbPromptParser();      
369    }
370    
371    if (config == wxT("")) {
372       configfile = wxT("");
373       
374       if (((wxTheApp->argc % 2) != 1)) {
375          Print(_("Error while parsing command line arguments, using defaults.\n"), CS_DEBUG);
376          Print(_("Usage: bwx-console [-c configfile] [-w tmp]\n"), CS_DEBUG);
377       }
378       else {
379          for (int c = 1; c < wxTheApp->argc; c += 2) {
380             if ((wxTheApp->argc >= c+2) && (wxString(wxTheApp->argv[c]) == wxT("-c"))) {
381                configfile = wxTheApp->argv[c+1];
382             }
383             if ((wxTheApp->argc >= c+2) && (wxString(wxTheApp->argv[c]) == wxT("-w"))) {
384                console_thread::SetWorkingDirectory(wxTheApp->argv[c+1]);
385             }
386             if (wxTheApp->argv[c][0] != '-') {
387                Print(_("Error while parsing command line arguments, using defaults.\n"), CS_DEBUG);
388                Print(_("Usage: bwx-console [-c configfile] [-w tmp]\n"), CS_DEBUG);
389                break;
390             }
391          }
392       }
393       
394       if (configfile == wxT("")) {
395          wxConfig::Set(new wxConfig(wxT("bwx-console"), wxT("bacula")));
396          if (!wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile)) {
397 #ifdef HAVE_MACOSX
398             wxFileName filename(::wxGetHomeDir());
399             filename.MakeAbsolute();
400             configfile = filename.GetLongPath();
401             if (!IsPathSeparator(configfile.Last())) {
402                configfile += '/';
403             }
404             configfile += L"Library/Preferences/org.bacula.wxconsole.conf";
405 #else
406             wxFileName filename(::wxGetCwd(), wxT("bwx-console.conf"));
407             filename.MakeAbsolute();
408             configfile = filename.GetLongPath();
409 #ifdef HAVE_WIN32
410             configfile.Replace(wxT("\\"), wxT("/"));
411 #endif //HAVE_WIN32
412 #endif //HAVE_MACOSX
413             wxConfig::Get()->Write(wxT("/ConfigFile"), configfile);
414    
415             int answer = wxMessageBox(
416                               wxString::Format(_(
417                               "It seems that it is the first time you run bwx-console.\nThis file (%s) has been choosen as default configuration file.\nDo you want to edit it? (if you click No you will have to select another file)"),
418                               configfile.c_str()),
419                               _("First run"),
420                               wxYES_NO | wxICON_QUESTION, this);
421             if (answer == wxYES) {
422                wxbConfigFileEditor(this, configfile).ShowModal();
423             }
424          }
425       }
426    }
427    else {
428       configfile = config;
429    }
430    
431    wxString err = console_thread::LoadConfig(configfile);
432    
433    while (err != wxT("")) {
434       int answer = wxMessageBox(
435                         wxString::Format(_(
436                            "Unable to read %s\nError: %s\nDo you want to choose another one? (Press no to edit this file)"),
437                            configfile.c_str(), err.c_str()),
438                         _("Unable to read configuration file"),
439                         wxYES_NO | wxCANCEL | wxICON_ERROR, this);
440       if (answer == wxNO) {
441          wxbConfigFileEditor(this, configfile).ShowModal();
442          err = console_thread::LoadConfig(configfile);
443       }
444       else if (answer == wxCANCEL) {
445          frame = NULL;
446          Close(true);
447          return;
448       }
449       else { // (answer == wxYES)
450          configfile = wxFileSelector(_("Please choose a configuration file to use"));
451          if ( !configfile.empty() ) {
452             err = console_thread::LoadConfig(configfile);
453          }
454          else {
455             frame = NULL;
456             Close(true);
457             return;
458          }
459       }
460       
461       if ((err == wxT("")) && (config == wxT(""))) {
462          answer = wxMessageBox(
463                            _("This configuration file has been successfully read, use it as default?"),
464                            _("Configuration file read successfully"),
465                            wxYES_NO | wxICON_QUESTION, this);
466          if (answer == wxYES) {
467               wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
468          }
469          break;
470       }
471    }
472    
473    // former was csprint
474    Print(wxString::Format(_("Using this configuration file: %s\n"), configfile.c_str()), CS_DEBUG);
475    
476    ct = new console_thread();
477    ct->Create();
478    ct->Run();
479    SetStatusText(_("Connecting to the director..."));
480 }
481
482 /* Register a new wxbDataParser */
483 void wxbMainFrame::Register(wxbDataParser* dp) {
484    parsers.Add(dp);
485 }
486    
487 /* Unregister a wxbDataParser */
488 void wxbMainFrame::Unregister(wxbDataParser* dp) {
489    int index;
490    if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
491       parsers.RemoveAt(index);
492    }
493    else {
494       Print(_("Failed to unregister a data parser !"), CS_DEBUG);
495    }
496 }
497
498 // event handlers
499
500 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
501 {
502    Print(_("Quitting.\n"), CS_DEBUG);
503    if (ct != NULL) {
504       ct->Delete();
505       ct = NULL;
506       wxTheApp->Yield();
507    }
508    console_thread::FreeLib();
509    frame = NULL;
510    wxTheApp->Yield();
511    Close(TRUE);
512 }
513
514 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
515 {
516    wxString msg;
517    msg.Printf(_("Welcome to Bacula bwx-console.\n"
518      "Written by Nicolas Boichat <nicolas@boichat.ch>\n"
519      "Copyright (C), 2005-2007 Free Software Foundation Europe, e.V.\n"));
520
521    wxMessageBox(msg, _("About Bacula bwx-console"), wxOK | wxICON_INFORMATION, this);
522 }
523
524 void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
525    wxString oriconfigfile;
526    wxConfig::Get()->Read(wxT("/ConfigFile"), &oriconfigfile);
527    wxString configfile = wxFileSelector(_("Please choose your default configuration file"));
528    if ( !configfile.empty() ) {
529       if (oriconfigfile != configfile) {
530          int answer = wxMessageBox(
531                            _("Use this configuration file as default?"),
532                            _("Configuration file"),
533                            wxYES_NO | wxICON_QUESTION, this);
534          if (answer == wxYES) {
535               wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
536               wxConfigBase::Get()->Flush();
537               StartConsoleThread(wxT(""));
538               return;
539          }
540       }
541    
542       StartConsoleThread(configfile);
543    }
544 }
545
546 void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
547    wxString configfile;
548    wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile);
549    int stat = wxbConfigFileEditor(this, configfile).ShowModal();
550    if (stat == wxOK) {
551       StartConsoleThread(configfile);
552    }
553 }
554
555 void wxbMainFrame::OnConnect(wxCommandEvent& event) {
556    StartConsoleThread(configfile);
557 }
558
559 void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
560    if (ct != NULL) {
561       ct->Delete();
562       ct = NULL;
563    }
564 }
565
566 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
567 {
568    lockedbyconsole = true;
569    DisablePanels();
570    typeCtrl->HistoryAdd(typeCtrl->GetValue());
571    wxString str = typeCtrl->GetValue() + wxT("\n");
572    Send(str);
573 }
574
575 /*
576  *  Called when data is arriving from director
577  */
578 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
579    wxbPrintObject* po = event.GetEventPrintObject();
580
581    Print(po->str, po->status);
582 }
583
584 /*
585  *  Prints data received from director to the console, and forwards it to the panels
586  */
587 void wxbMainFrame::Print(wxString str, int status)
588 {
589    if (lockedbyconsole) {
590       EnableConsole(false);
591    }
592    
593    if (status == CS_REMOVEPROMPT) {
594       if (consoleCtrl->GetLastPosition() > 0) {
595          consoleCtrl->Remove(consoleCtrl->GetLastPosition()-1, consoleCtrl->GetLastPosition()+1);
596       }
597       return;
598    }
599    
600    if (status == CS_TERMINATED) {
601       consoleCtrl->AppendText(consoleBuffer);
602       consoleBuffer = wxT("");
603       SetStatusText(_("Console thread terminated."));
604 #ifdef HAVE_WIN32
605       consoleCtrl->PageDown();
606 #else
607       consoleCtrl->ScrollLines(1);
608 #endif
609       ct = NULL;
610       DisablePanels();
611       int answer = wxMessageBox( _("Connection to the director lost. Quit program?"), 
612                                  _("Connection lost"),
613                         wxYES_NO | wxICON_EXCLAMATION, this);
614       if (answer == wxYES) {
615          frame = NULL;
616          Close(true);
617       }
618       menuFile->Enable(MenuConnect, true);
619       menuFile->SetLabel(MenuConnect, _("Connect"));
620       menuFile->SetHelpString(MenuConnect, _("Connect to the director"));
621       menuFile->Enable(MenuDisconnect, false);
622       menuFile->Enable(ChangeConfigFile, true);
623       menuFile->Enable(EditConfigFile, true);
624       return;
625    }
626    
627    if (status == CS_CONNECTED) {
628       SetStatusText(_("Connected to the director."));
629       typeCtrl->ClearCommandList();
630       bool parsed = false;
631       int retries = 3;
632       wxbDataTokenizer* dt = wxbUtils::WaitForEnd(wxT(".help"), true);
633       while (true) {
634          int i, j;
635          wxString str;
636          for (i = 0; i < (int)dt->GetCount(); i++) {
637             str = (*dt)[i];
638             str.RemoveLast();
639             if ((j = str.Find(' ')) > -1) {
640                typeCtrl->AddCommand(str.Mid(0, j), str.Mid(j+1));
641                parsed = true;
642             }
643          }
644          retries--;
645          if ((parsed) || (!retries))
646             break;
647          dt = wxbUtils::WaitForEnd(wxT(""), true);
648       }
649       EnablePanels();
650       menuFile->Enable(MenuConnect, true);
651       menuFile->SetLabel(MenuConnect, _("Reconnect"));
652       menuFile->SetHelpString(MenuConnect, _("Reconnect to the director"));
653       menuFile->Enable(MenuDisconnect, true);
654       menuFile->Enable(ChangeConfigFile, true);
655       menuFile->Enable(EditConfigFile, true);
656       return;
657    }
658    if (status == CS_DISCONNECTED) {
659       consoleCtrl->AppendText(consoleBuffer);
660       consoleBuffer = wxT("");
661 #ifdef HAVE_WIN32
662       consoleCtrl->PageDown();
663 #else
664       consoleCtrl->ScrollLines(1);
665 #endif
666       SetStatusText(_("Disconnected of the director."));
667       DisablePanels();
668       return;
669    }
670       
671    // CS_DEBUG is often sent by panels, 
672    // and resend it to them would sometimes cause infinite loops
673    
674    /* One promptcaught is normal, so we must have two true Print values to be
675     * sure that the prompt has effectively been caught.
676     */
677    int promptcaught = -1;
678    
679    if (status != CS_DEBUG) {
680       for (unsigned int i = 0; i < parsers.GetCount(); i++) {
681          promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
682       }
683          
684       if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
685          Print(_("Unexpected question has been received.\n"), CS_DEBUG);
686 //         Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
687          
688          wxString message;
689          if (promptparser->getIntroString() != wxT("")) {
690             message << promptparser->getIntroString() << wxT("\n");
691          }
692          message << promptparser->getQuestionString();
693          
694          if (promptparser->getChoices()) {
695             wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
696             int *numbers = new int[promptparser->getChoices()->GetCount()];
697             int n = 0;
698             
699             for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
700                if ((*promptparser->getChoices())[i] != wxT("")) {
701                   choices[n] = (*promptparser->getChoices())[i];
702                   numbers[n] = i;
703                   n++;
704                }
705             }
706             
707             int res = ::wxGetSingleChoiceIndex(message,
708                _("bwx-console: unexpected director's question."), n, choices, this);
709             if (res == -1) { //Cancel pressed
710                Send(wxT(".\n"));
711             }
712             else {
713                if (promptparser->isNumericalChoice()) {
714                   Send(wxString() << numbers[res] << wxT("\n"));
715                }
716                else {
717                   Send(wxString() << choices[res] << wxT("\n"));
718                }
719             }
720             delete[] choices;
721             delete[] numbers;
722          }
723          else {
724             Send(::wxGetTextFromUser(message,
725                _("bwx-console: unexpected director's question."),
726                wxT(""), this) + wxT("\n"));
727          }
728       }
729    }
730       
731    if (status == CS_END) {
732       if (lockedbyconsole) {
733          EnablePanels();
734          lockedbyconsole = false;
735       }
736       str = wxT("#");
737    }
738
739    if (status == CS_DEBUG) {
740       consoleCtrl->AppendText(consoleBuffer);
741       consoleBuffer = wxT("");
742 #ifdef HAVE_WIN32
743       consoleCtrl->PageDown();
744 #else
745       consoleCtrl->ScrollLines(1);
746 #endif
747       consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
748    }
749    else {
750       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
751    }
752    consoleBuffer << wxbUtils::ConvertToPrintable(str);
753    if (status == CS_PROMPT) {
754       if (lockedbyconsole) {
755          EnableConsole(true);
756       }
757       //consoleBuffer << wxT("<P>");
758    }
759    
760    if ((status == CS_END) || (status == CS_PROMPT) || (str.Find(wxT("\n")) > -1)) {
761       consoleCtrl->AppendText(consoleBuffer);
762       consoleBuffer = wxT("");
763
764 #ifdef HAVE_WIN32
765       consoleCtrl->PageDown();
766 #else
767       consoleCtrl->ScrollLines(1);
768 #endif
769    }
770    
771    //consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
772    
773    /*if (status != CS_DEBUG) {
774       consoleCtrl->AppendText("@");
775    }*/
776    //consoleCtrl->SetInsertionPointEnd();
777 }
778
779 /*
780  *  Sends data to the director
781  */
782 void wxbMainFrame::Send(wxString str)
783 {
784    if (ct != NULL) {
785       /* wxString may contain everything in UNICODE
786        * -> convert to UTF-8 and send to director
787        */
788       ct->Write (str.mb_str(wxConvUTF8));
789       typeCtrl->SetValue(wxT(""));
790       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
791       consoleCtrl->AppendText(wxbUtils::ConvertToPrintable(str));      
792       //consoleCtrl->PageDown();
793    }
794    
795 /*   if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
796       nlines = consoleCtrl->GetNumberOfLines()-1;
797    }
798    
799    consoleCtrl->ShowPosition(nlines);*/
800 }
801
802 /* Enable panels */
803 void wxbMainFrame::EnablePanels() {
804    for (int i = 0; panels[i] != NULL; i++) {
805       panels[i]->EnablePanel(true);
806    }
807    EnableConsole(true);
808 }
809
810 /* Disable panels, except the one passed as parameter */
811 void wxbMainFrame::DisablePanels(void* except) {
812    for (int i = 0; panels[i] != NULL; i++) {
813       if (panels[i] != except) {
814          panels[i]->EnablePanel(false);
815       }
816       else {
817          panels[i]->EnablePanel(true);
818       }
819    }
820    if (this != except) {
821       EnableConsole(false);
822    }
823 }
824
825 /* Enable or disable console typing */
826 void wxbMainFrame::EnableConsole(bool enable) {
827    typeCtrl->Enable(enable);
828    sendButton->Enable(enable);
829    if (enable) {
830       typeCtrl->SetFocus();
831    }
832 }
833
834 /*
835  *  Used by csprint, which is called by console thread.
836  *
837  *  In GTK and perhaps X11, only the main thread is allowed to interact with
838  *  graphical components, so by firing an event, the main loop will call OnPrint.
839  *
840  *  Calling OnPrint directly from console thread produces "unexpected async replies".
841  */
842 void firePrintEvent(wxString str, int status)
843 {
844    wxbPrintObject* po = new wxbPrintObject(str, status);
845
846    wxbThreadEvent evt(Thread);
847    evt.SetEventPrintObject(po);
848    
849    if (wxbMainFrame::GetInstance()) {
850       wxbMainFrame::GetInstance()->AddPendingEvent(evt);
851    }
852 }
853
854 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
855
856 /*
857  *  Called by console thread, this function forwards data line by line and end
858  *  signals to the GUI.
859  */
860
861 void csprint(wxString str, int status)
862 {
863    firePrintEvent(str, status);  
864 }
865
866
867 void csprint(const char* str, int status)
868 {
869    if (str != 0) {
870       firePrintEvent(wxString(str,wxConvUTF8), status);      
871    }
872    else {
873       firePrintEvent(wxT(""), status);
874    }
875 }