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