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"
30 #include "wxbconfigfileeditor.h"
34 #include "wxwin16x16.xpm"
36 #include <wx/arrimpl.cpp>
38 #include <wx/stattext.h>
40 #include <wx/config.h>
42 #include <wx/filename.h>
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 if (ct != NULL) { // && (!ct->IsRunning())
202 * Private constructor
204 wxbMainFrame::wxbMainFrame(const wxString& title, const wxPoint& pos, const wxSize& size, long style)
205 : wxFrame(NULL, -1, title, pos, size, style)
211 // set the frame icon
212 SetIcon(wxIcon(wxwin16x16_xpm));
216 menuFile = new wxMenu;
218 // the "About" item should be in the help menu
219 wxMenu *helpMenu = new wxMenu;
220 helpMenu->Append(Minimal_About, _T("&About...\tF1"), _T("Show about dialog"));
222 menuFile->Append(MenuConnect, _T("Connect"), _T("Connect to the director"));
223 menuFile->Append(MenuDisconnect, _T("Disconnect"), _T("Disconnect of the director"));
224 menuFile->AppendSeparator();
225 menuFile->Append(ChangeConfigFile, _T("Change of configuration file"), _T("Change your default configuration file"));
226 menuFile->Append(EditConfigFile, _T("Edit your configuration file"), _T("Edit your configuration file"));
227 menuFile->AppendSeparator();
228 menuFile->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
230 // now append the freshly created menu to the menu bar...
231 wxMenuBar *menuBar = new wxMenuBar();
232 menuBar->Append(menuFile, _T("&File"));
233 menuBar->Append(helpMenu, _T("&Help"));
235 // ... and attach this menu bar to the frame
237 #endif // wxUSE_MENUS
240 SetStatusText(wxString("Welcome to bacula wx-console ") << VERSION << " (" << BDATE << ")!\n");
242 wxPanel* global = new wxPanel(this, -1);
244 notebook = new wxNotebook(global, -1);
248 wxPanel* consolePanel = new wxPanel(notebook, -1);
249 notebook->AddPage(consolePanel, "Console");
251 consoleCtrl = new wxTextCtrl(consolePanel,-1,"",wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH);
252 wxFont font(10, wxMODERN, wxNORMAL, wxNORMAL);
253 #if defined __WXGTK12__ && !defined __WXGTK20__ // Fix for "chinese" fonts under gtk+ 1.2
254 font.SetDefaultEncoding(wxFONTENCODING_ISO8859_1);
255 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
256 Print("Warning : Unicode is disabled because you are using wxWidgets for GTK+ 1.2.\n", CS_DEBUG);
258 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
261 wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(2, 1, 0, 0);
262 consoleSizer->AddGrowableCol(0);
263 consoleSizer->AddGrowableRow(0);
265 typeCtrl = new wxTextCtrl(consolePanel,TypeText,"",wxDefaultPosition,wxSize(200,20), wxTE_PROCESS_ENTER);
266 sendButton = new wxButton(consolePanel, SendButton, "Send");
268 wxFlexGridSizer *typeSizer = new wxFlexGridSizer(1, 3, 0, 0);
269 typeSizer->AddGrowableCol(1);
270 typeSizer->AddGrowableRow(0);
272 typeSizer->Add(new wxStaticText(consolePanel, -1, "Command: "), 0, wxALIGN_CENTER | wxALL, 0);
273 typeSizer->Add(typeCtrl, 1, wxEXPAND | wxALL, 0);
274 typeSizer->Add(sendButton, 1, wxEXPAND | wxLEFT, 5);
276 consoleSizer->Add(consoleCtrl, 1, wxEXPAND | wxALL, 0);
277 consoleSizer->Add(typeSizer, 0, wxEXPAND | wxALL, 0);
279 consolePanel->SetAutoLayout( TRUE );
280 consolePanel->SetSizer( consoleSizer );
281 consoleSizer->SetSizeHints( consolePanel );
283 // Creates the list of panels which are included in notebook, and that need to receive director information
285 panels = new wxbPanel* [2];
286 panels[0] = new wxbRestorePanel(notebook);
289 for (int i = 0; panels[i] != NULL; i++) {
290 notebook->AddPage(panels[i], panels[i]->GetTitle());
293 wxBoxSizer* globalSizer = new wxBoxSizer(wxHORIZONTAL);
295 globalSizer->Add(new wxNotebookSizer(notebook), 1, wxEXPAND, 0);
297 global->SetSizer( globalSizer );
298 globalSizer->SetSizeHints( global );
300 wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
302 sizer->Add(global, 1, wxEXPAND | wxALL, 0);
305 sizer->SetSizeHints( this );
307 EnableConsole(false);
309 lockedbyconsole = false;
315 * Starts the thread interacting with the director
316 * If config is not empty, uses this config file.
318 void wxbMainFrame::StartConsoleThread(const wxString& config) {
319 menuFile->Enable(MenuConnect, false);
320 menuFile->Enable(MenuDisconnect, false);
321 menuFile->Enable(ChangeConfigFile, false);
322 menuFile->Enable(EditConfigFile, false);
328 if (promptparser == NULL) {
329 promptparser = new wxbPromptParser();
335 if ((wxTheApp->argc == 3) && (wxString(wxTheApp->argv[1]) == "-c")) {
336 configfile = wxTheApp->argv[2];
339 wxConfig::Set(new wxConfig("wx-console", "bacula"));
340 if (!wxConfig::Get()->Read("/ConfigFile", &configfile)) {
342 wxFileName filename(::wxGetHomeDir());
343 filename.MakeAbsolute();
344 configfile = filename.GetLongPath();
345 if (configfile.Last() != '/')
347 configfile += "Library/Preferences/org.bacula.wxconsole.conf";
349 wxFileName filename(::wxGetCwd(), "wx-console.conf");
350 filename.MakeAbsolute();
351 configfile = filename.GetLongPath();
353 configfile.Replace("\\", "/");
356 wxConfig::Get()->Write("/ConfigFile", configfile);
357 if (wxTheApp->argc > 1) {
358 Print("Error while parsing command line arguments, using defaults.\n", CS_DEBUG);
359 Print("Usage: wx-console [-c configfile]\n", CS_DEBUG);
362 int answer = wxMessageBox(
363 wxString("It seems that it is the first time you run wx-console.\n") <<
364 "This file (" << configfile << ") has been choosen as default configuration file.\n" <<
365 "Do you want to edit it? (if you click No you will have to select another file)",
367 wxYES_NO | wxICON_QUESTION, this);
368 if (answer == wxYES) {
369 wxbConfigFileEditor(this, configfile).ShowModal();
378 wxString err = console_thread::LoadConfig(configfile);
381 int answer = wxMessageBox(
382 wxString("Unable to read ") << configfile << "\n" <<
383 err << "\nDo you want to choose another one? (Press no to edit this file)",
384 "Unable to read configuration file",
385 wxYES_NO | wxCANCEL | wxICON_ERROR, this);
386 if (answer == wxNO) {
387 wxbConfigFileEditor(this, configfile).ShowModal();
388 err = console_thread::LoadConfig(configfile);
390 else if (answer == wxCANCEL) {
395 else { // (answer == wxYES)
396 configfile = wxFileSelector("Please choose a configuration file to use");
397 if ( !configfile.empty() ) {
398 err = console_thread::LoadConfig(configfile);
407 if ((err == "") && (config == "")) {
408 answer = wxMessageBox(
409 "This configuration file has been successfully read, use it as default?",
410 "Configuration file read successfully",
411 wxYES_NO | wxICON_QUESTION, this);
412 if (answer == wxYES) {
413 wxConfigBase::Get()->Write("/ConfigFile", configfile);
419 csprint(wxString("Using this configuration file: ") << configfile << "\n", CS_DEBUG);
421 ct = new console_thread();
424 SetStatusText("Connecting to the director...");
427 /* Register a new wxbDataParser */
428 void wxbMainFrame::Register(wxbDataParser* dp) {
432 /* Unregister a wxbDataParser */
433 void wxbMainFrame::Unregister(wxbDataParser* dp) {
435 if ((index = parsers.Index(dp)) != wxNOT_FOUND) {
436 parsers.RemoveAt(index);
439 Print("Failed to unregister a data parser !", CS_DEBUG);
445 void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
447 Print("Quitting.\n", CS_DEBUG);
452 console_thread::FreeLib();
457 void wxbMainFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
460 msg.Printf( _T("Welcome to Bacula wx-console.\nWritten by Nicolas Boichat <nicolas@boichat.ch>\n(C) 2004 Kern Sibbald and John Walker\n"));
462 wxMessageBox(msg, _T("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
465 void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
466 wxString oriconfigfile;
467 wxConfig::Get()->Read("/ConfigFile", &oriconfigfile);
468 wxString configfile = wxFileSelector("Please choose your default configuration file");
469 if ( !configfile.empty() ) {
470 if (oriconfigfile != configfile) {
471 int answer = wxMessageBox(
472 "Use this configuration file as default?",
473 "Configuration file",
474 wxYES_NO | wxICON_QUESTION, this);
475 if (answer == wxYES) {
476 wxConfigBase::Get()->Write("/ConfigFile", configfile);
477 StartConsoleThread("");
482 StartConsoleThread(configfile);
486 void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
488 wxConfig::Get()->Read("/ConfigFile", &configfile);
489 int stat = wxbConfigFileEditor(this, configfile).ShowModal();
491 StartConsoleThread(configfile);
495 void wxbMainFrame::OnConnect(wxCommandEvent& event) {
496 menuFile->Enable(MenuConnect, false);
497 menuFile->Enable(MenuDisconnect, false);
498 menuFile->Enable(ChangeConfigFile, false);
499 menuFile->Enable(EditConfigFile, false);
505 if (promptparser == NULL) {
506 promptparser = new wxbPromptParser();
509 ct = new console_thread();
512 SetStatusText("Connecting to the director...");
515 void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
522 void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
524 lockedbyconsole = true;
526 wxString str = typeCtrl->GetValue() + "\n";
531 * Called when data is arriving from director
533 void wxbMainFrame::OnPrint(wxbThreadEvent& event) {
534 wxbPrintObject* po = event.GetEventPrintObject();
536 Print(po->str, po->status);
540 * Prints data received from director to the console, and forwards it to the panels
542 void wxbMainFrame::Print(wxString str, int status)
544 if (lockedbyconsole) {
545 EnableConsole(false);
548 if (status == CS_TERMINATED) {
549 consoleCtrl->AppendText(consoleBuffer);
551 SetStatusText("Console thread terminated.");
554 int answer = wxMessageBox("Connection to the director lost. Quit program?", "Connection lost",
555 wxYES_NO | wxICON_EXCLAMATION, this);
556 if (answer == wxYES) {
560 menuFile->Enable(MenuConnect, true);
561 menuFile->SetLabel(MenuConnect, "Connect");
562 menuFile->SetHelpString(MenuConnect, "Connect to the director");
563 menuFile->Enable(MenuDisconnect, false);
564 menuFile->Enable(ChangeConfigFile, true);
565 menuFile->Enable(EditConfigFile, true);
569 if (status == CS_CONNECTED) {
570 SetStatusText("Connected to the director.");
572 menuFile->Enable(MenuConnect, true);
573 menuFile->SetLabel(MenuConnect, "Reconnect");
574 menuFile->SetHelpString(MenuConnect, "Reconnect to the director");
575 menuFile->Enable(MenuDisconnect, false);
576 menuFile->Enable(ChangeConfigFile, true);
577 menuFile->Enable(EditConfigFile, true);
580 if (status == CS_DISCONNECTED) {
581 consoleCtrl->AppendText(consoleBuffer);
583 SetStatusText("Disconnected of the director.");
588 // CS_DEBUG is often sent by panels,
589 // and resend it to them would sometimes cause infinite loops
591 /* One promptcaught is normal, so we must have two true Print values to be
592 * sure that the prompt has effectively been caught.
594 int promptcaught = -1;
596 if (status != CS_DEBUG) {
597 for (unsigned int i = 0; i < parsers.GetCount(); i++) {
598 promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
601 if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
602 Print("Unexpected question has been received.\n", CS_DEBUG);
603 // Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
606 if (promptparser->getIntroString() != "") {
607 message << promptparser->getIntroString() << "\n";
609 message << promptparser->getQuestionString();
611 if (promptparser->getChoices()) {
612 wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
613 int *numbers = new int[promptparser->getChoices()->GetCount()];
616 for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
617 if ((*promptparser->getChoices())[i] != "") {
618 choices[n] = (*promptparser->getChoices())[i];
624 int res = ::wxGetSingleChoiceIndex(message,
625 "wx-console: unexpected director's question.", n, choices, this);
630 Send(wxString() << numbers[res] << "\n");
634 Send(::wxGetTextFromUser(message,
635 "wx-console: unexpected director's question.", "", this) + "\n");
640 if (status == CS_END) {
641 if (lockedbyconsole) {
643 lockedbyconsole = false;
648 if (status == CS_DEBUG) {
649 consoleCtrl->AppendText(consoleBuffer);
651 consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
654 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
656 consoleBuffer << str;
657 if (status == CS_PROMPT) {
658 if (lockedbyconsole) {
661 consoleBuffer << "<P>";
664 if ((status == CS_END) || (status == CS_PROMPT) || (str.Find("\n") > -1)) {
665 consoleCtrl->AppendText(consoleBuffer);
668 consoleCtrl->ScrollLines(3);
671 // consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
673 /*if (status != CS_DEBUG) {
674 consoleCtrl->AppendText("@");
676 //consoleCtrl->SetInsertionPointEnd();
678 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
679 nlines = consoleCtrl->GetNumberOfLines()-1;
682 if (status == CS_END) {
683 consoleCtrl->ShowPosition(nlines);
688 * Sends data to the director
690 void wxbMainFrame::Send(wxString str)
693 ct->Write((const char*)str);
694 typeCtrl->SetValue("");
695 consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
696 consoleCtrl->AppendText(str);
697 consoleCtrl->ScrollLines(3);
700 /* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
701 nlines = consoleCtrl->GetNumberOfLines()-1;
704 consoleCtrl->ShowPosition(nlines);*/
708 void wxbMainFrame::EnablePanels() {
709 for (int i = 0; panels[i] != NULL; i++) {
710 panels[i]->EnablePanel(true);
715 /* Disable panels, except the one passed as parameter */
716 void wxbMainFrame::DisablePanels(void* except) {
717 for (int i = 0; panels[i] != NULL; i++) {
718 if (panels[i] != except) {
719 panels[i]->EnablePanel(false);
722 panels[i]->EnablePanel(true);
725 if (this != except) {
726 EnableConsole(false);
730 /* Enable or disable console typing */
731 void wxbMainFrame::EnableConsole(bool enable) {
732 typeCtrl->Enable(enable);
733 sendButton->Enable(enable);
735 typeCtrl->SetFocus();
740 * Used by csprint, which is called by console thread.
742 * In GTK and perhaps X11, only the main thread is allowed to interact with
743 * graphical components, so by firing an event, the main loop will call OnPrint.
745 * Calling OnPrint directly from console thread produces "unexpected async replies".
747 void firePrintEvent(wxString str, int status)
749 wxbPrintObject* po = new wxbPrintObject(str, status);
751 wxbThreadEvent evt(Thread);
752 evt.SetEventPrintObject(po);
754 if (wxbMainFrame::GetInstance()) {
755 wxbMainFrame::GetInstance()->AddPendingEvent(evt);
759 //wxString csBuffer; /* Temporary buffer for receiving data from console thread */
762 * Called by console thread, this function forwards data line by line and end
763 * signals to the GUI.
765 void csprint(const char* str, int status)
768 firePrintEvent(wxString(str), status);
771 firePrintEvent("", status);