New auto-open feature from Bo Peng (with help from Jean-Marc and Enrico)

* src/lyx_main.C (init): call Formats::setAutoOpen.
	
	* src/lyxrc.C (read): do not reset editor/viewer values of "none".

	* src/format.C (fixCommand): helper function: tweak command depending
	of the availability of OS viewer/editor.
	(setAutoOpen): run fixCommand over all the formats.

	* src/support/Makefile.am: under win32, link against shlwapi.dll.
	
	* src/support/os_*.C (canAutoOpenFile, autoOpenFile): new
	functions, used	to let the OS handle viewers and editors it knows about.

	* configure.ac: improve check for shlwapi.

	* lib/configure.py: remove check for native windows viewers.



git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@13855 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Jean-Marc Lasgouttes 2006-05-17 22:13:33 +00:00
parent f39c9e1d5e
commit b6e30400f5
11 changed files with 280 additions and 57 deletions

View File

@ -118,7 +118,8 @@ LYX_ADD_INC_DIR(CPPFLAGS,$dir/include)
LYX_ADD_LIB_DIR(LDFLAGS,$dir/lib)]) LYX_ADD_LIB_DIR(LDFLAGS,$dir/lib)])
### These are needed in windows ### 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_CHECK_LIB(gdi32, main)
AC_ARG_WITH(aiksaurus, AC_ARG_WITH(aiksaurus,

View File

@ -119,15 +119,17 @@ def checkProg(description, progs, rc_entry = [], path = [] ):
for searching but the whole string is used to replace for searching but the whole string is used to replace
%% for a rc_entry. So, feel free to add '$$i' etc for programs. %% 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 rc_entry: entry to outfile, can be
prog or 'none'), or one for each prog and 'none'. 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 # one rc entry for each progs plus none entry
if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1: if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
@ -231,17 +233,17 @@ def checkFormatEntries():
# #
#checkProg('a Postscript interpreter', ['gs'], #checkProg('a Postscript interpreter', ['gs'],
# rc_entry = [ r'\ps_command "%%"' ]) # 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 "" "%%" "" rc_entry = [ r'''\Format eps eps EPS "" "%%" ""
\Format ps ps Postscript t "%%" ""''' ]) \Format ps ps Postscript t "%%" ""''' ])
# #
checkProg('a PDF previewer', ['acrobat', 'acrord32', 'gsview32', \ checkProg('a PDF previewer', ['acrobat', 'acroread', 'gv', 'ghostview', \
'acroread', 'gv', 'ghostview', 'xpdf', 'kpdf', 'kghostview'], 'xpdf', 'kpdf', 'kghostview'],
rc_entry = [ r'''\Format pdf pdf "PDF (ps2pdf)" P "%%" "" rc_entry = [ r'''\Format pdf pdf "PDF (ps2pdf)" P "%%" ""
\Format pdf2 pdf "PDF (pdflatex)" F "%%" "" \Format pdf2 pdf "PDF (pdflatex)" F "%%" ""
\Format pdf3 pdf "PDF (dvipdfm)" m "%%" ""''' ]) \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 "%%" ""' ]) rc_entry = [ r'\Format dvi dvi DVI D "%%" ""' ])
# #
checkProg('a HTML previewer', ['mozilla file://$$p$$i', 'netscape'], 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. Use PATH to avoid any problems with paths-with-spaces.
''' '''
path_orig = os.environ["PATH"] 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 os.pathsep + path_orig
checkProg('a LaTeX -> LyX converter', ['tex2lyx -f $$i $$o', \ checkProg('a LaTeX -> LyX converter', ['tex2lyx -f $$i $$o', \

View File

@ -21,9 +21,13 @@
#include "frontends/Alert.h" //to be removed? #include "frontends/Alert.h" //to be removed?
#include "support/filetools.h" #include "support/filetools.h"
#include "support/lstrings.h"
#include "support/os.h"
#include "support/path.h" #include "support/path.h"
#include "support/systemcall.h" #include "support/systemcall.h"
#include <boost/filesystem/operations.hpp>
using lyx::support::bformat; using lyx::support::bformat;
using lyx::support::compare_ascii_no_case; using lyx::support::compare_ascii_no_case;
using lyx::support::contains; using lyx::support::contains;
@ -35,10 +39,14 @@ using lyx::support::Path;
using lyx::support::quoteName; using lyx::support::quoteName;
using lyx::support::subst; using lyx::support::subst;
using lyx::support::Systemcall; using lyx::support::Systemcall;
using lyx::support::token;
using std::string; using std::string;
using std::distance; using std::distance;
namespace fs = boost::filesystem;
namespace os = lyx::support::os;
extern LyXServerSocket * lyxsocket; extern LyXServerSocket * lyxsocket;
namespace { namespace {
@ -151,6 +159,39 @@ string Formats::getFormatFromFile(string const & filename) const
return string(); 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 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, bool Formats::view(Buffer const & buffer, string const & filename,
string const & format_name) const 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; return false;
}
Format const * format = getFormat(format_name); Format const * format = getFormat(format_name);
if (format && format->viewer().empty() && if (format && format->viewer().empty() &&
format->isChildFormat()) format->isChildFormat())
format = getFormat(format->parentFormat()); format = getFormat(format->parentFormat());
if (!format || format->viewer().empty()) { if (!format || format->viewer().empty()) {
// I believe this is the wrong place to show alerts, it should be done by // FIXME: I believe this is the wrong place to show alerts, it should be done
// the caller (this should be "utility" code) // by the caller (this should be "utility" code)
Alert::error(_("Cannot view file"), Alert::error(_("Cannot view file"),
bformat(_("No information for viewing %1$s"), bformat(_("No information for viewing %1$s"),
prettyName(format_name))); prettyName(format_name)));
return false; 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()); 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, bool Formats::edit(Buffer const & buffer, string const & filename,
string const & format_name) const 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; return false;
}
Format const * format = getFormat(format_name); Format const * format = getFormat(format_name);
if (format && format->editor().empty() && if (format && format->editor().empty() &&
format->isChildFormat()) format->isChildFormat())
format = getFormat(format->parentFormat()); format = getFormat(format->parentFormat());
if (!format || format->editor().empty()) { if (!format || format->editor().empty()) {
// I believe this is the wrong place to show alerts, it should be done by // FIXME: I believe this is the wrong place to show alerts, it should
// the caller (this should be "utility" code) // be done by the caller (this should be "utility" code)
Alert::error(_("Cannot edit file"), Alert::error(_("Cannot edit file"),
bformat(_("No information for editing %1$s"), bformat(_("No information for editing %1$s"),
prettyName(format_name))); prettyName(format_name)));
return false; 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(); string command = format->editor();

View File

@ -56,6 +56,10 @@ public:
std::string const & editor() const { std::string const & editor() const {
return editor_; return editor_;
} }
///
void setEditor(std::string const & v) {
editor_ = v;
}
private: private:
std::string name_; std::string name_;
/// ///
@ -93,6 +97,9 @@ public:
* string. * string.
*/ */
std::string getFormatFromFile(std::string const & filename) const; 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; int getNumber(std::string const & name) const;
/// ///

View File

@ -458,6 +458,9 @@ void LyX::init(bool gui)
// This one is generated in user_support directory by lib/configure.py. // This one is generated in user_support directory by lib/configure.py.
readRcFile("lyxrc.defaults"); readRcFile("lyxrc.defaults");
// Query the OS to know what formats are viewed natively
formats.setAutoOpen();
system_lyxrc = lyxrc; system_lyxrc = lyxrc;
system_formats = formats; system_formats = formats;
system_converters = converters; system_converters = converters;

View File

@ -1067,8 +1067,6 @@ int LyXRC::read(LyXLex & lexrc)
} }
if (lexrc.next()) { if (lexrc.next()) {
command = lexrc.getString(); command = lexrc.getString();
if (token(command, ' ', 0) == "none")
command.erase();
} }
formats.setViewer(format, command); formats.setViewer(format, command);
break; break;
@ -1094,12 +1092,8 @@ int LyXRC::read(LyXLex & lexrc)
if (le != LyXLex::LEX_FEOF && le != LyXLex::LEX_UNDEF) { if (le != LyXLex::LEX_FEOF && le != LyXLex::LEX_UNDEF) {
viewer = lexrc.getString(); viewer = lexrc.getString();
if (le == LyXLex::LEX_DATA) { if (le == LyXLex::LEX_DATA) {
if (token(viewer, ' ', 0) == "none")
viewer.erase();
if (lexrc.next()) { if (lexrc.next()) {
editor = lexrc.getString(); editor = lexrc.getString();
if (token(editor, ' ', 0) == "none")
editor.erase();
} }
} else { } else {
// We have got a known token. // We have got a known token.

View File

@ -9,6 +9,8 @@ EXTRA_DIST = package.C.in pch.h \
noinst_LTLIBRARIES = libsupport.la noinst_LTLIBRARIES = libsupport.la
libsupport_la_LIBADD = $(LIBSHLWAPI)
BUILT_SOURCES = $(PCH_FILE) package.C BUILT_SOURCES = $(PCH_FILE) package.C
AM_CPPFLAGS += $(PCH_FLAGS) -I$(srcdir)/.. $(BOOST_INCLUDES) AM_CPPFLAGS += $(PCH_FLAGS) -I$(srcdir)/.. $(BOOST_INCLUDES)

View File

@ -80,6 +80,25 @@ char path_separator();
*/ */
void cygwin_path_fix(bool use_cygwin_paths); 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 os
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx

View File

@ -21,6 +21,9 @@
#include <windows.h> #include <windows.h>
#include <io.h> #include <io.h>
#include <windef.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <sys/cygwin.h> #include <sys/cygwin.h>
@ -34,36 +37,6 @@ namespace lyx {
namespace support { namespace support {
namespace os { 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 { namespace {
bool cygwin_path_fix_ = false; bool cygwin_path_fix_ = false;
@ -88,8 +61,8 @@ bool is_windows_path(string const & p)
enum PathStyle { enum PathStyle {
posix, posix,
windows windows
}; };
@ -141,6 +114,75 @@ string convert_path_list(string const & p, PathStyle const & target)
} // namespace anon } // 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) string external_path(string const & p)
{ {
@ -231,6 +273,39 @@ void cygwin_path_fix(bool use_cygwin_paths)
cygwin_path_fix_ = 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<int>(ShellExecute(NULL, action,
win_path.c_str(), NULL, NULL, 1)) > 32;
}
} // namespace os } // namespace os
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx

View File

@ -115,6 +115,21 @@ char path_separator()
void cygwin_path_fix(bool) 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 os
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx

View File

@ -50,6 +50,9 @@
#include <io.h> #include <io.h>
#include <direct.h> // _getdrive #include <direct.h> // _getdrive
#include <shlobj.h> // SHGetFolderPath #include <shlobj.h> // SHGetFolderPath
#include <windef.h>
#include <shellapi.h>
#include <shlwapi.h>
// Must define SHGFP_TYPE_CURRENT for older versions of MinGW. // Must define SHGFP_TYPE_CURRENT for older versions of MinGW.
#if defined(__MINGW32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) #if defined(__MINGW32__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
@ -389,6 +392,37 @@ string const GetFolderPath::operator()(folder_id _id) const
return (result == 0) ? os::internal_path(folder_path) : string(); 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<int>(ShellExecute(NULL, action,
filename.c_str(), NULL, NULL, 1)) > 32;
}
} // namespace os } // namespace os
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx