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