]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/wxbmainframe.cpp
Add smartctl call to bacula-sd.conf as an example of getting
[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 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  *   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 wx-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 wx-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: wx-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: wx-console [-c configfile] [-w tmp]\n"), CS_DEBUG);
389                break;
390             }
391          }
392       }
393       
394       if (configfile == wxT("")) {
395          wxConfig::Set(new wxConfig(wxT("wx-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             configfile += "Library/Preferences/org.bacula.wxconsole.conf";
404 #else
405             wxFileName filename(::wxGetCwd(), wxT("wx-console.conf"));
406             filename.MakeAbsolute();
407             configfile = filename.GetLongPath();
408 #ifdef HAVE_WIN32
409             configfile.Replace(wxT("\\"), wxT("/"));
410 #endif //HAVE_WIN32
411 #endif //HAVE_MACOSX
412             wxConfig::Get()->Write(wxT("/ConfigFile"), configfile);
413    
414             int answer = wxMessageBox(
415                               wxString::Format(_(
416                               "It seems that it is the first time you run wx-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)"),
417                               configfile.c_str()),
418                               _("First run"),
419                               wxYES_NO | wxICON_QUESTION, this);
420             if (answer == wxYES) {
421                wxbConfigFileEditor(this, configfile).ShowModal();
422             }
423          }
424       }
425    }
426    else {
427       configfile = config;
428    }
429    
430    wxString err = console_thread::LoadConfig(configfile);
431    
432    while (err != wxT("")) {
433       int answer = wxMessageBox(
434                         wxString::Format(_(
435                            "Unable to read %s\nError: %s\nDo you want to choose another one? (Press no to edit this file)"),
436                            configfile.c_str(), err.c_str()),
437                         _("Unable to read configuration file"),
438                         wxYES_NO | wxCANCEL | wxICON_ERROR, this);
439       if (answer == wxNO) {
440          wxbConfigFileEditor(this, configfile).ShowModal();
441          err = console_thread::LoadConfig(configfile);
442       }
443       else if (answer == wxCANCEL) {
444          frame = NULL;
445          Close(true);
446          return;
447       }
448       else { // (answer == wxYES)
449          configfile = wxFileSelector(_("Please choose a configuration file to use"));
450          if ( !configfile.empty() ) {
451             err = console_thread::LoadConfig(configfile);
452          }
453          else {
454             frame = NULL;
455             Close(true);
456             return;
457          }
458       }
459       
460       if ((err == wxT("")) && (config == wxT(""))) {
461          answer = wxMessageBox(
462                            _("This configuration file has been successfully read, use it as default?"),
463                            _("Configuration file read successfully"),
464                            wxYES_NO | wxICON_QUESTION, this);
465          if (answer == wxYES) {
466               wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
467          }
468          break;
469       }
470    }
471    
472    // former was csprint
473    Print(wxString::Format(_("Using this configuration file: %s\n"), configfile.c_str()), CS_DEBUG);
474    
475    ct = new console_thread();
476    ct->Create();
477    ct->Run();
478    SetStatusText(_("Connecting to the director..."));
479 }
480
481 /* Register a new wxbDataParser */
482 void wxbMainFrame::Register(wxbDataParser* dp) {
483    parsers.Add(dp);
484 }
485    
486 /* Unregister a wxbDataParser */
487 void wxbMainFrame::Unregister(wxbDataParser* dp) {
488    int index;
489    if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
490       parsers.RemoveAt(index);
491    }
492    else {
493       Print(_("Failed to unregister a data parser !"), CS_DEBUG);
494    }
495 }
496
497 // event handlers
498
499 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
500 {
501    Print(_("Quitting.\n"), CS_DEBUG);
502    if (ct != NULL) {
503       ct->Delete();
504       ct = NULL;
505       wxTheApp->Yield();
506    }
507    console_thread::FreeLib();
508    frame = NULL;
509    wxTheApp->Yield();
510    Close(TRUE);
511 }
512
513 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
514 {
515    wxString msg;
516    msg.Printf(_("Welcome to Bacula wx-console.\n"
517      "Written by Nicolas Boichat <nicolas@boichat.ch>\n"
518      "Copyright (C), 2005-2007 Free Software Foundation Europe, e.V.\n"));
519
520    wxMessageBox(msg, _("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
521 }
522
523 void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
524    wxString oriconfigfile;
525    wxConfig::Get()->Read(wxT("/ConfigFile"), &oriconfigfile);
526    wxString configfile = wxFileSelector(_("Please choose your default configuration file"));
527    if ( !configfile.empty() ) {
528       if (oriconfigfile != configfile) {
529          int answer = wxMessageBox(
530                            _("Use this configuration file as default?"),
531                            _("Configuration file"),
532                            wxYES_NO | wxICON_QUESTION, this);
533          if (answer == wxYES) {
534               wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
535               wxConfigBase::Get()->Flush();
536               StartConsoleThread(wxT(""));
537               return;
538          }
539       }
540    
541       StartConsoleThread(configfile);
542    }
543 }
544
545 void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
546    wxString configfile;
547    wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile);
548    int stat = wxbConfigFileEditor(this, configfile).ShowModal();
549    if (stat == wxOK) {
550       StartConsoleThread(configfile);
551    }
552 }
553
554 void wxbMainFrame::OnConnect(wxCommandEvent& event) {
555    StartConsoleThread(configfile);
556 }
557
558 void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
559    if (ct != NULL) {
560       ct->Delete();
561       ct = NULL;
562    }
563 }
564
565 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
566 {
567    lockedbyconsole = true;
568    DisablePanels();
569    typeCtrl->HistoryAdd(typeCtrl->GetValue());
570    wxString str = typeCtrl->GetValue() + wxT("\n");
571    Send(str);
572 }
573
574 /*
575  *  Called when data is arriving from director
576  */
577 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
578    wxbPrintObject* po = event.GetEventPrintObject();
579
580    Print(po->str, po->status);
581 }
582
583 /*
584  *  Prints data received from director to the console, and forwards it to the panels
585  */
586 void wxbMainFrame::Print(wxString str, int status)
587 {
588    if (lockedbyconsole) {
589       EnableConsole(false);
590    }
591    
592    if (status == CS_REMOVEPROMPT) {
593       if (consoleCtrl->GetLastPosition() > 0) {
594          consoleCtrl->Remove(consoleCtrl->GetLastPosition()-1, consoleCtrl->GetLastPosition()+1);
595       }
596       return;
597    }
598    
599    if (status == CS_TERMINATED) {
600       consoleCtrl->AppendText(consoleBuffer);
601       consoleBuffer = wxT("");
602       SetStatusText(_("Console thread terminated."));
603 #ifdef HAVE_WIN32
604       consoleCtrl->PageDown();
605 #else
606       consoleCtrl->ScrollLines(1);
607 #endif
608       ct = NULL;
609       DisablePanels();
610       int answer = wxMessageBox( _("Connection to the director lost. Quit program?"), 
611                                  _("Connection lost"),
612                         wxYES_NO | wxICON_EXCLAMATION, this);
613       if (answer == wxYES) {
614          frame = NULL;
615          Close(true);
616       }
617       menuFile->Enable(MenuConnect, true);
618       menuFile->SetLabel(MenuConnect, _("Connect"));
619       menuFile->SetHelpString(MenuConnect, _("Connect to the director"));
620       menuFile->Enable(MenuDisconnect, false);
621       menuFile->Enable(ChangeConfigFile, true);
622       menuFile->Enable(EditConfigFile, true);
623       return;
624    }
625    
626    if (status == CS_CONNECTED) {
627       SetStatusText(_("Connected to the director."));
628       typeCtrl->ClearCommandList();
629       bool parsed = false;
630       int retries = 3;
631       wxbDataTokenizer* dt = wxbUtils::WaitForEnd(wxT(".help"), true);
632       while (true) {
633          int i, j;
634          wxString str;
635          for (i = 0; i < (int)dt->GetCount(); i++) {
636             str = (*dt)[i];
637             str.RemoveLast();
638             if ((j = str.Find(' ')) > -1) {
639                typeCtrl->AddCommand(str.Mid(0, j), str.Mid(j+1));
640                parsed = true;
641             }
642          }
643          retries--;
644          if ((parsed) || (!retries))
645             break;
646          dt = wxbUtils::WaitForEnd(wxT(""), true);
647       }
648       EnablePanels();
649       menuFile->Enable(MenuConnect, true);
650       menuFile->SetLabel(MenuConnect, _("Reconnect"));
651       menuFile->SetHelpString(MenuConnect, _("Reconnect to the director"));
652       menuFile->Enable(MenuDisconnect, true);
653       menuFile->Enable(ChangeConfigFile, true);
654       menuFile->Enable(EditConfigFile, true);
655       return;
656    }
657    if (status == CS_DISCONNECTED) {
658       consoleCtrl->AppendText(consoleBuffer);
659       consoleBuffer = wxT("");
660 #ifdef HAVE_WIN32
661       consoleCtrl->PageDown();
662 #else
663       consoleCtrl->ScrollLines(1);
664 #endif
665       SetStatusText(_("Disconnected of the director."));
666       DisablePanels();
667       return;
668    }
669       
670    // CS_DEBUG is often sent by panels, 
671    // and resend it to them would sometimes cause infinite loops
672    
673    /* One promptcaught is normal, so we must have two true Print values to be
674     * sure that the prompt has effectively been caught.
675     */
676    int promptcaught = -1;
677    
678    if (status != CS_DEBUG) {
679       for (unsigned int i = 0; i < parsers.GetCount(); i++) {
680          promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
681       }
682          
683       if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
684          Print(_("Unexpected question has been received.\n"), CS_DEBUG);
685 //         Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
686          
687          wxString message;
688          if (promptparser->getIntroString() != wxT("")) {
689             message << promptparser->getIntroString() << wxT("\n");
690          }
691          message << promptparser->getQuestionString();
692          
693          if (promptparser->getChoices()) {
694             wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
695             int *numbers = new int[promptparser->getChoices()->GetCount()];
696             int n = 0;
697             
698             for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
699                if ((*promptparser->getChoices())[i] != wxT("")) {
700                   choices[n] = (*promptparser->getChoices())[i];
701                   numbers[n] = i;
702                   n++;
703                }
704             }
705             
706             int res = ::wxGetSingleChoiceIndex(message,
707                _("wx-console: unexpected director's question."), n, choices, this);
708             if (res == -1) { //Cancel pressed
709                Send(wxT(".\n"));
710             }
711             else {
712                if (promptparser->isNumericalChoice()) {
713                   Send(wxString() << numbers[res] << wxT("\n"));
714                }
715                else {
716                   Send(wxString() << choices[res] << wxT("\n"));
717                }
718             }
719             delete[] choices;
720             delete[] numbers;
721          }
722          else {
723             Send(::wxGetTextFromUser(message,
724                _("wx-console: unexpected director's question."),
725                wxT(""), this) + wxT("\n"));
726          }
727       }
728    }
729       
730    if (status == CS_END) {
731       if (lockedbyconsole) {
732          EnablePanels();
733          lockedbyconsole = false;
734       }
735       str = wxT("#");
736    }
737
738    if (status == CS_DEBUG) {
739       consoleCtrl->AppendText(consoleBuffer);
740       consoleBuffer = wxT("");
741 #ifdef HAVE_WIN32
742       consoleCtrl->PageDown();
743 #else
744       consoleCtrl->ScrollLines(1);
745 #endif
746       consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
747    }
748    else {
749       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
750    }
751    consoleBuffer << wxbUtils::ConvertToPrintable(str);
752    if (status == CS_PROMPT) {
753       if (lockedbyconsole) {
754          EnableConsole(true);
755       }
756       //consoleBuffer << wxT("<P>");
757    }
758    
759    if ((status == CS_END) || (status == CS_PROMPT) || (str.Find(wxT("\n")) > -1)) {
760       consoleCtrl->AppendText(consoleBuffer);
761       consoleBuffer = wxT("");
762
763 #ifdef HAVE_WIN32
764       consoleCtrl->PageDown();
765 #else
766       consoleCtrl->ScrollLines(1);
767 #endif
768    }
769    
770    //consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
771    
772    /*if (status != CS_DEBUG) {
773       consoleCtrl->AppendText("@");
774    }*/
775    //consoleCtrl->SetInsertionPointEnd();
776 }
777
778 /*
779  *  Sends data to the director
780  */
781 void wxbMainFrame::Send(wxString str)
782 {
783    if (ct != NULL) {
784       /* wxString may contain everything in UNICODE
785        * -> convert to UTF-8 and send to director
786        */
787       ct->Write (str.mb_str(wxConvUTF8));
788       typeCtrl->SetValue(wxT(""));
789       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
790       consoleCtrl->AppendText(wxbUtils::ConvertToPrintable(str));      
791       //consoleCtrl->PageDown();
792    }
793    
794 /*   if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
795       nlines = consoleCtrl->GetNumberOfLines()-1;
796    }
797    
798    consoleCtrl->ShowPosition(nlines);*/
799 }
800
801 /* Enable panels */
802 void wxbMainFrame::EnablePanels() {
803    for (int i = 0; panels[i] != NULL; i++) {
804       panels[i]->EnablePanel(true);
805    }
806    EnableConsole(true);
807 }
808
809 /* Disable panels, except the one passed as parameter */
810 void wxbMainFrame::DisablePanels(void* except) {
811    for (int i = 0; panels[i] != NULL; i++) {
812       if (panels[i] != except) {
813          panels[i]->EnablePanel(false);
814       }
815       else {
816          panels[i]->EnablePanel(true);
817       }
818    }
819    if (this != except) {
820       EnableConsole(false);
821    }
822 }
823
824 /* Enable or disable console typing */
825 void wxbMainFrame::EnableConsole(bool enable) {
826    typeCtrl->Enable(enable);
827    sendButton->Enable(enable);
828    if (enable) {
829       typeCtrl->SetFocus();
830    }
831 }
832
833 /*
834  *  Used by csprint, which is called by console thread.
835  *
836  *  In GTK and perhaps X11, only the main thread is allowed to interact with
837  *  graphical components, so by firing an event, the main loop will call OnPrint.
838  *
839  *  Calling OnPrint directly from console thread produces "unexpected async replies".
840  */
841 void firePrintEvent(wxString str, int status)
842 {
843    wxbPrintObject* po = new wxbPrintObject(str, status);
844
845    wxbThreadEvent evt(Thread);
846    evt.SetEventPrintObject(po);
847    
848    if (wxbMainFrame::GetInstance()) {
849       wxbMainFrame::GetInstance()->AddPendingEvent(evt);
850    }
851 }
852
853 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
854
855 /*
856  *  Called by console thread, this function forwards data line by line and end
857  *  signals to the GUI.
858  */
859
860 void csprint(wxString str, int status)
861 {
862    firePrintEvent(str, status);  
863 }
864
865
866 void csprint(const char* str, int status)
867 {
868    if (str != 0) {
869       firePrintEvent(wxString(str,wxConvUTF8), status);      
870    }
871    else {
872       firePrintEvent(wxT(""), status);
873    }
874 }