#include "support/package.h"
#include "support/path.h"
#include "support/systemcall.h"
+#include "support/unicode.h"
#include <boost/bind.hpp>
#include <boost/filesystem/operations.hpp>
#include <iostream>
#include <csignal>
+#include <vector>
namespace lyx {
using support::createDirectory;
using support::createLyXTmpDir;
using support::destroyDir;
+using support::FileName;
using support::fileSearch;
using support::getEnv;
using support::i18nLibFileSearch;
using std::system;
#endif
-///
-frontend::Application * theApp = 0;
/// are we using the GUI at all?
/**
string cl_system_support;
string cl_user_support;
+std::string geometryArg;
+
+LyX * singleton_ = 0;
void showFileError(string const & error)
{
/// The main application class private implementation.
struct LyX::Singletons
{
+ Singletons(): iconv(ucs4_codeset, "UTF-8")
+ {
+ }
/// our function handler
LyXFunc lyxfunc_;
///
boost::scoped_ptr<frontend::Application> application_;
/// lyx session, containing lastfiles, lastfilepos, and lastopened
boost::scoped_ptr<Session> session_;
-};
+ ///
+ IconvProcessor iconv;
-boost::scoped_ptr<LyX> LyX::singleton_;
+ /// Files to load at start.
+ vector<FileName> files_to_load_;
+};
-LyX::~LyX()
+///
+frontend::Application * theApp()
{
- // Static data are not treated in the same way at all on the Mac (and
- // the LyX singleton has static methods). This is the reason why the
- // exit command on the Mac bypasses our dispatch machinery altogether.
- // On Linux and Windows we won't pass a second time through quit()
- // because quitting will already be set to true.
- if (!quitting)
- quit();
+ if (singleton_)
+ return &singleton_->application();
+ else
+ return 0;
}
-int LyX::exec(int & argc, char * argv[])
+LyX::~LyX()
{
- BOOST_ASSERT(!singleton_.get());
- // We must return from this before launching the gui so that
- // other parts of the code can access singleton_ through
- // LyX::ref and LyX::cref.
- singleton_.reset(new LyX);
- // Start the real execution loop.
- return singleton_->priv_exec(argc, argv);
}
LyX & LyX::ref()
{
- BOOST_ASSERT(singleton_.get());
- return *singleton_.get();
+ BOOST_ASSERT(singleton_);
+ return *singleton_;
}
LyX const & LyX::cref()
{
- BOOST_ASSERT(singleton_.get());
- return *singleton_.get();
+ BOOST_ASSERT(singleton_);
+ return *singleton_;
}
LyX::LyX()
- : first_start(false), geometryOption_(false)
+ : first_start(false)
{
+ singleton_ = this;
pimpl_.reset(new Singletons);
+ geometryArg.clear();
}
}
+IconvProcessor & LyX::iconvProcessor()
+{
+ return pimpl_->iconv;
+}
+
+
kb_keymap const & LyX::topLevelKeymap() const
{
BOOST_ASSERT(pimpl_->toplevel_keymap_.get());
}
-int LyX::priv_exec(int & argc, char * argv[])
+int LyX::exec(int & argc, char * argv[])
{
// Here we need to parse the command line. At least
// we need to parse for "-dbg" and "-help"
easyParse(argc, argv);
- support::init_package(argv[0], cl_system_support, cl_user_support,
- support::top_build_dir_is_one_level_up);
+ support::init_package(to_utf8(from_local8bit(argv[0])),
+ cl_system_support, cl_user_support,
+ support::top_build_dir_is_one_level_up);
- vector<string> files;
- int exit_status = execBatchCommands(argc, argv, files);
+ if (!use_gui) {
+ // FIXME: create a ConsoleApplication
+ int exit_status = init(argc, argv);
+ if (exit_status) {
+ prepareExit();
+ return exit_status;
+ }
- if (exit_status)
- return exit_status;
-
- if (use_gui) {
- // Force adding of font path _before_ Application is initialized
- support::addFontResources();
- pimpl_->application_.reset(createApplication(argc, argv));
- initGuiFont();
- // FIXME: this global pointer should probably go.
- theApp = pimpl_->application_.get();
- restoreGuiSession(files);
- // Start the real execution loop.
-
- // 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.
- */
- pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
- pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
- support::os::internal_path(package().temp_dir() + "/lyxsocket")));
-
- // handle the batch commands the user asked for
- if (!batch_command.empty()) {
- pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
+ loadFiles();
+
+ if (batch_command.empty() || pimpl_->buffer_list_.empty()) {
+ prepareExit();
+ return EXIT_SUCCESS;
}
- exit_status = pimpl_->application_->start(batch_command);
- // Kill the application object before exiting. This avoid crash
- // on exit on Linux.
- pimpl_->application_.reset();
- // Restore original font resources after Application is destroyed.
- support::restoreFontResources();
+ BufferList::iterator begin = pimpl_->buffer_list_.begin();
+ BufferList::iterator end = pimpl_->buffer_list_.end();
+
+ bool final_success = false;
+ for (BufferList::iterator I = begin; I != end; ++I) {
+ Buffer * buf = *I;
+ bool success = false;
+ buf->dispatch(batch_command, &success);
+ final_success |= success;
+ }
+ prepareExit();
+ return !final_success;
}
- else {
- // FIXME: create a ConsoleApplication
- theApp = 0;
+
+ // Force adding of font path _before_ Application is initialized
+ support::addFontResources();
+
+ // Let the frontend parse and remove all arguments that it knows
+ pimpl_->application_.reset(createApplication(argc, argv));
+
+ initGuiFont();
+
+ // Parse and remove all known arguments in the LyX singleton
+ // Give an error for all remaining ones.
+ int exit_status = init(argc, argv);
+ if (exit_status) {
+ // Kill the application object before exiting.
+ pimpl_->application_.reset();
+ use_gui = false;
+ prepareExit();
+ return exit_status;
}
+ // 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.
+ */
+ // Note: socket callback must be registered after init(argc, argv)
+ // such that package().temp_dir() is properly initialized.
+ pimpl_->lyx_server_.reset(new LyXServer(&pimpl_->lyxfunc_, lyxrc.lyxpipes));
+ pimpl_->lyx_socket_.reset(new LyXServerSocket(&pimpl_->lyxfunc_,
+ support::os::internal_path(package().temp_dir() + "/lyxsocket")));
+
+ // Start the real execution loop.
+ exit_status = pimpl_->application_->exec();
+
+ prepareExit();
+
+ // Restore original font resources after Application is destroyed.
+ support::restoreFontResources();
+
return exit_status;
}
pimpl_->buffer_list_.closeAll();
// do any other cleanup procedures now
- lyxerr[Debug::INFO] << "Deleting tmp dir " << package().temp_dir() << endl;
-
- if (!destroyDir(package().temp_dir())) {
- // FIXME UNICODE: package().temp_dir() could in theory contain utf8 characters.
- // We cannot use from_utf8() here because this involves the use of static data
- // that may have been destroyed already on Mac systems.
- docstring const msg =
- bformat(_("Unable to remove the temporary directory %1$s"),
- from_ascii(package().temp_dir()));
- Alert::warning(_("Unable to remove temporary directory"), msg);
+ if (package().temp_dir() != package().system_temp_dir()) {
+ lyxerr[Debug::INFO] << "Deleting tmp dir "
+ << package().temp_dir() << endl;
+
+ if (!destroyDir(FileName(package().temp_dir()))) {
+ docstring const msg =
+ bformat(_("Unable to remove the temporary directory %1$s"),
+ from_utf8(package().temp_dir()));
+ Alert::warning(_("Unable to remove temporary directory"), msg);
+ }
+ }
+
+ if (use_gui) {
+ if (pimpl_->session_)
+ pimpl_->session_->writeFile();
+ pimpl_->session_.reset();
+ pimpl_->lyx_server_.reset();
+ pimpl_->lyx_socket_.reset();
}
+
+ // Kill the application object before exiting. This avoids crashes
+ // when exiting on Linux.
+ if (pimpl_->application_)
+ pimpl_->application_.reset();
}
}
-void LyX::quit()
-{
- lyxerr[Debug::INFO] << "Running QuitLyX." << endl;
-
- prepareExit();
-
- if (use_gui) {
- pimpl_->session_->writeFile();
- pimpl_->lyx_server_.reset();
- pimpl_->lyx_socket_.reset();
- pimpl_->application_->exit(0);
- }
-}
-
-
-int LyX::execBatchCommands(int & argc, char * argv[],
- vector<string> & files)
+int LyX::init(int & argc, char * argv[])
{
// check for any spurious extra arguments
// other than documents
if (!success)
return EXIT_FAILURE;
- for (int argi = argc - 1; argi >= 1; --argi)
- files.push_back(os::internal_path(argv[argi]));
+ for (int argi = argc - 1; argi >= 1; --argi) {
+ // get absolute path of file and add ".lyx" to
+ // the filename if necessary
+ pimpl_->files_to_load_.push_back(fileSearch(string(),
+ os::internal_path(to_utf8(from_local8bit(argv[argi]))), "lyx"));
+ }
if (first_start)
- files.push_back(i18nLibFileSearch("examples", "splash.lyx"));
+ pimpl_->files_to_load_.push_back(i18nLibFileSearch("examples", "splash.lyx"));
- // Execute batch commands if available
- if (!batch_command.empty()) {
-
- lyxerr[Debug::INIT] << "About to handle -x '"
- << batch_command << '\'' << endl;
-
- Buffer * last_loaded = 0;
-
- vector<string>::const_iterator it = files.begin();
- vector<string>::const_iterator end = files.end();
-
- for (; it != end; ++it) {
- // get absolute path of file and add ".lyx" to
- // the filename if necessary
- string s = fileSearch(string(), *it, "lyx");
- if (s.empty()) {
- Buffer * const b = newFile(*it, string(), true);
- if (b)
- last_loaded = b;
- } else {
- Buffer * buf = pimpl_->buffer_list_.newBuffer(s, false);
- if (loadLyXFile(buf, s)) {
- last_loaded = buf;
- ErrorList const & el = buf->errorList("Parse");
- if (!el.empty())
- for_each(el.begin(), el.end(),
- boost::bind(&LyX::printError, this, _1));
- }
- else
- pimpl_->buffer_list_.release(buf);
- }
- }
+ return EXIT_SUCCESS;
+}
+
+
+void LyX::loadFiles()
+{
+ vector<FileName>::const_iterator it = pimpl_->files_to_load_.begin();
+ vector<FileName>::const_iterator end = pimpl_->files_to_load_.end();
+
+ for (; it != end; ++it) {
+ if (it->empty())
+ continue;
- // try to dispatch to last loaded buffer first
- if (last_loaded) {
- success = false;
- if (last_loaded->dispatch(batch_command, &success)) {
- prepareExit();
- return !success;
- }
+ Buffer * buf = pimpl_->buffer_list_.newBuffer(it->absFilename(), false);
+ if (loadLyXFile(buf, *it)) {
+ ErrorList const & el = buf->errorList("Parse");
+ if (!el.empty())
+ for_each(el.begin(), el.end(),
+ boost::bind(&LyX::printError, this, _1));
}
- files.clear(); // the files are already loaded
+ else
+ pimpl_->buffer_list_.release(buf);
}
+}
- return EXIT_SUCCESS;
+
+void LyX::execBatchCommands()
+{
+ // The advantage of doing this here is that the event loop
+ // is already started. So any need for interaction will be
+ // aknowledged.
+ restoreGuiSession();
+
+ // Execute batch commands if available
+ if (batch_command.empty())
+ return;
+
+ lyxerr[Debug::INIT] << "About to handle -x '"
+ << batch_command << '\'' << endl;
+
+ pimpl_->lyxfunc_.dispatch(lyxaction.lookupFunc(batch_command));
}
-void LyX::restoreGuiSession(vector<string> const & files)
+void LyX::restoreGuiSession()
{
LyXView * view = newLyXView();
- // load files
- for_each(files.begin(), files.end(),
- bind(&LyXView::loadLyXFile, view, _1, true));
-
- // if a file is specified, I assume that user wants to edit *that* file
- if (files.empty() && lyxrc.load_session) {
- vector<string> const & lastopened = pimpl_->session_->lastOpened().getfiles();
- // do not add to the lastfile list since these files are restored from
- // last seesion, and should be already there (regular files), or should
- // not be added at all (help files).
- for_each(lastopened.begin(), lastopened.end(),
- bind(&LyXView::loadLyXFile, view, _1, false));
+ // if some files were specified at command-line we assume that the
+ // user wants to edit *these* files and not to restore the session.
+ if (!pimpl_->files_to_load_.empty()) {
+ for_each(pimpl_->files_to_load_.begin(),
+ pimpl_->files_to_load_.end(),
+ bind(&LyXView::loadLyXFile, view, _1, true));
+ // clear this list to save a few bytes of RAM
+ pimpl_->files_to_load_.clear();
+ pimpl_->session_->lastOpened().clear();
+ return;
}
+
+ if (!lyxrc.load_session)
+ return;
+
+ vector<FileName> const & lastopened = pimpl_->session_->lastOpened().getfiles();
+ // do not add to the lastfile list since these files are restored from
+ // last session, and should be already there (regular files), or should
+ // not be added at all (help files).
+ for_each(lastopened.begin(), lastopened.end(),
+ bind(&LyXView::loadLyXFile, view, _1, false));
+
// clear this list to save a few bytes of RAM
pimpl_->session_->lastOpened().clear();
}
// initial geometry
unsigned int width = 690;
unsigned int height = 510;
+ // default icon size, will be overwritten by stored session value
+ unsigned int iconSizeXY = 0;
bool maximize = false;
// first try lyxrc
if (lyxrc.geometry_width != 0 && lyxrc.geometry_height != 0 ) {
height = convert<unsigned int>(val);
if (session().sessionInfo().load("WindowIsMaximized") == "yes")
maximize = true;
+ val = session().sessionInfo().load("IconSizeXY");
+ if (!val.empty())
+ iconSizeXY = convert<unsigned int>(val);
}
// if user wants to restore window position
posy = convert<int>(val);
}
- if (geometryOption_) {
+ if (!geometryArg.empty())
+ {
width = 0;
height = 0;
}
+
// create the main window
- LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize);
+ LyXView * view = &pimpl_->application_->createView(width, height, posx, posy, maximize, iconSizeXY, geometryArg);
return view;
}
if (!lyxrc.path_prefix.empty())
prependEnvPath("PATH", lyxrc.path_prefix);
- if (fs::exists(lyxrc.document_path) &&
- fs::is_directory(lyxrc.document_path))
+ FileName const document_path(lyxrc.document_path);
+ if (fs::exists(document_path.toFilesystemEncoding()) &&
+ fs::is_directory(document_path.toFilesystemEncoding()))
package().document_dir() = lyxrc.document_path;
- package().temp_dir() = createLyXTmpDir(lyxrc.tempdir_path);
+ package().temp_dir() = createLyXTmpDir(FileName(lyxrc.tempdir_path)).absFilename();
if (package().temp_dir().empty()) {
Alert::error(_("Could not create temporary directory"),
bformat(_("Could not create a temporary directory in\n"
pimpl_->buffer_list_.emergencyWriteAll();
if (use_gui) {
- pimpl_->lyx_server_->emergencyCleanup();
+ if (pimpl_->lyx_server_)
+ pimpl_->lyx_server_->emergencyCleanup();
pimpl_->lyx_server_.reset();
pimpl_->lyx_socket_.reset();
}
// return true if file does not exist or is older than configure.py.
bool needsUpdate(string const & file)
{
- static string const configure_script =
- addName(package().system_support(), "configure.py");
- string const absfile =
- addName(package().user_support(), file);
+ // We cannot initialize configure_script directly because the package
+ // is not initialized yet when static objects are constructed.
+ static string configure_script;
+ static bool firstrun = true;
+ if (firstrun) {
+ configure_script = FileName(addName(
+ package().system_support(),
+ "configure.py")).toFilesystemEncoding();
+ firstrun = false;
+ }
+ string const absfile = FileName(addName(
+ package().user_support(), file)).toFilesystemEncoding();
return (! fs::exists(absfile))
|| (fs::last_write_time(configure_script)
> fs::last_write_time(absfile));
bool LyX::queryUserLyXDir(bool explicit_userdir)
{
// Does user directory exist?
- if (fs::exists(package().user_support()) &&
- fs::is_directory(package().user_support())) {
+ string const user_support =
+ FileName(package().user_support()).toFilesystemEncoding();
+ if (fs::exists(user_support) && fs::is_directory(user_support)) {
first_start = false;
return needsUpdate("lyxrc.defaults")
{
lyxerr[Debug::INIT] << "About to read " << name << "... ";
- string const lyxrc_path = libFileSearch(string(), name);
+ FileName const lyxrc_path = libFileSearch(string(), name);
if (!lyxrc_path.empty()) {
lyxerr[Debug::INIT] << "Found in " << lyxrc_path << endl;
lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
- string const ui_path = libFileSearch("ui", name, "ui");
+ FileName const ui_path = libFileSearch("ui", name, "ui");
if (ui_path.empty()) {
lyxerr[Debug::INIT] << "Could not find " << name << endl;
{
lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
- string const lang_path = libFileSearch(string(), name);
+ FileName const lang_path = libFileSearch(string(), name);
if (lang_path.empty()) {
showFileError(name);
return false;
{
lyxerr[Debug::INIT] << "About to read " << name << "..." << endl;
- string const enc_path = libFileSearch(string(), name);
+ FileName const enc_path = libFileSearch(string(), name);
if (enc_path.empty()) {
showFileError(name);
return false;
return 2;
}
+int parse_geometry(string const & arg1, string const &)
+{
+ geometryArg = arg1;
+#if defined(_WIN32) || (defined(__CYGWIN__) && defined(X_DISPLAY_MISSING))
+ // remove also the arg
+ return 1;
+#else
+ // don't remove "-geometry"
+ return -1;
+#endif
+}
+
+
} // namespace anon
cmdmap["--export"] = parse_export;
cmdmap["-i"] = parse_import;
cmdmap["--import"] = parse_import;
+ cmdmap["-geometry"] = parse_geometry;
for (int i = 1; i < argc; ++i) {
std::map<string, cmd_helper>::const_iterator it
= cmdmap.find(argv[i]);
- // check for X11 -geometry option
- if (support::compare(argv[i], "-geometry") == 0)
- geometryOption_ = true;
-
// don't complain if not found - may be parsed later
if (it == cmdmap.end())
continue;
- string arg((i + 1 < argc) ? argv[i + 1] : "");
- string arg2((i + 2 < argc) ? argv[i + 2] : "");
+ string const arg((i + 1 < argc) ? to_utf8(from_local8bit(argv[i + 1])) : string());
+ string const arg2((i + 2 < argc) ? to_utf8(from_local8bit(argv[i + 2])) : string());
int const remove = 1 + it->second(arg, arg2);
// Now, remove used arguments by shifting
// the following ones remove places down.
- argc -= remove;
- for (int j = i; j < argc; ++j)
- argv[j] = argv[j + remove];
- --i;
+ if (remove > 0) {
+ argc -= remove;
+ for (int j = i; j < argc; ++j)
+ argv[j] = argv[j + remove];
+ --i;
+ }
}
batch_command = batch;
return LyX::ref().topLevelKeymap();
}
+
+IconvProcessor & utf8ToUcs4()
+{
+ return LyX::ref().iconvProcessor();
+}
+
} // namespace lyx