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