]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/wx-console/wxbmainframe.cpp
- Add Date, Job, level to updates to .bsr file in
[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 ammended 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, _T("&About...\tF1"), _T("Show about dialog"));
228
229    menuFile->Append(MenuConnect, _T("Connect"), _T("Connect to the director"));
230    menuFile->Append(MenuDisconnect, _T("Disconnect"), _T("Disconnect of the director"));
231    menuFile->AppendSeparator();
232    menuFile->Append(ChangeConfigFile, _T("Change of configuration file"), _T("Change your default configuration file"));
233    menuFile->Append(EditConfigFile, _T("Edit your configuration file"), _T("Edit your configuration file"));
234    menuFile->AppendSeparator();
235    menuFile->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
236
237    // now append the freshly created menu to the menu bar...
238    wxMenuBar *menuBar = new wxMenuBar();
239    menuBar->Append(menuFile, _T("&File"));
240    menuBar->Append(helpMenu, _T("&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(wxT("Welcome to bacula wx-console ")) << wxString(wxT(VERSION)) << wxString (wxT(" (")) << wxString(wxT(BDATE)) << wxString(wxT(")!\n")));
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, wxT("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(wxT("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(wxT("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, wxT("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, wxT("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(wxT("Error while parsing command line arguments, using defaults.\n"), CS_DEBUG);
357          Print(wxT("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(wxT("Error while parsing command line arguments, using defaults.\n"), CS_DEBUG);
369                Print(wxT("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(wxT("It seems that it is the first time you run wx-console.\n")) <<
397                               wxString(wxT("This file (")) << configfile << wxString(wxT(") has been choosen as default configuration file.\n")) << 
398                               wxString(wxT("Do you want to edit it? (if you click No you will have to select another file)")),
399                               wxT("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(wxT("Unable to read ")) << configfile << wxString(wxT("\n")) << 
416                            err << wxString(wxT("\nDo you want to choose another one? (Press no to edit this file)")),
417                         wxT("Unable to read configuration file"),
418                         wxYES_NO | wxCANCEL | wxICON_ERROR, this);
419       if (answer == wxNO) {
420          wxbConfigFileEditor(this, configfile).ShowModal();
421          err = console_thread::LoadConfig(configfile);
422       }
423       else if (answer == wxCANCEL) {
424          frame = NULL;
425          Close(true);
426          return;
427       }
428       else { // (answer == wxYES)
429          configfile = wxFileSelector(wxT("Please choose a configuration file to use"));
430          if ( !configfile.empty() ) {
431             err = console_thread::LoadConfig(configfile);
432          }
433          else {
434             frame = NULL;
435             Close(true);
436             return;
437          }
438       }
439       
440       if ((err == wxT("")) && (config == wxT(""))) {
441          answer = wxMessageBox(
442                            wxT("This configuration file has been successfully read, use it as default?"),
443                            wxT("Configuration file read successfully"),
444                            wxYES_NO | wxICON_QUESTION, this);
445          if (answer == wxYES) {
446               wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
447          }
448          break;
449       }
450    }
451    
452    // former was csprint
453    Print(wxString(wxT("Using this configuration file: ")) << configfile << wxT("\n"), CS_DEBUG);
454    
455    ct = new console_thread();
456    ct->Create();
457    ct->Run();
458    SetStatusText(wxT("Connecting to the director..."));
459 }
460
461 /* Register a new wxbDataParser */
462 void wxbMainFrame::Register(wxbDataParser* dp) {
463    parsers.Add(dp);
464 }
465    
466 /* Unregister a wxbDataParser */
467 void wxbMainFrame::Unregister(wxbDataParser* dp) {
468    int index;
469    if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
470       parsers.RemoveAt(index);
471    }
472    else {
473       Print(wxT("Failed to unregister a data parser !"), CS_DEBUG);
474    }
475 }
476
477 // event handlers
478
479 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
480 {
481    Print(wxT("Quitting.\n"), CS_DEBUG);
482    if (ct != NULL) {
483       ct->Delete();
484       ct = NULL;
485       wxTheApp->Yield();
486    }
487    console_thread::FreeLib();
488    frame = NULL;
489    wxTheApp->Yield();
490    Close(TRUE);
491 }
492
493 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
494 {
495    wxString msg;
496    msg.Printf( _T("Welcome to Bacula wx-console.\nWritten by Nicolas Boichat <nicolas@boichat.ch>\n(C) 2004 Kern Sibbald and John Walker\n"));
497
498    wxMessageBox(msg, _T("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
499 }
500
501 void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
502    wxString oriconfigfile;
503    wxConfig::Get()->Read(wxT("/ConfigFile"), &oriconfigfile);
504    wxString configfile = wxFileSelector(wxT("Please choose your default configuration file"));
505    if ( !configfile.empty() ) {
506       if (oriconfigfile != configfile) {
507          int answer = wxMessageBox(
508                            wxT("Use this configuration file as default?"),
509                            wxT("Configuration file"),
510                            wxYES_NO | wxICON_QUESTION, this);
511          if (answer == wxYES) {
512               wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
513               wxConfigBase::Get()->Flush();
514               StartConsoleThread(wxT(""));
515               return;
516          }
517       }
518    
519       StartConsoleThread(configfile);
520    }
521 }
522
523 void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
524    wxString configfile;
525    wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile);
526    int stat = wxbConfigFileEditor(this, configfile).ShowModal();
527    if (stat == wxOK) {
528       StartConsoleThread(configfile);
529    }
530 }
531
532 void wxbMainFrame::OnConnect(wxCommandEvent& event) {
533    StartConsoleThread(configfile);
534 }
535
536 void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
537    if (ct != NULL) {
538       ct->Delete();
539       ct = NULL;
540    }
541 }
542
543 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
544 {
545    lockedbyconsole = true;
546    DisablePanels();
547    typeCtrl->HistoryAdd(typeCtrl->GetValue());
548    wxString str = typeCtrl->GetValue() + wxT("\n");
549    Send(str);
550 }
551
552 /*
553  *  Called when data is arriving from director
554  */
555 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
556    wxbPrintObject* po = event.GetEventPrintObject();
557
558    Print(po->str, po->status);
559 }
560
561 /*
562  *  Prints data received from director to the console, and forwards it to the panels
563  */
564 void wxbMainFrame::Print(wxString str, int status)
565 {
566    if (lockedbyconsole) {
567       EnableConsole(false);
568    }
569    
570    if (status == CS_TERMINATED) {
571       consoleCtrl->AppendText(consoleBuffer);
572       consoleBuffer = wxT("");
573       SetStatusText(wxT("Console thread terminated."));
574       consoleCtrl->PageDown();
575       ct = NULL;
576       DisablePanels();
577       int answer = wxMessageBox(wxT("Connection to the director lost. Quit program?"), wxT("Connection lost"),
578                         wxYES_NO | wxICON_EXCLAMATION, this);
579       if (answer == wxYES) {
580          frame = NULL;
581          Close(true);
582       }
583       menuFile->Enable(MenuConnect, true);
584       menuFile->SetLabel(MenuConnect, wxT("Connect"));
585       menuFile->SetHelpString(MenuConnect, wxT("Connect to the director"));
586       menuFile->Enable(MenuDisconnect, false);
587       menuFile->Enable(ChangeConfigFile, true);
588       menuFile->Enable(EditConfigFile, true);
589       return;
590    }
591    
592    if (status == CS_CONNECTED) {
593       SetStatusText(wxT("Connected to the director."));
594       typeCtrl->ClearCommandList();
595       wxbDataTokenizer* dt = wxbUtils::WaitForEnd(wxT(".help"), true);
596       int i, j;
597       wxString str;
598       for (i = 0; i < (int)dt->GetCount(); i++) {
599          str = (*dt)[i];
600          str.RemoveLast();
601          if ((j = str.Find(' ')) > -1) {
602             typeCtrl->AddCommand(str.Mid(0, j), str.Mid(j+1));
603          }
604       }
605       EnablePanels();
606       menuFile->Enable(MenuConnect, true);
607       menuFile->SetLabel(MenuConnect, wxT("Reconnect"));
608       menuFile->SetHelpString(MenuConnect, wxT("Reconnect to the director"));
609       menuFile->Enable(MenuDisconnect, true);
610       menuFile->Enable(ChangeConfigFile, true);
611       menuFile->Enable(EditConfigFile, true);
612       return;
613    }
614    if (status == CS_DISCONNECTED) {
615       consoleCtrl->AppendText(consoleBuffer);
616       consoleBuffer = wxT("");
617       consoleCtrl->PageDown();
618       SetStatusText(wxT("Disconnected of the director."));
619       DisablePanels();
620       return;
621    }
622       
623    // CS_DEBUG is often sent by panels, 
624    // and resend it to them would sometimes cause infinite loops
625    
626    /* One promptcaught is normal, so we must have two true Print values to be
627     * sure that the prompt has effectively been caught.
628     */
629    int promptcaught = -1;
630    
631    if (status != CS_DEBUG) {
632       for (unsigned int i = 0; i < parsers.GetCount(); i++) {
633          promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
634       }
635          
636       if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
637          Print(wxT("Unexpected question has been received.\n"), CS_DEBUG);
638 //         Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
639          
640          wxString message;
641          if (promptparser->getIntroString() != wxT("")) {
642             message << promptparser->getIntroString() << wxT("\n");
643          }
644          message << promptparser->getQuestionString();
645          
646          if (promptparser->getChoices()) {
647             wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
648             int *numbers = new int[promptparser->getChoices()->GetCount()];
649             int n = 0;
650             
651             for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
652                if ((*promptparser->getChoices())[i] != wxT("")) {
653                   choices[n] = (*promptparser->getChoices())[i];
654                   numbers[n] = i;
655                   n++;
656                }
657             }
658             
659             int res = ::wxGetSingleChoiceIndex(message,
660                wxT("wx-console: unexpected director's question."), n, choices, this);
661             if (res == -1) { //Cancel pressed
662                Send(wxT(".\n"));
663             }
664             else {
665                if (promptparser->isNumericalChoice()) {
666                   Send(wxString() << numbers[res] << wxT("\n"));
667                }
668                else {
669                   Send(wxString() << choices[res] << wxT("\n"));
670                }
671             }
672          }
673          else {
674             Send(::wxGetTextFromUser(message,
675                wxT("wx-console: unexpected director's question."), wxT(""), this) + wxT("\n"));
676          }
677       }
678    }
679       
680    if (status == CS_END) {
681       if (lockedbyconsole) {
682          EnablePanels();
683          lockedbyconsole = false;
684       }
685       str = wxT("#");
686    }
687
688    if (status == CS_DEBUG) {
689       consoleCtrl->AppendText(consoleBuffer);
690       consoleBuffer = wxT("");
691       consoleCtrl->PageDown();
692       consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
693    }
694    else {
695       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
696    }
697    consoleBuffer << wxbUtils::ConvertToPrintable(str);
698    if (status == CS_PROMPT) {
699       if (lockedbyconsole) {
700          EnableConsole(true);
701       }
702       //consoleBuffer << "<P>";
703    }
704    
705    if ((status == CS_END) || (status == CS_PROMPT) || (str.Find(wxT("\n")) > -1)) {
706       consoleCtrl->AppendText(consoleBuffer);
707       consoleBuffer = wxT("");
708    
709       consoleCtrl->PageDown();
710    }
711    
712    // consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
713    
714    /*if (status != CS_DEBUG) {
715       consoleCtrl->AppendText("@");
716    }*/
717    //consoleCtrl->SetInsertionPointEnd();
718    
719 /*   if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
720       nlines = consoleCtrl->GetNumberOfLines()-1;
721    }
722    
723    if (status == CS_END) {
724       consoleCtrl->ShowPosition(nlines);
725    }*/
726 }
727
728 /*
729  *  Sends data to the director
730  */
731 void wxbMainFrame::Send(wxString str)
732 {
733    if (ct != NULL) {
734       ct->Write(str.mb_str(wxConvUTF8));
735       typeCtrl->SetValue(wxT(""));
736       consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
737       consoleCtrl->AppendText(wxbUtils::ConvertToPrintable(str));      
738       consoleCtrl->PageDown();
739    }
740    
741 /*   if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
742       nlines = consoleCtrl->GetNumberOfLines()-1;
743    }
744    
745    consoleCtrl->ShowPosition(nlines);*/
746 }
747
748 /* Enable panels */
749 void wxbMainFrame::EnablePanels() {
750    for (int i = 0; panels[i] != NULL; i++) {
751       panels[i]->EnablePanel(true);
752    }
753    EnableConsole(true);
754 }
755
756 /* Disable panels, except the one passed as parameter */
757 void wxbMainFrame::DisablePanels(void* except) {
758    for (int i = 0; panels[i] != NULL; i++) {
759       if (panels[i] != except) {
760          panels[i]->EnablePanel(false);
761       }
762       else {
763          panels[i]->EnablePanel(true);
764       }
765    }
766    if (this != except) {
767       EnableConsole(false);
768    }
769 }
770
771 /* Enable or disable console typing */
772 void wxbMainFrame::EnableConsole(bool enable) {
773    typeCtrl->Enable(enable);
774    sendButton->Enable(enable);
775    if (enable) {
776       typeCtrl->SetFocus();
777    }
778 }
779
780 /*
781  *  Used by csprint, which is called by console thread.
782  *
783  *  In GTK and perhaps X11, only the main thread is allowed to interact with
784  *  graphical components, so by firing an event, the main loop will call OnPrint.
785  *
786  *  Calling OnPrint directly from console thread produces "unexpected async replies".
787  */
788 void firePrintEvent(wxString str, int status)
789 {
790    wxbPrintObject* po = new wxbPrintObject(str, status);
791
792    wxbThreadEvent evt(Thread);
793    evt.SetEventPrintObject(po);
794    
795    if (wxbMainFrame::GetInstance()) {
796       wxbMainFrame::GetInstance()->AddPendingEvent(evt);
797    }
798 }
799
800 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
801
802 /*
803  *  Called by console thread, this function forwards data line by line and end
804  *  signals to the GUI.
805  */
806
807 void csprint(wxString str, int status)
808 {
809    firePrintEvent(str, status);  
810 }
811
812
813 void csprint(const char* str, int status)
814 {
815    if (str != 0) {
816       firePrintEvent(wxString(str,wxConvUTF8), status);      
817    }
818    else {
819       firePrintEvent(wxT(""), status);
820    }
821 }