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