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