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