commit 7b48e2aac0d63fe23cbeadf6ddd50ab0c26c1b6e
Author: Georg Baum <b...@lyx.org>
Date:   Sun Jan 11 14:06:17 2015 +0100

    Start qt event loop for commandline usage
    
    This is needed since src/support calls lots of qt code, and some parts of it
    (e.g. QFileInfo) require a running event loop. This fixes bug #4917 which is
    a symptom of the problem.
    The fix is to create a QCoreApplication for tex2lyx, lyxclient and LyX 
running
    without GUI. In the future this could be extended, i.e. to split up the
    frontend Application class in a GUI and a console class, and having the
    console class use LyXConsoleApp in the same way as Application now uses
    GuiApplication. If that is done, one could also think of moving
    support/ConsoleApplication to frontend/console/ConsoleApplication.

diff --git a/src/LyX.cpp b/src/LyX.cpp
index 0a21b4b..8b75b44 100644
--- a/src/LyX.cpp
+++ b/src/LyX.cpp
@@ -50,6 +50,7 @@
 #include "frontends/alert.h"
 #include "frontends/Application.h"
 
+#include "support/ConsoleApplication.h"
 #include "support/lassert.h"
 #include "support/debug.h"
 #include "support/environment.h"
@@ -195,6 +196,27 @@ struct LyX::Impl {
 };
 
 
+/// The main application class for console mode
+class LyXConsoleApp : public ConsoleApplication
+{
+public:
+       LyXConsoleApp(LyX * lyx, int & argc, char * argv[])
+               : ConsoleApplication(lyx_package, argc, argv), lyx_(lyx),
+                 argc_(argc), argv_(argv)
+       {
+       }
+       void doExec()
+       {
+               int const exit_status = lyx_->execWithoutGui(argc_, argv_);
+               exit(exit_status);
+       }
+private:
+       LyX * lyx_;
+       int & argc_;
+       char ** argv_;
+};
+
+
 ///
 frontend::Application * theApp()
 {
@@ -287,39 +309,13 @@ int LyX::exec(int & argc, char * argv[])
        setLocale();
 
        if (!use_gui) {
-               // FIXME: create a ConsoleApplication
-               int exit_status = init(argc, argv);
-               if (exit_status) {
-                       prepareExit();
-                       return exit_status;
-               }
-
-               // this is correct, since return values are inverted.
-               exit_status = !loadFiles();
+               LyXConsoleApp app(this, argc, argv);
 
-               if (pimpl_->batch_commands.empty() || 
pimpl_->buffer_list_.empty()) {
-                       prepareExit();
-                       return exit_status;
-               }
+               // Reestablish our defaults, as Qt overwrites them
+               // after creating app
+               setLocale();//???
 
-               BufferList::iterator begin = pimpl_->buffer_list_.begin();
-
-               bool final_success = false;
-               for (BufferList::iterator I = begin; I != 
pimpl_->buffer_list_.end(); ++I) {
-                       Buffer * buf = *I;
-                       if (buf != buf->masterBuffer())
-                               continue;
-                       vector<string>::const_iterator bcit  = 
pimpl_->batch_commands.begin();
-                       vector<string>::const_iterator bcend = 
pimpl_->batch_commands.end();
-                       DispatchResult dr;
-                       for (; bcit != bcend; ++bcit) {
-                               LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " 
<< *bcit);
-                               buf->dispatch(*bcit, dr);
-                               final_success |= !dr.error();
-                       }
-               }
-               prepareExit();
-               return !final_success;
+               return app.exec();
        }
 
        // Let the frontend parse and remove all arguments that it knows
@@ -472,6 +468,43 @@ int LyX::init(int & argc, char * argv[])
 }
 
 
+int LyX::execWithoutGui(int & argc, char * argv[])
+{
+       int exit_status = init(argc, argv);
+       if (exit_status) {
+               prepareExit(); 
+               return exit_status;   
+       }                      
+
+       // this is correct, since return values are inverted.
+       exit_status = !loadFiles();
+
+       if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
+               prepareExit();
+               return exit_status;
+       }
+
+       BufferList::iterator begin = pimpl_->buffer_list_.begin();
+
+       bool final_success = false;
+       for (BufferList::iterator I = begin; I != pimpl_->buffer_list_.end(); 
++I) {
+               Buffer * buf = *I;
+               if (buf != buf->masterBuffer())
+                       continue;
+               vector<string>::const_iterator bcit  = 
pimpl_->batch_commands.begin();
+               vector<string>::const_iterator bcend = 
pimpl_->batch_commands.end();
+               DispatchResult dr;
+               for (; bcit != bcend; ++bcit) {
+                       LYXERR(Debug::ACTION, "Buffer::dispatch: cmd: " << 
*bcit);
+                       buf->dispatch(*bcit, dr);
+                       final_success |= !dr.error();
+               }
+       }
+       prepareExit();
+       return !final_success;
+}
+
+
 bool LyX::loadFiles()
 {
        LATTEST(!use_gui);
diff --git a/src/LyX.h b/src/LyX.h
index 70b8b7e..ccce7a3 100644
--- a/src/LyX.h
+++ b/src/LyX.h
@@ -60,8 +60,8 @@ class Application;
 
 /// initial startup
 class LyX {
+       friend class LyXConsoleApp;
 public:
-
        LyX();
        ~LyX();
 
@@ -85,6 +85,9 @@ private:
        */
        int init(int & argc, char * argv[]);
 
+       /// Execute commandline commands if no GUI was requested.
+       int execWithoutGui(int & argc, char * argv[]);
+
        /// Execute batch commands if available.
        void execCommands();
 
diff --git a/src/client/client.cpp b/src/client/client.cpp
index c781bc6..4d7eea5 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -12,6 +12,7 @@
 
 #include <config.h>
 
+#include "support/ConsoleApplication.h"
 #include "support/debug.h"
 #include "support/FileName.h"
 #include "support/FileNameList.h"
@@ -54,6 +55,7 @@
 #include <cerrno>
 #include <cstdio>
 #include <cstdlib>
+#include <exception>
 #include <string>
 #include <vector>
 #include <map>
@@ -427,6 +429,16 @@ namespace cmdline {
     docstring mainTmp(from_ascii("/tmp"));
 
 
+class StopException : public exception
+{
+public:
+       StopException(int status) : status_(status) {}
+       int status() const { return status_; }
+private:
+       int status_;
+};
+
+
 void usage()
 {
        cerr <<
@@ -453,7 +465,7 @@ void usage()
 int h(vector<docstring> const &)
 {
        usage();
-       exit(0);
+       throw StopException(EXIT_SUCCESS);
 }
 
 
@@ -548,15 +560,38 @@ int p(vector<docstring> const & arg)
 
 
 } // namespace cmdline
-} // namespace lyx
 
-
-int main(int argc, char * argv[])
+/// The main application class
+class LyXClientApp : public ConsoleApplication
 {
-       using namespace lyx;
-       lyxerr.setStream(cerr);
+public:
+       LyXClientApp(int & argc, char * argv[])
+               : ConsoleApplication("client" PROGRAM_SUFFIX, argc, argv),
+                 argc_(argc), argv_(argv)
+       {
+       }
+       void doExec()
+       {
+               try {
+                       int const exit_status = run();
+                       exit(exit_status);
+               }
+               catch (cmdline::StopException & e) {
+                       exit(e.status());
+               }
+       }
+private:
+       int run();
+       int & argc_;
+       char ** argv_;
+};
 
 
+int LyXClientApp::run()
+{
+       // qt changes this, and our numeric conversions require the C locale
+       setlocale(LC_NUMERIC, "C");
+
        // Set defaults
        char const * const lyxsocket = getenv("LYXSOCKET");
        if (lyxsocket)
@@ -576,11 +611,11 @@ int main(int argc, char * argv[])
        args.helper["-p"] = cmdline::p;
 
        // Command line failure conditions:
-       if ((!args.parse(argc, argv))
+       if ((!args.parse(argc_, argv_))
           || (args.isset["-c"] && args.isset["-g"])
           || (args.isset["-a"] && args.isset["-p"])) {
                cmdline::usage();
-               return 1;
+               return EXIT_FAILURE;
        }
 
        scoped_ptr<LyXDataSocket> server;
@@ -677,6 +712,17 @@ int main(int argc, char * argv[])
        return EXIT_SUCCESS;
 }
 
+} // namespace lyx
+
+
+int main(int argc, char * argv[])
+{
+       lyx::lyxerr.setStream(cerr);
+
+       lyx::LyXClientApp app(argc, argv);
+       return app.exec();
+}
+
 
 namespace boost {
 
diff --git a/src/support/ConsoleApplication.cpp 
b/src/support/ConsoleApplication.cpp
new file mode 100644
index 0000000..ace4fc5
--- /dev/null
+++ b/src/support/ConsoleApplication.cpp
@@ -0,0 +1,51 @@
+/**
+ * \file ConsoleApplication.cpp
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Georg Baum
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#include <config.h>
+
+#include "support/ConsoleApplication.h"
+
+#include "support/ConsoleApplicationPrivate.h"
+
+
+namespace lyx {
+
+namespace support {
+
+
+ConsoleApplication::~ConsoleApplication()
+{
+       delete d;
+}
+
+
+ConsoleApplication::ConsoleApplication(std::string const & app,
+               int & argc, char ** argv)
+       : d(new ConsoleApplicationPrivate(this, app, argc, argv))
+{
+}
+
+
+int ConsoleApplication::exec()
+{
+       return d->execute();
+}
+
+
+void ConsoleApplication::exit(int status)
+{
+       d->exit(status);
+}
+
+
+} // namespace support
+} // namespace lyx
+
+#include "moc_ConsoleApplicationPrivate.cpp"
diff --git a/src/support/ConsoleApplication.h b/src/support/ConsoleApplication.h
new file mode 100644
index 0000000..bfb5d07
--- /dev/null
+++ b/src/support/ConsoleApplication.h
@@ -0,0 +1,61 @@
+/**
+ * \file ConsoleApplication.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Georg Baum
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef CONSOPLEAPPLICATION_H
+#define CONSOPLEAPPLICATION_H
+
+#include "support/strfwd.h"
+
+namespace lyx {
+
+namespace support {
+
+class ConsoleApplicationPrivate;
+
+/// The main application class for console mode.
+/**
+There should be only one instance of this class. No Qt object
+initialisation should be done before the instantiation of this class.
+This class could be moved to src/frontends/console in the future.
+This would make sense if more console app related code is created. 
+*/
+class ConsoleApplication
+{
+       friend class ConsoleApplicationPrivate;
+public:
+       ConsoleApplication(std::string const & app, int & argc, char ** argv);
+       virtual ~ConsoleApplication();
+
+       /// Start the event loop and execute the application
+       int exec();
+
+protected:
+       /// Do the real work.
+       /// This is called after the event loop was started.
+       virtual void doExec() = 0;
+       /**
+        * Exit the application with status \p status.
+        * This must be called from doExec(), otherwise the application runs
+        * forever.
+        * Note that in contrast to the ISO C function ::exit() this method does
+        * return. It only registers that the program is to be stopped with the
+        * given status code, and this happens the next time the event loop is
+        * processed.
+        */
+       void exit(int status);
+
+private:
+       ConsoleApplicationPrivate * const d;
+};
+
+} // namespace support
+} // namespace lyx
+
+#endif // CONSOPLEAPPLICATION_H
diff --git a/src/support/ConsoleApplicationPrivate.h 
b/src/support/ConsoleApplicationPrivate.h
new file mode 100644
index 0000000..74aa161
--- /dev/null
+++ b/src/support/ConsoleApplicationPrivate.h
@@ -0,0 +1,64 @@
+/**
+ * \file ConsoleApplicationPrivate.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author Georg Baum
+ *
+ * Full author contact details are available in file CREDITS.
+ */
+
+#ifndef CONSOPLEAPPLICATIONPRIVATE_H
+#define CONSOPLEAPPLICATIONPRIVATE_H
+
+#include "support/qstring_helpers.h"
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QTimer>
+
+#include <string>
+
+
+namespace lyx {
+
+namespace support {
+
+class ConsoleApplication;
+
+class ConsoleApplicationPrivate : public QCoreApplication
+{
+       Q_OBJECT
+public:
+       ConsoleApplicationPrivate(ConsoleApplication * owner,
+               std::string const & app, int & argc, char ** argv)
+               : QCoreApplication(argc, argv), owner_(owner)
+       {
+               setOrganizationName("LyX");
+               setOrganizationDomain("lyx.org");
+               setApplicationName(toqstr(app));
+
+               qsrand(QDateTime::currentDateTime().toTime_t());
+       }
+       int execute()
+       {
+               // set timer to do the work asynchronously after the event
+               // loop was started
+               QTimer::singleShot(0, this, SLOT(doExec()));
+               // start event loop
+               return exec();
+       }
+private Q_SLOTS:
+       void doExec()
+       {
+               owner_->doExec();
+       }
+private:
+       ConsoleApplication * owner_;
+};
+
+
+} // namespace support
+} // namespace lyx
+
+#endif // CONSOPLEAPPLICATIONPRIVATE_H
diff --git a/src/support/Makefile.am b/src/support/Makefile.am
index fe7be80..b843b51 100644
--- a/src/support/Makefile.am
+++ b/src/support/Makefile.am
@@ -13,7 +13,9 @@ BUILT_SOURCES = $(PCH_FILE)
 #########################  Qt stuff  #############################
 #
 
-MOCHEADER = SystemcallPrivate.h
+MOCHEADER = \
+       ConsoleApplicationPrivate.h \
+       SystemcallPrivate.h
 
 MOCEDFILES = $(MOCHEADER:%.h=moc_%.cpp)
 
@@ -36,6 +38,9 @@ liblyxsupport_a_SOURCES = \
        FileMonitor.cpp \
        RandomAccessList.h \
        bind.h \
+       ConsoleApplication.cpp \
+       ConsoleApplication.h \
+       ConsoleApplicationPrivate.h \
        convert.cpp \
        convert.h \
        copied_ptr.h \
diff --git a/src/tex2lyx/tex2lyx.cpp b/src/tex2lyx/tex2lyx.cpp
index dc46770..2104854 100644
--- a/src/tex2lyx/tex2lyx.cpp
+++ b/src/tex2lyx/tex2lyx.cpp
@@ -24,6 +24,7 @@
 #include "Preamble.h"
 #include "TextClass.h"
 
+#include "support/ConsoleApplication.h"
 #include "support/convert.h"
 #include "support/ExceptionMessage.h"
 #include "support/filetools.h"
@@ -35,6 +36,7 @@
 
 #include <cstdlib>
 #include <algorithm>
+#include <exception>
 #include <iostream>
 #include <string>
 #include <sstream>
@@ -531,6 +533,44 @@ int error_code = 0;
 typedef int (*cmd_helper)(string const &, string const &);
 
 
+class StopException : public exception
+{
+       public:
+               StopException(int status) : status_(status) {}
+               int status() const { return status_; }
+       private:
+               int status_;
+};
+
+
+/// The main application class
+class TeX2LyXApp : public ConsoleApplication
+{
+public:
+       TeX2LyXApp(int & argc, char * argv[])
+               : ConsoleApplication("tex2lyx" PROGRAM_SUFFIX, argc, argv),
+                 argc_(argc), argv_(argv)
+       {
+       }
+       void doExec()
+       {
+               try {
+                       int const exit_status = run();
+                       exit(exit_status);
+               }
+               catch (StopException & e) {
+                       exit(e.status());
+               }
+       }
+private:
+       void easyParse();
+       /// Do the real work
+       int run();
+       int & argc_;
+       char ** argv_;
+};
+
+
 int parse_help(string const &, string const &)
 {
        cout << "Usage: tex2lyx [options] infile.tex [outfile.lyx]\n"
@@ -558,7 +598,7 @@ int parse_help(string const &, string const &)
                "\tand \"SYSDIR/layouts\" are searched for layout and module 
files.\n"
                "Check the tex2lyx man page for more details."
             << endl;
-       exit(error_code);
+       throw StopException(error_code);
 }
 
 
@@ -570,14 +610,14 @@ int parse_version(string const &, string const &)
             << endl;
 
        cout << lyx_version_info << endl;
-       exit(error_code);
+       throw StopException(error_code);
 }
 
 
 void error_message(string const & message)
 {
        cerr << "tex2lyx: " << message << "\n\n";
-       error_code = 1;
+       error_code = EXIT_FAILURE;
        parse_help(string(), string());
 }
 
@@ -687,7 +727,7 @@ int parse_copyfiles(string const &, string const &)
 }
 
 
-void easyParse(int & argc, char * argv[])
+void TeX2LyXApp::easyParse()
 {
        map<string, cmd_helper> cmdmap;
 
@@ -710,29 +750,29 @@ void easyParse(int & argc, char * argv[])
        cmdmap["-roundtrip"] = parse_roundtrip;
        cmdmap["-copyfiles"] = parse_copyfiles;
 
-       for (int i = 1; i < argc; ++i) {
+       for (int i = 1; i < argc_; ++i) {
                map<string, cmd_helper>::const_iterator it
-                       = cmdmap.find(argv[i]);
+                       = cmdmap.find(argv_[i]);
 
                // don't complain if not found - may be parsed later
                if (it == cmdmap.end()) {
-                       if (argv[i][0] == '-')
-                               error_message(string("Unknown option `") + 
argv[i] + "'.");
+                       if (argv_[i][0] == '-')
+                               error_message(string("Unknown option `") + 
argv_[i] + "'.");
                        else
                                continue;
                }
 
-               string arg = (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
-               string arg2 = (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
+               string arg = (i + 1 < argc_) ? os::utf8_argv(i + 1) : string();
+               string arg2 = (i + 2 < argc_) ? os::utf8_argv(i + 2) : string();
 
                int const remove = 1 + it->second(arg, arg2);
 
                // Now, remove used arguments by shifting
                // the following ones remove places down.
                os::remove_internal_args(i, remove);
-               argc -= remove;
-               for (int j = i; j < argc; ++j)
-                       argv[j] = argv[j + remove];
+               argc_ -= remove;
+               for (int j = i; j < argc_; ++j)
+                       argv_[j] = argv_[j + remove];
                --i;
        }
 }
@@ -960,18 +1000,13 @@ bool tex2tex(string const & infilename, FileName const & 
outfilename,
        return false;
 }
 
-} // namespace lyx
 
+namespace {
 
-int main(int argc, char * argv[])
+int TeX2LyXApp::run()
 {
-       using namespace lyx;
-
-       //setlocale(LC_CTYPE, "");
-
-       lyxerr.setStream(cerr);
-
-       os::init(argc, argv);
+       // qt changes this, and our numeric conversions require the C locale
+       setlocale(LC_NUMERIC, "C");
 
        try {
                init_package(internal_path(os::utf8_argv(0)), string(), 
string());
@@ -982,9 +1017,9 @@ int main(int argc, char * argv[])
                        return EXIT_FAILURE;
        }
 
-       easyParse(argc, argv);
+       easyParse();
 
-       if (argc <= 1)
+       if (argc_ <= 1)
                error_message("Not enough arguments.");
 
        try {
@@ -1017,7 +1052,7 @@ int main(int argc, char * argv[])
        infilename = makeAbsPath(infilename).absFileName();
 
        string outfilename;
-       if (argc > 2) {
+       if (argc_ > 2) {
                outfilename = internal_path(os::utf8_argv(2));
                if (outfilename != "-")
                        outfilename = makeAbsPath(outfilename).absFileName();
@@ -1107,11 +1142,27 @@ int main(int argc, char * argv[])
                        if (tex2tex(infilename, FileName(outfilename), 
default_encoding))
                                return EXIT_SUCCESS;
                } else {
-                       if (tex2lyx(infilename, FileName(outfilename), 
default_encoding))
+                       if (lyx::tex2lyx(infilename, FileName(outfilename), 
default_encoding))
                                return EXIT_SUCCESS;
                }
        }
        return EXIT_FAILURE;
 }
 
+} // anonymous namespace
+} // namespace lyx
+
+
+int main(int argc, char * argv[])
+{
+       //setlocale(LC_CTYPE, "");
+
+       lyx::lyxerr.setStream(cerr);
+
+       os::init(argc, argv);
+
+       lyx::TeX2LyXApp app(argc, argv);
+       return app.exec();
+}
+
 // }])

Reply via email to