diff --git a/livesupport/src/products/gLiveSupport/src/GLiveSupport.cxx b/livesupport/src/products/gLiveSupport/src/GLiveSupport.cxx index 9b7def4b1..d185ab398 100644 --- a/livesupport/src/products/gLiveSupport/src/GLiveSupport.cxx +++ b/livesupport/src/products/gLiveSupport/src/GLiveSupport.cxx @@ -33,6 +33,18 @@ #include "configure.h" #endif +#ifdef HAVE_PWD_H +#include +#else +#error need pwd.h +#endif + +#ifdef HAVE_SYS_STAT_H +#include +#else +#error need sys/stat.h +#endif + #include #include @@ -70,6 +82,16 @@ using namespace LiveSupport::GLiveSupport; const std::string LiveSupport :: GLiveSupport :: GLiveSupport :: configElementNameStr = "gLiveSupport"; +/*------------------------------------------------------------------------------ + * The name of the configuration file for this class + *----------------------------------------------------------------------------*/ +const std::string configFileDirStr = "/.livesupport/"; + +/*------------------------------------------------------------------------------ + * The name of the configuration file for this class + *----------------------------------------------------------------------------*/ +const std::string configFileNameStr = "gLiveSupport.xml"; + /*------------------------------------------------------------------------------ * The name of the config element for the list of supported languages *----------------------------------------------------------------------------*/ @@ -298,9 +320,19 @@ GLiveSupport :: configure(const xmlpp::Element & element) ++it; } - // save the config file so we can modify it later + // save the configuration so we can modify it later // TODO: move configuration code to the OptionsContainer class? - optionsContainer.reset(new OptionsContainer(element)); + Ptr::Ref configFileName(new Glib::ustring); + struct passwd * pwd = getpwuid(getuid()); + if (pwd) { + configFileName->append(pwd->pw_dir); + } else { + throw std::logic_error("this never happens: getpwuid() returned 0"); + } + configFileName->append(configFileDirStr); + mkdir(configFileName->c_str(), 0700); // create dir if does not exist + configFileName->append(configFileNameStr); + optionsContainer.reset(new OptionsContainer(element, configFileName)); } @@ -531,6 +563,10 @@ GLiveSupport :: logout(void) throw () authentication->logout(sessionId); sessionId.reset(); + if (optionsContainer->isChanged()) { + optionsContainer->writeToFile(); + } + return true; } diff --git a/livesupport/src/products/gLiveSupport/src/OptionsContainer.cxx b/livesupport/src/products/gLiveSupport/src/OptionsContainer.cxx index 4b11cbac2..66fbe6ce1 100644 --- a/livesupport/src/products/gLiveSupport/src/OptionsContainer.cxx +++ b/livesupport/src/products/gLiveSupport/src/OptionsContainer.cxx @@ -34,6 +34,7 @@ #endif #include +#include #include "OptionsContainer.h" @@ -55,8 +56,12 @@ using namespace LiveSupport::GLiveSupport; /*------------------------------------------------------------------------------ * Constructor. *----------------------------------------------------------------------------*/ -OptionsContainer :: OptionsContainer(const xmlpp::Element & optionsElement) +OptionsContainer :: OptionsContainer( + const xmlpp::Element & optionsElement, + Ptr::Ref configFileName) throw () + : configFileName(configFileName), + changed(false) { optionsDocument.create_root_node_by_import(&optionsElement, true); // true == recursive @@ -76,13 +81,13 @@ OptionsContainer :: setOptionItem(OptionItemString optionItem, switch (optionItem) { case outputPlayerDeviceName : - targetNode = getNode("/outputPlayer/audioPlayer/gstreamerPlayer/" + targetNode = getNode("outputPlayer/audioPlayer/gstreamerPlayer/" "@audioDevice"); isAttribute = true; break; case cuePlayerDeviceName : - targetNode = getNode("/cuePlayer/audioPlayer/gstreamerPlayer/" + targetNode = getNode("cuePlayer/audioPlayer/gstreamerPlayer/" "@audioDevice"); isAttribute = true; break; @@ -92,12 +97,14 @@ OptionsContainer :: setOptionItem(OptionItemString optionItem, xmlpp::Attribute * attr = dynamic_cast(targetNode); if (attr != 0) { attr->set_value(*value); + changed = true; return; } } else { xmlpp::TextNode * text = dynamic_cast(targetNode); if (text != 0) { text->set_content(*value); + changed = true; return; } } @@ -177,3 +184,18 @@ OptionsContainer :: getNode(const Glib::ustring & xPath) } } + +/*------------------------------------------------------------------------------ + * Save the options to a file. + *----------------------------------------------------------------------------*/ +void +OptionsContainer :: writeToFile(void) throw () +{ + if (configFileName) { + std::ofstream file(configFileName->c_str()); + optionsDocument.write_to_stream_formatted(file, "utf-8"); + file.close(); + changed = false; + } +} + diff --git a/livesupport/src/products/gLiveSupport/src/OptionsContainer.h b/livesupport/src/products/gLiveSupport/src/OptionsContainer.h index dd956d042..437178186 100644 --- a/livesupport/src/products/gLiveSupport/src/OptionsContainer.h +++ b/livesupport/src/products/gLiveSupport/src/OptionsContainer.h @@ -72,7 +72,17 @@ class OptionsContainer /** * The XML document containing the options. */ - xmlpp::Document optionsDocument; + xmlpp::Document optionsDocument; + + /** + * The file name (including path) used by writeToFile(). + */ + Ptr::Ref configFileName; + + /** + * Remember if we have been changed. + */ + bool changed; /** * Default constructor. @@ -99,9 +109,28 @@ class OptionsContainer * Constructor with XML element parameter. * * @param optionsElement the XML element containing the options + * @param configFileName the name (with path) of the configuration + * file used by writeToFile() + * @see writeToFile() */ - OptionsContainer(const xmlpp::Element & optionsElement) throw (); + OptionsContainer(const xmlpp::Element & optionsElement, + Ptr::Ref configFileName) + throw (); + /** + * Report if the object has been changed. + * + * It returns true if there has been any calls to setOptionItem() + * since its construction or the last call to writeToFile(). + * + * @return whether the options have been changed + */ + bool + isChanged(void) throw () + { + return changed; + } + /** * The list of string options one can set. * @@ -133,6 +162,16 @@ class OptionsContainer Ptr::Ref getOptionItem(OptionItemString optionItem) throw (std::invalid_argument); + + /** + * Save the options to a file. + * + * This writes the options in XML format to the file specified in the + * constructor, under $HOME/.livesupport. If the directory does not + * exist, it is created. + */ + void + writeToFile(void) throw (); }; diff --git a/livesupport/src/products/gLiveSupport/src/OptionsWindow.cxx b/livesupport/src/products/gLiveSupport/src/OptionsWindow.cxx index d0665bffd..9311bd439 100644 --- a/livesupport/src/products/gLiveSupport/src/OptionsWindow.cxx +++ b/livesupport/src/products/gLiveSupport/src/OptionsWindow.cxx @@ -88,14 +88,14 @@ OptionsWindow :: OptionsWindow (Ptr::Ref gLiveSupport, // build up the notepad for the various sections mainNotebook = Gtk::manage(new ScrolledNotebook); - Gtk::Box * aboutSectionBox = constructAboutSection(); Gtk::Box * soundSectionBox = constructSoundSection(); + Gtk::Box * aboutSectionBox = constructAboutSection(); try { - mainNotebook->appendPage(*aboutSectionBox, - *getResourceUstring("aboutSectionLabel")); mainNotebook->appendPage(*soundSectionBox, *getResourceUstring("soundSectionLabel")); + mainNotebook->appendPage(*aboutSectionBox, + *getResourceUstring("aboutSectionLabel")); } catch (std::invalid_argument &e) { // TODO: signal error @@ -156,7 +156,7 @@ OptionsWindow :: OptionsWindow (Ptr::Ref gLiveSupport, void OptionsWindow :: onCancelButtonClicked(void) throw () { - onCloseButtonClicked(); + onCloseButtonClicked(false); } @@ -166,6 +166,33 @@ OptionsWindow :: onCancelButtonClicked(void) throw () void OptionsWindow :: onApplyButtonClicked(void) throw () { + Ptr::Ref + optionsContainer = gLiveSupport->getOptionsContainer(); + + // check for changes in the Sound tab + Ptr::Ref + oldCueDevice = optionsContainer->getOptionItem( + OptionsContainer::cuePlayerDeviceName ); + Ptr::Ref + newCueDevice(new Glib::ustring(cuePlayerEntry->get_text())); + + if (*oldCueDevice != *newCueDevice) { + optionsContainer->setOptionItem( + OptionsContainer::cuePlayerDeviceName, + newCueDevice ); + } + + Ptr::Ref + oldOutputDevice = optionsContainer->getOptionItem( + OptionsContainer::outputPlayerDeviceName ); + Ptr::Ref + newOutputDevice(new Glib::ustring(outputPlayerEntry->get_text())); + + if (*oldOutputDevice != *newOutputDevice) { + optionsContainer->setOptionItem( + OptionsContainer::outputPlayerDeviceName, + newOutputDevice ); + } } @@ -175,7 +202,8 @@ OptionsWindow :: onApplyButtonClicked(void) throw () void OptionsWindow :: onOkButtonClicked(void) throw () { - onCloseButtonClicked(); + onApplyButtonClicked(); + onCloseButtonClicked(false); } @@ -183,8 +211,11 @@ OptionsWindow :: onOkButtonClicked(void) throw () * Event handler for the Close button. *----------------------------------------------------------------------------*/ void -OptionsWindow :: onCloseButtonClicked(void) throw () +OptionsWindow :: onCloseButtonClicked(bool needConfirm) throw () { + if (needConfirm) { + //TODO: add confirmation dialog + } gLiveSupport->putWindowPosition(shared_from_this()); hide(); } @@ -248,7 +279,7 @@ OptionsWindow :: constructSoundSection(void) throw () new Gtk::Label(cuePlayerLabelContents) ); audioDeviceTable->attach(*cuePlayerLabel, 0, 1, 0, 1); - EntryBin * cuePlayerEntry = Gtk::manage(wf->createEntryBin()); + cuePlayerEntry = Gtk::manage(wf->createEntryBin()); cuePlayerEntry->set_text(*optionsContainer->getOptionItem( OptionsContainer::cuePlayerDeviceName )); audioDeviceTable->attach(*cuePlayerEntry, 1, 2, 0, 1); @@ -267,19 +298,14 @@ OptionsWindow :: constructSoundSection(void) throw () new Gtk::Label(outputPlayerLabelContents) ); audioDeviceTable->attach(*outputPlayerLabel, 0, 1, 1, 2); - EntryBin * outputPlayerEntry = Gtk::manage(wf->createEntryBin()); + outputPlayerEntry = Gtk::manage(wf->createEntryBin()); outputPlayerEntry->set_text(*optionsContainer->getOptionItem( OptionsContainer::outputPlayerDeviceName )); audioDeviceTable->attach(*outputPlayerEntry, 1, 2, 1, 2); - // TODO: remove this - Gtk::Label * notFinishedWarning = Gtk::manage(new Gtk::Label( - "Note: device settings can not be edited (yet)." )); - // make a new box and pack the components into it Gtk::VBox * section = Gtk::manage(new Gtk::VBox); section->pack_start(*audioDeviceTable, Gtk::PACK_SHRINK, 5); - section->pack_start(*notFinishedWarning, Gtk::PACK_SHRINK, 20); return section; } diff --git a/livesupport/src/products/gLiveSupport/src/OptionsWindow.h b/livesupport/src/products/gLiveSupport/src/OptionsWindow.h index 087b1fba9..f70f299e1 100644 --- a/livesupport/src/products/gLiveSupport/src/OptionsWindow.h +++ b/livesupport/src/products/gLiveSupport/src/OptionsWindow.h @@ -93,7 +93,7 @@ using namespace LiveSupport::Widgets; */ class OptionsWindow : public WhiteWindow, public LocalizedObject { - protected: + private: /** * The notepad holding the different sections. */ @@ -119,6 +119,16 @@ class OptionsWindow : public WhiteWindow, public LocalizedObject */ Gtk::Button * okButton; + /** + * The entry field for the cue player device's name. + */ + EntryBin * cuePlayerEntry; + + /** + * The entry field for the output player device's name. + */ + EntryBin * outputPlayerEntry; + /** * The gLiveSupport object, handling the logic of the application. */ @@ -129,6 +139,24 @@ class OptionsWindow : public WhiteWindow, public LocalizedObject */ bool isChanged; + /** + * Construct the "About" section. + * + * @return a pointer to the new box (already Gtk::manage()'ed) + */ + Gtk::VBox* + constructAboutSection(void) throw (); + + /** + * Construct the "Sound" section. + * + * @return a pointer to the new box (already Gtk::manage()'ed) + */ + Gtk::VBox* + constructSoundSection(void) throw (); + + + protected: /** * Event handler for the Cancel button. */ @@ -150,28 +178,15 @@ class OptionsWindow : public WhiteWindow, public LocalizedObject /** * Event handler for the Close button. * - * @see WhiteWindow::onCloseButtonClicked() + * @param needConfirm if true, we check if changes has been + * made to the input fields, and if yes, then + * a "save changes?" dialog is displayed + * @see WhiteWindow::onCloseButtonClicked() */ virtual void - onCloseButtonClicked(void) throw (); - - /** - * Construct the "About" section. - * - * @return a pointer to the new box (already Gtk::manage()'ed) - */ - Gtk::VBox* - constructAboutSection(void) throw (); - - /** - * Construct the "Sound" section. - * - * @return a pointer to the new box (already Gtk::manage()'ed) - */ - Gtk::VBox* - constructSoundSection(void) throw (); - + onCloseButtonClicked(bool needConfirm = true) throw (); + public: /** * Constructor.