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