5 * Nicolas Boichat, April 2004
9 Copyright (C) 2004 Kern Sibbald and John Walker
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License
13 as published by the Free Software Foundation; either version 2
14 of the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "wxbmainframe.h" // class's header file
28 #include "wxbrestorepanel.h"
32 #include "wxwin16x16.xpm"
34 #include <wx/arrimpl.cpp>
36 #include <wx/stattext.h>
38 // ----------------------------------------------------------------------------
39 // event tables and other macros for wxWindows
40 // ----------------------------------------------------------------------------
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
46 // IDs for the controls and the menu commands
52 // it is important for the id corresponding to the "About" command to have
53 // this standard value as otherwise it won't be handled properly under Mac
54 // (where it is special and put into the "Apple" menu)
55 Minimal_About = wxID_ABOUT,
62 * wxbTHREAD_EVENT declaration, used by csprint
64 BEGIN_DECLARE_EVENT_TYPES()
65 DECLARE_EVENT_TYPE(wxbTHREAD_EVENT, 1)
66 END_DECLARE_EVENT_TYPES()
68 DEFINE_EVENT_TYPE(wxbTHREAD_EVENT)
70 typedef void (wxEvtHandler::*wxThreadEventFunction)(wxbThreadEvent&);
72 #define EVT_THREAD_EVENT(id, fn) \
73 DECLARE_EVENT_TABLE_ENTRY( \
74 wxbTHREAD_EVENT, id, wxID_ANY, \
75 (wxObjectEventFunction)(wxEventFunction)(wxThreadEventFunction)&fn, \
79 // the event tables connect the wxWindows events with the functions (event
80 // handlers) which process them. It can be also done at run-time, but for the
81 // simple menu events like this the static method is much simpler.
82 BEGIN_EVENT_TABLE(wxbMainFrame, wxFrame)
83 EVT_MENU(Minimal_Quit, wxbMainFrame::OnQuit)
84 EVT_MENU(Minimal_About, wxbMainFrame::OnAbout)
85 EVT_TEXT_ENTER(TypeText, wxbMainFrame::OnEnter)
86 EVT_THREAD_EVENT(Thread, wxbMainFrame::OnPrint)
87 EVT_BUTTON(SendButton, wxbMainFrame::OnEnter)
90 // ----------------------------------------------------------------------------
92 // ----------------------------------------------------------------------------
95 * wxbThreadEvent constructor
97 wxbThreadEvent::wxbThreadEvent(int id): wxEvent(id, wxbTHREAD_EVENT) {
102 * wxbThreadEvent destructor
104 wxbThreadEvent::~wxbThreadEvent()
106 if (m_eventObject != NULL) {
107 delete m_eventObject;
112 * wxbThreadEvent copy constructor
114 wxbThreadEvent::wxbThreadEvent(const wxbThreadEvent& te)
116 this->m_eventType = te.m_eventType;
117 this->m_id = te.m_id;
118 if (te.m_eventObject != NULL) {
119 this->m_eventObject = new wxbPrintObject(*((wxbPrintObject*)te.m_eventObject));
122 this->m_eventObject = NULL;
124 this->m_skipped = te.m_skipped;
125 this->m_timeStamp = te.m_timeStamp;
129 * Must be implemented (abstract in wxEvent)
131 wxEvent* wxbThreadEvent::Clone() const
133 return new wxbThreadEvent(*this);
137 * Gets the wxbPrintObject attached to this event, containing data sent by director
139 wxbPrintObject* wxbThreadEvent::GetEventPrintObject()
141 return (wxbPrintObject*)m_eventObject;
145 * Sets the wxbPrintObject attached to this event
147 void wxbThreadEvent::SetEventPrintObject(wxbPrintObject* object)
149 m_eventObject = (wxObject*)object;
152 // ----------------------------------------------------------------------------
154 // ----------------------------------------------------------------------------
156 wxbMainFrame *wxbMainFrame::frame = NULL;
159 * Singleton constructor
161 wxbMainFrame* wxbMainFrame::CreateInstance(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
163 frame = new wxbMainFrame(title, pos, size, style);
168 * Returns singleton instance
170 wxbMainFrame* wxbMainFrame::GetInstance()
178 wxbMainFrame::~wxbMainFrame()
180 if (ct != NULL) { // && (!ct->IsRunning())
187 * Private constructor
189 wxbMainFrame::wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
190 : wxFrame(NULL, -1, title, pos, size, style)
194 // set the frame icon
195 SetIcon(wxIcon(wxwin16x16_xpm));
199 wxMenu *menuFile = new wxMenu;
201 // the "About" item should be in the help menu
202 wxMenu *helpMenu = new wxMenu;
203 helpMenu->Append(Minimal_About, _T("&About...\tF1"), _T("Show about dialog"));
205 menuFile->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
207 // now append the freshly created menu to the menu bar...
208 wxMenuBar *menuBar = new wxMenuBar();
209 menuBar->Append(menuFile, _T("&File"));
210 menuBar->Append(helpMenu, _T("&Help"));
212 // ... and attach this menu bar to the frame
214 #endif // wxUSE_MENUS
217 SetStatusText(wxString("Welcome to bacula wx-console ") << VERSION << " (" << BDATE << ")!\n");
219 wxPanel* global = new wxPanel(this, -1);
221 notebook = new wxNotebook(global, -1);
225 wxPanel* consolePanel = new wxPanel(notebook, -1);
226 notebook->AddPage(consolePanel, "Console");
228 consoleCtrl = new wxTextCtrl(consolePanel,-1,"",wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH);
229 wxFont font(10, wxMODERN, wxNORMAL, wxNORMAL);
230 #if defined __WXGTK12__ && !defined __WXGTK20__ // Fix for "chinese" fonts under gtk+ 1.2
231 font.SetDefaultEncoding(wxFONTENCODING_ISO8859_1);
232 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
233 Print("Warning : Unicode is disabled because you are using wxWidgets for GTK+ 1.2.\n", CS_DEBUG);
235 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
238 wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(2, 1, 0, 0);
239 consoleSizer->AddGrowableCol(0);
240 consoleSizer->AddGrowableRow(0);
242 typeCtrl = new wxTextCtrl(consolePanel,TypeText,"",wxDefaultPosition,wxSize(200,20), wxTE_PROCESS_ENTER);
243 sendButton = new wxButton(consolePanel, SendButton, "Send");
245 wxFlexGridSizer *typeSizer = new wxFlexGridSizer(1, 3, 0, 0);
246 typeSizer->AddGrowableCol(1);
247 typeSizer->AddGrowableRow(0);
249 typeSizer->Add(new wxStaticText(consolePanel, -1, "Command: "), 0, wxALIGN_CENTER | wxALL, 0);
250 typeSizer->Add(typeCtrl, 1, wxEXPAND | wxALL, 0);
251 typeSizer->Add(sendButton, 1, wxEXPAND | wxLEFT, 5);
253 consoleSizer->Add(consoleCtrl, 1, wxEXPAND | wxALL, 0);
254 consoleSizer->Add(typeSizer, 0, wxEXPAND | wxALL, 0);
256 consolePanel->SetAutoLayout( TRUE );
257 consolePanel->SetSizer( consoleSizer );
258 consoleSizer->SetSizeHints( consolePanel );
260 // Creates the list of panels which are included in notebook, and that need to receive director information
262 panels = new wxbPanel* [2];
263 panels[0] = new wxbRestorePanel(notebook);
266 for (int i = 0; panels[i] != NULL; i++) {
267 notebook->AddPage(panels[i], panels[i]->GetTitle());
270 wxBoxSizer* globalSizer = new wxBoxSizer(wxHORIZONTAL);
272 globalSizer->Add(new wxNotebookSizer(notebook), 1, wxEXPAND, 0);
274 global->SetSizer( globalSizer );
275 globalSizer->SetSizeHints( global );
277 wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
279 sizer->Add(global, 1, wxEXPAND | wxALL, 0);
282 sizer->SetSizeHints( this );
284 EnableConsole(false);
286 lockedbyconsole = false;
292 * Starts the thread interacting with the director
294 void wxbMainFrame::StartConsoleThread()
300 promptparser = new wxbPromptParser();
306 if ((wxTheApp->argc == 3) && (wxString(wxTheApp->argv[1]) == "-c")) {
307 configfile = wxTheApp->argv[2];
311 configfile = "/Library/Preferences/org.bacula.wxconsole.conf";
313 configfile = "./wx-console.conf";
315 if (wxTheApp->argc > 1) {
316 Print("Error while parsing command line arguments, using defaults.\n", CS_DEBUG);
317 Print("Usage: wx-console [-c configfile]\n", CS_DEBUG);
321 ct = new console_thread(configfile);
324 SetStatusText("Connecting to the director...");
327 /* Register a new wxbDataParser */
328 void wxbMainFrame::Register(wxbDataParser* dp) {
332 /* Unregister a wxbDataParser */
333 void wxbMainFrame::Unregister(wxbDataParser* dp) {
335 if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
336 parsers.RemoveAt(index);
339 Print("Failed to unregister a data parser !", CS_DEBUG);
345 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
347 Print("Quitting.\n", CS_DEBUG);
356 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
359 msg.Printf( _T("Welcome to Bacula wx-console.\nWritten by Nicolas Boichat <nicolas@boichat.ch>\n(C) 2004 Kern Sibbald and John Walker\n"));
361 wxMessageBox(msg, _T("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
364 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
366 lockedbyconsole = true;
368 wxString str = typeCtrl->GetValue() + "\n";
373 * Called when data is arriving from director
375 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
376 wxbPrintObject* po = event.GetEventPrintObject();
378 Print(po->str, po->status);
382 * Prints data received from director to the console, and forwards it to the panels
384 void wxbMainFrame::Print(wxString str, int status)
386 if (lockedbyconsole) {
387 EnableConsole(false);
390 if (status == CS_TERMINATED) {
391 consoleCtrl->AppendText(consoleBuffer);
393 SetStatusText("Console thread terminated.");
396 int answer = wxMessageBox("Connection to the director lost. Quit program?", "Connection lost",
397 wxYES_NO | wxICON_EXCLAMATION, this);
398 if (answer == wxYES) {
405 if (status == CS_CONNECTED) {
406 SetStatusText("Connected to the director.");
410 if (status == CS_DISCONNECTED) {
411 consoleCtrl->AppendText(consoleBuffer);
413 SetStatusText("Disconnected of the director.");
418 // CS_DEBUG is often sent by panels,
419 // and resend it to them would sometimes cause infinite loops
421 /* One promptcaught is normal, so we must have two true Print values to be
422 * sure that the prompt has effectively been caught.
424 int promptcaught = -1;
426 if (status != CS_DEBUG) {
427 for (unsigned int i = 0; i < parsers.GetCount(); i++) {
428 promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
431 if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
432 Print("Unexpected question has been received.\n", CS_DEBUG);
433 // Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
436 if (promptparser->getIntroString() != "") {
437 message << promptparser->getIntroString() << "\n";
439 message << promptparser->getQuestionString();
441 if (promptparser->getChoices()) {
442 wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
443 int *numbers = new int[promptparser->getChoices()->GetCount()];
446 for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
447 if ((*promptparser->getChoices())[i] != "") {
448 choices[n] = (*promptparser->getChoices())[i];
454 int res = ::wxGetSingleChoiceIndex(message,
455 "wx-console: unexpected director's question.", n, choices, this);
460 Send(wxString() << numbers[res] << "\n");
464 Send(::wxGetTextFromUser(message,
465 "wx-console: unexpected director's question.", "", this) + "\n");
470 if (status == CS_END) {
471 if (lockedbyconsole) {
473 lockedbyconsole = false;
478 if (status == CS_DEBUG) {
479 consoleCtrl->AppendText(consoleBuffer);
481 consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
484 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
486 consoleBuffer << str;
487 if (status == CS_PROMPT) {
488 if (lockedbyconsole) {
491 consoleBuffer << "<P>";
494 if ((status == CS_END) || (status == CS_PROMPT) || (str.Find("\n") > -1)) {
495 consoleCtrl->AppendText(consoleBuffer);
498 consoleCtrl->ScrollLines(3);
501 // consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
503 /*if (status != CS_DEBUG) {
504 consoleCtrl->AppendText("@");
506 //consoleCtrl->SetInsertionPointEnd();
508 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
509 nlines = consoleCtrl->GetNumberOfLines()-1;
512 if (status == CS_END) {
513 consoleCtrl->ShowPosition(nlines);
518 * Sends data to the director
520 void wxbMainFrame::Send(wxString str)
523 ct->Write((const char*)str);
524 typeCtrl->SetValue("");
525 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
526 consoleCtrl->AppendText(str);
527 consoleCtrl->ScrollLines(3);
530 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
531 nlines = consoleCtrl->GetNumberOfLines()-1;
534 consoleCtrl->ShowPosition(nlines);*/
538 void wxbMainFrame::EnablePanels() {
539 for (int i = 0; panels[i] != NULL; i++) {
540 panels[i]->EnablePanel(true);
545 /* Disable panels, except the one passed as parameter */
546 void wxbMainFrame::DisablePanels(void* except) {
547 for (int i = 0; panels[i] != NULL; i++) {
548 if (panels[i] != except) {
549 panels[i]->EnablePanel(false);
552 panels[i]->EnablePanel(true);
555 if (this != except) {
556 EnableConsole(false);
560 /* Enable or disable console typing */
561 void wxbMainFrame::EnableConsole(bool enable) {
562 typeCtrl->Enable(enable);
563 sendButton->Enable(enable);
565 typeCtrl->SetFocus();
570 * Used by csprint, which is called by console thread.
572 * In GTK and perhaps X11, only the main thread is allowed to interact with
573 * graphical components, so by firing an event, the main loop will call OnPrint.
575 * Calling OnPrint directly from console thread produces "unexpected async replies".
577 void firePrintEvent(wxString str, int status)
579 wxbPrintObject* po = new wxbPrintObject(str, status);
581 wxbThreadEvent evt(Thread);
582 evt.SetEventPrintObject(po);
584 if (wxbMainFrame::GetInstance()) {
585 wxbMainFrame::GetInstance()->AddPendingEvent(evt);
589 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
592 * Called by console thread, this function forwards data line by line and end
593 * signals to the GUI.
595 void csprint(const char* str, int status)
598 firePrintEvent(wxString(str), status);
601 firePrintEvent("", status);