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.\n"
398 "This file (%s) has been choosen as default configuration file.\n"
399 "Do you want to edit it? (if you click No you will have to select another file)"),
402 wxYES_NO | wxICON_QUESTION, this);
403 if (answer == wxYES) {
404 wxbConfigFileEditor(this, configfile).ShowModal();
413 wxString err = console_thread::LoadConfig(configfile);
415 while (err != wxT("")) {
416 int answer = wxMessageBox(
418 "Unable to read %s\n"
420 "Do you want to choose another one? (Press no to edit this file)"),
421 configfile.c_str(), err.c_str()),
422 _("Unable to read configuration file"),
423 wxYES_NO | wxCANCEL | wxICON_ERROR, this);
424 if (answer == wxNO) {
425 wxbConfigFileEditor(this, configfile).ShowModal();
426 err = console_thread::LoadConfig(configfile);
428 else if (answer == wxCANCEL) {
433 else { // (answer == wxYES)
434 configfile = wxFileSelector(_("Please choose a configuration file to use"));
435 if ( !configfile.empty() ) {
436 err = console_thread::LoadConfig(configfile);
445 if ((err == wxT("")) && (config == wxT(""))) {
446 answer = wxMessageBox(
447 _("This configuration file has been successfully read, use it as default?"),
448 _("Configuration file read successfully"),
449 wxYES_NO | wxICON_QUESTION, this);
450 if (answer == wxYES) {
451 wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
457 // former was csprint
458 Print(wxString::Format(_("Using this configuration file: %s\n"), configfile.c_str()), CS_DEBUG);
460 ct = new console_thread();
463 SetStatusText(_("Connecting to the director..."));
466 /* Register a new wxbDataParser */
467 void wxbMainFrame::Register(wxbDataParser* dp) {
471 /* Unregister a wxbDataParser */
472 void wxbMainFrame::Unregister(wxbDataParser* dp) {
474 if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
475 parsers.RemoveAt(index);
478 Print(_("Failed to unregister a data parser !"), CS_DEBUG);
484 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
486 Print(_("Quitting.\n"), CS_DEBUG);
492 console_thread::FreeLib();
498 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
501 msg.Printf(_("Welcome to Bacula wx-console.\nWritten by Nicolas Boichat <nicolas@boichat.ch>\n(C) 2005 Kern Sibbald\n"));
503 wxMessageBox(msg, _("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
506 void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
507 wxString oriconfigfile;
508 wxConfig::Get()->Read(wxT("/ConfigFile"), &oriconfigfile);
509 wxString configfile = wxFileSelector(_("Please choose your default configuration file"));
510 if ( !configfile.empty() ) {
511 if (oriconfigfile != configfile) {
512 int answer = wxMessageBox(
513 _("Use this configuration file as default?"),
514 _("Configuration file"),
515 wxYES_NO | wxICON_QUESTION, this);
516 if (answer == wxYES) {
517 wxConfigBase::Get()->Write(wxT("/ConfigFile"), configfile);
518 wxConfigBase::Get()->Flush();
519 StartConsoleThread(wxT(""));
524 StartConsoleThread(configfile);
528 void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
530 wxConfig::Get()->Read(wxT("/ConfigFile"), &configfile);
531 int stat = wxbConfigFileEditor(this, configfile).ShowModal();
533 StartConsoleThread(configfile);
537 void wxbMainFrame::OnConnect(wxCommandEvent& event) {
538 StartConsoleThread(configfile);
541 void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
548 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
550 lockedbyconsole = true;
552 typeCtrl->HistoryAdd(typeCtrl->GetValue());
553 wxString str = typeCtrl->GetValue() + wxT("\n");
558 * Called when data is arriving from director
560 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
561 wxbPrintObject* po = event.GetEventPrintObject();
563 Print(po->str, po->status);
567 * Prints data received from director to the console, and forwards it to the panels
569 void wxbMainFrame::Print(wxString str, int status)
571 if (lockedbyconsole) {
572 EnableConsole(false);
575 if (status == CS_TERMINATED) {
576 consoleCtrl->AppendText(consoleBuffer);
577 consoleBuffer = wxT("");
578 SetStatusText(_("Console thread terminated."));
579 consoleCtrl->PageDown();
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("");
623 consoleCtrl->PageDown();
624 SetStatusText(_("Disconnected of the director."));
629 // CS_DEBUG is often sent by panels,
630 // and resend it to them would sometimes cause infinite loops
632 /* One promptcaught is normal, so we must have two true Print values to be
633 * sure that the prompt has effectively been caught.
635 int promptcaught = -1;
637 if (status != CS_DEBUG) {
638 for (unsigned int i = 0; i < parsers.GetCount(); i++) {
639 promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
642 if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
643 Print(_("Unexpected question has been received.\n"), CS_DEBUG);
644 // Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
647 if (promptparser->getIntroString() != wxT("")) {
648 message << promptparser->getIntroString() << wxT("\n");
650 message << promptparser->getQuestionString();
652 if (promptparser->getChoices()) {
653 wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
654 int *numbers = new int[promptparser->getChoices()->GetCount()];
657 for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
658 if ((*promptparser->getChoices())[i] != wxT("")) {
659 choices[n] = (*promptparser->getChoices())[i];
665 int res = ::wxGetSingleChoiceIndex(message,
666 _("wx-console: unexpected director's question."), n, choices, this);
667 if (res == -1) { //Cancel pressed
671 if (promptparser->isNumericalChoice()) {
672 Send(wxString() << numbers[res] << wxT("\n"));
675 Send(wxString() << choices[res] << wxT("\n"));
680 Send(::wxGetTextFromUser(message,
681 _("wx-console: unexpected director's question."),
682 wxT(""), this) + wxT("\n"));
687 if (status == CS_END) {
688 if (lockedbyconsole) {
690 lockedbyconsole = false;
695 if (status == CS_DEBUG) {
696 consoleCtrl->AppendText(consoleBuffer);
697 consoleBuffer = wxT("");
698 consoleCtrl->PageDown();
699 consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
702 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
704 consoleBuffer << wxbUtils::ConvertToPrintable(str);
705 if (status == CS_PROMPT) {
706 if (lockedbyconsole) {
709 //consoleBuffer << "<P>";
712 if ((status == CS_END) || (status == CS_PROMPT) || (str.Find(wxT("\n")) > -1)) {
713 consoleCtrl->AppendText(consoleBuffer);
714 consoleBuffer = wxT("");
716 consoleCtrl->PageDown();
719 // consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
721 /*if (status != CS_DEBUG) {
722 consoleCtrl->AppendText("@");
724 //consoleCtrl->SetInsertionPointEnd();
726 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
727 nlines = consoleCtrl->GetNumberOfLines()-1;
730 if (status == CS_END) {
731 consoleCtrl->ShowPosition(nlines);
736 * Sends data to the director
738 void wxbMainFrame::Send(wxString str)
741 ct->Write(str.mb_str(wxConvUTF8));
742 typeCtrl->SetValue(wxT(""));
743 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
744 consoleCtrl->AppendText(wxbUtils::ConvertToPrintable(str));
745 consoleCtrl->PageDown();
748 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
749 nlines = consoleCtrl->GetNumberOfLines()-1;
752 consoleCtrl->ShowPosition(nlines);*/
756 void wxbMainFrame::EnablePanels() {
757 for (int i = 0; panels[i] != NULL; i++) {
758 panels[i]->EnablePanel(true);
763 /* Disable panels, except the one passed as parameter */
764 void wxbMainFrame::DisablePanels(void* except) {
765 for (int i = 0; panels[i] != NULL; i++) {
766 if (panels[i] != except) {
767 panels[i]->EnablePanel(false);
770 panels[i]->EnablePanel(true);
773 if (this != except) {
774 EnableConsole(false);
778 /* Enable or disable console typing */
779 void wxbMainFrame::EnableConsole(bool enable) {
780 typeCtrl->Enable(enable);
781 sendButton->Enable(enable);
783 typeCtrl->SetFocus();
788 * Used by csprint, which is called by console thread.
790 * In GTK and perhaps X11, only the main thread is allowed to interact with
791 * graphical components, so by firing an event, the main loop will call OnPrint.
793 * Calling OnPrint directly from console thread produces "unexpected async replies".
795 void firePrintEvent(wxString str, int status)
797 wxbPrintObject* po = new wxbPrintObject(str, status);
799 wxbThreadEvent evt(Thread);
800 evt.SetEventPrintObject(po);
802 if (wxbMainFrame::GetInstance()) {
803 wxbMainFrame::GetInstance()->AddPendingEvent(evt);
807 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
810 * Called by console thread, this function forwards data line by line and end
811 * signals to the GUI.
814 void csprint(wxString str, int status)
816 firePrintEvent(str, status);
820 void csprint(const char* str, int status)
823 firePrintEvent(wxString(str,wxConvUTF8), status);
826 firePrintEvent(wxT(""), status);