*
* Main frame
*
- * Nicolas Boichat, April 2004
+ * Nicolas Boichat, July 2004
*
*/
/*
#include "wxbrestorepanel.h"
+#include "wxbconfigfileeditor.h"
+
#include "csprint.h"
#include "wxwin16x16.xpm"
#include <wx/arrimpl.cpp>
+#include <wx/stattext.h>
+
+#include <wx/config.h>
+
+#include <wx/filename.h>
+
+#undef Yield /* MinGW defines Yield */
+
// ----------------------------------------------------------------------------
// event tables and other macros for wxWindows
// ----------------------------------------------------------------------------
// this standard value as otherwise it won't be handled properly under Mac
// (where it is special and put into the "Apple" menu)
Minimal_About = wxID_ABOUT,
- TypeText = 2,
- Thread = 3
+
+ ChangeConfigFile = 2,
+ EditConfigFile = 3,
+ MenuConnect = 4,
+ MenuDisconnect = 5,
+ TypeText = 6,
+ SendButton = 7,
+ Thread = 8
};
/*
DEFINE_EVENT_TYPE(wxbTHREAD_EVENT)
+typedef void (wxEvtHandler::*wxThreadEventFunction)(wxbThreadEvent&);
+
+#define EVT_THREAD_EVENT(id, fn) \
+ DECLARE_EVENT_TABLE_ENTRY( \
+ wxbTHREAD_EVENT, id, wxID_ANY, \
+ (wxObjectEventFunction)(wxEventFunction)(wxThreadEventFunction)&fn, \
+ (wxObject *) NULL \
+ ),
+
// the event tables connect the wxWindows events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.
BEGIN_EVENT_TABLE(wxbMainFrame, wxFrame)
EVT_MENU(Minimal_Quit, wxbMainFrame::OnQuit)
EVT_MENU(Minimal_About, wxbMainFrame::OnAbout)
+ EVT_MENU(ChangeConfigFile, wxbMainFrame::OnChangeConfig)
+ EVT_MENU(EditConfigFile, wxbMainFrame::OnEditConfig)
+ EVT_MENU(MenuConnect, wxbMainFrame::OnConnect)
+ EVT_MENU(MenuDisconnect, wxbMainFrame::OnDisconnect)
EVT_TEXT_ENTER(TypeText, wxbMainFrame::OnEnter)
- EVT_CUSTOM(wxbTHREAD_EVENT, Thread, wxbMainFrame::OnPrint)
+ EVT_THREAD_EVENT(Thread, wxbMainFrame::OnPrint)
+ EVT_BUTTON(SendButton, wxbMainFrame::OnEnter)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------
*/
wxbMainFrame::~wxbMainFrame()
{
- if ((ct != NULL) && (!ct->IsRunning())) {
+ if (ct != NULL) { // && (!ct->IsRunning())
ct->Delete();
}
+ frame = NULL;
}
/*
: wxFrame(NULL, -1, title, pos, size, style)
{
ct = NULL;
+
+ promptparser = NULL;
// set the frame icon
SetIcon(wxIcon(wxwin16x16_xpm));
#if wxUSE_MENUS
// create a menu bar
- wxMenu *menuFile = new wxMenu;
+ menuFile = new wxMenu;
// the "About" item should be in the help menu
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(Minimal_About, _T("&About...\tF1"), _T("Show about dialog"));
+ menuFile->Append(MenuConnect, _T("Connect"), _T("Connect to the director"));
+ menuFile->Append(MenuDisconnect, _T("Disconnect"), _T("Disconnect of the director"));
+ menuFile->AppendSeparator();
+ menuFile->Append(ChangeConfigFile, _T("Change of configuration file"), _T("Change your default configuration file"));
+ menuFile->Append(EditConfigFile, _T("Edit your configuration file"), _T("Edit your configuration file"));
+ menuFile->AppendSeparator();
menuFile->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
// now append the freshly created menu to the menu bar...
SetMenuBar(menuBar);
#endif // wxUSE_MENUS
-#if wxUSE_STATUSBAR
- // create a status bar just for fun (by default with 1 pane only)
- CreateStatusBar(2);
- SetStatusText(_T("Welcome to Bacula wx-console!"));
-#endif // wxUSE_STATUSBAR
+ CreateStatusBar(1);
+ SetStatusText(wxString("Welcome to bacula wx-console ") << VERSION << " (" << BDATE << ")!\n");
wxPanel* global = new wxPanel(this, -1);
notebook->AddPage(consolePanel, "Console");
consoleCtrl = new wxTextCtrl(consolePanel,-1,"",wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH);
- consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, wxFont(10, wxMODERN, wxNORMAL, wxNORMAL)));
-
- typeCtrl = new wxTextCtrl(consolePanel,TypeText,"",wxDefaultPosition,wxSize(200,20), wxTE_PROCESS_ENTER);
+ wxFont font(10, wxMODERN, wxNORMAL, wxNORMAL);
+#if defined __WXGTK12__ && !defined __WXGTK20__ // Fix for "chinese" fonts under gtk+ 1.2
+ font.SetDefaultEncoding(wxFONTENCODING_ISO8859_1);
+ consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
+ Print("Warning : Unicode is disabled because you are using wxWidgets for GTK+ 1.2.\n", CS_DEBUG);
+#else
+ consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK, wxNullColour, font));
+#endif
wxFlexGridSizer *consoleSizer = new wxFlexGridSizer(2, 1, 0, 0);
consoleSizer->AddGrowableCol(0);
consoleSizer->AddGrowableRow(0);
+ typeCtrl = new wxbHistoryTextCtrl(consolePanel,TypeText,"",wxDefaultPosition,wxSize(200,20));
+ sendButton = new wxButton(consolePanel, SendButton, "Send");
+
+ wxFlexGridSizer *typeSizer = new wxFlexGridSizer(1, 3, 0, 0);
+ typeSizer->AddGrowableCol(1);
+ typeSizer->AddGrowableRow(0);
+
+ typeSizer->Add(new wxStaticText(consolePanel, -1, "Command: "), 0, wxALIGN_CENTER | wxALL, 0);
+ typeSizer->Add(typeCtrl, 1, wxEXPAND | wxALL, 0);
+ typeSizer->Add(sendButton, 1, wxEXPAND | wxLEFT, 5);
+
consoleSizer->Add(consoleCtrl, 1, wxEXPAND | wxALL, 0);
- consoleSizer->Add(typeCtrl, 0, wxEXPAND | wxALL, 0);
+ consoleSizer->Add(typeSizer, 0, wxEXPAND | wxALL, 0);
consolePanel->SetAutoLayout( TRUE );
consolePanel->SetSizer( consoleSizer );
sizer->SetSizeHints( this );
this->SetSize(size);
EnableConsole(false);
+
+ lockedbyconsole = false;
+
+ consoleBuffer = "";
+
+ configfile = "";
}
/*
* Starts the thread interacting with the director
+ * If config is not empty, uses this config file.
*/
-void wxbMainFrame::StartConsoleThread()
-{
+void wxbMainFrame::StartConsoleThread(const wxString& config) {
+ menuFile->Enable(MenuConnect, false);
+ menuFile->Enable(MenuDisconnect, false);
+ menuFile->Enable(ChangeConfigFile, false);
+ menuFile->Enable(EditConfigFile, false);
+
if (ct != NULL) {
ct->Delete();
+ ct = NULL;
+ wxTheApp->Yield();
}
+ if (promptparser == NULL) {
+ promptparser = new wxbPromptParser();
+ }
+
+ if (config == "") {
+ if ((wxTheApp->argc == 3) && (wxString(wxTheApp->argv[1]) == "-c")) {
+ configfile = wxTheApp->argv[2];
+ }
+ else {
+ wxConfig::Set(new wxConfig("wx-console", "bacula"));
+ if (!wxConfig::Get()->Read("/ConfigFile", &configfile)) {
+#ifdef HAVE_MACOSX
+ wxFileName filename(::wxGetHomeDir());
+ filename.MakeAbsolute();
+ configfile = filename.GetLongPath();
+ if (configfile.Last() != '/')
+ configfile += '/';
+ configfile += "Library/Preferences/org.bacula.wxconsole.conf";
+#else
+ wxFileName filename(::wxGetCwd(), "wx-console.conf");
+ filename.MakeAbsolute();
+ configfile = filename.GetLongPath();
+#ifdef HAVE_WIN32
+ configfile.Replace("\\", "/");
+#endif //HAVE_WIN32
+#endif //HAVE_MACOSX
+ wxConfig::Get()->Write("/ConfigFile", configfile);
+ if (wxTheApp->argc > 1) {
+ Print("Error while parsing command line arguments, using defaults.\n", CS_DEBUG);
+ Print("Usage: wx-console [-c configfile]\n", CS_DEBUG);
+ }
+
+ int answer = wxMessageBox(
+ wxString("It seems that it is the first time you run wx-console.\n") <<
+ "This file (" << configfile << ") has been choosen as default configuration file.\n" <<
+ "Do you want to edit it? (if you click No you will have to select another file)",
+ "First run",
+ wxYES_NO | wxICON_QUESTION, this);
+ if (answer == wxYES) {
+ wxbConfigFileEditor(this, configfile).ShowModal();
+ }
+ }
+ }
+ }
+ else {
+ configfile = config;
+ }
+
+ wxString err = console_thread::LoadConfig(configfile);
+
+ while (err != "") {
+ int answer = wxMessageBox(
+ wxString("Unable to read ") << configfile << "\n" <<
+ err << "\nDo you want to choose another one? (Press no to edit this file)",
+ "Unable to read configuration file",
+ wxYES_NO | wxCANCEL | wxICON_ERROR, this);
+ if (answer == wxNO) {
+ wxbConfigFileEditor(this, configfile).ShowModal();
+ err = console_thread::LoadConfig(configfile);
+ }
+ else if (answer == wxCANCEL) {
+ frame = NULL;
+ Close(true);
+ return;
+ }
+ else { // (answer == wxYES)
+ configfile = wxFileSelector("Please choose a configuration file to use");
+ if ( !configfile.empty() ) {
+ err = console_thread::LoadConfig(configfile);
+ }
+ else {
+ frame = NULL;
+ Close(true);
+ return;
+ }
+ }
+
+ if ((err == "") && (config == "")) {
+ answer = wxMessageBox(
+ "This configuration file has been successfully read, use it as default?",
+ "Configuration file read successfully",
+ wxYES_NO | wxICON_QUESTION, this);
+ if (answer == wxYES) {
+ wxConfigBase::Get()->Write("/ConfigFile", configfile);
+ }
+ break;
+ }
+ }
+
+ csprint(wxString("Using this configuration file: ") << configfile << "\n", CS_DEBUG);
+
ct = new console_thread();
ct->Create();
ct->Run();
+ SetStatusText("Connecting to the director...");
}
/* Register a new wxbDataParser */
void wxbMainFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
- // TRUE is to force the frame to close
+ Print("Quitting.\n", CS_DEBUG);
+ if (ct != NULL) {
+ ct->Delete();
+ ct = NULL;
+ wxTheApp->Yield();
+ }
+ console_thread::FreeLib();
+ frame = NULL;
+ wxTheApp->Yield();
Close(TRUE);
}
wxMessageBox(msg, _T("About Bacula wx-console"), wxOK | wxICON_INFORMATION, this);
}
+void wxbMainFrame::OnChangeConfig(wxCommandEvent& event) {
+ wxString oriconfigfile;
+ wxConfig::Get()->Read("/ConfigFile", &oriconfigfile);
+ wxString configfile = wxFileSelector("Please choose your default configuration file");
+ if ( !configfile.empty() ) {
+ if (oriconfigfile != configfile) {
+ int answer = wxMessageBox(
+ "Use this configuration file as default?",
+ "Configuration file",
+ wxYES_NO | wxICON_QUESTION, this);
+ if (answer == wxYES) {
+ wxConfigBase::Get()->Write("/ConfigFile", configfile);
+ wxConfigBase::Get()->Flush();
+ StartConsoleThread("");
+ return;
+ }
+ }
+
+ StartConsoleThread(configfile);
+ }
+}
+
+void wxbMainFrame::OnEditConfig(wxCommandEvent& event) {
+ wxString configfile;
+ wxConfig::Get()->Read("/ConfigFile", &configfile);
+ int stat = wxbConfigFileEditor(this, configfile).ShowModal();
+ if (stat == wxOK) {
+ StartConsoleThread(configfile);
+ }
+}
+
+void wxbMainFrame::OnConnect(wxCommandEvent& event) {
+ StartConsoleThread(configfile);
+}
+
+void wxbMainFrame::OnDisconnect(wxCommandEvent& event) {
+ if (ct != NULL) {
+ ct->Delete();
+ ct = NULL;
+ }
+}
+
void wxbMainFrame::OnEnter(wxCommandEvent& WXUNUSED(event))
{
+ lockedbyconsole = true;
+ DisablePanels();
+ typeCtrl->HistoryAdd(typeCtrl->GetValue());
wxString str = typeCtrl->GetValue() + "\n";
Send(str);
}
*/
void wxbMainFrame::Print(wxString str, int status)
{
+ if (lockedbyconsole) {
+ EnableConsole(false);
+ }
+
+ if (status == CS_TERMINATED) {
+ consoleCtrl->AppendText(consoleBuffer);
+ consoleBuffer = "";
+ SetStatusText("Console thread terminated.");
+ consoleCtrl->ScrollLines(3);
+ ct = NULL;
+ DisablePanels();
+ int answer = wxMessageBox("Connection to the director lost. Quit program?", "Connection lost",
+ wxYES_NO | wxICON_EXCLAMATION, this);
+ if (answer == wxYES) {
+ frame = NULL;
+ Close(true);
+ }
+ menuFile->Enable(MenuConnect, true);
+ menuFile->SetLabel(MenuConnect, "Connect");
+ menuFile->SetHelpString(MenuConnect, "Connect to the director");
+ menuFile->Enable(MenuDisconnect, false);
+ menuFile->Enable(ChangeConfigFile, true);
+ menuFile->Enable(EditConfigFile, true);
+ return;
+ }
+
if (status == CS_CONNECTED) {
+ SetStatusText("Connected to the director.");
EnablePanels();
+ menuFile->Enable(MenuConnect, true);
+ menuFile->SetLabel(MenuConnect, "Reconnect");
+ menuFile->SetHelpString(MenuConnect, "Reconnect to the director");
+ menuFile->Enable(MenuDisconnect, true);
+ menuFile->Enable(ChangeConfigFile, true);
+ menuFile->Enable(EditConfigFile, true);
return;
}
if (status == CS_DISCONNECTED) {
+ consoleCtrl->AppendText(consoleBuffer);
+ consoleBuffer = "";
+ consoleCtrl->ScrollLines(3);
+ SetStatusText("Disconnected of the director.");
DisablePanels();
return;
}
// CS_DEBUG is often sent by panels,
- // and resend it to them would sometimes cause an infinite loop
+ // and resend it to them would sometimes cause infinite loops
+
+ /* One promptcaught is normal, so we must have two true Print values to be
+ * sure that the prompt has effectively been caught.
+ */
+ int promptcaught = -1;
+
if (status != CS_DEBUG) {
for (unsigned int i = 0; i < parsers.GetCount(); i++) {
- parsers[i]->Print(str, status);
- }
+ promptcaught += parsers[i]->Print(str, status) ? 1 : 0;
+ }
+
+ if ((status == CS_PROMPT) && (promptcaught < 1) && (promptparser->isPrompt())) {
+ Print("Unexpected question has been received.\n", CS_DEBUG);
+// Print(wxString("(") << promptparser->getIntroString() << "/-/" << promptparser->getQuestionString() << ")\n", CS_DEBUG);
+
+ wxString message;
+ if (promptparser->getIntroString() != "") {
+ message << promptparser->getIntroString() << "\n";
+ }
+ message << promptparser->getQuestionString();
+
+ if (promptparser->getChoices()) {
+ wxString *choices = new wxString[promptparser->getChoices()->GetCount()];
+ int *numbers = new int[promptparser->getChoices()->GetCount()];
+ int n = 0;
+
+ for (unsigned int i = 0; i < promptparser->getChoices()->GetCount(); i++) {
+ if ((*promptparser->getChoices())[i] != "") {
+ choices[n] = (*promptparser->getChoices())[i];
+ numbers[n] = i;
+ n++;
+ }
+ }
+
+ int res = ::wxGetSingleChoiceIndex(message,
+ "wx-console: unexpected director's question.", n, choices, this);
+ if (res == -1) {
+ Send("\n");
+ }
+ else {
+ Send(wxString() << numbers[res] << "\n");
+ }
+ }
+ else {
+ Send(::wxGetTextFromUser(message,
+ "wx-console: unexpected director's question.", "", this) + "\n");
+ }
+ }
}
-
+
if (status == CS_END) {
+ if (lockedbyconsole) {
+ EnablePanels();
+ lockedbyconsole = false;
+ }
str = "#";
}
- consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
- (*consoleCtrl) << str;
- consoleCtrl->SetInsertionPointEnd();
+ if (status == CS_DEBUG) {
+ consoleCtrl->AppendText(consoleBuffer);
+ consoleBuffer = "";
+ consoleCtrl->ScrollLines(3);
+ consoleCtrl->SetDefaultStyle(wxTextAttr(wxColour(0, 128, 0)));
+ }
+ else {
+ consoleCtrl->SetDefaultStyle(wxTextAttr(*wxBLACK));
+ }
+ consoleBuffer << str;
+ if (status == CS_PROMPT) {
+ if (lockedbyconsole) {
+ EnableConsole(true);
+ }
+ //consoleBuffer << "<P>";
+ }
+
+ if ((status == CS_END) || (status == CS_PROMPT) || (str.Find("\n") > -1)) {
+ consoleCtrl->AppendText(consoleBuffer);
+ consoleBuffer = "";
+
+ consoleCtrl->ScrollLines(3);
+ }
+
+// consoleCtrl->ShowPosition(consoleCtrl->GetLastPosition());
+
+ /*if (status != CS_DEBUG) {
+ consoleCtrl->AppendText("@");
+ }*/
+ //consoleCtrl->SetInsertionPointEnd();
+
+/* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
+ nlines = consoleCtrl->GetNumberOfLines()-1;
+ }
+
+ if (status == CS_END) {
+ consoleCtrl->ShowPosition(nlines);
+ }*/
}
/*
*/
void wxbMainFrame::Send(wxString str)
{
- ct->Write((const char*)str);
- typeCtrl->SetValue("");
- consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
- (*consoleCtrl) << str;
+ if (ct != NULL) {
+ ct->Write((const char*)str);
+ typeCtrl->SetValue("");
+ consoleCtrl->SetDefaultStyle(wxTextAttr(*wxRED));
+ consoleCtrl->AppendText(str);
+ consoleCtrl->ScrollLines(3);
+ }
+
+/* if ((consoleCtrl->GetNumberOfLines()-1) > nlines) {
+ nlines = consoleCtrl->GetNumberOfLines()-1;
+ }
+
+ consoleCtrl->ShowPosition(nlines);*/
}
/* Enable panels */
/* Enable or disable console typing */
void wxbMainFrame::EnableConsole(bool enable) {
typeCtrl->Enable(enable);
+ sendButton->Enable(enable);
+ if (enable) {
+ typeCtrl->SetFocus();
+ }
}
/*
wxbThreadEvent evt(Thread);
evt.SetEventPrintObject(po);
-
- wxbMainFrame::GetInstance()->AddPendingEvent(evt);
+
+ if (wxbMainFrame::GetInstance()) {
+ wxbMainFrame::GetInstance()->AddPendingEvent(evt);
+ }
}
-wxString csBuffer; /* Temporary buffer for receiving data from console thread */
+//wxString csBuffer; /* Temporary buffer for receiving data from console thread */
/*
* Called by console thread, this function forwards data line by line and end
* signals to the GUI.
*/
-void csprint(char* str, int status)
+void csprint(const char* str, int status)
{
if (str != 0) {
- int len = strlen(str);
- bool allnewline = true;
- for (int i = 0; i < len; i++) {
- if (!(allnewline = (str[i] == '\n')))
- break;
- }
-
- if (allnewline) {
- firePrintEvent(csBuffer << "\n", CS_DATA);
- csBuffer = "";
- for (int i = 1; i < len; i++) {
- firePrintEvent("\n", status);
- }
- }
- else {
- wxStringTokenizer tkz(str, "\n",
- wxTOKEN_RET_DELIMS | wxTOKEN_RET_EMPTY | wxTOKEN_RET_EMPTY_ALL);
-
- while ( tkz.HasMoreTokens() ) {
- csBuffer << tkz.GetNextToken();
- if (csBuffer.Length() != 0) {
- if ((csBuffer.GetChar(csBuffer.Length()-1) == '\n') ||
- (csBuffer.GetChar(csBuffer.Length()-1) == '\r')) {
- firePrintEvent(csBuffer, status);
- csBuffer = "";
- }
- }
- }
- }
-
- if (csBuffer == "$ ") { // Restore console
- firePrintEvent(csBuffer, status);
- csBuffer = "";
- }
+ firePrintEvent(wxString(str), status);
}
-
- if (status != CS_DATA) {
- if (csBuffer.Length() != 0) {
- firePrintEvent(csBuffer, CS_DATA);
- }
- csBuffer = "";
+ else {
firePrintEvent("", status);
}
}