]> git.lyx.org Git - lyx.git/blobdiff - src/graphics/GraphicsConverter.C
* Make the graphics files conform strictly to the Pimpl idiom by moving
[lyx.git] / src / graphics / GraphicsConverter.C
index ea028a7c54fad60adabcd19daca880fddfb53981..5d08e460d56f8b9e80b52ab1d45fbc22cc2376e5 100644 (file)
@@ -1,9 +1,9 @@
-/*
- * \file GraphicsConverter.C
- * Copyright 2002 the LyX Team
- * Read the file COPYING
+/**
+ *  \file GraphicsConverter.C
+ *  Copyright 2002 the LyX Team
+ *  Read the file COPYING
  *
- * \author Angus Leeming <a.leeming@ic.ac.uk>
+ *  \author Angus Leeming <leeming@lyx.org>
  */
 
 #include <config.h>
 
 #include "converter.h"
 #include "debug.h"
-#include "gettext.h"
-
-#include "frontends/Alert.h"
 
 #include "support/filetools.h"
 #include "support/forkedcall.h"
-#include "support/path.h"
+#include "support/lyxlib.h"
+
+#include <boost/bind.hpp>
+#include <boost/signals/trackable.hpp>
 
+#include "Lsstream.h"
 #include <fstream>
+#include <sys/types.h> // needed for pid_t
 
-namespace {
+extern string system_lyxdir;
 
-string const move_file(string const & from_file, string const & to_file)
-{
-       if (from_file == to_file)
-               return string();
+using std::endl;
 
-       ostringstream command;
-       command << "fromfile=" << from_file << "\n"
-               << "tofile="   << to_file << "\n\n"
-               << "'mv' -f ${fromfile} ${tofile}\n"
-               << "if [ $? -ne 0 ]; then\n"
-               << "\t'cp' -f ${fromfile} ${tofile}\n"
-               << "\tif [ $? -ne 0 ]; then\n"
-               << "\t\texit 1\n"
-               << "\tfi\n"
-               << "\t'rm' -f ${fromfile}\n"
-               << "fi\n";
+namespace grfx {
 
-       return command.str().c_str();
+struct Converter::Impl : public boost::signals::trackable {
+       ///
+       Impl(string const &, string const &, string const &, string const &);
+
+       ///
+       void startConversion();
+
+       /** This method is connected to a signal passed to the forked call
+        *  class, passing control back here when the conversion is completed.
+        *  Cleans-up the temporary files, emits the finishedConversion
+        *  signal and removes the Converter from the list of all processes.
+        */
+       void converted(string const & cmd, pid_t pid, int retval);
+
+       /** At the end of the conversion process inform the outside world
+        *  by emitting a signal.
+        */
+       typedef boost::signal1<void, bool> SignalType;
+       ///
+       SignalType finishedConversion;
+
+       ///
+       string script_command_;
+       ///
+       string script_file_;
+       ///
+       string to_file_;
+       ///
+       bool valid_process_;
+       ///
+       bool finished_;
+};
+
+
+bool Converter::isReachable(string const & from_format_name,
+                           string const & to_format_name)
+{
+       return converters.isReachable(from_format_name, to_format_name);
 }
 
-} // namespace anon
 
-namespace grfx {
+Converter::Converter(string const & from_file,   string const & to_file_base,
+                    string const & from_format, string const & to_format)
+       : pimpl_(new Impl(from_file, to_file_base, from_format, to_format))
+{}
+
+
+Converter::~Converter()
+{}
 
-GConverter & GConverter::get()
+
+void Converter::startConversion() const
 {
-       static GConverter singleton;
-       return singleton;
+       pimpl_->startConversion();
 }
-       
 
-bool GConverter::isReachable(string const & from_format_name,
-                            string const & to_format_name) const
+
+boost::signals::connection Converter::connect(slot_type const & slot) const
 {
-       return converters.isReachable(from_format_name, to_format_name);
+       return pimpl_->finishedConversion.connect(slot);
+}
+
+
+string const & Converter::convertedFile() const
+{
+       static string const empty;
+       return pimpl_->finished_ ? pimpl_->to_file_ : empty;
 }
 
+} // namespace grfx
+
+//------------------------------
+// Implementation details follow
+//------------------------------
+
+namespace {
+
+/** Build the conversion script, returning true if able to build it.
+ *  The script is output to the ostringstream 'script'.
+ */
+bool build_script(string const & from_file, string const & to_file_base,
+                 string const & from_format, string const & to_format,
+                 ostringstream & script);
 
-void GConverter::convert(string const & from_file, string const & to_file_base,
-                        string const & from_format, string const & to_format,
-                        SignalTypePtr on_finish)
+} // namespace anon
+
+
+namespace grfx {
+
+Converter::Impl::Impl(string const & from_file,   string const & to_file_base,
+                     string const & from_format, string const & to_format)
+       : valid_process_(false), finished_(false)
 {
+       lyxerr[Debug::GRAPHICS] << "Converter c-tor:\n"
+               << "\tfrom_file:      " << from_file
+               << "\n\tto_file_base: " << to_file_base
+               << "\n\tfrom_format:  " << from_format
+               << "\n\tto_format:    " << to_format << endl;
+
        // The conversion commands are stored in a stringstream
        ostringstream script;
        script << "#!/bin/sh\n";
-
        bool const success = build_script(from_file, to_file_base,
                                          from_format, to_format, script);
 
-       if (!success) {
-               lyxerr[Debug::GRAPHICS]
-                       << "Unable to build the conversion script" << std::endl;
-               on_finish->emit(string());
+       if (!success)
                return;
-       }
 
-       lyxerr[Debug::GRAPHICS] << "Conversion script:\n\n"
-                               << script.str().c_str() << "\n" << std::endl;
+       lyxerr[Debug::GRAPHICS] << "\tConversion script:"
+                               << "\n--------------------------------------\n"
+                               << script.str().c_str()
+                               << "\n--------------------------------------\n";
 
        // Output the script to file.
        static int counter = 0;
-       string const script_file = OnlyPath(to_file_base) + "lyxconvert" +
-               tostr(counter++) + ".sh";
+       script_file_ = OnlyPath(to_file_base) + "lyxconvert" +
+                      tostr(counter++) + ".sh";
 
-       std::ofstream fs(script_file.c_str());
-       if (!fs.good()) {
-               // Unable to output the conversion script to file.
-               on_finish->emit(string());
+       std::ofstream fs(script_file_.c_str());
+       if (!fs.good())
                return;
-       }
-               
+
        fs << script.str().c_str();
        fs.close();
 
-       // Create a dummy command for ease of understanding of the
+       // The converted image is to be stored in this file
+       // We do not use ChangeExtension here because this is a
+       // basename, which may nevertheless contain a dot
+       to_file_ = to_file_base + '.' + formats.extension(to_format);
+
+       // The command needed to run the conversion process
+       // We create a dummy command for ease of understanding of the
        // list of forked processes.
        // Note that 'sh ' is absolutely essential, or execvp will fail.
-       string const script_command =
-               "sh " + script_file + " " +
-               OnlyFilename(from_file) + " " + to_format;
-
-       string const to_file =
-               ChangeExtension(to_file_base, formats.extension(to_format));
-
-       // Launch the conversion process.
-       ConvProcessPtr shared_ptr;
-       shared_ptr.reset(new ConvProcess(script_file, script_command, 
-                                        to_file, on_finish));
-       all_processes_.push_back(shared_ptr);
+       script_command_ = "sh " + script_file_ + " " +
+                         OnlyFilename(from_file) + " " + to_format;
+
+       // All is ready to go
+       valid_process_ = true;
 }
 
 
-namespace {
+void Converter::Impl::startConversion()
+{
+       if (!valid_process_) {
+               converted(string(), 0, 1);
+               return;
+       }
 
-typedef boost::shared_ptr<ConvProcess> ConvProcessPtr;
-class Find_Ptr {
-public:
-       Find_Ptr(ConvProcess * ptr) : ptr_(ptr) {}
+       // Initiate the conversion
+       Forkedcall::SignalTypePtr convert_ptr;
+       convert_ptr.reset(new Forkedcall::SignalType);
 
-       bool operator()(ConvProcessPtr const & ptr)
-       {
-               return ptr.get() == ptr_;
-       }
+       convert_ptr->connect(
+               boost::bind(&Impl::converted, this, _1, _2, _3));
 
-private:
-       ConvProcess * ptr_;
-};
+       Forkedcall call;
+       int retval = call.startscript(script_command_, convert_ptr);
+       if (retval > 0) {
+               // Unable to even start the script, so clean-up the mess!
+               converted(string(), 0, 1);
+       }
+}
 
-} // namespace anon
 
-void GConverter::erase(ConvProcess * process)
+void Converter::Impl::converted(string const & /* cmd */,
+                               pid_t /* pid */, int retval)
 {
-       std::list<ConvProcessPtr>::iterator begin = all_processes_.begin();
-       std::list<ConvProcessPtr>::iterator end   = all_processes_.end();
-       std::list<ConvProcessPtr>::iterator it =
-               std::find_if(begin, end, Find_Ptr(process));
-
-       if (it == end)
+       if (finished_)
+               // We're done already!
                return;
 
-       all_processes_.erase(it);
+       finished_ = true;
+       // Clean-up behind ourselves
+       lyx::unlink(script_file_);
+
+       if (retval > 0) {
+               lyx::unlink(to_file_);
+               to_file_.erase();
+               finishedConversion(false);
+       } else {
+               finishedConversion(true);
+       }
 }
 
+} // namespace grfx
+
+namespace {
+
+string const move_file(string const & from_file, string const & to_file)
+{
+       if (from_file == to_file)
+               return string();
+
+       ostringstream command;
+       command << "fromfile=" << from_file << "\n"
+               << "tofile="   << to_file << "\n\n"
+               << "'mv' -f ${fromfile} ${tofile}\n"
+               << "if [ $? -ne 0 ]; then\n"
+               << "\t'cp' -f ${fromfile} ${tofile}\n"
+               << "\tif [ $? -ne 0 ]; then\n"
+               << "\t\texit 1\n"
+               << "\tfi\n"
+               << "\t'rm' -f ${fromfile}\n"
+               << "fi\n";
+
+       return command.str().c_str();
+}
 
-bool GConverter::build_script(string const & from_file, 
-                             string const & to_file_base,
-                             string const & from_format,
-                             string const & to_format,
-                             ostringstream & script) const
+bool build_script(string const & from_file,
+                 string const & to_file_base,
+                 string const & from_format,
+                 string const & to_format,
+                 ostringstream & script)
 {
+       lyxerr[Debug::GRAPHICS] << "build_script ... ";
        typedef Converters::EdgePath EdgePath;
 
        string const to_file = ChangeExtension(to_file_base,
@@ -168,16 +257,14 @@ bool GConverter::build_script(string const & from_file,
 
        if (from_format == to_format) {
                script << move_file(QuoteName(from_file), QuoteName(to_file));
+               lyxerr[Debug::GRAPHICS] << "ready (from == to)" << endl;
                return true;
        }
 
        EdgePath edgepath = converters.getPath(from_format, to_format);
 
        if (edgepath.empty()) {
-               Alert::alert(_("Cannot convert file"),
-                          _("No information for converting from ")
-                          + formats.prettyName(from_format) + _(" to ")
-                          + formats.prettyName(to_format));
+               lyxerr[Debug::GRAPHICS] << "ready (edgepath.empty())" << endl;
                return false;
        }
 
@@ -195,11 +282,13 @@ bool GConverter::build_script(string const & from_file,
        string const token_from("$$i");
        string const token_base("$$b");
        string const token_to("$$o");
+       string const token_lib("$$s");
 
        EdgePath::const_iterator it  = edgepath.begin();
        EdgePath::const_iterator end = edgepath.end();
+
        for (; it != end; ++it) {
-               Converter const & conv = converters.get(*it);
+               ::Converter const & conv = converters.get(*it);
 
                // Build the conversion command
                string const infile      = outfile;
@@ -215,11 +304,12 @@ bool GConverter::build_script(string const & from_file,
                command = subst(command, token_from, "${infile}");
                command = subst(command, token_base, "${infile_base}");
                command = subst(command, token_to,   "${outfile}");
+               command = subst(command, token_lib,  system_lyxdir + "scripts");
 
                // Store in the shell script
                script << "\n" << command << "\n\n";
 
-               // Test that this was successful. If not, remove 
+               // Test that this was successful. If not, remove
                // ${outfile} and exit the shell script
                script << "if [ $? -ne 0 ]; then\n"
                       << "\t'rm' -f ${outfile}\n"
@@ -248,46 +338,9 @@ bool GConverter::build_script(string const & from_file,
 
        // Move the final outfile to to_file
        script << move_file("${outfile}", QuoteName(to_file));
+       lyxerr[Debug::GRAPHICS] << "ready!" << endl;
 
        return true;
 }
 
-
-ConvProcess::ConvProcess(string const & script_file, 
-                        string const & script_command,
-                        string const & to_file, SignalTypePtr on_finish)
-       : script_file_(script_file), to_file_(to_file), on_finish_(on_finish)
-{
-       Forkedcall::SignalTypePtr convert_ptr;
-       convert_ptr.reset(new Forkedcall::SignalType);
-
-       convert_ptr->connect(SigC::slot(this, &ConvProcess::converted));
-
-       Forkedcall call;
-       int retval = call.startscript(script_command, convert_ptr);
-       if (retval > 0) {
-               // Unable to even start the script, so clean-up the mess!
-               converted(string(), 0, 1);
-       }
-}
-
-
-void ConvProcess::converted(string /* cmd */, pid_t /* pid */, int retval)
-{
-       // Clean-up behind ourselves
-       lyx::unlink(script_file_);
-
-       if (retval > 0) {
-               lyx::unlink(to_file_);
-               to_file_.erase();
-       }
-
-       if (on_finish_.get()) {
-               on_finish_->emit(to_file_);
-       }
-
-       grfx::GConverter::get().erase(this);
-}
-
-
-} // namespace grfx
+} // namespace anon