5 * Nicolas Boichat, July 2004
10 Copyright (C) 2004-2005 Kern Sibbald
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.
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.
24 #include "wxbmainframe.h" // class's header file
26 #include "wxbrestorepanel.h"
28 #include "wxbconfigfileeditor.h"
32 #include "wxwin16x16.xpm"
34 #include <wx/arrimpl.cpp>
36 #include <wx/stattext.h>
37 #include <wx/statline.h>
38 #include <wx/config.h>
40 #include <wx/filename.h>
42 #undef Yield /* MinGW defines Yield */
44 // ----------------------------------------------------------------------------
45 // event tables and other macros for wxWindows
46 // ----------------------------------------------------------------------------
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 // IDs for the controls and the menu commands
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,
73 * wxbTHREAD_EVENT declaration, used by csprint
75 BEGIN_DECLARE_EVENT_TYPES()
76 DECLARE_EVENT_TYPE(wxbTHREAD_EVENT, 1)
77 END_DECLARE_EVENT_TYPES()
79 DEFINE_EVENT_TYPE(wxbTHREAD_EVENT)
81 typedef void (wxEvtHandler::*wxThreadEventFunction)(wxbThreadEvent&);
83 #define EVT_THREAD_EVENT(id, fn) \
84 DECLARE_EVENT_TABLE_ENTRY( \
85 wxbTHREAD_EVENT, id, wxID_ANY, \
86 (wxObjectEventFunction)(wxEventFunction)(wxThreadEventFunction)&fn, \
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)
105 // ----------------------------------------------------------------------------
107 // ----------------------------------------------------------------------------
110 * wxbThreadEvent constructor
112 wxbThreadEvent::wxbThreadEvent(int id): wxEvent(id, wxbTHREAD_EVENT) {
113 m_eventObject = NULL;
117 * wxbThreadEvent destructor
119 wxbThreadEvent::~wxbThreadEvent()
121 if (m_eventObject != NULL) {
122 delete m_eventObject;
127 * wxbThreadEvent copy constructor
129 wxbThreadEvent::wxbThreadEvent(const wxbThreadEvent& te)
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));
137 this->m_eventObject = NULL;
139 this->m_skipped = te.m_skipped;
140 this->m_timeStamp = te.m_timeStamp;
144 * Must be implemented (abstract in wxEvent)
146 wxEvent* wxbThreadEvent::Clone() const
148 return new wxbThreadEvent(*this);
152 * Gets the wxbPrintObject attached to this event, containing data sent by director
154 wxbPrintObject* wxbThreadEvent::GetEventPrintObject()
156 return (wxbPrintObject*)m_eventObject;
160 * Sets the wxbPrintObject attached to this event
162 void wxbThreadEvent::SetEventPrintObject(wxbPrintObject* object)
164 m_eventObject = (wxObject*)object;
167 // ----------------------------------------------------------------------------
169 // ----------------------------------------------------------------------------
171 wxbMainFrame *wxbMainFrame::frame = NULL;
174 * Singleton constructor
176 wxbMainFrame* wxbMainFrame::CreateInstance(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
178 frame = new wxbMainFrame(title, pos, size, style);
183 * Returns singleton instance
185 wxbMainFrame* wxbMainFrame::GetInstance()
193 wxbMainFrame::~wxbMainFrame()
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());
200 if (ct != NULL) { // && (!ct->IsRunning())
207 * Private constructor
209 wxbMainFrame::wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
210 : wxFrame(NULL, -1, title, pos, size, style)
212 lockedbyconsole = false;
218 // set the frame icon
219 SetIcon(wxIcon(wxwin16x16_xpm));
223 menuFile = new wxMenu;
225 // the "About" item should be in the help menu
226 wxMenu *helpMenu = new wxMenu;
227 helpMenu->Append(Minimal_About, _("&About...\tF1"), _("Show about dialog"));
229 menuFile->Append(MenuConnect, _("Connect"), _("Connect to the director"));
230 menuFile->Append(MenuDisconnect, _("Disconnect"), _("Disconnect of the director"));
231 menuFile->AppendSeparator();
232 menuFile->Append(ChangeConfigFile, _("Change of configuration file"), _("Change your default configuration file"));
233 menuFile->Append(EditConfigFile, _("Edit your configuration file"), _("Edit your configuration file"));
234 menuFile->AppendSeparator();
235 menuFile->Append(Minimal_Quit, _("E&xit\tAlt-X"), _("Quit this program"));
237 // now append the freshly created menu to the menu bar...
238 wxMenuBar *menuBar = new wxMenuBar();
239 menuBar->Append(menuFile, _("&File"));
240 menuBar->Append(helpMenu, _("&Help"));
242 // ... and attach this menu bar to the frame
244 #endif // wxUSE_MENUS
248 SetStatusText(wxString::Format(_("Welcome to bacula wx-console %s (%s)!\n"), wxT(VERSION), wxT(BDATE)));
250 wxPanel* global = new wxPanel(this, -1);
252 notebook = new wxNotebook(global, -1);
256 wxPanel* consolePanel = new wxPanel(notebook, -1);
257 notebook->AddPage(consolePanel, _("Console"));
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(_("Warning : Unicode is disabled because you are using wxWidgets for GTK+ 1.2.\n"), CS_DEBUG);
266 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
267 #if (wxUSE_UNICODE == 0) && __WXGTK20__
268 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);
272 helpCtrl = new wxStaticText(consolePanel, -1, _("Type your command below:"));
274 wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(4, 1, 0, 0);
275 consoleSizer->AddGrowableCol(0);
276 consoleSizer->AddGrowableRow(0);
278 typeCtrl = new wxbHistoryTextCtrl(helpCtrl, consolePanel,TypeText,wxT(""),wxDefaultPosition,wxSize(200,20));
279 sendButton = new wxButton(consolePanel, SendButton, _("Send"));
281 wxFlexGridSizer *typeSizer = new wxFlexGridSizer(1, 2, 0, 0);
282 typeSizer->AddGrowableCol(0);
283 typeSizer->AddGrowableRow(0);
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);
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);
294 consolePanel->SetAutoLayout( TRUE );
295 consolePanel->SetSizer( consoleSizer );
296 consoleSizer->SetSizeHints( consolePanel );
298 // Creates the list of panels which are included in notebook, and that need to receive director information
300 panels = new wxbPanel* [2];
301 panels[0] = new wxbRestorePanel(notebook);
304 for (int i = 0; panels[i] != NULL; i++) {
305 notebook->AddPage(panels[i], panels[i]->GetTitle());
308 wxBoxSizer* globalSizer = new wxBoxSizer(wxHORIZONTAL);
310 #if wxCHECK_VERSION(2, 6, 0)
311 globalSizer->Add(notebook, 1, wxEXPAND, 0);
313 globalSizer->Add(new wxNotebookSizer(notebook), 1, wxEXPAND, 0);
316 global->SetSizer( globalSizer );
317 globalSizer->SetSizeHints( global );
319 wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
321 sizer->Add(global, 1, wxEXPAND | wxALL, 0);
324 sizer->SetSizeHints( this );
326 EnableConsole(false);
328 consoleBuffer = wxT("");
330 configfile = wxT("");
334 * Starts the thread interacting with the director
335 * If config is not empty, uses this config file.
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);
348 if (promptparser == NULL) {
349 promptparser = new wxbPromptParser();
352 if (config == wxT("")) {
353 configfile = wxT("");
355 if (((wxTheApp->argc % 2) != 1)) {
356 Print(_("Error while parsing command line arguments, using defaults.\n"), CS_DEBUG);
357 Print(_("Usage: wx-console [-c configfile] [-w tmp]\n"), CS_DEBUG);
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];
364 if ((wxTheApp->argc >= c+2) && (wxString(wxTheApp->argv[c]) == wxT("-w"))) {
365 console_thread::SetWorkingDirectory(wxTheApp->argv[c+1]);
367 if (wxTheApp->argv[c][0] != '-') {
368 Print(_("Error while parsing command line arguments, using defaults.\n"), CS_DEBUG);
369 Print(_("Usage: wx-console [-c configfile] [-w tmp]\n"), CS_DEBUG);
375 if (configfile == wxT("")) {
376 wxConfig::Set(new wxConfig(wxT("wx-console"), wxT("bacula")));
377 if (!wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile)) {
379 wxFileName filename(::wxGetHomeDir());
380 filename.MakeAbsolute();
381 configfile = filename.GetLongPath();
382 if (configfile.Last() != '/')
384 configfile += "Library/Preferences/org.bacula.wxconsole.conf";
386 wxFileName filename(::wxGetCwd(), wxT("wx-console.conf"));
387 filename.MakeAbsolute();
388 configfile = filename.GetLongPath();
390 configfile.Replace(wxT("\\"), wxT("/"));
393 wxConfig::Get()->Write(wxT("/ConfigFile"), configfile);
395 int answer = wxMessageBox(
397 "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)"),
400 wxYES_NO | wxICON_QUESTION, this);
401 if (answer == wxYES) {
402 wxbConfigFileEditor(this, configfile).ShowModal();
411 wxString err = console_thread::LoadConfig(configfile);
413 while (err != wxT("")) {
414 int answer = wxMessageBox(
416 "Unable to read %s\nError: %s\nDo you want to choose another one? (Press no to edit this file)"),
417 configfile.c_str(), err.c_str()),
418 _("Unable to read configuration file"),
419 wxYES_NO | wxCANCEL | wxICON_ERROR, this);
420 if (answer == wxNO) {
421 wxbConfigFileEditor(this, configfile).ShowModal();
422 err = console_thread::LoadConfig(configfile);
424 else if (answer == wxCANCEL) {
429 else { // (answer == wxYES)
430 configfile = wxFileSelector(_("Please choose a configuration file to use"));
431 if ( !configfile.empty() ) {
432 err = console_thread::LoadConfig(configfile);
441 if ((err == wxT("")) && (config == wxT(""))) {
442 answer = wxMessageBox(
443 _("This configuration file has been successfully read, use it as default?"),
444 _("Configuration file read successfully"),
445 wxYES_NO | wxICON_QUESTION, this);
446 if (answer == wxYES) {
447 wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
453 // former was csprint
454 Print(wxString::Format(_("Using this configuration file: %s\n"), configfile.c_str()), CS_DEBUG);
456 ct = new console_thread();
459 SetStatusText(_("Connecting to the director..."));
462 /* Register a new wxbDataParser */
463 void wxbMainFrame::Register(wxbDataParser* dp) {
467 /* Unregister a wxbDataParser */
468 void wxbMainFrame::Unregister(wxbDataParser* dp) {
470 if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
471 parsers.RemoveAt(index);
474 Print(_("Failed to unregister a data parser !"), CS_DEBUG);
480 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
482 Print(_("Quitting.\n"), CS_DEBUG);
488 console_thread::FreeLib();
494 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
497 msg.Printf(_("Welcome to Bacula wx-console.\nWritten by Nicolas Boichat <nicolas@boichat.ch>\n(C) 2005 Kern Sibbald\n"));
499 wxMessageBox(msg, _("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
502 void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
503 wxString oriconfigfile;
504 wxConfig::Get()->Read(wxT("/ConfigFile"), &oriconfigfile);
505 wxString configfile = wxFileSelector(_("Please choose your default configuration file"));
506 if ( !configfile.empty() ) {
507 if (oriconfigfile != configfile) {
508 int answer = wxMessageBox(
509 _("Use this configuration file as default?"),
510 _("Configuration file"),
511 wxYES_NO | wxICON_QUESTION, this);
512 if (answer == wxYES) {
513 wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
514 wxConfigBase::Get()->Flush();
515 StartConsoleThread(wxT(""));
520 StartConsoleThread(configfile);
524 void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
526 wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile);
527 int stat = wxbConfigFileEditor(this, configfile).ShowModal();
529 StartConsoleThread(configfile);
533 void wxbMainFrame::OnConnect(wxCommandEvent& event) {
534 StartConsoleThread(configfile);
537 void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
544 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
546 lockedbyconsole = true;
548 typeCtrl->HistoryAdd(typeCtrl->GetValue());
549 wxString str = typeCtrl->GetValue() + wxT("\n");
554 * Called when data is arriving from director
556 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
557 wxbPrintObject* po = event.GetEventPrintObject();
559 Print(po->str, po->status);
563 * Prints data received from director to the console, and forwards it to the panels
565 void wxbMainFrame::Print(wxString str, int status)
567 if (lockedbyconsole) {
568 EnableConsole(false);
571 if (status == CS_TERMINATED) {
572 consoleCtrl->AppendText(consoleBuffer);
573 consoleBuffer = wxT("");
574 SetStatusText(_("Console thread terminated."));
576 consoleCtrl->PageDown();
578 consoleCtrl->ScrollLines(1);
582 int answer = wxMessageBox( _("Connection to the director lost. Quit program?"),
583 _("Connection lost"),
584 wxYES_NO | wxICON_EXCLAMATION, this);
585 if (answer == wxYES) {
589 menuFile->Enable(MenuConnect, true);
590 menuFile->SetLabel(MenuConnect, _("Connect"));
591 menuFile->SetHelpString(MenuConnect, _("Connect to the director"));
592 menuFile->Enable(MenuDisconnect, false);
593 menuFile->Enable(ChangeConfigFile, true);
594 menuFile->Enable(EditConfigFile, true);
598 if (status == CS_CONNECTED) {
599 SetStatusText(_("Connected to the director."));
600 typeCtrl->ClearCommandList();
601 wxbDataTokenizer* dt = wxbUtils::WaitForEnd(wxT(".help"), true);
604 for (i = 0; i < (int)dt->GetCount(); i++) {
607 if ((j = str.Find(' ')) > -1) {
608 typeCtrl->AddCommand(str.Mid(0, j), str.Mid(j+1));
612 menuFile->Enable(MenuConnect, true);
613 menuFile->SetLabel(MenuConnect, _("Reconnect"));
614 menuFile->SetHelpString(MenuConnect, _("Reconnect to the director"));
615 menuFile->Enable(MenuDisconnect, true);
616 menuFile->Enable(ChangeConfigFile, true);
617 menuFile->Enable(EditConfigFile, true);
620 if (status == CS_DISCONNECTED) {
621 consoleCtrl->AppendText(consoleBuffer);
622 consoleBuffer = wxT("");
624 consoleCtrl->PageDown();
626 consoleCtrl->ScrollLines(1);
628 SetStatusText(_("Disconnected of the director."));
633 // CS_DEBUG is often sent by panels,
634 // and resend it to them would sometimes cause infinite loops
636 /* One promptcaught is normal, so we must have two true Print values to be
637 * sure that the prompt has effectively been caught.
639 int promptcaught = -1;
641 if (status != CS_DEBUG) {
642 for (unsigned int i = 0; i < parsers.GetCount(); i++) {
643 promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
646 if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
647 Print(_("Unexpected question has been received.\n"), CS_DEBUG);
648 // Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
651 if (promptparser->getIntroString() != wxT("")) {
652 message << promptparser->getIntroString() << wxT("\n");
654 message << promptparser->getQuestionString();
656 if (promptparser->getChoices()) {
657 wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
658 int *numbers = new int[promptparser->getChoices()->GetCount()];
661 for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
662 if ((*promptparser->getChoices())[i] != wxT("")) {
663 choices[n] = (*promptparser->getChoices())[i];
669 int res = ::wxGetSingleChoiceIndex(message,
670 _("wx-console: unexpected director's question."), n, choices, this);
671 if (res == -1) { //Cancel pressed
675 if (promptparser->isNumericalChoice()) {
676 Send(wxString() << numbers[res] << wxT("\n"));
679 Send(wxString() << choices[res] << wxT("\n"));
684 Send(::wxGetTextFromUser(message,
685 _("wx-console: unexpected director's question."),
686 wxT(""), this) + wxT("\n"));
691 if (status == CS_END) {
692 if (lockedbyconsole) {
694 lockedbyconsole = false;
699 if (status == CS_DEBUG) {
700 consoleCtrl->AppendText(consoleBuffer);
701 consoleBuffer = wxT("");
703 consoleCtrl->PageDown();
705 consoleCtrl->ScrollLines(1);
707 consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
710 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
712 consoleBuffer << wxbUtils::ConvertToPrintable(str);
713 if (status == CS_PROMPT) {
714 if (lockedbyconsole) {
717 //consoleBuffer << "<P>";
720 if ((status == CS_END) || (status == CS_PROMPT) || (str.Find(wxT("\n")) > -1)) {
721 consoleCtrl->AppendText(consoleBuffer);
722 consoleBuffer = wxT("");
725 consoleCtrl->PageDown();
727 consoleCtrl->ScrollLines(1);
731 //consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
733 /*if (status != CS_DEBUG) {
734 consoleCtrl->AppendText("@");
736 //consoleCtrl->SetInsertionPointEnd();
740 * Sends data to the director
742 void wxbMainFrame::Send(wxString str)
745 ct->Write(str.mb_str(wxConvUTF8));
746 typeCtrl->SetValue(wxT(""));
747 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
748 consoleCtrl->AppendText(wxbUtils::ConvertToPrintable(str));
749 //consoleCtrl->PageDown();
752 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
753 nlines = consoleCtrl->GetNumberOfLines()-1;
756 consoleCtrl->ShowPosition(nlines);*/
760 void wxbMainFrame::EnablePanels() {
761 for (int i = 0; panels[i] != NULL; i++) {
762 panels[i]->EnablePanel(true);
767 /* Disable panels, except the one passed as parameter */
768 void wxbMainFrame::DisablePanels(void* except) {
769 for (int i = 0; panels[i] != NULL; i++) {
770 if (panels[i] != except) {
771 panels[i]->EnablePanel(false);
774 panels[i]->EnablePanel(true);
777 if (this != except) {
778 EnableConsole(false);
782 /* Enable or disable console typing */
783 void wxbMainFrame::EnableConsole(bool enable) {
784 typeCtrl->Enable(enable);
785 sendButton->Enable(enable);
787 typeCtrl->SetFocus();
792 * Used by csprint, which is called by console thread.
794 * In GTK and perhaps X11, only the main thread is allowed to interact with
795 * graphical components, so by firing an event, the main loop will call OnPrint.
797 * Calling OnPrint directly from console thread produces "unexpected async replies".
799 void firePrintEvent(wxString str, int status)
801 wxbPrintObject* po = new wxbPrintObject(str, status);
803 wxbThreadEvent evt(Thread);
804 evt.SetEventPrintObject(po);
806 if (wxbMainFrame::GetInstance()) {
807 wxbMainFrame::GetInstance()->AddPendingEvent(evt);
811 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
814 * Called by console thread, this function forwards data line by line and end
815 * signals to the GUI.
818 void csprint(wxString str, int status)
820 firePrintEvent(str, status);
824 void csprint(const char* str, int status)
827 firePrintEvent(wxString(str,wxConvUTF8), status);
830 firePrintEvent(wxT(""), status);