]> git.lyx.org Git - lyx.git/blobdiff - src/LyX.cpp
* add PreBabelPreamble to Language definition (fixes #4786).
[lyx.git] / src / LyX.cpp
index 30cb397700c80164464487605f34758692d53082..7c2376f980082b8a8575c2f8b391fb01cafcd683 100644 (file)
@@ -4,10 +4,10 @@
  * Licence details can be found in the file COPYING.
  *
  * \author Alfredo Braunstein
- * \author Lars Gullik Bjønnes
+ * \author Lars Gullik Bjønnes
  * \author Jean-Marc Lasgouttes
  * \author John Levon
- * \author André Pönitz
+ * \author André Pönitz
  *
  * Full author contact details are available in file CREDITS.
  */
 
 #include "LyX.h"
 
-#include "LayoutFile.h"
+#include "AppleSpellChecker.h"
+#include "AspellChecker.h"
 #include "Buffer.h"
 #include "BufferList.h"
 #include "CmdDef.h"
-#include "Color.h"
+#include "ColorSet.h"
 #include "ConverterCache.h"
 #include "Converter.h"
 #include "CutAndPaste.h"
+#include "EnchantChecker.h"
 #include "Encoding.h"
 #include "ErrorList.h"
 #include "Format.h"
 #include "FuncStatus.h"
+#include "HunspellChecker.h"
 #include "KeyMap.h"
 #include "Language.h"
+#include "LayoutFile.h"
 #include "Lexer.h"
+#include "LyX.h"
 #include "LyXAction.h"
-#include "LyXFunc.h"
 #include "LyXRC.h"
 #include "ModuleList.h"
 #include "Mover.h"
 #include "Server.h"
 #include "ServerSocket.h"
 #include "Session.h"
-#include "ToolbarBackend.h"
 
 #include "frontends/alert.h"
 #include "frontends/Application.h"
 
+#include "graphics/Previews.h"
+
+#include "support/lassert.h"
 #include "support/debug.h"
 #include "support/environment.h"
 #include "support/ExceptionMessage.h"
@@ -57,7 +63,7 @@
 #include "support/Path.h"
 #include "support/Systemcall.h"
 
-#include <boost/bind.hpp>
+#include "support/bind.h"
 #include <boost/scoped_ptr.hpp>
 
 #include <algorithm>
@@ -83,6 +89,22 @@ namespace os = support::os;
 
 bool use_gui = true;
 
+
+// We default to open documents in an already running instance, provided that
+// the lyxpipe has been setup. This can be overridden either on the command
+// line or through preference settings.
+
+RunMode run_mode = PREFERRED;
+
+
+// Tell what files can be silently overwritten during batch export.
+// Possible values are: NO_FILES, MAIN_FILE, ALL_FILES, UNSPECIFIED.
+// Unless specified on command line (through the -f switch) or through the
+// environment variable LYX_FORCE_OVERWRITE, the default will be MAIN_FILE.
+
+OverwriteFiles force_overwrite = UNSPECIFIED;
+
+
 namespace {
 
 // Filled with the command line arguments "foo" of "-sysdir foo" or
@@ -115,19 +137,25 @@ void reconfigureUserLyXDir()
 
 } // namespace anon
 
-
 /// The main application class private implementation.
 struct LyX::Impl
 {
-       Impl()
+       Impl() : spell_checker_(0), apple_spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0)
        {
                // Set the default User Interface language as soon as possible.
                // The language used will be derived from the environment
                // variables.
                messages_["GUI"] = Messages();
        }
-       /// our function handler
-       LyXFunc lyxfunc_;
+
+       ~Impl()
+       {
+               delete apple_spell_checker_;
+               delete aspell_checker_;
+               delete enchant_checker_;
+               delete hunspell_checker_;
+       }
+
        ///
        BufferList buffer_list_;
        ///
@@ -163,7 +191,20 @@ struct LyX::Impl
        /// has this user started lyx for the first time?
        bool first_start;
        /// the parsed command line batch command if any
-       string batch_command;
+       vector<string> batch_commands;
+
+       ///
+       graphics::Previews preview_;
+       ///
+       SpellChecker * spell_checker_;
+       ///
+       SpellChecker * apple_spell_checker_;
+       ///
+       SpellChecker * aspell_checker_;
+       ///
+       SpellChecker * enchant_checker_;
+       ///
+       SpellChecker * hunspell_checker_;
 };
 
 ///
@@ -179,10 +220,11 @@ frontend::Application * theApp()
 LyX::~LyX()
 {
        delete pimpl_;
+       singleton_ = 0;
 }
 
 
-void LyX::exit(int exit_code) const
+void lyx_exit(int exit_code)
 {
        if (exit_code)
                // Something wrong happened so better save everything, just in
@@ -202,20 +244,6 @@ void LyX::exit(int exit_code) const
 }
 
 
-LyX & LyX::ref()
-{
-       BOOST_ASSERT(singleton_);
-       return *singleton_;
-}
-
-
-LyX const & LyX::cref()
-{
-       BOOST_ASSERT(singleton_);
-       return *singleton_;
-}
-
-
 LyX::LyX()
        : first_start(false)
 {
@@ -224,105 +252,7 @@ LyX::LyX()
 }
 
 
-BufferList & LyX::bufferList()
-{
-       return pimpl_->buffer_list_;
-}
-
-
-BufferList const & LyX::bufferList() const
-{
-       return pimpl_->buffer_list_;
-}
-
-
-Session & LyX::session()
-{
-       BOOST_ASSERT(pimpl_->session_.get());
-       return *pimpl_->session_.get();
-}
-
-
-Session const & LyX::session() const
-{
-       BOOST_ASSERT(pimpl_->session_.get());
-       return *pimpl_->session_.get();
-}
-
-
-LyXFunc & LyX::lyxFunc()
-{
-       return pimpl_->lyxfunc_;
-}
-
-
-LyXFunc const & LyX::lyxFunc() const
-{
-       return pimpl_->lyxfunc_;
-}
-
-
-Server & LyX::server()
-{
-       BOOST_ASSERT(pimpl_->lyx_server_.get());
-       return *pimpl_->lyx_server_.get();
-}
-
-
-Server const & LyX::server() const
-{
-       BOOST_ASSERT(pimpl_->lyx_server_.get());
-       return *pimpl_->lyx_server_.get();
-}
-
-
-ServerSocket & LyX::socket()
-{
-       BOOST_ASSERT(pimpl_->lyx_socket_.get());
-       return *pimpl_->lyx_socket_.get();
-}
-
-
-ServerSocket const & LyX::socket() const
-{
-       BOOST_ASSERT(pimpl_->lyx_socket_.get());
-       return *pimpl_->lyx_socket_.get();
-}
-
-
-frontend::Application & LyX::application()
-{
-       BOOST_ASSERT(pimpl_->application_.get());
-       return *pimpl_->application_.get();
-}
-
-
-frontend::Application const & LyX::application() const
-{
-       BOOST_ASSERT(pimpl_->application_.get());
-       return *pimpl_->application_.get();
-}
-
-
-CmdDef & LyX::topLevelCmdDef()
-{
-       return pimpl_->toplevel_cmddef_;
-}
-
-
-Converters & LyX::converters()
-{
-       return pimpl_->converters_;
-}
-
-
-Converters & LyX::systemConverters()
-{
-       return pimpl_->system_converters_;
-}
-
-
-Messages & LyX::getMessages(string const & language)
+Messages & LyX::messages(string const & language)
 {
        map<string, Messages>::iterator it = pimpl_->messages_.find(language);
 
@@ -332,37 +262,54 @@ Messages & LyX::getMessages(string const & language)
        pair<map<string, Messages>::iterator, bool> result =
                        pimpl_->messages_.insert(make_pair(language, Messages(language)));
 
-       BOOST_ASSERT(result.second);
+       LASSERT(result.second, /**/);
        return result.first->second;
 }
 
 
-Messages & LyX::getGuiMessages()
-{
-       return pimpl_->messages_["GUI"];
-}
-
-
-void LyX::setGuiLanguage(string const & language)
+void setRcGuiLanguage()
 {
-       pimpl_->messages_["GUI"] = Messages(language);
+       LASSERT(singleton_, /**/);
+       if (lyxrc.gui_language == "auto")
+               return;
+       Language const * language = languages.getLanguage(lyxrc.gui_language);
+       if (language) {
+               LYXERR(Debug::LOCALE, "Setting LANGUAGE to " << language->code());
+               if (!setEnv("LANGUAGE", language->code()))
+                       LYXERR(Debug::LOCALE, "\t... failed!");
+       }
+       LYXERR(Debug::LOCALE, "Setting LC_ALL to en_US");
+       if (!setEnv("LC_ALL", "en_US"))
+               LYXERR(Debug::LOCALE, "\t... failed!");
+       Messages::init();
+       singleton_->pimpl_->messages_["GUI"] = Messages();
 }
 
 
 int LyX::exec(int & argc, char * argv[])
 {
+       // Minimal setting of locale before parsing command line
+       try {
+               init_package(os::utf8_argv(0), string(), string(),
+                       top_build_dir_is_one_level_up);
+       } catch (ExceptionMessage const & message) {
+               LYXERR(Debug::LOCALE, message.title_ + ", " + message.details_);
+       }
+       // FIXME: This breaks out of source build under Windows.
+       locale_init();
+
        // Here we need to parse the command line. At least
        // we need to parse for "-dbg" and "-help"
        easyParse(argc, argv);
 
        try {
-               init_package(to_utf8(from_local8bit(argv[0])),
-                             cl_system_support, cl_user_support,
-                             top_build_dir_is_one_level_up);
+               init_package(os::utf8_argv(0),
+                       cl_system_support, cl_user_support,
+                       top_build_dir_is_one_level_up);
        } catch (ExceptionMessage const & message) {
                if (message.type_ == ErrorException) {
                        Alert::error(message.title_, message.details_);
-                       exit(1);
+                       lyx_exit(1);
                } else if (message.type_ == WarningException) {
                        Alert::warning(message.title_, message.details_);
                }
@@ -380,11 +327,12 @@ int LyX::exec(int & argc, char * argv[])
                        return exit_status;
                }
 
-               loadFiles();
+               // this is correct, since return values are inverted.
+               exit_status = !loadFiles();
 
-               if (pimpl_->batch_command.empty() || pimpl_->buffer_list_.empty()) {
+               if (pimpl_->batch_commands.empty() || pimpl_->buffer_list_.empty()) {
                        prepareExit();
-                       return EXIT_SUCCESS;
+                       return exit_status;
                }
 
                BufferList::iterator begin = pimpl_->buffer_list_.begin();
@@ -394,9 +342,14 @@ int LyX::exec(int & argc, char * argv[])
                        Buffer * buf = *I;
                        if (buf != buf->masterBuffer())
                                continue;
-                       bool success = false;
-                       buf->dispatch(pimpl_->batch_command, &success);
-                       final_success |= success;
+                       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;
@@ -405,6 +358,10 @@ int LyX::exec(int & argc, char * argv[])
        // Let the frontend parse and remove all arguments that it knows
        pimpl_->application_.reset(createApplication(argc, argv));
 
+       // Reestablish our defaults, as Qt overwrites them
+       // after createApplication()
+       locale_init();
+
        // Parse and remove all known arguments in the LyX singleton
        // Give an error for all remaining ones.
        int exit_status = init(argc, argv);
@@ -416,21 +373,36 @@ int LyX::exec(int & argc, char * argv[])
                return exit_status;
        }
 
+       // If not otherwise specified by a command line option or
+       // by preferences, we default to reuse a running instance.
+       if (run_mode == PREFERRED)
+               run_mode = USE_REMOTE;
+
        // FIXME
        /* Create a CoreApplication class that will provide the main event loop
        * and the socket callback registering. With Qt4, only QtCore
        * library would be needed.
        * When this is done, a server_mode could be created and the following two
        * line would be moved out from here.
+       * However, note that the first of the two lines below triggers the
+       * "single instance" behavior, which should occur right at this point.
        */
        // Note: socket callback must be registered after init(argc, argv)
        // such that package().temp_dir() is properly initialized.
-       pimpl_->lyx_server_.reset(new Server(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
-       pimpl_->lyx_socket_.reset(new ServerSocket(&pimpl_->lyxfunc_,
-                       FileName(package().temp_dir().absFilename() + "/lyxsocket")));
+       pimpl_->lyx_server_.reset(new Server(lyxrc.lyxpipes));
+       pimpl_->lyx_socket_.reset(new ServerSocket(
+                       FileName(package().temp_dir().absFileName() + "/lyxsocket")));
 
        // Start the real execution loop.
-       exit_status = pimpl_->application_->exec();
+       if (!theServer().deferredLoadingToOtherInstance())
+               exit_status = pimpl_->application_->exec();
+       else if (!pimpl_->files_to_load_.empty()) {
+               vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
+               vector<string>::const_iterator end = pimpl_->files_to_load_.end();
+               lyxerr << _("The following files could not be loaded:") << endl;
+               for (; it != end; ++it)
+                       lyxerr << *it << endl;
+       }
 
        prepareExit();
 
@@ -444,6 +416,9 @@ void LyX::prepareExit()
        cap::clearCutStack();
        cap::clearSelection();
 
+       // Write the index file of the converter cache
+       ConverterCache::get().writeIndex();
+
        // close buffers first
        pimpl_->buffer_list_.closeAll();
 
@@ -458,14 +433,21 @@ void LyX::prepareExit()
 
        // do any other cleanup procedures now
        if (package().temp_dir() != package().system_temp_dir()) {
-               LYXERR(Debug::INFO, "Deleting tmp dir "
-                                   << package().temp_dir().absFilename());
-
-               if (!package().temp_dir().destroyDirectory()) {
+               string const abs_tmpdir = package().temp_dir().absFileName();
+               if (!contains(package().temp_dir().absFileName(), "lyx_tmpdir")) {
                        docstring const msg =
-                               bformat(_("Unable to remove the temporary directory %1$s"),
-                               from_utf8(package().temp_dir().absFilename()));
-                       Alert::warning(_("Unable to remove temporary directory"), msg);
+                               bformat(_("%1$s does not appear like a LyX created temporary directory."),
+                               from_utf8(abs_tmpdir));
+                       Alert::warning(_("Cannot remove temporary directory"), msg);
+               } else {
+                       LYXERR(Debug::INFO, "Deleting tmp dir "
+                               << package().temp_dir().absFileName());
+                       if (!package().temp_dir().destroyDirectory()) {
+                               docstring const msg =
+                                       bformat(_("Unable to remove the temporary directory %1$s"),
+                                       from_utf8(package().temp_dir().absFileName()));
+                               Alert::warning(_("Unable to remove temporary directory"), msg);
+                       }
                }
        }
 
@@ -478,7 +460,7 @@ void LyX::prepareExit()
 
 void LyX::earlyExit(int status)
 {
-       BOOST_ASSERT(pimpl_->application_.get());
+       LASSERT(pimpl_->application_.get(), /**/);
        // LyX::pimpl_::application_ is not initialised at this
        // point so it's safe to just exit after some cleanup.
        prepareExit();
@@ -494,7 +476,7 @@ int LyX::init(int & argc, char * argv[])
                if (argv[argi][0] == '-') {
                        lyxerr << to_utf8(
                                bformat(_("Wrong command line option `%1$s'. Exiting."),
-                               from_utf8(argv[argi]))) << endl;
+                               from_utf8(os::utf8_argv(argi)))) << endl;
                        return EXIT_FAILURE;
                }
        }
@@ -507,31 +489,22 @@ int LyX::init(int & argc, char * argv[])
                return EXIT_FAILURE;
 
        // Remaining arguments are assumed to be files to load.
-       for (int argi = argc - 1; argi >= 1; --argi)
-               pimpl_->files_to_load_.push_back(to_utf8(from_local8bit(argv[argi])));
+       for (int argi = 1; argi < argc; ++argi)
+               pimpl_->files_to_load_.push_back(os::utf8_argv(argi));
 
        if (first_start) {
                pimpl_->files_to_load_.push_back(
-                       i18nLibFileSearch("examples", "splash.lyx").absFilename());
+                       i18nLibFileSearch("examples", "splash.lyx").absFileName());
        }
 
        return EXIT_SUCCESS;
 }
 
 
-void LyX::addFileToLoad(string const & fname)
-{
-       vector<string>::const_iterator cit = find(
-               pimpl_->files_to_load_.begin(), pimpl_->files_to_load_.end(),
-               fname);
-
-       if (cit == pimpl_->files_to_load_.end())
-               pimpl_->files_to_load_.push_back(fname);
-}
-
-
-void LyX::loadFiles()
+bool LyX::loadFiles()
 {
+       LASSERT(!use_gui, /**/);
+       bool success = true;
        vector<string>::const_iterator it = pimpl_->files_to_load_.begin();
        vector<string>::const_iterator end = pimpl_->files_to_load_.end();
 
@@ -544,75 +517,86 @@ void LyX::loadFiles()
                if (fname.empty())
                        continue;
 
-               Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFilename(), false);
-               if (buf->loadLyXFile(fname)) {
+               Buffer * buf = pimpl_->buffer_list_.newBuffer(fname.absFileName(), false);
+               if (buf->loadLyXFile() == Buffer::ReadSuccess) {
                        ErrorList const & el = buf->errorList("Parse");
                        if (!el.empty())
                                for_each(el.begin(), el.end(),
-                               boost::bind(&LyX::printError, this, _1));
+                               bind(&LyX::printError, this, _1));
                }
-               else
+               else {
                        pimpl_->buffer_list_.release(buf);
+                       success = false;
+               }
        }
+       return success;
 }
 
 
-void LyX::execBatchCommands()
+void execBatchCommands()
+{
+       LASSERT(singleton_, /**/);
+       singleton_->execCommands();
+}
+
+
+void LyX::execCommands()
 {
        // The advantage of doing this here is that the event loop
        // is already started. So any need for interaction will be
        // aknowledged.
 
        // if reconfiguration is needed.
-       while (LayoutFileList::get().empty()) {
-           switch (Alert::prompt(
-                   _("No textclass is found"),
-                   _("LyX cannot continue because no textclass is found. "
-                     "You can either reconfigure normally, or reconfigure using "
-                     "default textclasses, or quit LyX."),
-                   0, 2,
-                   _("&Reconfigure"),
-                   _("&Use Default"),
-                   _("&Exit LyX")))
+       if (LayoutFileList::get().empty()) {
+               switch (Alert::prompt(
+                       _("No textclass is found"),
+                       _("LyX will only have minimal functionality because no textclasses "
+                               "have been found. You can either try to reconfigure LyX normally, "
+                               "try to reconfigure using only the defaults, or continue."),
+                       0, 2,
+                       _("&Reconfigure"),
+                       _("&Use Defaults"),
+                       _("&Continue")))
                {
                case 0:
                        // regular reconfigure
-                       pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
+                       lyx::dispatch(FuncRequest(LFUN_RECONFIGURE, ""));
                        break;
                case 1:
                        // reconfigure --without-latex-config
-                       pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_RECONFIGURE,
+                       lyx::dispatch(FuncRequest(LFUN_RECONFIGURE,
                                " --without-latex-config"));
                        break;
                default:
-                       pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_LYX_QUIT));
-                       return;
+                       break;
                }
        }
-       
+
        // create the first main window
-       pimpl_->lyxfunc_.dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
+       lyx::dispatch(FuncRequest(LFUN_WINDOW_NEW, geometryArg));
 
        if (!pimpl_->files_to_load_.empty()) {
                // if some files were specified at command-line we assume that the
                // user wants to edit *these* files and not to restore the session.
                for (size_t i = 0; i != pimpl_->files_to_load_.size(); ++i) {
-                       pimpl_->lyxfunc_.dispatch(
+                       lyx::dispatch(
                                FuncRequest(LFUN_FILE_OPEN, pimpl_->files_to_load_[i]));
                }
                // clear this list to save a few bytes of RAM
                pimpl_->files_to_load_.clear();
-       }
-       else
+       } else
                pimpl_->application_->restoreGuiSession();
 
        // Execute batch commands if available
-       if (pimpl_->batch_command.empty())
+       if (pimpl_->batch_commands.empty())
                return;
 
-       LYXERR(Debug::INIT, "About to handle -x '" << pimpl_->batch_command << '\'');
-
-       pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(pimpl_->batch_command));
+       vector<string>::const_iterator bcit  = pimpl_->batch_commands.begin();
+       vector<string>::const_iterator bcend = pimpl_->batch_commands.end();
+       for (; bcit != bcend; bcit++) {
+               LYXERR(Debug::INIT, "About to handle -x '" << *bcit << '\'');
+               lyx::dispatch(lyxaction.lookupFunc(*bcit));
+       }
 }
 
 
@@ -623,7 +607,7 @@ The SIGHUP signal does not exist on Windows and does not need to be handled.
 
 Windows handles SIGFPE and SIGSEGV signals as expected.
 
-Cntl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
+Ctrl+C interrupts (mapped to SIGINT by Windows' POSIX compatability layer)
 cause a new thread to be spawned. This may well result in unexpected
 behaviour by the single-threaded LyX.
 
@@ -658,7 +642,7 @@ static void error_handler(int err_sig)
 
        // We have received a signal indicating a fatal error, so
        // try and save the data ASAP.
-       LyX::cref().emergencyCleanup();
+       emergencyCleanup();
 
        // These lyxerr calls may or may not work:
 
@@ -671,21 +655,23 @@ static void error_handler(int err_sig)
 
        // This shouldn't matter here, however, as we've already invoked
        // emergencyCleanup.
+       docstring msg;
        switch (err_sig) {
 #ifdef SIGHUP
        case SIGHUP:
-               lyxerr << "\nlyx: SIGHUP signal caught\nBye." << endl;
+               msg = _("SIGHUP signal caught!\nBye.");
                break;
 #endif
        case SIGFPE:
-               lyxerr << "\nlyx: SIGFPE signal caught\nBye." << endl;
+               msg = _("SIGFPE signal caught!\nBye.");
                break;
        case SIGSEGV:
-               lyxerr << "\nlyx: SIGSEGV signal caught\n"
-                         "Sorry, you have found a bug in LyX. "
+               msg = _("SIGSEGV signal caught!\n"
+                         "Sorry, you have found a bug in LyX, "
+                         "hope you have not lost any data.\n"
                          "Please read the bug-reporting instructions "
-                         "in Help->Introduction and send us a bug report, "
-                         "if necessary. Thanks !\nBye." << endl;
+                         "in 'Help->Introduction' and send us a bug report, "
+                         "if necessary. Thanks !\nBye.");
                break;
        case SIGINT:
        case SIGTERM:
@@ -693,6 +679,12 @@ static void error_handler(int err_sig)
                break;
        }
 
+       if (!msg.empty()) {
+               lyxerr << "\nlyx: " << msg << endl;
+               // try to make a GUI message
+               Alert::error(_("LyX crashed!"), msg);
+       }
+
        // Deinstall the signal handlers
 #ifdef SIGHUP
        signal(SIGHUP, SIG_DFL);
@@ -704,11 +696,18 @@ static void error_handler(int err_sig)
 
 #ifdef SIGHUP
        if (err_sig == SIGSEGV ||
-           (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty()))
+               (err_sig != SIGHUP && !getEnv("LYXDEBUG").empty())) {
 #else
-       if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty())
+       if (err_sig == SIGSEGV || !getEnv("LYXDEBUG").empty()) {
 #endif
+#ifdef _MSC_VER
+               // with abort() it crashes again.
+               exit(err_sig);
+#else
                abort();
+#endif
+       }
+
        exit(0);
 }
 
@@ -734,15 +733,15 @@ bool LyX::init()
        signal(SIGTERM, error_handler);
        // SIGPIPE can be safely ignored.
 
-       lyxrc.tempdir_path = package().temp_dir().absFilename();
-       lyxrc.document_path = package().document_dir().absFilename();
+       lyxrc.tempdir_path = package().temp_dir().absFileName();
+       lyxrc.document_path = package().document_dir().absFileName();
 
        if (lyxrc.example_path.empty()) {
-               lyxrc.example_path = addPath(package().system_support().absFilename(),
+               lyxrc.example_path = addPath(package().system_support().absFileName(),
                                              "examples");
        }
        if (lyxrc.template_path.empty()) {
-               lyxrc.template_path = addPath(package().system_support().absFilename(),
+               lyxrc.template_path = addPath(package().system_support().absFileName(),
                                              "templates");
        }
 
@@ -755,14 +754,14 @@ bool LyX::init()
                return false;
 
        // Set the language defined by the distributor.
-       //setGuiLanguage(lyxrc.gui_language);
+       setRcGuiLanguage();
 
        // Set the PATH correctly.
 #if !defined (USE_POSIX_PACKAGING)
        // Add the directory containing the LyX executable to the path
        // so that LyX can find things like tex2lyx.
        if (package().build_support().empty())
-               prependEnvPath("PATH", package().binary_dir().absFilename());
+               prependEnvPath("PATH", package().binary_dir().absFileName());
 #endif
        if (!lyxrc.path_prefix.empty())
                prependEnvPath("PATH", lyxrc.path_prefix);
@@ -771,9 +770,20 @@ bool LyX::init()
        if (queryUserLyXDir(package().explicit_user_support()))
                reconfigureUserLyXDir();
 
-       // no need for a splash when there is no GUI
        if (!use_gui) {
+               // No need for a splash when there is no GUI
                first_start = false;
+               // Default is to overwrite the main file during export, unless
+               // the -f switch was specified or LYX_FORCE_OVERWRITE was set
+               if (force_overwrite == UNSPECIFIED) {
+                       string const what = getEnv("LYX_FORCE_OVERWRITE");
+                       if (what == "all")
+                               force_overwrite = ALL_FILES;
+                       else if (what == "none")
+                               force_overwrite = NO_FILES;
+                       else
+                               force_overwrite = MAIN_FILE;
+               }
        }
 
        // This one is generated in user_support directory by lib/configure.py.
@@ -786,6 +796,9 @@ bool LyX::init()
        // Read lyxrc.dist again to be able to override viewer auto-detection.
        readRcFile("lyxrc.dist");
 
+       // Set again the language defined by the distributor.
+       setRcGuiLanguage();
+
        system_lyxrc = lyxrc;
        system_formats = formats;
        pimpl_->system_converters_ = pimpl_->converters_;
@@ -801,34 +814,28 @@ bool LyX::init()
        if (!readLanguagesFile("languages"))
                return false;
 
-       // Load the layouts
+       // Set the language defined by the user.
+       setRcGuiLanguage();
+
        LYXERR(Debug::INIT, "Reading layouts...");
-       if (!LyXSetStyle())
-               return false;
+       // Load the layouts
+       LayoutFileList::get().read();
        //...and the modules
-       moduleList.load();
+       theModuleList.read();
 
        // read keymap and ui files in batch mode as well
        // because InsetInfo needs to know these to produce
        // the correct output
 
-       // Set the language defined by the user.
-       //setGuiLanguage(lyxrc.gui_language);
-
        // Set up command definitions
        pimpl_->toplevel_cmddef_.read(lyxrc.def_file);
 
+       // FIXME
        // Set up bindings
        pimpl_->toplevel_keymap_.read("site");
        pimpl_->toplevel_keymap_.read(lyxrc.bind_file);
        // load user bind file user.bind
-       pimpl_->toplevel_keymap_.read("user");
-
-       pimpl_->lyxfunc_.initKeySequences(&pimpl_->toplevel_keymap_);
-
-       // Read menus
-       if (use_gui && !readUIFile(lyxrc.ui_file))
-               return false;
+       pimpl_->toplevel_keymap_.read("user", 0, KeyMap::MissingOK);
 
        if (lyxerr.debugging(Debug::LYXRC))
                lyxrc.print();
@@ -841,12 +848,12 @@ bool LyX::init()
        if (document_path.exists() && document_path.isDirectory())
                package().document_dir() = document_path;
 
-       package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path));
+       package().set_temp_dir(createLyXTmpDir(FileName(lyxrc.tempdir_path)));
        if (package().temp_dir().empty()) {
                Alert::error(_("Could not create temporary directory"),
                             bformat(_("Could not create a temporary directory in\n"
-                                                   "%1$s. Make sure that this\n"
-                                                   "path exists and is writable and try again."),
+                                                      "\"%1$s\"\n"
+                                                          "Make sure that this path exists and is writable and try again."),
                                     from_utf8(lyxrc.tempdir_path)));
                // createLyXTmpDir() tries sufficiently hard to create a
                // usable temp dir, so the probability to come here is
@@ -857,7 +864,7 @@ bool LyX::init()
        }
 
        LYXERR(Debug::INIT, "LyX tmp dir: `"
-                           << package().temp_dir().absFilename() << '\'');
+                           << package().temp_dir().absFileName() << '\'');
 
        LYXERR(Debug::INIT, "Reading session information '.lyx/session'...");
        pimpl_->session_.reset(new Session(lyxrc.num_lastfiles));
@@ -865,29 +872,24 @@ bool LyX::init()
        // This must happen after package initialization and after lyxrc is
        // read, therefore it can't be done by a static object.
        ConverterCache::init();
-       
-       // init the global menubar on Mac. This must be done after the session
-       // was recovered to know the "last files".
-       if (use_gui)
-               theApp()->initGlobalMenu();
-       
+
        return true;
 }
 
 
-void LyX::emergencyCleanup() const
+void emergencyCleanup()
 {
        // what to do about tmpfiles is non-obvious. we would
        // like to delete any we find, but our lyxdir might
        // contain documents etc. which might be helpful on
        // a crash
 
-       pimpl_->buffer_list_.emergencyWriteAll();
+       singleton_->pimpl_->buffer_list_.emergencyWriteAll();
        if (use_gui) {
-               if (pimpl_->lyx_server_)
-                       pimpl_->lyx_server_->emergencyCleanup();
-               pimpl_->lyx_server_.reset();
-               pimpl_->lyx_socket_.reset();
+               if (singleton_->pimpl_->lyx_server_)
+                       singleton_->pimpl_->lyx_server_->emergencyCleanup();
+               singleton_->pimpl_->lyx_server_.reset();
+               singleton_->pimpl_->lyx_socket_.reset();
        }
 }
 
@@ -901,13 +903,13 @@ static bool needsUpdate(string const & file)
        static bool firstrun = true;
        if (firstrun) {
                configure_script =
-                       FileName(addName(package().system_support().absFilename(),
+                       FileName(addName(package().system_support().absFileName(),
                                "configure.py"));
                firstrun = false;
        }
 
-       FileName absfile = 
-               FileName(addName(package().user_support().absFilename(), file));
+       FileName absfile =
+               FileName(addName(package().user_support().absFileName(), file));
        return !absfile.exists()
                || configure_script.lastModified() > absfile.lastModified();
 }
@@ -936,7 +938,7 @@ bool LyX::queryUserLyXDir(bool explicit_userdir)
                    bformat(_("You have specified a non-existent user "
                                           "LyX directory, %1$s.\n"
                                           "It is needed to keep your own configuration."),
-                           from_utf8(package().user_support().absFilename())),
+                           from_utf8(package().user_support().absFileName())),
                    1, 0,
                    _("&Create directory"),
                    _("&Exit LyX"))) {
@@ -945,7 +947,7 @@ bool LyX::queryUserLyXDir(bool explicit_userdir)
        }
 
        lyxerr << to_utf8(bformat(_("LyX: Creating directory %1$s"),
-                         from_utf8(sup.absFilename()))) << endl;
+                         from_utf8(sup.absFileName()))) << endl;
 
        if (!sup.createDirectory(0755)) {
                // Failed, so let's exit.
@@ -975,100 +977,6 @@ bool LyX::readRcFile(string const & name)
        return true;
 }
 
-
-// Read the ui file `name'
-bool LyX::readUIFile(string const & name, bool include)
-{
-       enum {
-               ui_menuset = 1,
-               ui_toolbars,
-               ui_toolbarset,
-               ui_include,
-               ui_last
-       };
-
-       LexerKeyword uitags[] = {
-               { "include", ui_include },
-               { "menuset", ui_menuset },
-               { "toolbars", ui_toolbars },
-               { "toolbarset", ui_toolbarset }
-       };
-
-       // Ensure that a file is read only once (prevents include loops)
-       static list<string> uifiles;
-       list<string>::const_iterator it  = uifiles.begin();
-       list<string>::const_iterator end = uifiles.end();
-       it = find(it, end, name);
-       if (it != end) {
-               LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
-                                   << "Is this an include loop?");
-               return false;
-       }
-
-       LYXERR(Debug::INIT, "About to read " << name << "...");
-
-
-       FileName ui_path;
-       if (include) {
-               ui_path = libFileSearch("ui", name, "inc");
-               if (ui_path.empty())
-                       ui_path = libFileSearch("ui",
-                                               changeExtension(name, "inc"));
-       }
-       else
-               ui_path = libFileSearch("ui", name, "ui");
-
-       if (ui_path.empty()) {
-               LYXERR(Debug::INIT, "Could not find " << name);
-               showFileError(name);
-               return false;
-       }
-
-       uifiles.push_back(name);
-
-       LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
-       Lexer lex(uitags);
-       lex.setFile(ui_path);
-       if (!lex.isOK()) {
-               lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
-                      << endl;
-       }
-
-       if (lyxerr.debugging(Debug::PARSER))
-               lex.printTable(lyxerr);
-
-       while (lex.isOK()) {
-               switch (lex.lex()) {
-               case ui_include: {
-                       lex.next(true);
-                       string const file = lex.getString();
-                       if (!readUIFile(file, true))
-                               return false;
-                       break;
-               }
-               case ui_menuset:
-                       theApp()->readMenus(lex);
-                       break;
-
-               case ui_toolbarset:
-                       toolbarbackend.readToolbars(lex);
-                       break;
-
-               case ui_toolbars:
-                       toolbarbackend.readToolbarSettings(lex);
-                       break;
-
-               default:
-                       if (!rtrim(lex.getString()).empty())
-                               lex.printError("LyX::ReadUIFile: "
-                                              "Unknown menu tag: `$$Token'");
-                       break;
-               }
-       }
-       return true;
-}
-
-
 // Read the languages file `name'
 bool LyX::readLanguagesFile(string const & name)
 {
@@ -1109,12 +1017,10 @@ bool LyX::readEncodingsFile(string const & enc_name,
 
 namespace {
 
-string batch;
-
 /// return the the number of arguments consumed
-typedef boost::function<int(string const &, string const &)> cmd_helper;
+typedef boost::function<int(string const &, string const &, string &)> cmd_helper;
 
-int parse_dbg(string const & arg, string const &)
+int parse_dbg(string const & arg, string const &, string &)
 {
        if (arg.empty()) {
                lyxerr << to_utf8(_("List of supported debug flags:")) << endl;
@@ -1123,13 +1029,13 @@ int parse_dbg(string const & arg, string const &)
        }
        lyxerr << to_utf8(bformat(_("Setting debug level to %1$s"), from_utf8(arg))) << endl;
 
-       lyxerr.level(Debug::value(arg));
+       lyxerr.setLevel(Debug::value(arg));
        Debug::showLevel(lyxerr, lyxerr.level());
        return 1;
 }
 
 
-int parse_help(string const &, string const &)
+int parse_help(string const &, string const &, string &)
 {
        lyxerr <<
                to_utf8(_("Usage: lyx [ command line switches ] [ name.lyx ... ]\n"
@@ -1147,17 +1053,29 @@ int parse_help(string const &, string const &)
                  "                  where fmt is the export format of choice.\n"
                  "                  Look on Tools->Preferences->File formats->Format\n"
                  "                  to get an idea which parameters should be passed.\n"
+                 "                  Note that the order of -e and -x switches matters.\n"
                  "\t-i [--import] fmt file.xxx\n"
                  "                  where fmt is the import format of choice\n"
                  "                  and file.xxx is the file to be imported.\n"
-                 "\t-version        summarize version and build info\n"
+                 "\t-f [--force-overwrite] what\n"
+                 "                  where what is either `all', `main' or `none',\n"
+                 "                  specifying whether all files, main file only, or no files,\n"
+                 "                  respectively, are to be overwritten during a batch export.\n"
+                 "                  Anything else is equivalent to `all', but is not consumed.\n"
+                 "\t-n [--no-remote]\n"
+                 "                  open documents in a new instance\n"
+                 "\t-r [--remote]\n"
+                 "                  open documents in an already running instance\n"
+                 "                  (a working lyxpipe is needed)\n"
+                 "\t-batch    execute commands without launching GUI and exit.\n"
+                 "\t-version  summarize version and build info\n"
                               "Check the LyX man page for more details.")) << endl;
        exit(0);
        return 0;
 }
 
 
-int parse_version(string const &, string const &)
+int parse_version(string const &, string const &, string &)
 {
        lyxerr << "LyX " << lyx_version
               << " (" << lyx_release_date << ")" << endl;
@@ -1169,7 +1087,7 @@ int parse_version(string const &, string const &)
 }
 
 
-int parse_sysdir(string const & arg, string const &)
+int parse_sysdir(string const & arg, string const &, string &)
 {
        if (arg.empty()) {
                Alert::error(_("No system directory"),
@@ -1181,7 +1099,7 @@ int parse_sysdir(string const & arg, string const &)
 }
 
 
-int parse_userdir(string const & arg, string const &)
+int parse_userdir(string const & arg, string const &, string &)
 {
        if (arg.empty()) {
                Alert::error(_("No user directory"),
@@ -1193,7 +1111,7 @@ int parse_userdir(string const & arg, string const &)
 }
 
 
-int parse_execute(string const & arg, string const &)
+int parse_execute(string const & arg, string const &, string & batch)
 {
        if (arg.empty()) {
                Alert::error(_("Incomplete command"),
@@ -1205,7 +1123,7 @@ int parse_execute(string const & arg, string const &)
 }
 
 
-int parse_export(string const & type, string const &)
+int parse_export(string const & type, string const &, string & batch)
 {
        if (type.empty()) {
                lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
@@ -1218,7 +1136,7 @@ int parse_export(string const & type, string const &)
 }
 
 
-int parse_import(string const & type, string const & file)
+int parse_import(string const & type, string const & file, string & batch)
 {
        if (type.empty()) {
                lyxerr << to_utf8(_("Missing file type [eg latex, ps...] after "
@@ -1235,16 +1153,50 @@ int parse_import(string const & type, string const & file)
 }
 
 
-int parse_geometry(string const & arg1, string const &)
+int parse_geometry(string const & arg1, string const &, string &)
 {
        geometryArg = arg1;
-#if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
-       // remove also the arg
-       return 1;
-#else
-       // don't remove "-geometry"
+       // don't remove "-geometry", it will be pruned out later in the
+       // frontend if need be.
        return -1;
-#endif
+}
+
+
+int parse_batch(string const &, string const &, string &)
+{
+       use_gui = false;
+       return 0;
+}
+
+
+int parse_noremote(string const &, string const &, string &)
+{
+       run_mode = NEW_INSTANCE;
+       return 0;
+}
+
+
+int parse_remote(string const &, string const &, string &)
+{
+       run_mode = USE_REMOTE;
+       return 0;
+}
+
+
+int parse_force(string const & arg, string const &, string &)
+{
+       if (arg == "all") {
+               force_overwrite = ALL_FILES;
+               return 1;
+       } else if (arg == "main") {
+               force_overwrite = MAIN_FILE;
+               return 1;
+       } else if (arg == "none") {
+               force_overwrite = NO_FILES;
+               return 1;
+       }
+       force_overwrite = ALL_FILES;
+       return 0;
 }
 
 
@@ -1269,6 +1221,13 @@ void LyX::easyParse(int & argc, char * argv[])
        cmdmap["-i"] = parse_import;
        cmdmap["--import"] = parse_import;
        cmdmap["-geometry"] = parse_geometry;
+       cmdmap["-batch"] = parse_batch;
+       cmdmap["-f"] = parse_force;
+       cmdmap["--force-overwrite"] = parse_force;
+       cmdmap["-n"] = parse_noremote;
+       cmdmap["--no-remote"] = parse_noremote;
+       cmdmap["-r"] = parse_remote;
+       cmdmap["--remote"] = parse_remote;
 
        for (int i = 1; i < argc; ++i) {
                map<string, cmd_helper>::const_iterator it
@@ -1279,117 +1238,217 @@ void LyX::easyParse(int & argc, char * argv[])
                        continue;
 
                string const arg =
-                       (i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string();
+                       (i + 1 < argc) ? os::utf8_argv(i + 1) : string();
                string const arg2 =
-                       (i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string();
+                       (i + 2 < argc) ? os::utf8_argv(i + 2) : string();
 
-               int const remove = 1 + it->second(arg, arg2);
+               string batch;
+               int const remove = 1 + it->second(arg, arg2, batch);
+               if (!batch.empty())
+                       pimpl_->batch_commands.push_back(batch);
 
                // Now, remove used arguments by shifting
                // the following ones remove places down.
                if (remove > 0) {
+                       os::remove_internal_args(i, remove);
                        argc -= remove;
                        for (int j = i; j < argc; ++j)
                                argv[j] = argv[j + remove];
                        --i;
                }
        }
-
-       pimpl_->batch_command = batch;
 }
 
 
 FuncStatus getStatus(FuncRequest const & action)
 {
-       return LyX::ref().lyxFunc().getStatus(action);
+       LASSERT(theApp(), /**/);
+       return theApp()->getStatus(action);
 }
 
 
 void dispatch(FuncRequest const & action)
 {
-       LyX::ref().lyxFunc().dispatch(action);
+       LASSERT(theApp(), /**/);
+       return theApp()->dispatch(action);
 }
 
 
-BufferList & theBufferList()
+void dispatch(FuncRequest const & action, DispatchResult & dr)
 {
-       return LyX::ref().bufferList();
+       LASSERT(theApp(), /**/);
+       return theApp()->dispatch(action, dr);
 }
 
 
-LyXFunc & theLyXFunc()
+vector<string> & theFilesToLoad()
 {
-       return LyX::ref().lyxFunc();
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->files_to_load_;
+}
+
+
+BufferList & theBufferList()
+{
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->buffer_list_;
 }
 
 
 Server & theServer()
 {
        // FIXME: this should not be use_gui dependent
-       BOOST_ASSERT(use_gui);
-       return LyX::ref().server();
+       LASSERT(use_gui, /**/);
+       LASSERT(singleton_, /**/);
+       return *singleton_->pimpl_->lyx_server_.get();
 }
 
 
 ServerSocket & theServerSocket()
 {
        // FIXME: this should not be use_gui dependent
-       BOOST_ASSERT(use_gui);
-       return LyX::ref().socket();
+       LASSERT(use_gui, /**/);
+       LASSERT(singleton_, /**/);
+       return *singleton_->pimpl_->lyx_socket_.get();
 }
 
 
 KeyMap & theTopLevelKeymap()
 {
-       return LyX::ref().pimpl_->toplevel_keymap_;
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->toplevel_keymap_;
 }
 
 
 Converters & theConverters()
 {
-       return  LyX::ref().converters();
+       LASSERT(singleton_, /**/);
+       return  singleton_->pimpl_->converters_;
 }
 
 
 Converters & theSystemConverters()
 {
-       return  LyX::ref().systemConverters();
+       LASSERT(singleton_, /**/);
+       return  singleton_->pimpl_->system_converters_;
 }
 
 
 Movers & theMovers()
 {
-       return  LyX::ref().pimpl_->movers_;
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->movers_;
 }
 
 
 Mover const & getMover(string  const & fmt)
 {
-       return  LyX::ref().pimpl_->movers_(fmt);
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->movers_(fmt);
 }
 
 
 void setMover(string const & fmt, string const & command)
 {
-       LyX::ref().pimpl_->movers_.set(fmt, command);
+       LASSERT(singleton_, /**/);
+       singleton_->pimpl_->movers_.set(fmt, command);
 }
 
 
 Movers & theSystemMovers()
 {
-       return  LyX::ref().pimpl_->system_movers_;
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->system_movers_;
 }
 
 
-Messages & getMessages(string const & language)
+Messages const & getMessages(string const & language)
 {
-       return LyX::ref().getMessages(language);
+       LASSERT(singleton_, /**/);
+       return singleton_->messages(language);
 }
 
 
-Messages & getGuiMessages()
+Messages const & getGuiMessages()
 {
-       return LyX::ref().getGuiMessages();
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->messages_["GUI"];
+}
+
+
+graphics::Previews & thePreviews()
+{
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->preview_;
+}
+
+
+Session & theSession()
+{
+       LASSERT(singleton_, /**/);
+       return *singleton_->pimpl_->session_.get();
+}
+
+
+CmdDef & theTopLevelCmdDef()
+{
+       LASSERT(singleton_, /**/);
+       return singleton_->pimpl_->toplevel_cmddef_;
+}
+
+
+SpellChecker * theSpellChecker()
+{
+       if (!singleton_->pimpl_->spell_checker_)
+               setSpellChecker();
+       return singleton_->pimpl_->spell_checker_;
+}
+
+
+void setSpellChecker()
+{
+       SpellChecker::ChangeNumber speller_change_number =singleton_->pimpl_->spell_checker_ ?
+               singleton_->pimpl_->spell_checker_->changeNumber() : 0;
+
+       if (lyxrc.spellchecker == "native") {
+#if defined(USE_MACOSX_PACKAGING)
+               if (!singleton_->pimpl_->apple_spell_checker_)
+                       singleton_->pimpl_->apple_spell_checker_ = new AppleSpellChecker();
+               singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->apple_spell_checker_;
+#else
+               singleton_->pimpl_->spell_checker_ = 0;
+#endif
+       } else if (lyxrc.spellchecker == "aspell") {
+#if defined(USE_ASPELL)
+               if (!singleton_->pimpl_->aspell_checker_)
+                       singleton_->pimpl_->aspell_checker_ = new AspellChecker();
+               singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_;
+#else
+               singleton_->pimpl_->spell_checker_ = 0;
+#endif
+       } else if (lyxrc.spellchecker == "enchant") {
+#if defined(USE_ENCHANT)
+               if (!singleton_->pimpl_->enchant_checker_)
+                       singleton_->pimpl_->enchant_checker_ = new EnchantChecker();
+               singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_;
+#else
+               singleton_->pimpl_->spell_checker_ = 0;
+#endif
+       } else if (lyxrc.spellchecker == "hunspell") {
+#if defined(USE_HUNSPELL)
+               if (!singleton_->pimpl_->hunspell_checker_)
+                       singleton_->pimpl_->hunspell_checker_ = new HunspellChecker();
+               singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_;
+#else
+               singleton_->pimpl_->spell_checker_ = 0;
+#endif
+       } else {
+               singleton_->pimpl_->spell_checker_ = 0;
+       }
+       if (singleton_->pimpl_->spell_checker_) {
+               singleton_->pimpl_->spell_checker_->changeNumber(speller_change_number);
+               singleton_->pimpl_->spell_checker_->advanceChangeNumber();
+       }
 }
 
 } // namespace lyx