Although this is unrelated to my patch, I will add a fs::exists check in view() function to make the error message clearer.
This is in the attached updated patch. Allow me to reiterate what this patch does: Under windows (including cygwin): 1. configure.py does not actually search for programs. %% is replaced by 'auto'. 2. A viewer item under the view menu will appear if * a program is explicitly specified, or * auto is used, and the format is auto-openable The menu item will *not* appear if the viewer is empty. 3. The windows default viewer will be used to view a file. OK to apply? Bo
Index: src/graph.C =================================================================== --- src/graph.C (revision 13784) +++ src/graph.C (working copy) @@ -77,8 +77,9 @@ int const i = Q_.front(); Q_.pop(); Format const & format = formats.get(i); - if (!only_viewable || !format.viewer().empty() || - format.isChildFormat()) + if (!only_viewable || format.isChildFormat() || + (!format.viewer().empty() && + (format.viewer() != "auto" || format.isAutoViewable()))) result.push_back(i); vector<int>::const_iterator cit = Index: src/format.C =================================================================== --- src/format.C (revision 13784) +++ src/format.C (working copy) @@ -24,6 +24,8 @@ #include "support/path.h" #include "support/systemcall.h" +#include <boost/filesystem/operations.hpp> + using lyx::support::bformat; using lyx::support::compare_ascii_no_case; using lyx::support::contains; @@ -35,10 +37,16 @@ using lyx::support::quoteName; using lyx::support::subst; using lyx::support::Systemcall; +using lyx::support::VIEW; +using lyx::support::EDIT; +using lyx::support::canAutoOpenFile; +using lyx::support::autoOpenFile; using std::string; using std::distance; +namespace fs = boost::filesystem; + extern LyXServerSocket * lyxsocket; namespace { @@ -87,7 +95,9 @@ Format::Format(string const & n, string const & e, string const & p, string const & s, string const & v, string const & ed) : name_(n), extension_(e), prettyname_(p), shortcut_(s), viewer_(v), editor_(ed) -{} +{ + autoViewable_ = canAutoOpenFile(e, VIEW); +} bool Format::dummy() const @@ -216,8 +226,12 @@ 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() && @@ -231,6 +245,17 @@ prettyName(format_name))); return false; } + // viewer is 'auto' + if (format->viewer() == "auto") { + if (autoOpenFile(filename, 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,8 +297,12 @@ 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() && @@ -287,6 +316,17 @@ prettyName(format_name))); return false; } + // editor is 'auto' + if (format->editor() == "auto") { + if (autoOpenFile(filename, EDIT)) + return true; + else { + Alert::error(_("Cannot edit file"), + bformat(_("Auto-edit file %1$s failed"), + filename)); + return false; + } + } string command = format->editor(); Index: src/support/os.h =================================================================== --- src/support/os.h (revision 13784) +++ src/support/os.h (working copy) @@ -80,6 +80,23 @@ */ void cygwin_path_fix(bool use_cygwin_paths); +#if defined(__CYGWIN__) || defined(__CYGWIN32__) +enum PathStyle { + posix, + windows +}; + +/// Converts a path to a target style. +std::string convert_path(std::string const & p, PathStyle const & target); + +/// Converts a path list to a target style. +std::string convert_path_list(std::string const & p, PathStyle const & target); + +/// Copy cygwin environment variables to the Windows environment +/// if they're not already there. +void setup_windows_environment(void); +#endif + } // namespace os } // namespace support } // namespace lyx Index: src/support/filetools.C =================================================================== --- src/support/filetools.C (revision 13784) +++ src/support/filetools.C (working copy) @@ -51,6 +51,12 @@ #include <fstream> #include <sstream> +#if defined(_WIN32) || defined(__CYGWIN__) +# include <windef.h> +# include <shellapi.h> +# include <shlwapi.h> +#endif + #ifndef CXX_GLOBAL_CSTD using std::fgetc; using std::isalnum; @@ -1225,5 +1231,64 @@ return cmp; } + +bool canAutoOpenFile(string const & ext, auto_open_mode const mode) +{ +#if defined(_WIN32) || defined(__CYGWIN__) + if (ext == "") + 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 + if (mode == VIEW) + return S_OK == AssocQueryString(0, ASSOCSTR_EXECUTABLE, + full_ext.c_str(), "open", buf, &bufSize); + else + return S_OK == AssocQueryString(0, ASSOCSTR_EXECUTABLE, + full_ext.c_str(), "edit", buf, &bufSize); +#else + // currently, no default viewer is tried for non-windows system + // support for KDE/Gnome/Macintosh may be added later + return false; +#endif +} + + +bool autoOpenFile(string const & filename, auto_open_mode const mode) +{ +#if defined(_WIN32) || defined(__CYGWIN__) + lyxerr[Debug::FILES] << "Auto open file: " << filename << std::endl; + + // reference: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc + // /platform/shell/reference/functions/shellexecute.asp +# ifdef __CYGWIN__ + string const converted_path = + os::convert_path(filename, os::PathStyle(os::windows)); + char const * const win_path = converted_path.c_str(); + // Need to sync the Windows environment + os::setup_windows_environment(); +# else + char const * const win_path = filename.c_str(); +# endif + if (mode == VIEW) + return reinterpret_cast<int>(ShellExecute(NULL, "open", + win_path, NULL, NULL, 1)) > 32; + else + return reinterpret_cast<int>(ShellExecute(NULL, "edit", + win_path, NULL, NULL, 1)) > 32; +#else + // currently, no default viewer is tried for non-windows system + // support for KDE/Gnome/Macintosh may be added later + return false; +#endif +} + } //namespace support } // namespace lyx Index: src/support/os_cygwin.C =================================================================== --- src/support/os_cygwin.C (revision 13784) +++ src/support/os_cygwin.C (working copy) @@ -86,13 +86,9 @@ (!contains(p, '\\') && (p.length() <= 1 || p[1] == ':')); } +} // namespace anon -enum PathStyle { - posix, - windows -}; - string convert_path(string const & p, PathStyle const & target) { char path_buf[PATH_MAX]; @@ -139,9 +135,7 @@ return subst(p, '\\', '/'); } -} // namespace anon - string external_path(string const & p) { return convert_path(p, cygwin_path_fix_ ? PathStyle(windows) @@ -199,6 +193,49 @@ } +// Copy cygwin environment variables to the Windows environment +// if they're not already there. +void setup_windows_environment(void) +{ + 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()); + } +} + + // returns a string suitable to be passed to popen when // reading a pipe char const * popen_read_mode() Index: src/support/filetools.h =================================================================== --- src/support/filetools.h (revision 13784) +++ src/support/filetools.h (working copy) @@ -266,6 +266,25 @@ cmd_ret const runCommand(std::string const & cmd); +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 support } // namespace lyx Index: src/support/Makefile.am =================================================================== --- src/support/Makefile.am (revision 13784) +++ src/support/Makefile.am (working copy) @@ -9,6 +9,8 @@ noinst_LTLIBRARIES = libsupport.la +libsupport_la_LIBADD = $(LIBSHLWAPI) + BUILT_SOURCES = $(PCH_FILE) package.C AM_CPPFLAGS += $(PCH_FLAGS) -I$(srcdir)/.. $(BOOST_INCLUDES) Index: src/format.h =================================================================== --- src/format.h (revision 13784) +++ src/format.h (working copy) @@ -49,6 +49,10 @@ return viewer_; } /// + bool const isAutoViewable() const { + return autoViewable_; + } + /// void setViewer(std::string const & v) { viewer_ = v; } @@ -67,6 +71,8 @@ /// std::string viewer_; /// + bool autoViewable_; + /// std::string editor_; }; Index: lib/configure.py =================================================================== --- lib/configure.py (revision 13784) +++ lib/configure.py (working copy) @@ -108,7 +108,7 @@ ## Searching some useful programs -def checkProg(description, progs, rc_entry = [], path = [] ): +def checkProg(description, progs, rc_entry = [], path = [], searchProg = True): ''' This function will search a program in $PATH plus given path If found, return directory and program name (not the options). @@ -119,21 +119,32 @@ 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 + searchProg: whether or not search given programs. If searchProg == False, + 1. rc_entry[0] will be used, with %% replaced by 'auto' + 2. ['', 'auto'] will be returned. + This is currently used under windows. ''' # one rc entry for each progs plus none entry if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1: print "rc entry should have one item or item for each prog and none." sys.exit(2) print 'checking for ' + description + '...' + if not searchProg: + print '+checking for "auto"... yes' + if len(rc_entry) > 0: + addToRC(rc_entry[0].replace('%%', 'auto')) + return ['', 'auto'] ## print '(' + ','.join(progs) + ')', for idx in range(len(progs)): # ac_prog may have options, ac_word is the command name @@ -186,22 +197,26 @@ return '' -def checkFormatEntries(): +def checkFormatEntries(check = True): ''' Check all formats (\Format entries) ''' checkProg('a Tgif viewer and editor', ['tgif'], - rc_entry = [ r'\Format tgif obj Tgif "" "%%" "%%"']) + rc_entry = [ r'\Format tgif obj Tgif "" "%%" "%%"'], + searchProg = check) # checkProg('a FIG viewer and editor', ['xfig'], - rc_entry = [ r'\Format fig fig FIG "" "%%" "%%"'] ) + rc_entry = [ r'\Format fig fig FIG "" "%%" "%%"'], + searchProg = check) # checkProg('a Grace viewer and editor', ['xmgrace'], - rc_entry = [ r'\Format agr agr Grace "" "%%" "%%"'] ) + rc_entry = [ r'\Format agr agr Grace "" "%%" "%%"'], + searchProg = check) # checkProg('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'], - rc_entry = [ r'\Format fen fen FEN "" "%%" "%%"' ]) + rc_entry = [ r'\Format fen fen FEN "" "%%" "%%"'], + searchProg = check) # - path, iv = checkProg('a raster image viewer', ['xv', 'kview', 'gimp']) - path, ie = checkProg('a raster image editor', ['gimp']) + path, iv = checkProg('a raster image viewer', ['xv', 'kview', 'gimp'], searchProg = check) + path, ie = checkProg('a raster image editor', ['gimp'], searchProg = check) addToRC(r'''\Format bmp bmp BMP "" "%s" "%s" \Format gif gif GIF "" "%s" "%s" \Format jpg jpg JPEG "" "%s" "%s" @@ -227,25 +242,30 @@ \Format linuxdoc sgml LinuxDoc x "" "%%" \Format pdflatex tex "LaTeX (pdflatex)" "" "" "%%" \Format text txt "Plain text" a "" "%%" -\Format textparagraph txt "Plain text (paragraphs)" "" "" "%%"''' ]) +\Format textparagraph txt "Plain text (paragraphs)" "" "" "%%"''' ], + searchProg = check) # #checkProg('a Postscript interpreter', ['gs'], # rc_entry = [ r'\ps_command "%%"' ]) checkProg('a Postscript previewer', ['gsview32', 'gv', 'ghostview -swap', 'kghostview'], rc_entry = [ r'''\Format eps eps EPS "" "%%" "" -\Format ps ps Postscript t "%%" ""''' ]) +\Format ps ps Postscript t "%%" ""''' ], + searchProg = check) # checkProg('a PDF previewer', ['acrobat', 'acrord32', 'gsview32', \ '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 "%%" ""''' ]) +\Format pdf3 pdf "PDF (dvipdfm)" m "%%" ""'''], + searchProg = check) # checkProg('a DVI previewer', ['xdvi', 'windvi', 'yap', 'kdvi'], - rc_entry = [ r'\Format dvi dvi DVI D "%%" ""' ]) + rc_entry = [ r'\Format dvi dvi DVI D "%%" ""' ], + searchProg = check) # checkProg('a HTML previewer', ['mozilla file://$$p$$i', 'netscape'], - rc_entry = [ r'\Format html html HTML H "%%" ""' ]) + rc_entry = [ r'\Format html html HTML H "%%" ""' ], + searchProg = check) # # entried that do not need checkProg addToRC(r'''\Format date "" "date command" "" "" "" @@ -273,7 +293,7 @@ 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', \ @@ -671,7 +691,8 @@ ''') # check latex LATEX = checkLatex() - checkFormatEntries() + # viewers under windows are handled automatically + checkFormatEntries(os.name != 'nt' and sys.platform != 'cygwin') checkConverterEntries() (chk_linuxdoc, bool_linuxdoc, linuxdoc_cmd) = checkLinuxDoc() (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook() Index: config/cygwin.m4 =================================================================== --- config/cygwin.m4 (revision 13784) +++ config/cygwin.m4 (working copy) @@ -22,4 +22,10 @@ done ;; esac + + case $host_os in + cygwin* | mingw* ) + AC_SUBST(LIBSHLWAPI, [-lshlwapi]) + ;; + esac ])