mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-09 18:31:04 +00:00
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:
parent
f39c9e1d5e
commit
b6e30400f5
@ -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,
|
||||
|
@ -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', \
|
||||
|
83
src/format.C
83
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 <boost/filesystem/operations.hpp>
|
||||
|
||||
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();
|
||||
|
||||
|
@ -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;
|
||||
///
|
||||
|
@ -458,6 +458,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;
|
||||
|
@ -1067,8 +1067,6 @@ int LyXRC::read(LyXLex & lexrc)
|
||||
}
|
||||
if (lexrc.next()) {
|
||||
command = lexrc.getString();
|
||||
if (token(command, ' ', 0) == "none")
|
||||
command.erase();
|
||||
}
|
||||
formats.setViewer(format, command);
|
||||
break;
|
||||
@ -1094,12 +1092,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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -21,6 +21,9 @@
|
||||
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <windef.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include <sys/cygwin.h>
|
||||
|
||||
@ -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<int>(ShellExecute(NULL, action,
|
||||
win_path.c_str(), NULL, NULL, 1)) > 32;
|
||||
}
|
||||
|
||||
|
||||
} // namespace os
|
||||
} // namespace support
|
||||
} // namespace lyx
|
||||
|
@ -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
|
||||
|
@ -50,6 +50,9 @@
|
||||
#include <io.h>
|
||||
#include <direct.h> // _getdrive
|
||||
#include <shlobj.h> // SHGetFolderPath
|
||||
#include <windef.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
// Must define SHGFP_TYPE_CURRENT for older versions of MinGW.
|
||||
#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();
|
||||
}
|
||||
|
||||
|
||||
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 support
|
||||
} // namespace lyx
|
||||
|
Loading…
Reference in New Issue
Block a user