diff --git a/ChangeLog b/ChangeLog index 228d6e0867..dc682cc15d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2006-05-16 Jean-Marc Lasgouttes + + * configure.ac: improve check for shlwapi. + 2006-05-12 Jean-Marc Lasgouttes * configure.ac: check for shlwapi and gdi32 libraries (needed in diff --git a/configure.ac b/configure.ac index cce6d0225f..c8342879b7 100644 --- a/configure.ac +++ b/configure.ac @@ -118,7 +118,8 @@ LYX_ADD_INC_DIR(CPPFLAGS,$dir/include) LYX_ADD_LIB_DIR(LDFLAGS,$dir/lib)]) ### These are needed in windows -AC_CHECK_LIB(shlwapi, main) +AC_CHECK_LIB(shlwapi, main, [LIBSHLWAPI=-lshlwapi]) +AC_SUBST(LIBSHLWAPI) AC_CHECK_LIB(gdi32, main) AC_ARG_WITH(aiksaurus, diff --git a/lib/ChangeLog b/lib/ChangeLog index 3eac11515d..6077332515 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,7 @@ +2006-05-16 Jean-Marc Lasgouttes + + * configure.py: remove check for native windows viewers. + 2005-05-05 José Matos * examples/*.lyx: diff --git a/lib/configure.py b/lib/configure.py index b7ac1eac55..f5ebf903c9 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -119,15 +119,17 @@ def checkProg(description, progs, rc_entry = [], path = [] ): for searching but the whole string is used to replace %% for a rc_entry. So, feel free to add '$$i' etc for programs. - path: additional path + path: additional pathes - rc_entry: entry to outfile, can be emtpy, one pattern (%% for chosen - prog or 'none'), or one for each prog and 'none'. + rc_entry: entry to outfile, can be + 1. emtpy: no rc entry will be added + 2. one pattern: %% will be replaced by the first found program, + or 'none' is no program is found. + 3. several patterns for each prog and 'none'. This is used + when different programs have different usages. If you do not + want 'none' entry to be added to the RC file, you can specify + an entry for each prog and use '' for the 'none' entry. - NOTE: if you do not want 'none' entry to be added to the RC file, - specify an entry for each prog and use '' for 'none' entry. - - FIXME: under windows, we should check registry instead of $PATH ''' # one rc entry for each progs plus none entry if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1: @@ -231,17 +233,17 @@ def checkFormatEntries(): # #checkProg('a Postscript interpreter', ['gs'], # rc_entry = [ r'\ps_command "%%"' ]) - checkProg('a Postscript previewer', ['gsview32', 'gv', 'ghostview -swap', 'kghostview'], + checkProg('a Postscript previewer', ['gv', 'ghostview -swap', 'kghostview'], rc_entry = [ r'''\Format eps eps EPS "" "%%" "" \Format ps ps Postscript t "%%" ""''' ]) # - checkProg('a PDF previewer', ['acrobat', 'acrord32', 'gsview32', \ - 'acroread', 'gv', 'ghostview', 'xpdf', 'kpdf', 'kghostview'], + checkProg('a PDF previewer', ['acrobat', 'acroread', 'gv', 'ghostview', \ + 'xpdf', 'kpdf', 'kghostview'], rc_entry = [ r'''\Format pdf pdf "PDF (ps2pdf)" P "%%" "" \Format pdf2 pdf "PDF (pdflatex)" F "%%" "" \Format pdf3 pdf "PDF (dvipdfm)" m "%%" ""''' ]) # - checkProg('a DVI previewer', ['xdvi', 'windvi', 'yap', 'kdvi'], + checkProg('a DVI previewer', ['xdvi', 'kdvi'], rc_entry = [ r'\Format dvi dvi DVI D "%%" ""' ]) # checkProg('a HTML previewer', ['mozilla file://$$p$$i', 'netscape'], @@ -273,7 +275,7 @@ def checkConverterEntries(): Use PATH to avoid any problems with paths-with-spaces. ''' path_orig = os.environ["PATH"] - os.environ["PATH"] = os.path.join('..','src','tex2lyx') + \ + os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \ os.pathsep + path_orig checkProg('a LaTeX -> LyX converter', ['tex2lyx -f $$i $$o', \ diff --git a/src/ChangeLog b/src/ChangeLog index 947c041013..23e2da7d26 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,18 @@ +2006-05-16 Jean-Marc Lasgouttes + + * lyx_main.C (init): call Formats::setAutoOpen. + + * lyxrc.C (read): do not reset editor/viewer values of "none". + + * format.C (fixCommand): helper function: tweak command depending + of the availability of OS viewer/editor. + (setAutoOpen): run fixCommand over all the formats. + +2006-05-16 Bo Peng + + * format.C (edit, view): call autoOpenFile if editor/viewer is + "auto"; add more alerts for errors. + 2006-05-05 Jean-Marc Lasgouttes * lyx_main.C (init): call queryUserLyXDir just after reading diff --git a/src/format.C b/src/format.C index 9ba1f517e4..79ba386604 100644 --- a/src/format.C +++ b/src/format.C @@ -21,9 +21,13 @@ #include "frontends/Alert.h" //to be removed? #include "support/filetools.h" +#include "support/lstrings.h" +#include "support/os.h" #include "support/path.h" #include "support/systemcall.h" +#include + using lyx::support::bformat; using lyx::support::compare_ascii_no_case; using lyx::support::contains; @@ -35,10 +39,14 @@ using lyx::support::Path; using lyx::support::QuoteName; using lyx::support::subst; using lyx::support::Systemcall; +using lyx::support::token; using std::string; using std::distance; +namespace fs = boost::filesystem; +namespace os = lyx::support::os; + extern LyXServerSocket * lyxsocket; namespace { @@ -151,6 +159,39 @@ string Formats::getFormatFromFile(string const & filename) const return string(); } +namespace { + +string fixCommand(string const & cmd, string const & ext, + os::auto_open_mode mode) +{ + // configure.py says we do not want a viewer/editor + if (cmd.empty()) + return cmd; + + // Does the OS manage this format? + if (os::canAutoOpenFile(ext, mode)) + return "auto"; + + // if configure.py found nothing, clear the command + if (token(cmd, ' ', 0) == "none") + return string(); + + // use the command found by configure.py + return cmd; +} + +} + +void Formats::setAutoOpen() +{ + FormatList::iterator fit = formatlist.begin(); + FormatList::iterator const fend = formatlist.end(); + for ( ; fit != fend ; ++fit) { + fit->setViewer(fixCommand(fit->viewer(), fit->extension(), os::VIEW)); + fit->setEditor(fixCommand(fit->editor(), fit->extension(), os::EDIT)); + } +} + int Formats::getNumber(string const & name) const { @@ -216,21 +257,36 @@ void Formats::setViewer(string const & name, string const & command) bool Formats::view(Buffer const & buffer, string const & filename, string const & format_name) const { - if (filename.empty()) + if (filename.empty() || !fs::exists(filename)) { + Alert::error(_("Cannot view file"), + bformat(_("File does not exist: %1$s"), + filename)); return false; + } Format const * format = getFormat(format_name); if (format && format->viewer().empty() && format->isChildFormat()) format = getFormat(format->parentFormat()); if (!format || format->viewer().empty()) { -// I believe this is the wrong place to show alerts, it should be done by -// the caller (this should be "utility" code) +// FIXME: I believe this is the wrong place to show alerts, it should be done +// by the caller (this should be "utility" code) Alert::error(_("Cannot view file"), bformat(_("No information for viewing %1$s"), prettyName(format_name))); return false; } + // viewer is 'auto' + if (format->viewer() == "auto") { + if (os::autoOpenFile(filename, os::VIEW)) + return true; + else { + Alert::error(_("Cannot view file"), + bformat(_("Auto-view file %1$s failed"), + filename)); + return false; + } + } string command = LibScriptSearch(format->viewer()); @@ -272,21 +328,36 @@ bool Formats::view(Buffer const & buffer, string const & filename, bool Formats::edit(Buffer const & buffer, string const & filename, string const & format_name) const { - if (filename.empty()) + if (filename.empty() || !fs::exists(filename)) { + Alert::error(_("Cannot edit file"), + bformat(_("File does not exist: %1$s"), + filename)); return false; + } Format const * format = getFormat(format_name); if (format && format->editor().empty() && format->isChildFormat()) format = getFormat(format->parentFormat()); if (!format || format->editor().empty()) { -// I believe this is the wrong place to show alerts, it should be done by -// the caller (this should be "utility" code) +// FIXME: I believe this is the wrong place to show alerts, it should +// be done by the caller (this should be "utility" code) Alert::error(_("Cannot edit file"), bformat(_("No information for editing %1$s"), prettyName(format_name))); return false; } + // editor is 'auto' + if (format->editor() == "auto") { + if (os::autoOpenFile(filename, os::EDIT)) + return true; + else { + Alert::error(_("Cannot edit file"), + bformat(_("Auto-edit file %1$s failed"), + filename)); + return false; + } + } string command = format->editor(); diff --git a/src/format.h b/src/format.h index 54e337ff0a..d8a76c2ca1 100644 --- a/src/format.h +++ b/src/format.h @@ -56,6 +56,10 @@ public: std::string const & editor() const { return editor_; } + /// + void setEditor(std::string const & v) { + editor_ = v; + } private: std::string name_; /// @@ -93,6 +97,9 @@ public: * string. */ std::string getFormatFromFile(std::string const & filename) const; + /// Set editor and/or viewer to "auto" for formats that can be + /// opened by the OS. + void setAutoOpen(); /// int getNumber(std::string const & name) const; /// diff --git a/src/lyx_main.C b/src/lyx_main.C index e19c0a942e..727cbb10f4 100644 --- a/src/lyx_main.C +++ b/src/lyx_main.C @@ -455,6 +455,9 @@ void LyX::init(bool gui) // This one is generated in user_support directory by lib/configure.py. readRcFile("lyxrc.defaults"); + // Query the OS to know what formats are viewed natively + formats.setAutoOpen(); + system_lyxrc = lyxrc; system_formats = formats; system_converters = converters; diff --git a/src/lyxrc.C b/src/lyxrc.C index 36bc4fc70f..064b4caa05 100644 --- a/src/lyxrc.C +++ b/src/lyxrc.C @@ -1034,8 +1034,6 @@ int LyXRC::read(LyXLex & lexrc) } if (lexrc.next()) { command = lexrc.getString(); - if (token(command, ' ', 0) == "none") - command.erase(); } formats.setViewer(format, command); break; @@ -1061,12 +1059,8 @@ int LyXRC::read(LyXLex & lexrc) if (le != LyXLex::LEX_FEOF && le != LyXLex::LEX_UNDEF) { viewer = lexrc.getString(); if (le == LyXLex::LEX_DATA) { - if (token(viewer, ' ', 0) == "none") - viewer.erase(); if (lexrc.next()) { editor = lexrc.getString(); - if (token(editor, ' ', 0) == "none") - editor.erase(); } } else { // We have got a known token. diff --git a/src/support/ChangeLog b/src/support/ChangeLog index 112b699aaf..3069964ec3 100644 --- a/src/support/ChangeLog +++ b/src/support/ChangeLog @@ -1,3 +1,15 @@ +2006-05-16 Bo Peng + + * Makefile.am: under win32, link against shlwapi.dll. + + * os_*.C (canAutoOpenFile, autoOpenFile): new functions, used + to let the OS handle viewers and editors it knows about. + +2006-05-16 Enrico Forestieri + + * os_cygwin.C (init): Copy cygwin environment variables to the + Windows environment if they're not already there. + 2006-04-27 Jean-Marc Lasgouttes * package.C.in (get_build_dirs): Look at Makefile instead of diff --git a/src/support/Makefile.am b/src/support/Makefile.am index 9f343acd59..445be6e28b 100644 --- a/src/support/Makefile.am +++ b/src/support/Makefile.am @@ -9,6 +9,8 @@ EXTRA_DIST = package.C.in pch.h \ noinst_LTLIBRARIES = libsupport.la +libsupport_la_LIBADD = $(LIBSHLWAPI) + BUILT_SOURCES = $(PCH_FILE) package.C AM_CPPFLAGS += $(PCH_FLAGS) -I$(srcdir)/.. $(BOOST_INCLUDES) diff --git a/src/support/os.h b/src/support/os.h index 4d6d54d231..415868f802 100644 --- a/src/support/os.h +++ b/src/support/os.h @@ -80,6 +80,25 @@ char path_separator(); */ void cygwin_path_fix(bool use_cygwin_paths); +enum auto_open_mode { + VIEW, + EDIT +}; + +/** Check whether or not a file can be viewed by a default viewer + * \param extension (without leading .) + * \mode can be opened in VIEW or EDIT mode + * \returns whether or not the format can be viewed + */ +bool canAutoOpenFile(std::string const & ext, auto_open_mode const mode=VIEW); + +/** view a file, with given command and parameter. + * \param filename + * \param mode open in VIEW or EDIT mode + * \returns whether or not the file is viewed (or edited) successfully. + */ +bool autoOpenFile(std::string const & filename, auto_open_mode const mode=VIEW); + } // namespace os } // namespace support } // namespace lyx diff --git a/src/support/os_cygwin.C b/src/support/os_cygwin.C index e169f10e29..094b50788a 100644 --- a/src/support/os_cygwin.C +++ b/src/support/os_cygwin.C @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #include @@ -34,36 +37,6 @@ namespace lyx { namespace support { namespace os { -void os::init(int, char *[]) -{} - - -string current_root() -{ - return string("/"); -} - - -string::size_type common_path(string const & p1, string const & p2) -{ - string::size_type i = 0; - string::size_type p1_len = p1.length(); - string::size_type p2_len = p2.length(); - while (i < p1_len && i < p2_len && uppercase(p1[i]) == uppercase(p2[i])) - ++i; - if ((i < p1_len && i < p2_len) - || (i < p1_len && p1[i] != '/' && i == p2_len) - || (i < p2_len && p2[i] != '/' && i == p1_len)) - { - if (i) - --i; // here was the last match - while (i && p1[i] != '/') - --i; - } - return i; -} - - namespace { bool cygwin_path_fix_ = false; @@ -88,8 +61,8 @@ bool is_windows_path(string const & p) enum PathStyle { - posix, - windows + posix, + windows }; @@ -141,6 +114,75 @@ string convert_path_list(string const & p, PathStyle const & target) } // namespace anon +void os::init(int, char *[]) +{ + // Copy cygwin environment variables to the Windows environment + // if they're not already there. + + char **envp = environ; + char curval[2]; + string var; + string val; + bool temp_seen = false; + + while (envp && *envp) { + val = split(*envp++, var, '='); + + if (var == "TEMP") + temp_seen = true; + + if (GetEnvironmentVariable(var.c_str(), curval, 2) == 0 + && GetLastError() == ERROR_ENVVAR_NOT_FOUND) { + /* Convert to Windows style where necessary */ + if (var == "PATH" || var == "LD_LIBRARY_PATH") { + string const winpathlist = + convert_path_list(val, PathStyle(windows)); + if (!winpathlist.empty()) { + SetEnvironmentVariable(var.c_str(), + winpathlist.c_str()); + } + } else if (var == "HOME" || var == "TMPDIR" || + var == "TMP" || var == "TEMP") { + string const winpath = + convert_path(val, PathStyle(windows)); + SetEnvironmentVariable(var.c_str(), winpath.c_str()); + } else { + SetEnvironmentVariable(var.c_str(), val.c_str()); + } + } + } + if (!temp_seen) { + string const winpath = convert_path("/tmp", PathStyle(windows)); + SetEnvironmentVariable("TEMP", winpath.c_str()); + } +} + + +string current_root() +{ + return string("/"); +} + + +string::size_type common_path(string const & p1, string const & p2) +{ + string::size_type i = 0; + string::size_type p1_len = p1.length(); + string::size_type p2_len = p2.length(); + while (i < p1_len && i < p2_len && uppercase(p1[i]) == uppercase(p2[i])) + ++i; + if ((i < p1_len && i < p2_len) + || (i < p1_len && p1[i] != '/' && i == p2_len) + || (i < p2_len && p2[i] != '/' && i == p1_len)) + { + if (i) + --i; // here was the last match + while (i && p1[i] != '/') + --i; + } + return i; +} + string external_path(string const & p) { @@ -231,6 +273,39 @@ void cygwin_path_fix(bool use_cygwin_paths) cygwin_path_fix_ = use_cygwin_paths; } + +bool canAutoOpenFile(string const & ext, auto_open_mode const mode) +{ + if (ext.empty()) + return false; + + string full_ext = ext; + // if the extension is passed without leading dot + if (full_ext[0] != '.') + full_ext = "." + ext; + + DWORD bufSize = MAX_PATH + 100; + TCHAR buf[MAX_PATH + 100]; + // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc + // /platform/shell/reference/shlwapi/registry/assocquerystring.asp + char const * action = (mode == VIEW) ? "open" : "edit"; + return S_OK == AssocQueryString(0, ASSOCSTR_EXECUTABLE, + full_ext.c_str(), action, buf, &bufSize); +} + + +bool autoOpenFile(string const & filename, auto_open_mode const mode) +{ + // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc + // /platform/shell/reference/functions/shellexecute.asp + string const win_path = + os::convert_path(filename, os::PathStyle(os::windows)); + char const * action = (mode == VIEW) ? "open" : "edit"; + return reinterpret_cast(ShellExecute(NULL, action, + win_path.c_str(), NULL, NULL, 1)) > 32; +} + + } // namespace os } // namespace support } // namespace lyx diff --git a/src/support/os_unix.C b/src/support/os_unix.C index 0ffce418c9..1ad2b14212 100644 --- a/src/support/os_unix.C +++ b/src/support/os_unix.C @@ -115,6 +115,21 @@ char path_separator() void cygwin_path_fix(bool) {} +bool canAutoOpenFile(string const & /*ext*/, auto_open_mode const /*mode*/) +{ + // currently, no default viewer is tried for non-windows system + // support for KDE/Gnome/Macintosh may be added later + return false; +} + + +bool autoOpenFile(string const & /*filename*/, auto_open_mode const /*mode*/) +{ + // currently, no default viewer is tried for non-windows system + // support for KDE/Gnome/Macintosh may be added later + return false; +} + } // namespace os } // namespace support } // namespace lyx diff --git a/src/support/os_win32.C b/src/support/os_win32.C index 71d2ff5d56..4b0604efe9 100644 --- a/src/support/os_win32.C +++ b/src/support/os_win32.C @@ -49,6 +49,9 @@ #include #include // _getdrive #include // SHGetFolderPath +#include +#include +#include // Must define SHGFP_TYPE_CURRENT for older versions of MinGW. #if defined(__MINGW32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) @@ -347,6 +350,37 @@ string const GetFolderPath::operator()(folder_id _id) const return (result == 0) ? os::internal_path(folder_path) : string(); } + +bool canAutoOpenFile(string const & ext, auto_open_mode const mode) +{ + if (ext.empty()) + return false; + + string full_ext = ext; + // if the extension is passed without leading dot + if (full_ext[0] != '.') + full_ext = "." + ext; + + DWORD bufSize = MAX_PATH + 100; + TCHAR buf[MAX_PATH + 100]; + // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc + // /platform/shell/reference/shlwapi/registry/assocquerystring.asp + char const * action = (mode == VIEW) ? "open" : "edit"; + return S_OK == AssocQueryString(0, ASSOCSTR_EXECUTABLE, + full_ext.c_str(), action, buf, &bufSize); +} + + +bool autoOpenFile(string const & filename, auto_open_mode const mode) +{ + // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc + // /platform/shell/reference/functions/shellexecute.asp + char const * action = (mode == VIEW) ? "open" : "edit"; + return reinterpret_cast(ShellExecute(NULL, action, + filename.c_str(), NULL, NULL, 1)) > 32; +} + + } // namespace os } // namespace support } // namespace lyx diff --git a/status.14x b/status.14x index 7cebc33767..31348730d3 100644 --- a/status.14x +++ b/status.14x @@ -72,6 +72,9 @@ What's new - Don't jump back to previous cursor position when trying to click on an inset (bug 2526). +- LyX now automatically uses file viewers and editors set at OS level + [windows only] + - When pasting contents to a tabular with the middle mouse button, the language has sometimes be changed. This is fixed now.