Merge branch '2.3.2-staging' into 2.3.x

This commit is contained in:
Richard Heck 2018-04-14 23:52:31 -04:00
commit bcb002039b
135 changed files with 9608 additions and 2497 deletions

View File

@ -16,7 +16,7 @@ EXTRA_DIST = \
1.6.2/src/hunspell/hunvisapi.h.in \
1.6.2/src/hunspell/utf_info.cxx
AM_CPPFLAGS += -DHUNSPELL_STATIC
AM_CPPFLAGS += -DHUNSPELL_STATIC @STDLIB_DEBUG@
liblyxhunspell_a_SOURCES = \
1.6.2/src/hunspell/affentry.cxx \

View File

@ -228,6 +228,9 @@ The following options allow you to tweak the generated code more precisely (see
--without-included-boost is specified). You may have to use
--disable-stdlib-debug when linking development versions against
your system's boost library.
The same problem applies to hunspell (as of hunspell 1.5). So either
compile --with-included-hunspell or --disable-stdlib-debug when
linking development versions against your system's hunspell library.
o --enable-monolithic-build[=boost,client,insets,mathed,core,tex2lyx,frontend-qt4]
that enables monolithic build of the given parts of the source

View File

@ -420,6 +420,7 @@ if test x$GXX = xyes; then
lyx_flags="$lyx_flags stdlib-debug"
AC_DEFINE(_GLIBCXX_DEBUG, 1, [libstdc++ debug mode])
AC_DEFINE(_GLIBCXX_DEBUG_PEDANTIC, 1, [libstdc++ pedantic debug mode])
AC_SUBST(STDLIB_DEBUG, "-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC")
;;
esac
fi

View File

@ -60,6 +60,24 @@ AC_DEFUN([CHECK_WITH_ENCHANT],
fi
])
AC_DEFUN([LYX_HAVE_HUNSPELL_CXXABI],
[
AC_MSG_CHECKING([whether hunspell C++ (rather than C) ABI is provided])
save_CXXFLAGS=$CXXFLAGS
CXXFLAGS="$ENCHANT_CFLAGS $AM_CXXFLAGS $CXXFLAGS"
# in the C++ ABI, stem() returns a vector, in the C ABI, it returns an int
AC_TRY_COMPILE([#include <hunspell/hunspell.hxx>],
[Hunspell sp("foo", "bar");
int i = sp.stem("test").size();],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_HUNSPELL_CXXABI, 1, [Define to 1 if hunspell C++ (rather than C) ABI is detected])
have_hunspell_cxx_abi=yes
],
[AC_MSG_RESULT(no)])
CXXFLAGS=$save_CXXFLAGS
])
# Macro to add for using hunspell spellchecker libraries! -*- sh -*-
AC_DEFUN([CHECK_WITH_HUNSPELL],
[
@ -83,6 +101,12 @@ AC_DEFUN([CHECK_WITH_HUNSPELL],
AC_MSG_RESULT(no)
fi
fi
LYX_HAVE_HUNSPELL_CXXABI
if test $enable_stdlib_debug = "yes" -a -n "$have_hunspell_cxx_abi" ; then
LYX_WARNING([Compiling LyX with stdlib-debug and system hunspell libraries may lead to
crashes. Consider using --disable-stdlib-debug or --with-included-hunspell.])
fi
])
dnl Usage: LYX_USE_INCLUDED_HUNSPELL : select if the included hunspell should
@ -113,6 +137,7 @@ AC_DEFUN([LYX_CHECK_SPELL_ENGINES],
dnl the user wanted to use the included hunspell, so do not check for external hunspell
lyx_use_hunspell=true
AC_DEFINE(USE_HUNSPELL, 1, [Define as 1 to use the hunspell library])
AC_DEFINE(HAVE_HUNSPELL_CXXABI, 1, [Define to 1 if hunspell C++ (rather than C) ABI is detected])
lyx_flags="$lyx_flags use-hunspell"
else
CHECK_WITH_HUNSPELL

View File

@ -60,38 +60,43 @@ cursor.
* Clean-up of drawing code
The goal is to make painting with drawing disable fast enough that it
can be used after every metrics computation. Then we can separate real
drawing from metrics.
** Make SinglePar update flag useful again.
Other changes are only clean-ups.
The current code can be very expensive when moving cursor inside a
huge table, for example. We should test the flag again, although this
will probably lead to some glitches here and there.
** Set Row::changed() in a finer way
*** singleParUpdate
When the height of the current paragraph changes, there is no need for
a full screen update. Only the rows after the current one need to have
their position recomputed.
This is also true when scrolling (how to do that?)
*** redoParagraph
It should be possible to check whether the new row is the same as the
old one and keep its changed() status in this case. This would reduce
a lot the amount of stuff to redraw.
** Put labels and friends in the Row as elements
It should not be necessary to access the Paragraph object to draw.
Adding the static elements to Row is a lot of work, but worth it IMO.
** Create a unique row by paragraph and break it afterwards
This should be a performance gain (only if paragraph breaking still
shows as expensive after the rest is done)
** do not add the vertical margin of main text to first/last row
Would make code cleaner. Probably no so difficult.
** When a paragraph ends with a newline, compute correctly the height of the extra row.
** Rewrite TextMetrics::editXY, checkInsetHit using row information (getPosNearX)?
The helper version should return a Row::Element instead of an InsetTable.
** Set inset position during metrics phase
In order to do that, a no-paint drawing will be initiated after every
redoParagraph. This code path will need to be made as fast as possible.
Effect: avoid depending on actual drawing having taken place. In turn,
it will allow to do drawing on paint events, like any reasonable
application would do.
** Cleanup after complete metrics
Then the following can be done:
+ remove hack in InsetMathNest::drawSelection
+ remove painting when not inside in drawParagraph
+ remove Cursor::inCoordCache?
** Paint directly to screen
Instead of using an intermediary pixmap. I have no idea of how
difficult it will prove.
One benefit will be that subpixel aliasing will work again (#9972)
** Merging bv::updateMetrics and tm::metrics
While the full metrics computation tries hard to limit the number of
@ -101,25 +106,25 @@ insets. We should re-use the bv::updateMetrics logic:
+ transfer all the logic of bv::updateMetrics to tm.
+ Main InsetText should not be special.
The difficuly for a tall table cell for example, is that it may be
The difficulty for a tall table cell for example, is that it may be
necessary to break the whole contents to know the width of the cell.
* Description of current drawing mechanism
** Two stage drawing
** Three-stage drawing
There are two parts to drawing the work area:
There are three parts to drawing the work area:
+ the metrics phase computes the size of insets and breaks the
paragraphs into rows. It stores the dimension of insets (both
normal and math) in bv::coordCache.
+ the drawing phase draws the contents and caches the inset
positions. Since the caching of positions is useful in itself,
there is a provision for drawing "without" drawing when the only
thing we want is to cache inset positions
(Painter::setDrawingEnabled).
+ the nodraw drawing phase paints the screen (see below) with a null
painter. The only useful effect is to store the inset positions.
+ an update() signal is sent. This in turn will trigger a paint
event, and the actual screen painting will happen then.
The machinery is controlled via bv::processUpdateFlags. This method is
called at the end of bv::mouseEventDispatch and in
@ -138,25 +143,37 @@ DecorationUpdate). It triggers a recomputation of the metrics when either:
existing metrics. Note that the Update::SinglePar flag is *never*
taken into account.
If a computation of metrics has taken place, Force is removed from the
flags and ForceDraw is added instead.
It is OK to call processUptateFlags several times before an update. In
this case, the effects are cumulative.processUpdateFlags execute the
metrics-related actions, but defers the actual drawing to the next
paint event.
The screen is drawn (with appropriate update strategy), except when
update flag is Update::None.
** Metrics computation
** Metrics computation (and nodraw drawing phase)
This is triggered by bv::updateMetrics, which calls tm::redoParagraph for
all visible paragraphs. Paragraphs above or below the screen (needed
all visible paragraphs. Some Paragraphs above or below the screen (needed
for page up/down) and computed as needed.
tm::redoParagraph will call Inset::metrics for each inset. In the case
of text insets, this will invoke recursively tm::metrics, which redoes
all the paragraphs of the inset.
At the end of the function, bv::updatePosCache is called. It triggers
a repaint of the document with a NullPainter (a painter that does
nothing). This has the effect of caching all insets positions.
** Drawing the work area.
This is done in bv::draw. This method is triggered mainly by
Buffer::changed, which draws all the work areas that show the given buffer.
This is done in bv::draw. This method is triggered by a paint event,
mainly called through Buffer::changed, which draws all the work areas
that show the given buffer.
Note that, When Buffer::changed is called outside of
bv::processUpdateFlags, it is not clear whether the update strategy
@ -186,3 +203,6 @@ The action depends on the update strategy:
+ SingleParUpdate: only tries to repaint current paragraph in a way
that is not yet very clear to me.
BufferView::draw can also be called with a null painter from
BufferView::updateMetrics().

View File

@ -72,6 +72,36 @@ check_type_size("long long" HAVE_LONG_LONG)
check_type_size(wchar_t HAVE_WCHAR_T)
check_type_size(wint_t HAVE_WINT_T)
if(HUNSPELL_FOUND)
# check whether hunspell C++ (rather than C) ABI is provided
set(HunspellTestFile "${CMAKE_BINARY_DIR}/hunspelltest.cpp")
file(WRITE "${HunspellTestFile}"
"
#include <hunspell/hunspell.hxx>
int main()
{
Hunspell sp(\"foo\", \"bar\");
int i = sp.stem(\"test\").size();
return(0);
}
"
)
try_compile(HAVE_HUNSPELL_CXXABI
"${CMAKE_BINARY_DIR}"
"${HunspellTestFile}"
CMAKE_FLAGS
"-DINCLUDE_DIRECTORIES:STRING=${HUNSPELL_INCLUDE_DIR}"
"-DCMAKE_CXX_LINK_EXECUTABLE='${CMAKE_COMMAD} echo not linking now...'"
OUTPUT_VARIABLE LOG2)
message(STATUS "HAVE_HUNSPELL_CXXABI = ${HAVE_HUNSPELL_CXXABI}")
#message(STATUS "LOG2 = ${LOG2}")
if(LYX_EXTERNAL_HUNSPELL AND LYX_STDLIB_DEBUG AND HAVE_HUNSPELL_CXXABI)
message(WARNING "Compiling LyX with stdlib-debug and system hunspell libraries may lead to crashes. Consider using -DLYX_STDLIB_DEBUG=OFF or -DLYX_EXTERNAL_HUNSPELL=OFF.")
endif()
endif()
#check_cxx_source_compiles(
# "

View File

@ -66,6 +66,9 @@
#define HAVE_ALLOCA
#endif
/* whether hunspell C++ (rather than C) ABI is provided */
#cmakedefine HAVE_HUNSPELL_CXXABI 1
#cmakedefine HAVE_ICONV_CONST 1
#ifdef HAVE_ICONV_CONST
#define ICONV_CONST const

View File

@ -118,6 +118,9 @@
@bMin Ding
@iE-mail: u5032331 () uds ! anu ! edu ! au
Chinese (simplified) translations
@bAlexander Dunlap
@iE-mail: alexander.dunlap () gmail ! com
Improvement to recent files support
@bAnders Ekberg
@iE-mail: anek () chalmers ! se
Improvements to the Swedish translation of the Windows Installer

View File

@ -111,6 +111,10 @@
opened in editable mode. This state used to be hardcoded at compile
time.
* buffer-anonymize (2.3.2)
Replace all text n the document by streams of `a'. This is useful
for sharing private documents in a bug report.
* font-crossout
Cross out characters.

View File

@ -768,10 +768,10 @@ def checkFormatEntries(dtl_tools):
def checkConverterEntries():
''' Check all converters (\converter entries) '''
checkProg('the pdflatex program', ['pdflatex $$i'],
rc_entry = [ r'\converter pdflatex pdf2 "%%" "latex=pdflatex"' ])
rc_entry = [ r'\converter pdflatex pdf2 "%%" "latex=pdflatex,hyperref-driver=pdftex"' ])
checkProg('XeTeX', ['xelatex $$i'],
rc_entry = [ r'\converter xetex pdf4 "%%" "latex=xelatex"' ])
rc_entry = [ r'\converter xetex pdf4 "%%" "latex=xelatex,hyperref-driver=xetex"' ])
checkLuatex()
@ -923,7 +923,7 @@ def checkConverterEntries():
rc_entry = [ r'\converter rtf html "%%" ""' ])
# Do not define a converter to pdf6, ps is a pure export format
checkProg('a PS to PDF converter', ['ps2pdf $$i $$o'],
rc_entry = [ r'\converter ps pdf "%%" ""' ])
rc_entry = [ r'\converter ps pdf "%%" "hyperref-driver=dvips"' ])
#
checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
rc_entry = [ r'\converter ps text2 "%%" ""' ])
@ -973,13 +973,13 @@ def checkConverterEntries():
rc_entry = [ r'\converter dvi text4 "%%" ""' ])
#
checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
rc_entry = [ r'\converter dvi ps "%%" ""' ])
rc_entry = [ r'\converter dvi ps "%%" "hyperref-driver=dvips"' ])
#
checkProg('a DVI to cropped EPS converter', ['dvips -E -o $$o $$i'],
rc_entry = [ r'\converter dvi eps3 "%%" ""' ])
#
checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
rc_entry = [ r'\converter dvi pdf3 "%%" ""' ])
checkProg('a DVI to PDF converter', ['dvipdfmx', 'dvipdfm'],
rc_entry = [ r'\converter dvi pdf3 "%% -o $$o $$i" "hyperref-driver=%%"' ])
#
checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
rc_entry = [ r'\converter ps fax "%%" ""'])

View File

@ -124,11 +124,12 @@ logicalmkup
\papercolumns 1
\papersides 2
\paperpagestyle headings
\tracking_changes false
\tracking_changes true
\output_changes false
\html_math_output 0
\html_css_as_file 0
\html_be_strict true
\author -712698321 "Jürgen Spitzmüller"
\end_header
\begin_body
@ -2515,6 +2516,35 @@ value
format:
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
\change_inserted -712698321 1523206314
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\change_inserted -712698321 1523206193
hyperref-driver
\end_layout
\end_inset
The name of the driver that needs to be loaded with the
\family sans
hyperref
\family default
package for this converter.
The loading of the correct driver is necessary to get some PDF-specific
features.
See the
\family sans
hyperref
\family default
manual for details.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
\begin_inset Flex Code
@ -2622,8 +2652,34 @@ $$b
\end_layout
\begin_layout Standard
None of these last three are presently used in any of the converters that
are installed with \SpecialChar LyX
\change_inserted -712698321 1523206384
A suitable hyperref-driver is set for some converters that are installed
with \SpecialChar LyX
.
\change_deleted -712698321 1523206388
None of these
\change_inserted -712698321 1523206389
The
\change_unchanged
last three
\change_inserted -712698321 1523206400
flags, however,
\change_unchanged
are presently
\change_inserted -712698321 1523206407
not
\change_unchanged
used in any of the
\change_inserted -712698321 1523206437
pre-installed
\change_unchanged
converters
\change_deleted -712698321 1523206442
that are installed with \SpecialChar LyX
\change_unchanged
.
\end_layout

View File

@ -145,6 +145,7 @@ enumitem
\html_math_output 0
\html_css_as_file 0
\html_be_strict true
\author 5863208 "ab"
\end_header
\begin_body
@ -19952,6 +19953,24 @@ LyX options
tab.
Joining an existing group can be done using the context menu of the image
and checking the name of the desired group.
\change_inserted 5863208 1518729806
If there are too many images which need to be assigned to a single group
you can simply put all of them into single selection and choose
\family sans
Unify
\begin_inset space ~
\end_inset
Graphics
\begin_inset space ~
\end_inset
Groups
\family default
in context menu.
\change_unchanged
\end_layout
\begin_layout Section

View File

@ -1989,6 +1989,25 @@ key=value
\begin_layout Labeling
\labelwidthstring 00.00.0000
\family typewriter
hyperref-driver
\family default
Der Name der Treiberdatei, die für diesen Konverter mit dem
\family sans
Hyperref
\family default
-Paket geladen werden soll.
Dies ist nötig, um bestimmte PDF-Features verwenden zu können.
Konsultieren Sie das
\family sans
Hyperref
\family default
-Handbuch für Einzelheiten.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
\family typewriter
parselog
\family default
@ -2082,9 +2101,11 @@ index
\end_layout
\begin_layout Standard
Keines dieser Flags wird zur Zeit in einem Konverter benutzt, der zusammen
mit \SpecialChar LyX
installiert wird.
Ein passender Hyperref-Treiber wird für einige mit \SpecialChar LyX
installierten Konverter
definiert.
Die zuletzt aufgeführten drei Flags hingegen werden zurzeit von keinem
der vorinstallierten Konverter verwendet.
\end_layout
\begin_layout Standard

View File

@ -647,6 +647,14 @@ contributors = [
"27 April 2014",
u"Chinese (simplified) translations"),
contributor(u"Alexander Dunlap",
"alexander.dunlap () gmail ! com",
"GPL",
"licensing statement",
"m=151914230920804",
"20 February 2018",
u"Improvement to recent files support"),
contributor(u"Anders Ekberg",
"anek () chalmers ! se",
"GPL",

View File

@ -364,6 +364,7 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -177,6 +177,7 @@ Style Bibliography
Size Huge
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -228,5 +228,6 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -226,4 +226,5 @@ Style Bibliography
Shape Smallcaps
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -1126,6 +1126,7 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -126,6 +126,7 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -596,6 +596,7 @@ Style Bibliography
Series Bold
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -326,5 +326,6 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -468,4 +468,5 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -481,4 +481,5 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -288,6 +288,7 @@ Style "Bibliography (plain)"
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -289,5 +289,6 @@ Style Bibliography
Size Larger
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -367,4 +367,5 @@ Style References
Series Bold
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -288,6 +288,7 @@ Style Bibliography
Series Bold
EndFont
TocLevel 0
IsTocCaption 0
End

View File

@ -520,6 +520,7 @@ Style Bibliography
Family Sans
EndFont
TocLevel 1
IsTocCaption 0
End
Style Recipient

View File

@ -371,7 +371,8 @@ Style Bibliography
LabelType Bibliography
LabelString ""
LabelBottomSep 0
TocLevel 1
TocLevel 1
IsTocCaption 0
End
###

View File

@ -349,6 +349,7 @@ Style Bibliography
Shape Smallcaps
EndFont
TocLevel 1
IsTocCaption 0
End
NoStyle Chapter

View File

@ -165,4 +165,5 @@ Style Bibliography
Size Tiny
EndFont
TocLevel 1
IsTocCaption 0
End

View File

@ -82,4 +82,5 @@ Style Bibliography
span.bibitemlabel:after { content: "] "; }
EndHTMLStyle
TocLevel 1
ISTocCaption 0
End

View File

@ -594,6 +594,7 @@ Style Bibliography
Series Bold
EndFont
TocLevel 0
IsTocCaption 0
End
Style Description

View File

@ -705,7 +705,6 @@ $$
\vspace{}
\vspace*{}
\whiledo{}{}
\xymatrix{} % this is basically an array => the contents would be parsed badly (bug 8396)
% LaTeX environments.
% They have always one extra "argument":

View File

@ -358,6 +358,7 @@ Menuset
Item "Apply Last Text Style|A" "textstyle-apply"
Submenu "Text Style|x" "edit_textstyles"
Item "Paragraph Settings...|P" "layout-paragraph"
OptItem "Unify Graphics Groups|U" "graphics-unify"
LanguageSelector
Separator
Item "Fullscreen Mode" "ui-toggle fullscreen"

View File

@ -2626,15 +2626,16 @@ bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
flag.setOnOff(params().output_changes);
break;
case LFUN_BUFFER_TOGGLE_COMPRESSION: {
case LFUN_BUFFER_TOGGLE_COMPRESSION:
flag.setOnOff(params().compressed);
break;
}
case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC: {
case LFUN_BUFFER_TOGGLE_OUTPUT_SYNC:
flag.setOnOff(params().output_sync);
break;
}
case LFUN_BUFFER_ANONYMIZE:
break;
default:
return false;
@ -2664,7 +2665,8 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
string const argument = to_utf8(func.argument());
// We'll set this back to false if need be.
bool dispatched = true;
undo().beginUndoGroup();
// This handles undo groups automagically
UndoGroupHelper ugh(this);
switch (func.action()) {
case LFUN_BUFFER_TOGGLE_READ_ONLY:
@ -2907,12 +2909,20 @@ void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
params().output_sync = !params().output_sync;
break;
case LFUN_BUFFER_ANONYMIZE: {
undo().recordUndoFullBuffer(CursorData());
CursorData cur(doc_iterator_begin(this));
for ( ; cur ; cur.forwardPar())
cur.paragraph().anonymize();
dr.forceBufferUpdate();
break;
}
default:
dispatched = false;
break;
}
dr.dispatched(dispatched);
undo().endUndoGroup();
}
@ -3185,9 +3195,27 @@ vector<docstring> const Buffer::prepareBibFilePaths(OutputParams const & runpara
string utf8input = to_utf8(it->first);
string database =
prepareFileNameForLaTeX(utf8input, ".bib", runparams.nice);
FileName const try_in_file =
FileName try_in_file =
makeAbsPath(database + ".bib", filePath());
bool const not_from_texmf = try_in_file.isReadableFile();
bool not_from_texmf = try_in_file.isReadableFile();
// If the file has not been found, try with the real file name
// (it might come from a child in a sub-directory)
if (!not_from_texmf) {
try_in_file = it->second;
if (try_in_file.isReadableFile()) {
// Check if the file is in texmf
FileName kpsefile(findtexfile(changeExtension(utf8input, "bib"), "bib", true));
not_from_texmf = kpsefile.empty()
|| kpsefile.absFileName() != try_in_file.absFileName();
if (not_from_texmf)
// If this exists, make path relative to the master
// FIXME Unicode
database = removeExtension(
prepareFileNameForLaTeX(to_utf8(makeRelPath(from_utf8(try_in_file.absFileName()),
from_utf8(filePath()))),
".bib", runparams.nice));
}
}
if (!runparams.inComment && !runparams.dryrun && !runparams.nice &&
not_from_texmf) {
@ -4301,6 +4329,7 @@ Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir
return ExportNoPathToFormat;
}
runparams.flavor = converters.getFlavor(path, this);
runparams.hyperref_driver = converters.getHyperrefDriver(path);
Graph::EdgePath::const_iterator it = path.begin();
Graph::EdgePath::const_iterator en = path.end();
for (; it != en; ++it)
@ -5360,7 +5389,6 @@ void Buffer::Impl::fileExternallyModified(bool const exists)
"checksum unchanged: " << filename);
return;
}
lyx_clean = bak_clean = false;
// If the file has been deleted, only mark the file as dirty since it is
// pointless to prompt for reloading. If later a file is moved into this
// location, then the externally modified warning will appear then.

View File

@ -394,7 +394,7 @@ BufferParams::BufferParams()
papersize = PAPER_DEFAULT;
orientation = ORIENTATION_PORTRAIT;
use_geometry = false;
biblio_style = "plain";
biblio_style = string();
use_bibtopic = false;
multibib = string();
use_indices = false;
@ -2094,8 +2094,8 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
OutputParams tmp_params = features.runparams();
pdfoptions().writeLaTeX(tmp_params, os,
features.isProvided("hyperref"));
// correctly break URLs with hyperref and dvi output
if (features.runparams().flavor == OutputParams::LATEX
// correctly break URLs with hyperref and dvi/ps output
if (features.runparams().hyperref_driver == "dvips"
&& features.isAvailable("breakurl"))
os << "\\usepackage{breakurl}\n";
} else if (features.isRequired("nameref"))
@ -3426,6 +3426,9 @@ bool BufferParams::addCiteEngine(vector<string> const & engine)
string const & BufferParams::defaultBiblioStyle() const
{
if (!biblio_style.empty())
return biblio_style;
map<string, string> const & bs = documentClass().defaultBiblioStyle();
auto cit = bs.find(theCiteEnginesList.getTypeAsString(citeEngineType()));
if (cit != bs.end())

View File

@ -70,6 +70,7 @@
#include "frontends/Application.h"
#include "frontends/Delegates.h"
#include "frontends/FontMetrics.h"
#include "frontends/NullPainter.h"
#include "frontends/Painter.h"
#include "frontends/Selection.h"
@ -227,14 +228,15 @@ enum ScreenUpdateStrategy {
struct BufferView::Private
{
Private(BufferView & bv) : update_strategy_(NoScreenUpdate),
Private(BufferView & bv) : update_strategy_(FullScreenUpdate),
update_flags_(Update::Force),
wh_(0), cursor_(bv),
anchor_pit_(0), anchor_ypos_(0),
inlineCompletionUniqueChars_(0),
last_inset_(0), clickable_inset_(false),
mouse_position_cache_(),
bookmark_edit_position_(-1), gui_(0),
horiz_scroll_offset_(0)
horiz_scroll_offset_(0), repaint_caret_row_(false)
{
xsel_cache_.set = false;
}
@ -244,6 +246,8 @@ struct BufferView::Private
///
ScreenUpdateStrategy update_strategy_;
///
Update::flags update_flags_;
///
CoordCache coord_cache_;
/// Estimated average par height for scrollbar.
@ -311,6 +315,12 @@ struct BufferView::Private
/// a slice pointing to the start of the row where cursor was
/// at previous draw event
CursorSlice last_row_slice_;
/// a slice pointing to where the cursor has been drawn after the current
/// draw() call.
CursorSlice caret_slice_;
/// indicates whether the caret slice needs to be repainted in this draw() run.
bool repaint_caret_row_;
};
@ -438,79 +448,85 @@ bool BufferView::needsFitCursor() const
}
namespace {
// this is for debugging only.
string flagsAsString(Update::flags flags)
{
if (flags == Update::None)
return "None ";
return string((flags & Update::FitCursor) ? "FitCursor " : "")
+ ((flags & Update::Force) ? "Force " : "")
+ ((flags & Update::ForceDraw) ? "ForceDraw " : "")
+ ((flags & Update::SinglePar) ? "SinglePar " : "");
}
}
void BufferView::processUpdateFlags(Update::flags flags)
{
// This is close to a hot-path.
LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags()"
<< "[fitcursor = " << (flags & Update::FitCursor)
<< ", forceupdate = " << (flags & Update::Force)
<< ", singlepar = " << (flags & Update::SinglePar)
<< "] buffer: " << &buffer_);
// FIXME Does this really need doing here? It's done in updateBuffer, and
// if the Buffer doesn't need updating, then do the macros?
buffer_.updateMacros();
// Now do the first drawing step if needed. This consists on updating
// the CoordCache in updateMetrics().
// The second drawing step is done in WorkArea::redraw() if needed.
// FIXME: is this still true now that Buffer::changed() is used all over?
LYXERR(Debug::PAINTING, "BufferView::processUpdateFlags( "
<< flagsAsString(flags) << ") buffer: " << &buffer_);
// Case when no explicit update is requested.
if (!flags) {
if (flags == Update::None)
return;
// SinglePar is ignored for now (this should probably change). We
// set it ourselves below, at the price of always rebreaking the
// paragraph at cursor. This can be expensive for large tables.
flags = flags & ~Update::SinglePar;
// First check whether the metrics and inset positions should be updated
if (flags & Update::Force) {
// This will update the CoordCache items and replace Force
// with ForceDraw in flags.
updateMetrics(flags);
}
// Then make sure that the screen contains the cursor if needed
if (flags & Update::FitCursor) {
if (needsFitCursor()) {
scrollToCursor(d->cursor_, false);
// Metrics have to be recomputed (maybe again)
updateMetrics(flags);
}
flags = flags & ~Update::FitCursor;
}
// Finally detect whether we can only repaint a single paragraph
if (!(flags & Update::ForceDraw)) {
if (singleParUpdate())
flags = flags | Update::SinglePar;
else
updateMetrics(flags);
}
// Add flags to the the update flags. These will be reset to None
// after the redraw is actually done
d->update_flags_ = d->update_flags_ | flags;
LYXERR(Debug::PAINTING, "Cumulative flags: " << flagsAsString(flags));
// Now compute the update strategy
// Possibly values in flag are None, Decoration, ForceDraw
LATTEST((d->update_flags_ & ~(Update::None | Update::SinglePar
| Update::Decoration | Update::ForceDraw)) == 0);
if (d->update_flags_ & Update::ForceDraw)
d->update_strategy_ = FullScreenUpdate;
else if (d->update_flags_ & Update::Decoration)
d->update_strategy_ = DecorationUpdate;
else if (d->update_flags_ & Update::SinglePar)
d->update_strategy_ = SingleParUpdate;
else {
// no need to redraw anything.
d->update_strategy_ = NoScreenUpdate;
return;
}
if (flags == Update::Decoration) {
d->update_strategy_ = DecorationUpdate;
buffer_.changed(false);
return;
}
if (flags == Update::FitCursor
|| flags == (Update::Decoration | Update::FitCursor)) {
// tell the frontend to update the screen if needed.
if (needsFitCursor()) {
showCursor();
return;
}
if (flags & Update::Decoration) {
d->update_strategy_ = DecorationUpdate;
buffer_.changed(false);
return;
}
// no screen update is needed in principle, but this
// could change if cursor row needs horizontal scrolling.
d->update_strategy_ = NoScreenUpdate;
buffer_.changed(false);
return;
}
bool const full_metrics = flags & Update::Force || !singleParUpdate();
if (full_metrics)
// We have to update the full screen metrics.
updateMetrics();
if (!(flags & Update::FitCursor)) {
// Nothing to do anymore. Trigger a redraw and return
buffer_.changed(false);
return;
}
// updateMetrics() does not update paragraph position
// This is done at draw() time. So we need a redraw!
buffer_.changed(false);
if (needsFitCursor()) {
// The cursor is off screen so ensure it is visible.
// refresh it:
showCursor();
}
updateHoveredInset();
// Trigger a redraw.
buffer_.changed(false);
}
@ -625,8 +641,7 @@ void BufferView::scrollDocView(int const value, bool update)
// If the offset is less than 2 screen height, prefer to scroll instead.
if (abs(value) <= 2 * height_) {
d->anchor_ypos_ -= value;
buffer_.changed(true);
updateHoveredInset();
processUpdateFlags(Update::Force);
return;
}
@ -823,12 +838,7 @@ bool BufferView::moveToPosition(pit_type bottom_pit, pos_type bottom_pos,
d->cursor_.setCurrentFont();
// Do not forget to reset the anchor (see #9912)
d->cursor_.resetAnchor();
// To center the screen on this new position we need the
// paragraph position which is computed at draw() time.
// So we need a redraw!
buffer_.changed(false);
if (needsFitCursor())
showCursor();
processUpdateFlags(Update::FitCursor);
}
return success;
@ -870,19 +880,15 @@ void BufferView::showCursor()
void BufferView::showCursor(DocIterator const & dit,
bool recenter, bool update)
{
if (scrollToCursor(dit, recenter) && update) {
buffer_.changed(true);
updateHoveredInset();
}
if (scrollToCursor(dit, recenter) && update)
processUpdateFlags(Update::Force);
}
void BufferView::scrollToCursor()
{
if (scrollToCursor(d->cursor_, false)) {
buffer_.changed(true);
updateHoveredInset();
}
if (scrollToCursor(d->cursor_, false))
processUpdateFlags(Update::Force);
}
@ -1128,6 +1134,10 @@ bool BufferView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
flag.setEnabled(true);
break;
case LFUN_GRAPHICS_UNIFY:
flag.setEnabled(cur.selection());
break;
case LFUN_WORD_FINDADV: {
FindAndReplaceOptions opt;
istringstream iss(to_utf8(cmd.argument()));
@ -1649,6 +1659,45 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
break;
}
case LFUN_GRAPHICS_UNIFY: {
cur.recordUndoFullBuffer();
DocIterator from, to;
from = cur.selectionBegin();
to = cur.selectionEnd();
string const newId = cmd.getArg(0);
bool fetchId = newId.empty(); //if we wait for groupId from first graphics inset
InsetGraphicsParams grp_par;
if (!fetchId)
InsetGraphics::string2params(graphics::getGroupParams(buffer_, newId), buffer_, grp_par);
if (!from.nextInset()) //move to closest inset
from.forwardInset();
while (!from.empty() && from < to) {
Inset * inset = from.nextInset();
if (!inset)
break;
if (inset->lyxCode() == GRAPHICS_CODE) {
InsetGraphics & ig = static_cast<InsetGraphics &>(*inset);
InsetGraphicsParams inspar = ig.getParams();
if (fetchId) {
grp_par = inspar;
fetchId = false;
} else {
grp_par.filename = inspar.filename;
ig.setParams(grp_par);
}
}
from.forwardInset();
}
dr.screenUpdate(Update::Force); //needed if triggered from context menu
break;
}
case LFUN_STATISTICS: {
DocIterator from, to;
if (cur.selection()) {
@ -1708,8 +1757,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
bool const in_texted = cur.inTexted();
cur.setCursor(doc_iterator_begin(cur.buffer()));
cur.selHandle(false);
buffer_.changed(true);
updateHoveredInset();
// Force an immediate computation of metrics because we need it below
processUpdateFlags(Update::Force);
d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_,
true, act == LFUN_SCREEN_UP);
@ -1743,8 +1792,7 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
if (scroll_value)
scroll(scroll_step * scroll_value);
}
buffer_.changed(true);
updateHoveredInset();
dr.screenUpdate(Update::ForceDraw);
dr.forceBufferUpdate();
break;
}
@ -1974,6 +2022,8 @@ void BufferView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
icp["key"] = from_utf8(arg);
if (!opt1.empty())
icp["before"] = from_utf8(opt1);
icp["literal"] =
from_ascii(InsetCitation::last_literal ? "true" : "false");
string icstr = InsetCommand::params2string(icp);
FuncRequest fr(LFUN_INSET_INSERT, icstr);
lyx::dispatch(fr);
@ -2638,7 +2688,7 @@ bool BufferView::singleParUpdate()
// the singlePar optimisation.
return false;
d->update_strategy_ = SingleParUpdate;
tm.updatePosCache(bottom_pit);
LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent()
<< " y2: " << pm.position() + pm.descent()
@ -2649,6 +2699,13 @@ bool BufferView::singleParUpdate()
void BufferView::updateMetrics()
{
updateMetrics(d->update_flags_);
d->update_strategy_ = FullScreenUpdate;
}
void BufferView::updateMetrics(Update::flags & update_flags)
{
if (height_ == 0 || width_ == 0)
return;
@ -2691,6 +2748,7 @@ void BufferView::updateMetrics()
}
}
anchor_pm.setPosition(d->anchor_ypos_);
tm.updatePosCache(d->anchor_pit_);
LYXERR(Debug::PAINTING, "metrics: "
<< " anchor pit = " << d->anchor_pit_
@ -2706,6 +2764,7 @@ void BufferView::updateMetrics()
y1 -= pm.descent();
// Save the paragraph position in the cache.
pm.setPosition(y1);
tm.updatePosCache(pit1);
y1 -= pm.ascent();
}
@ -2719,6 +2778,7 @@ void BufferView::updateMetrics()
y2 += pm.ascent();
// Save the paragraph position in the cache.
pm.setPosition(y2);
tm.updatePosCache(pit2);
y2 += pm.descent();
}
@ -2730,7 +2790,11 @@ void BufferView::updateMetrics()
<< " pit1 = " << pit1
<< " pit2 = " << pit2);
d->update_strategy_ = FullScreenUpdate;
// metrics is done, full drawing is necessary now
update_flags = (update_flags & ~Update::Force) | Update::ForceDraw;
// Now update the positions of insets in the cache.
updatePosCache();
if (lyxerr.debugging(Debug::WORKAREA)) {
LYXERR(Debug::WORKAREA, "BufferView::updateMetrics");
@ -2739,6 +2803,15 @@ void BufferView::updateMetrics()
}
void BufferView::updatePosCache()
{
// this is the "nodraw" drawing stage: only set the positions of the
// insets in metrics cache.
frontend::NullPainter np;
draw(np, false);
}
void BufferView::insertLyXFile(FileName const & fname)
{
LASSERT(d->cursor_.inTexted(), return);
@ -2873,7 +2946,7 @@ bool BufferView::paragraphVisible(DocIterator const & dit) const
}
void BufferView::cursorPosAndHeight(Point & p, int & h) const
void BufferView::caretPosAndHeight(Point & p, int & h) const
{
Cursor const & cur = cursor();
Font const font = cur.real_current_font;
@ -2946,7 +3019,30 @@ void BufferView::setCurrentRowSlice(CursorSlice const & rowSlice)
}
void BufferView::checkCursorScrollOffset(PainterInfo & pi)
namespace {
bool sliceInRow(CursorSlice const & cs, Text const * text, Row const & row)
{
/* The normal case is the last line. The previous line takes care
* of empty rows (e.g. empty paragraphs). Cursor boundary issues
* are taken care of when setting caret_slice_ in
* BufferView::draw.
*/
return !cs.empty() && cs.text() == text && cs.pit() == row.pit()
&& ((row.pos() == row.endpos() && row.pos() == cs.pos())
|| (row.pos() <= cs.pos() && cs.pos() < row.endpos()));
}
}
bool BufferView::needRepaint(Text const * text, Row const & row) const
{
return d->repaint_caret_row_ && sliceInRow(d->caret_slice_, text, row);
}
void BufferView::checkCursorScrollOffset()
{
CursorSlice rowSlice = d->cursor_.bottom();
TextMetrics const & tm = textMetrics(rowSlice.text());
@ -2963,35 +3059,6 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
// Set the row on which the cursor lives.
setCurrentRowSlice(rowSlice);
// If insets referred to by cursor are not all in the cache, the positions
// need to be recomputed.
if (!d->cursor_.inCoordCache()) {
/** FIXME: the code below adds an extraneous computation of
* inset positions, and can therefore be bad for performance
* (think for example about a very large tabular inset.
* Redawing the row where it is means redrawing the whole
* screen).
*
* The bug that this fixes is the following: assume that there
* is a very large math inset. Upon entering the inset, when
* pressing `End', the row is not scrolled and the cursor is
* not visible. The extra row computation makes sure that the
* inset positions are correctly computed and set in the
* cache. This would not happen if we did not have two-stage
* drawing.
*
* A proper fix would be to always have proper inset positions
* at this point.
*/
// Force the recomputation of inset positions
bool const drawing = pi.pain.isDrawingEnabled();
pi.pain.setDrawingEnabled(false);
// No need to care about vertical position.
RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0);
rp.paintText();
pi.pain.setDrawingEnabled(drawing);
}
// Current x position of the cursor in pixels
int cur_x = getPos(d->cursor_).x_;
@ -3035,30 +3102,46 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
}
void BufferView::draw(frontend::Painter & pain)
void BufferView::draw(frontend::Painter & pain, bool paint_caret)
{
if (height_ == 0 || width_ == 0)
return;
LYXERR(Debug::PAINTING, "\t\t*** START DRAWING ***");
LYXERR(Debug::PAINTING, (pain.isNull() ? "\t\t--- START NODRAW ---"
: "\t\t*** START DRAWING ***"));
Text & text = buffer_.text();
TextMetrics const & tm = d->text_metrics_[&text];
int const y = tm.first().second->position();
PainterInfo pi(this, pain);
/** A repaint of the previous caret row is needed if there is
* caret painted on screen and either
* 1/ a new caret has to be painted at a place different from
* the existing one;
* 2/ there is no need for a caret anymore.
*/
d->repaint_caret_row_ = !d->caret_slice_.empty() &&
((paint_caret && d->cursor_.top() != d->caret_slice_)
|| ! paint_caret);
// Check whether the row where the cursor lives needs to be scrolled.
// Update the drawing strategy if needed.
checkCursorScrollOffset(pi);
checkCursorScrollOffset();
switch (d->update_strategy_) {
case NoScreenUpdate:
// If no screen painting is actually needed, only some the different
// coordinates of insets and paragraphs needs to be updated.
// no screen painting is actually needed. In nodraw stage
// however, the different coordinates of insets and paragraphs
// needs to be updated.
LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
pi.full_repaint = true;
pi.pain.setDrawingEnabled(false);
tm.draw(pi, 0, y);
pi.full_repaint = false;
if (pain.isNull()) {
pi.full_repaint = true;
tm.draw(pi, 0, y);
} else if (d->repaint_caret_row_) {
pi.full_repaint = false;
tm.draw(pi, 0, y);
}
break;
case SingleParUpdate:
@ -3103,7 +3186,8 @@ void BufferView::draw(frontend::Painter & pain)
}
break;
}
LYXERR(Debug::PAINTING, "\n\t\t*** END DRAWING ***");
LYXERR(Debug::PAINTING, (pain.isNull() ? "\t\t --- END NODRAW ---"
: "\t\t *** END DRAWING ***"));
// The scrollbar needs an update.
updateScrollbar();
@ -3114,13 +3198,29 @@ void BufferView::draw(frontend::Painter & pain)
for (pit_type pit = firstpm.first; pit <= lastpm.first; ++pit) {
ParagraphMetrics const & pm = tm.parMetrics(pit);
if (pm.position() + pm.descent() > 0) {
if (d->anchor_pit_ != pit
|| d->anchor_ypos_ != pm.position())
LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
<< " anchor ypos = " << d->anchor_ypos_);
d->anchor_pit_ = pit;
d->anchor_ypos_ = pm.position();
break;
}
}
LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
<< " anchor ypos = " << d->anchor_ypos_);
if (!pain.isNull()) {
// reset the update flags, everything has been done
d->update_flags_ = Update::None;
}
// Remember what has just been done for the next draw() step
if (paint_caret) {
d->caret_slice_ = d->cursor_.top();
if (d->caret_slice_.pos() > 0
&& (d->cursor_.boundary()
|| d->caret_slice_.pos() == d->caret_slice_.lastpos()))
--d->caret_slice_.pos();
} else
d->caret_slice_ = CursorSlice();
}

View File

@ -42,10 +42,10 @@ class FuncStatus;
class Intl;
class Inset;
class Length;
class PainterInfo;
class ParIterator;
class ParagraphMetrics;
class Point;
class Row;
class TexRow;
class Text;
class TextMetrics;
@ -120,11 +120,12 @@ public:
/// \return true if the BufferView is at the bottom of the document.
bool isBottomScreen() const;
/// perform pending metrics updates.
/** \c Update::FitCursor means first to do a FitCursor, and to
/// Add \p flags to current update flags and trigger an update.
/* If this method is invoked several times before the update
* actually takes place, the effect is cumulative.
* \c Update::FitCursor means first to do a FitCursor, and to
* force an update if screen position changes.
* \c Update::Force means to force an update in any case.
* \retval true if a screen redraw is needed
*/
void processUpdateFlags(Update::flags flags);
@ -132,6 +133,9 @@ public:
/// Only to be called with good y coordinates (after a bv::metrics)
bool needsFitCursor() const;
/// returns true if this row needs to be repainted (to erase caret)
bool needRepaint(Text const * text, Row const & row) const;
// Returns the amount of horizontal scrolling applied to the
// top-level row where the cursor lies
int horizScrollOffset() const;
@ -283,6 +287,10 @@ public:
/// update the internal \c ViewMetricsInfo.
void updateMetrics();
// this is the "nodraw" drawing stage: only set the positions of the
// insets in metrics cache.
void updatePosCache();
///
TextMetrics const & textMetrics(Text const * t) const;
TextMetrics & textMetrics(Text const * t);
@ -300,12 +308,11 @@ public:
bool paragraphVisible(DocIterator const & dit) const;
/// is the cursor currently visible in the view
bool cursorInView(Point const & p, int h) const;
/// get the position and height of the cursor
void cursorPosAndHeight(Point & p, int & h) const;
/// get the position and height of the caret
void caretPosAndHeight(Point & p, int & h) const;
///
void draw(frontend::Painter & pain);
void draw(frontend::Painter & pain, bool paint_caret);
/// get this view's keyboard map handler.
Intl & getIntl();
@ -361,13 +368,15 @@ private:
/// Update current paragraph metrics.
/// \return true if no further update is needed.
bool singleParUpdate();
/// do the work for the public updateMetrics()
void updateMetrics(Update::flags & update_flags);
// Set the row on which the cursor lives.
void setCurrentRowSlice(CursorSlice const & rowSlice);
// Check whether the row where the cursor lives needs to be scrolled.
// Update the drawing strategy if needed.
void checkCursorScrollOffset(PainterInfo & pi);
void checkCursorScrollOffset();
/// The minimal size of the document that is visible. Used
/// when it is allowed to scroll below the document.

View File

@ -136,6 +136,8 @@ void Converter::readFlags()
nice_ = true;
else if (flag_name == "needauth")
need_auth_ = true;
else if (flag_name == "hyperref-driver")
href_driver_ = flag_value;
}
if (!result_dir_.empty() && result_file_.empty())
result_file_ = "index." + theFormats().extension(to_);
@ -283,6 +285,18 @@ OutputParams::FLAVOR Converters::getFlavor(Graph::EdgePath const & path,
}
string Converters::getHyperrefDriver(Graph::EdgePath const & path)
{
for (Graph::EdgePath::const_iterator cit = path.begin();
cit != path.end(); ++cit) {
Converter const & conv = converterlist_[*cit];
if (!conv.hyperref_driver().empty())
return conv.hyperref_driver();
}
return string();
}
bool Converters::checkAuth(Converter const & conv, string const & doc_fname,
bool use_shell_escape)
{

View File

@ -79,6 +79,8 @@ public:
std::string const result_file() const { return result_file_; }
///
std::string const parselog() const { return parselog_; }
///
std::string const hyperref_driver() const { return href_driver_; }
private:
///
@ -114,6 +116,8 @@ private:
trivstring result_file_;
/// Command to convert the program output to a LaTeX log file format
trivstring parselog_;
/// The hyperref driver
trivstring href_driver_;
};
@ -159,6 +163,8 @@ public:
///
OutputParams::FLAVOR getFlavor(Graph::EdgePath const & path,
Buffer const * buffer = 0);
///
std::string getHyperrefDriver(Graph::EdgePath const & path);
/// Flags for converting files
enum ConversionFlags {
/// No special flags

View File

@ -453,19 +453,6 @@ int Cursor::currentMode()
}
bool Cursor::inCoordCache() const
{
// the root inset is not in cache, but we do not need it.
if (depth() == 1)
return true;
CoordCache::Insets const & icache = bv_->coordCache().getInsets();
for (size_t i = 1 ; i < depth() ; ++i)
if (!icache.has(&(*this)[i].inset()))
return false;
return true;
}
void Cursor::getPos(int & x, int & y) const
{
Point p = bv().getPos(*this);

View File

@ -215,8 +215,6 @@ public:
/// are we entering a macro name?
bool & macromode() { return macromode_; }
/// returns true when all insets in cursor stack are in cache
bool inCoordCache() const;
/// returns x,y position
void getPos(int & x, int & y) const;
/// return logical positions between which the cursor is situated

View File

@ -40,6 +40,7 @@
#include "insets/InsetBibitem.h"
#include "insets/InsetBranch.h"
#include "insets/InsetCitation.h"
#include "insets/InsetCommand.h"
#include "insets/InsetFlex.h"
#include "insets/InsetGraphics.h"
@ -360,6 +361,16 @@ pasteSelectionHelper(DocIterator const & cur, ParagraphList const & parlist,
break;
}
case CITE_CODE: {
InsetCitation & cit = static_cast<InsetCitation &>(*it);
// This actually only needs to be done if the cite engine
// differs, but we do it in general.
cit.redoLabel();
// We need to update the list of citations.
need_update = true;
break;
}
case BIBITEM_CODE: {
// check for duplicates
InsetBibitem & bib = static_cast<InsetBibitem &>(*it);

View File

@ -349,9 +349,11 @@ int Font::latexWriteStartChanges(odocstream & os, BufferParams const & bparams,
// If the current language is Hebrew, Arabic, or Farsi
// the numbers are written Left-to-Right. ArabTeX package
// reorders the number automatically but the packages used
// for Hebrew and Farsi (Arabi) do not.
if (!runparams.pass_thru && bits_.number() == FONT_ON
// and bidi (polyglossia) reorder the number automatically
// but the packages used for Hebrew and Farsi (Arabi) do not.
if (!runparams.use_polyglossia
&& !runparams.pass_thru
&& bits_.number() == FONT_ON
&& prev.fontInfo().number() != FONT_ON
&& (language()->lang() == "hebrew"
|| language()->lang() == "farsi"
@ -552,9 +554,11 @@ int Font::latexWriteEndChanges(otexstream & os, BufferParams const & bparams,
// If the current language is Hebrew, Arabic, or Farsi
// the numbers are written Left-to-Right. ArabTeX package
// reorders the number automatically but the packages used
// for Hebrew and Farsi (Arabi) do not.
if (!runparams.pass_thru && bits_.number() == FONT_ON
// and bidi (polyglossia) reorder the number automatically
// but the packages used for Hebrew and Farsi (Arabi) do not.
if (!runparams.use_polyglossia
&& !runparams.pass_thru
&& bits_.number() == FONT_ON
&& next.fontInfo().number() != FONT_ON
&& (language()->lang() == "hebrew"
|| language()->lang() == "farsi"

View File

@ -474,6 +474,10 @@ enum FuncCode
LFUN_TOOLBAR_MOVABLE, // daniel, 20160712
LFUN_FONT_CROSSOUT, // uwestoehr 20170404
LFUN_DEVEL_MODE_TOGGLE, // lasgouttes 20170723
//370
LFUN_EXPORT_CANCEL, // rgh, 20171227
LFUN_BUFFER_ANONYMIZE, // sanda, 20180201
LFUN_GRAPHICS_UNIFY, // sanda, 20180207
LFUN_LASTACTION // end of the table
};

View File

@ -356,7 +356,11 @@ SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl)
LYXERR(Debug::GUI, "spellCheck: \"" <<
wl.word() << "\", lang = " << wl.lang()->lang()) ;
#ifdef HAVE_HUNSPELL_CXXABI
if (h->spell(word_to_check, &info))
#else
if (h->spell(word_to_check.c_str(), &info))
#endif
return d->learned(wl) ? LEARNED_WORD : WORD_OK;
if (info & SPELL_COMPOUND) {
@ -411,6 +415,11 @@ void HunspellChecker::suggest(WordLangTuple const & wl,
return;
string const encoding = h->get_dic_encoding();
string const word_to_check = to_iconv_encoding(wl.word(), encoding);
#ifdef HAVE_HUNSPELL_CXXABI
vector<string> wlst = h->suggest(word_to_check);
for (auto const s : wlst)
suggestions.push_back(from_iconv_encoding(s, encoding));
#else
char ** suggestion_list;
int const suggestion_number = h->suggest(&suggestion_list, word_to_check.c_str());
if (suggestion_number <= 0)
@ -418,6 +427,7 @@ void HunspellChecker::suggest(WordLangTuple const & wl,
for (int i = 0; i != suggestion_number; ++i)
suggestions.push_back(from_iconv_encoding(suggestion_list[i], encoding));
h->free_list(&suggestion_list, suggestion_number);
#endif
}
@ -430,6 +440,11 @@ void HunspellChecker::stem(WordLangTuple const & wl,
return;
string const encoding = h->get_dic_encoding();
string const word_to_check = to_iconv_encoding(wl.word(), encoding);
#ifdef HAVE_HUNSPELL_CXXABI
vector<string> wlst = h->stem(word_to_check);
for (auto const s : wlst)
suggestions.push_back(from_iconv_encoding(s, encoding));
#else
char ** suggestion_list;
int const suggestion_number = h->stem(&suggestion_list, word_to_check.c_str());
if (suggestion_number <= 0)
@ -437,6 +452,7 @@ void HunspellChecker::stem(WordLangTuple const & wl,
for (int i = 0; i != suggestion_number; ++i)
suggestions.push_back(from_iconv_encoding(suggestion_list[i], encoding));
h->free_list(&suggestion_list, suggestion_number);
#endif
}

View File

@ -120,7 +120,7 @@ enum LayoutTags {
/////////////////////
Layout::Layout()
: add_to_toc_(false), is_toc_caption_(false)
: add_to_toc_(false), is_toc_caption_(true)
{
unknown_ = false;
margintype = MARGIN_STATIC;

View File

@ -1950,8 +1950,10 @@ void LyXAction::init()
/*!
* \var lyx::FuncCode lyx::LFUN_INSET_BEGIN
* \li Action: Move the cursor to the beginning of the current inset
if it is not already there, or at the beginning of the
enclosing inset otherwise
if it is not already there. If the cursor is already at
the beginning of the current inset, move it to the
beginning of the enclosing inset or the main work area,
respectively, if there is no enclosing inset.
* \li Syntax: inset-begin
* \li Origin: lasgouttes, 16 Mar 2009
* \endvar
@ -1961,8 +1963,10 @@ void LyXAction::init()
/*!
* \var lyx::FuncCode lyx::LFUN_INSET_BEGIN_SELECT
* \li Action: Move the cursor to the beginning of the current inset
if it is not already there, or at the beginning of the
enclosing inset otherwise (adding the
if it is not already there. If the cursor is already at
the beginning of the current inset, move it to the
beginning of the enclosing inset or the main work area,
respectively, if there is no enclosing inset (adding the
traversed text to the selection).
* \li Syntax: inset-begin-select
* \li Origin: lasgouttes, 16 Mar 2009
@ -2021,9 +2025,11 @@ void LyXAction::init()
/*!
* \var lyx::FuncCode lyx::LFUN_INSET_END
* \li Action: Move the cursor to the end of the current inset
if it is not already there, or at the end of the
enclosing inset otherwise
* \li Action: Move the cursor to the end of the current inset if it
is not already there. If the cursor is already at the
end of the current inset, move it to the end of the
enclosing inset or the main work area, respectively, if
there is no enclosing inset.
* \li Syntax: inset-end
* \li Origin: lasgouttes, 16 Mar 2009
* \endvar
@ -2032,9 +2038,11 @@ void LyXAction::init()
/*!
* \var lyx::FuncCode lyx::LFUN_INSET_END_SELECT
* \li Action: Move the cursor to the end of the current inset
if it is not already there, or at the end of the
enclosing inset otherwise (adding the
* \li Action: Move the cursor to the end of the current inset if it
is not already there. If the cursor is already at the
end of the current inset, move it to the end of the
enclosing inset or the main work area, respectively, if
there is no enclosing inset (adding the
traversed text to the selection).
* \li Syntax: inset-end-select
* \li Origin: lasgouttes, 16 Mar 2009
@ -3545,6 +3553,17 @@ void LyXAction::init()
*/
{ LFUN_SET_GRAPHICS_GROUP, "set-graphics-group", Noop, Edit },
/*!
* \var lyx::FuncCode lyx::LFUN_GRAPHICS_UNIFY
* \li Action: Set the same group for all graphics insets in the marked block.
* \li Syntax: graphics-unify [<GROUP>]
* \li Params: <GROUP>: Id for an existing group. In case the Id is an empty string,
the group Id from the first graphics inset will be used.
* \li Origin: sanda, 7 Feb 2018
* \endvar
*/
{ LFUN_GRAPHICS_UNIFY, "graphics-unify", Noop, Edit },
/*!
* \var lyx::FuncCode lyx::LFUN_SPACE_INSERT
@ -4209,6 +4228,16 @@ void LyXAction::init()
*/
{ LFUN_WORD_REPLACE, "word-replace", Noop, Edit },
/*!
* \var lyx::FuncCode lyx::LFUN_BUFFER_ANONYMIZE
* \li Action: For debug purposes only. Convert all [a-zA-Z0-1] characters to
single character. Useful when submitting docs to list or bugzilla.
* \li Syntax: buffer-anonymize
* \li Origin: sanda, Feb 1 2018
* \endvar
*/
{ LFUN_BUFFER_ANONYMIZE, "buffer-anonymize", Noop, Edit },
/*!
* \var lyx::FuncCode lyx::LFUN_WORD_RIGHT
* \li Action: Moves the cursor to the next beginning of a word "on the right".

View File

@ -195,6 +195,7 @@ LexerKeyword lyxrcTags[] = {
{ "\\use_converter_needauth", LyXRC::RC_USE_CONVERTER_NEEDAUTH },
{ "\\use_converter_needauth_forbidden", LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN },
{ "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS },
{ "\\use_native_filedialog", LyXRC::RC_USE_NATIVE_FILEDIALOG },
{ "\\use_pixmap_cache", LyXRC::RC_USE_PIXMAP_CACHE },
{ "\\use_qimage", LyXRC::RC_USE_QIMAGE },
// compatibility with versions older than 1.4.0 only
@ -273,6 +274,7 @@ void LyXRC::setDefaults()
num_lastfiles = 20;
check_lastfiles = true;
use_lastfilepos = true;
use_native_filedialog = true;
load_session = false;
make_backup = true;
save_compressed = false;
@ -875,6 +877,9 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
case RC_ACCEPT_COMPOUND:
lexrc >> spellchecker_accept_compound;
break;
case RC_USE_NATIVE_FILEDIALOG:
lexrc >> use_native_filedialog;
break;
case RC_USE_SYSTEM_COLORS:
lexrc >> use_system_colors;
break;
@ -2412,6 +2417,16 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
if (tag != RC_LAST)
break;
// fall through
case RC_USE_NATIVE_FILEDIALOG:
if (ignore_system_lyxrc ||
use_native_filedialog != system_lyxrc.use_native_filedialog) {
os << "\\use_native_filedialog "
<< convert<string>(use_native_filedialog)
<< '\n';
}
if (tag != RC_LAST)
break;
// fall through
case RC_USE_SYSTEM_COLORS:
if (ignore_system_lyxrc ||
use_system_colors != system_lyxrc.use_system_colors) {
@ -3031,6 +3046,7 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
case LyXRC::RC_USE_CONVERTER_CACHE:
case LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN:
case LyXRC::RC_USE_CONVERTER_NEEDAUTH:
case LyXRC::RC_USE_NATIVE_FILEDIALOG:
case LyXRC::RC_USE_SYSTEM_COLORS:
case LyXRC::RC_USE_TOOLTIP:
case LyXRC::RC_USE_PIXMAP_CACHE:

View File

@ -172,6 +172,7 @@ public:
RC_USE_CONVERTER_CACHE,
RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN,
RC_USE_CONVERTER_NEEDAUTH,
RC_USE_NATIVE_FILEDIALOG,
RC_USE_SYSTEM_COLORS,
RC_USE_TOOLTIP,
RC_USE_PIXMAP_CACHE,
@ -333,6 +334,8 @@ public:
bool use_tooltip;
/// Use the colors from current system theme?
bool use_system_colors;
/// use native file dialog or our own ?
bool use_native_filedialog;
/// Use pixmap cache?
bool use_pixmap_cache;
/// Use QImage backend?

View File

@ -130,7 +130,7 @@ public:
bool selected;
/// Whether the spell checker is enabled for the parent
bool do_spellcheck;
///
/// True when it can be assumed that the screen has been cleared
bool full_repaint;
/// Current background color
ColorCode background_color;

View File

@ -172,6 +172,10 @@ public:
*/
std::string index_command;
/** Hyperref driver
*/
std::string hyperref_driver;
/** Line length to use with plaintext or LaTeX export.
*/
size_type linelen;

View File

@ -97,6 +97,10 @@ void PDFOptions::writeLaTeX(OutputParams & runparams, otexstream & os,
string opt;
string hyperset;
// Driver needed by specific converters
if (!runparams.hyperref_driver.empty())
opt += runparams.hyperref_driver + ",";
// since LyX uses unicode, also set the PDF strings to unicode strings with the
// hyperref option "unicode"
opt += "unicode=true,";

View File

@ -1437,43 +1437,18 @@ bool Paragraph::Private::latexSpecialT3(char_type const c, otexstream & os,
void Paragraph::Private::validate(LaTeXFeatures & features) const
{
if (layout_->inpreamble && inset_owner_) {
bool const is_command = layout_->latextype == LATEX_COMMAND;
Font f;
// Using a string stream here circumvents the encoding
// FIXME: Using a string stream here circumvents the encoding
// switching machinery of odocstream. Therefore the
// output is wrong if this paragraph contains content
// that needs to switch encoding.
Buffer const & buf = inset_owner_->buffer();
BufferParams const & bp = features.runparams().is_child
? buf.masterParams() : buf.params();
otexstringstream os;
os << layout_->preamble();
if (is_command) {
os << '\\' << from_ascii(layout_->latexname());
// we have to provide all the optional arguments here, even though
// the last one is the only one we care about.
// Separate handling of optional argument inset.
if (!layout_->latexargs().empty()) {
OutputParams rp = features.runparams();
rp.local_font = &owner_->getFirstFontSettings(bp);
latexArgInsets(*owner_, os, rp, layout_->latexargs());
}
os << from_ascii(layout_->latexparam());
}
size_t const length = os.length();
// this will output "{" at the beginning, but not at the end
owner_->latex(bp, f, os, features.runparams(), 0, -1, true);
if (os.length() > length) {
if (is_command) {
os << '}';
if (!layout_->postcommandargs().empty()) {
OutputParams rp = features.runparams();
rp.local_font = &owner_->getFirstFontSettings(bp);
latexArgInsets(*owner_, os, rp, layout_->postcommandargs(), "post:");
}
}
TeXOnePar(buf, buf.text(), buf.getParFromID(owner_->id()).pit(), os,
features.runparams(), string(), 0, -1, true);
if (os.length() > length)
features.addPreambleSnippet(os.release(), true);
}
}
if (features.runparams().flavor == OutputParams::HTML
@ -2426,7 +2401,9 @@ void Paragraph::latex(BufferParams const & bparams,
// if the paragraph is empty, the loop will not be entered at all
if (empty()) {
if (style.isCommand()) {
// For InTitle commands, we have already opened a group
// in output_latex::TeXOnePar.
if (style.isCommand() && !style.intitle) {
os << '{';
++column;
}
@ -2464,7 +2441,9 @@ void Paragraph::latex(BufferParams const & bparams,
os << "}] ";
column +=3;
}
if (style.isCommand()) {
// For InTitle commands, we have already opened a group
// in output_latex::TeXOnePar.
if (style.isCommand() && !style.intitle) {
os << '{';
++column;
}
@ -3531,6 +3510,8 @@ void Paragraph::forOutliner(docstring & os, size_t const maxlen,
size_t tmplen = shorten ? maxlen + 1 : maxlen;
if (label && !labelString().empty())
os += labelString() + ' ';
if (!layout().isTocCaption())
return;
for (pos_type i = 0; i < size() && os.length() < tmplen; ++i) {
if (isDeleted(i))
continue;
@ -4176,6 +4157,15 @@ SpellChecker::Result Paragraph::spellCheck(pos_type & from, pos_type & to,
}
void Paragraph::anonymize()
{
// This is a very crude anonymization for now
for (char_type & c : d->text_)
if (isLetterChar(c) || isNumber(c))
c = 'a';
}
void Paragraph::Private::markMisspelledWords(
pos_type const & first, pos_type const & last,
SpellChecker::Result result,

View File

@ -505,6 +505,10 @@ public:
/// presently used only in the XHTML output routines.
std::string magicLabel() const;
/// anonymizes the paragraph contents (but not the paragraphs
/// contained inside it. Does not handle undo.
void anonymize();
private:
/// Expand the counters for the labelstring of \c layout
docstring expandParagraphLabel(Layout const &, BufferParams const &,

View File

@ -47,8 +47,6 @@
#include "support/lstrings.h"
#include "support/textutils.h"
#include <boost/crc.hpp>
#include <algorithm>
#include <list>
#include <stack>
@ -84,42 +82,6 @@ void ParagraphMetrics::reset(Paragraph const & par)
}
size_t ParagraphMetrics::computeRowSignature(Row const & row,
BufferView const & bv) const
{
boost::crc_32_type crc;
for (pos_type i = row.pos(); i < row.endpos(); ++i) {
if (par_->isInset(i)) {
Inset const * in = par_->getInset(i);
Dimension const d = in->dimension(bv);
int const b[] = { d.wid, d.asc, d.des };
crc.process_bytes(b, sizeof(b));
} else {
char_type const b[] = { par_->getChar(i) };
crc.process_bytes(b, sizeof(char_type));
}
if (bv.buffer().params().track_changes) {
Change change = par_->lookupChange(i);
char_type const b[] = { static_cast<char_type>(change.type) };
// 1 byte is enough to encode Change::Type
crc.process_bytes(b, 1);
}
}
pos_type const b1[] = { row.sel_beg, row.sel_end };
crc.process_bytes(b1, sizeof(b1));
Dimension const & d = row.dimension();
int const b2[] = { row.begin_margin_sel,
row.end_margin_sel,
d.wid, d.asc, d.des };
crc.process_bytes(b2, sizeof(b2));
crc.process_bytes(&row.separator, sizeof(row.separator));
return crc.checksum();
}
void ParagraphMetrics::setPosition(int position)
{
position_ = position;

View File

@ -85,9 +85,6 @@ public:
///
bool hfillExpansion(Row const & row, pos_type pos) const;
///
size_t computeRowSignature(Row const &, BufferView const & bv) const;
///
int position() const { return position_; }
void setPosition(int position);

View File

@ -162,19 +162,13 @@ Row::Row()
: separator(0), label_hfill(0), left_margin(0), right_margin(0),
sel_beg(-1), sel_end(-1),
begin_margin_sel(false), end_margin_sel(false),
changed_(false), crc_(0),
changed_(true),
pit_(0), pos_(0), end_(0),
right_boundary_(false), flushed_(false), rtl_(false)
right_boundary_(false), flushed_(false), rtl_(false),
changebar_(false)
{}
void Row::setCrc(size_type crc) const
{
changed_ = crc != crc_;
crc_ = crc;
}
bool Row::isMarginSelected(bool left_margin, DocIterator const & beg,
DocIterator const & end) const
{
@ -209,8 +203,8 @@ void Row::setSelectionAndMargins(DocIterator const & beg,
setSelection(beg.pos(), end.pos());
if (selection()) {
end_margin_sel = isMarginSelected(false, beg, end);
begin_margin_sel = isMarginSelected(true, beg, end);
change(end_margin_sel, isMarginSelected(false, beg, end));
change(begin_margin_sel, isMarginSelected(true, beg, end));
}
}
@ -218,18 +212,18 @@ void Row::setSelectionAndMargins(DocIterator const & beg,
void Row::setSelection(pos_type beg, pos_type end) const
{
if (pos_ >= beg && pos_ <= end)
sel_beg = pos_;
change(sel_beg, pos_);
else if (beg > pos_ && beg <= end_)
sel_beg = beg;
change(sel_beg, beg);
else
sel_beg = -1;
change(sel_beg, -1);
if (end_ >= beg && end_ <= end)
sel_end = end_;
change(sel_end,end_);
else if (end < end_ && end >= pos_)
sel_end = end;
change(sel_end, end);
else
sel_end = -1;
change(sel_end, -1);
}
@ -371,6 +365,8 @@ void Row::finalizeLast()
if (elt.final)
return;
elt.final = true;
if (elt.change.changed())
changebar_ = true;
if (elt.type == STRING) {
dim_.wid -= elt.dim.wid;

View File

@ -136,12 +136,33 @@ public:
///
Row();
/**
* Helper function: set variable \c var to value \c val, and mark
* row as changed is the values were different. This is intended
* for use when changing members of the row object.
*/
template<class T1, class T2>
void change(T1 & var, T2 const val) {
if (var != val)
changed(true);
var = val;
}
/**
* Helper function: set variable \c var to value \c val, and mark
* row as changed is the values were different. This is intended
* for use when changing members of the row object.
* This is the const version, useful for mutable members.
*/
template<class T1, class T2>
void change(T1 & var, T2 const val) const {
if (var != val)
changed(true);
var = val;
}
///
bool changed() const { return changed_; }
///
void setChanged(bool c) { changed_ = c; }
///
void setCrc(size_type crc) const;
void changed(bool c) const { changed_ = c; }
/// Set the selection begin and end.
/**
* This is const because we update the selection status only at draw()
@ -266,6 +287,11 @@ public:
void reverseRTL(bool rtl_par);
///
bool isRTL() const { return rtl_; }
///
bool needsChangeBar() const { return changebar_; }
///
void needsChangeBar(bool ncb) { changebar_ = ncb; }
/// Find row element that contains \c pos, and compute x offset.
const_iterator const findElement(pos_type pos, bool boundary, double & x) const;
@ -310,8 +336,6 @@ private:
/// has the Row appearance changed since last drawing?
mutable bool changed_;
/// CRC of row contents.
mutable size_type crc_;
/// Index of the paragraph that contains this row
pit_type pit_;
/// first pos covered by this row
@ -326,6 +350,8 @@ private:
Dimension dim_;
/// true when this row lives in a right-to-left paragraph
bool rtl_;
/// true when a changebar should be drawn in the margin
bool changebar_;
};

View File

@ -247,18 +247,6 @@ void RowPainter::paintChange(Row::Element const & e) const
void RowPainter::paintChangeBar() const
{
pos_type const start = row_.pos();
pos_type end = row_.endpos();
if (par_.size() == end) {
// this is the last row of the paragraph;
// thus, we must also consider the imaginary end-of-par character
end++;
}
if (start == end || !par_.isChanged(start, end))
return;
int const height = tm_.isLastRow(row_)
? row_.ascent()
: row_.height();
@ -576,7 +564,7 @@ void RowPainter::paintText()
paintStringAndSel(e);
// Paint the spelling marks if enabled.
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && pi_.pain.isDrawingEnabled())
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && !pi_.pain.isNull())
paintMisspelledMark(e);
break;

View File

@ -99,6 +99,18 @@ string envName(Spacing::Space space, bool useSetSpace)
return useSetSpace ? name : support::ascii_lowercase(name);
}
string cmdName(Spacing::Space space, bool useSetSpace)
{
static char const * const cmd_names[]
= { "SingleSpacing", "OnehalfSpacing", "DoubleSpacing", "SetStretch", ""};
string const name = cmd_names[space];
if (useSetSpace && name == "SetStretch")
return "setSpacing";
return useSetSpace ? name : support::ascii_lowercase(name);
}
} // namespace
string const Spacing::writeEnvirBegin(bool useSetSpace) const
@ -118,6 +130,16 @@ string const Spacing::writeEnvirEnd(bool useSetSpace) const
}
string const Spacing::writeCmd(bool useSetSpace) const
{
string const name = cmdName(space, useSetSpace);
if (space == Other)
return "\\" + name + "{" + getValueAsString() + '}';
else
return name.empty() ? string() : "\\" + name + "{}";
}
string const Spacing::writePreamble(bool useSetSpace) const
{
string preamble;

View File

@ -62,6 +62,9 @@ public:
std::string const writeEnvirEnd(bool useSetSpace) const;
/// useSetSpace is true when using the variant supported by
/// the memoir class.
std::string const writeCmd(bool useSetSpace) const;
/// useSetSpace is true when using the variant supported by
/// the memoir class.
std::string const writePreamble(bool useSetSpace) const;
private:

View File

@ -1977,7 +1977,7 @@ docstring Text::getPossibleLabel(Cursor const & cur) const
Layout const * layout = &(pars_[pit].layout());
docstring text;
docstring par_text = pars_[pit].asString();
docstring par_text = pars_[pit].asString(AS_STR_SKIPDELETE);
// The return string of math matrices might contain linebreaks
par_text = subst(par_text, '\n', '-');

View File

@ -860,6 +860,18 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
cur.upDownInText(up, needsUpdate);
needsUpdate |= cur.beforeDispatchCursor().inMathed();
} else {
pos_type newpos = up ? 0 : cur.lastpos();
if (lyxrc.mac_like_cursor_movement && cur.pos() != newpos) {
needsUpdate |= cur.selHandle(select);
// we do not reset the targetx of the cursor
cur.pos() = newpos;
needsUpdate |= bv->checkDepm(cur, bv->cursor());
cur.updateTextTargetOffset();
if (needsUpdate)
cur.forceBufferUpdate();
break;
}
// if the cursor cannot be moved up or down do not remove
// the selection right now, but wait for the next dispatch.
if (select)

View File

@ -43,7 +43,9 @@
#include "frontends/FontMetrics.h"
#include "frontends/Painter.h"
#include "frontends/NullPainter.h"
#include "support/convert.h"
#include "support/debug.h"
#include "support/lassert.h"
@ -198,6 +200,14 @@ bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
}
void TextMetrics::updatePosCache(pit_type pit) const
{
frontend::NullPainter np;
PainterInfo pi(bv_, np);
drawParagraph(pi, pit, origin_.x_, par_metrics_[pit].position());
}
int TextMetrics::rightMargin(ParagraphMetrics const & pm) const
{
return text_->isMainText() ? pm.rightMargin(*bv_) : 0;
@ -459,7 +469,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
row.pit(pit);
need_new_row = breakRow(row, right_margin);
setRowHeight(row);
row.setChanged(false);
row.changed(true);
if (row_index || row.endpos() < par.size()
|| (row.right_boundary() && par.inInset().lyxCode() != CELL_CODE)) {
/* If there is more than one row or the row has been
@ -955,6 +965,10 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
row.addVirtual(end, docstring(1, char_type(0x00B6)), f, Change());
}
// Is there a end-of-paragaph change?
if (i == end && par.lookupChange(end).changed() && !need_new_row)
row.needsChangeBar(true);
// if the row is too large, try to cut at last separator. In case
// of success, reset indication that the row was broken abruptly.
int const next_width = max_width_ - leftMargin(row.pit(), row.endpos())
@ -1214,6 +1228,7 @@ void TextMetrics::newParMetricsDown()
redoParagraph(pit);
par_metrics_[pit].setPosition(last.second.position()
+ last.second.descent() + par_metrics_[pit].ascent());
updatePosCache(pit);
}
@ -1228,6 +1243,7 @@ void TextMetrics::newParMetricsUp()
redoParagraph(pit);
par_metrics_[pit].setPosition(first.second.position()
- first.second.ascent() - par_metrics_[pit].descent());
updatePosCache(pit);
}
// y is screen coordinate
@ -1486,14 +1502,14 @@ int TextMetrics::cursorX(CursorSlice const & sl,
int TextMetrics::cursorY(CursorSlice const & sl, bool boundary) const
{
//lyxerr << "TextMetrics::cursorY: boundary: " << boundary << endl;
ParagraphMetrics const & pm = par_metrics_[sl.pit()];
ParagraphMetrics const & pm = parMetrics(sl.pit());
if (pm.rows().empty())
return 0;
int h = 0;
h -= par_metrics_[0].rows()[0].ascent();
h -= parMetrics(0).rows()[0].ascent();
for (pit_type pit = 0; pit < sl.pit(); ++pit) {
h += par_metrics_[pit].height();
h += parMetrics(pit).height();
}
int pos = sl.pos();
if (pos && boundary)
@ -1797,8 +1813,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
return;
size_t const nrows = pm.rows().size();
// Use fast lane when drawing is disabled.
if (!pi.pain.isDrawingEnabled()) {
// Use fast lane in nodraw stage.
if (pi.pain.isNull()) {
for (size_t i = 0; i != nrows; ++i) {
Row const & row = pm.rows()[i];
@ -1850,17 +1866,11 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
if (i)
y += row.ascent();
RowPainter rp(pi, *text_, row, row_x, y);
// It is not needed to draw on screen if we are not inside.
bool const inside = (y + row.descent() >= 0
&& y - row.ascent() < ww);
pi.pain.setDrawingEnabled(inside);
if (!inside) {
// Paint only the insets to set inset cache correctly
// FIXME: remove paintOnlyInsets when we know that positions
// have already been set.
rp.paintOnlyInsets();
// Inset positions have already been set in nodraw stage.
y += row.descent();
continue;
}
@ -1874,27 +1884,30 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
// whether this row is the first or last and update the margins.
if (row.selection()) {
if (row.sel_beg == 0)
row.begin_margin_sel = sel_beg.pit() < pit;
row.change(row.begin_margin_sel, sel_beg.pit() < pit);
if (row.sel_end == sel_end_par.lastpos())
row.end_margin_sel = sel_end.pit() > pit;
row.change(row.end_margin_sel, sel_end.pit() > pit);
}
// Row signature; has row changed since last paint?
row.setCrc(pm.computeRowSignature(row, *bv_));
// has row changed since last paint?
bool row_has_changed = row.changed()
|| bv_->hadHorizScrollOffset(text_, pit, row.pos());
|| bv_->hadHorizScrollOffset(text_, pit, row.pos())
|| bv_->needRepaint(text_, row);
// Take this opportunity to spellcheck the row contents.
if (row_has_changed && pi.do_spellcheck && lyxrc.spellcheck_continuously) {
text_->getPar(pit).spellCheck();
}
RowPainter rp(pi, *text_, row, row_x, y);
// Don't paint the row if a full repaint has not been requested
// and if it has not changed.
if (!pi.full_repaint && !row_has_changed) {
// Paint only the insets if the text itself is
// unchanged.
rp.paintOnlyInsets();
row.changed(false);
y += row.descent();
continue;
}
@ -1905,21 +1918,28 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
LYXERR(Debug::PAINTING, "Clear rect@("
<< max(row_x, 0) << ", " << y - row.ascent() << ")="
<< width() << " x " << row.height());
pi.pain.fillRectangle(max(row_x, 0), y - row.ascent(),
width(), row.height(), pi.background_color);
// FIXME: this is a hack. We know that at least this
// amount of pixels can be cleared on right and left.
// Doing so gets rid of caret ghosts when the cursor is at
// the begining/end of row. However, it will not work if
// the caret has a ridiculous width like 6. (see ticket
// #10797)
pi.pain.fillRectangle(max(row_x, 0) - Inset::TEXT_TO_INSET_OFFSET,
y - row.ascent(),
width() + 2 * Inset::TEXT_TO_INSET_OFFSET,
row.height(), pi.background_color);
}
// Instrumentation for testing row cache (see also
// 12 lines lower):
if (lyxerr.debugging(Debug::PAINTING)
&& (row.selection() || pi.full_repaint || row_has_changed)) {
string const foreword = text_->isMainText() ?
"main text redraw " : "inset text redraw: ";
LYXERR(Debug::PAINTING, foreword << "pit=" << pit << " row=" << i
<< " row_selection=" << row.selection()
<< " full_repaint=" << pi.full_repaint
<< " row_has_changed=" << row_has_changed
<< " drawingEnabled=" << pi.pain.isDrawingEnabled());
&& (row.selection() || pi.full_repaint || row_has_changed)) {
string const foreword = text_->isMainText() ? "main text redraw "
: "inset text redraw: ";
LYXERR0(foreword << "pit=" << pit << " row=" << i
<< (row.selection() ? " row_selection": "")
<< (pi.full_repaint ? " full_repaint" : "")
<< (row_has_changed ? " row_has_changed" : ""));
}
// Backup full_repaint status and force full repaint
@ -1930,7 +1950,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
rp.paintSelection();
rp.paintAppendix();
rp.paintDepthBar();
rp.paintChangeBar();
if (row.needsChangeBar())
rp.paintChangeBar();
if (i == 0 && !row.isRTL())
rp.paintFirst();
if (i == nrows - 1 && row.isRTL())
@ -1944,11 +1965,25 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
row_x + row.right_x() > bv_->workWidth());
y += row.descent();
#if 0
// This debug code shows on screen which rows are repainted.
// FIXME: since the updates related to caret blinking restrict
// the painter to a small rectangle, the numbers are not
// updated when this happens. Change the code in
// GuiWorkArea::Private::show/hideCaret if this is important.
static int count = 0;
++count;
FontInfo fi(sane_font);
fi.setSize(FONT_SIZE_TINY);
fi.setColor(Color_red);
pi.pain.text(row_x, y, convert<docstring>(count), fi);
#endif
// Restore full_repaint status.
pi.full_repaint = tmp;
row.changed(false);
}
// Re-enable screen drawing for future use of the painter.
pi.pain.setDrawingEnabled(true);
//LYXERR(Debug::PAINTING, ".");
}

View File

@ -62,6 +62,11 @@ public:
///
void newParMetricsUp();
/// The "nodraw" drawing stage for one single paragraph: set the
/// positions of the insets contained this paragraph in metrics
/// cache. Related to BufferView::updatePosCache.
void updatePosCache(pit_type pit) const;
/// Gets the fully instantiated font at a given position in a paragraph
/// Basically the same routine as Paragraph::getFont() in Paragraph.cpp.
/// The difference is that this one is used for displaying, and thus we

View File

@ -18,6 +18,7 @@
#include "Undo.h"
#include "Buffer.h"
#include "BufferList.h"
#include "BufferParams.h"
#include "buffer_funcs.h"
#include "Cursor.h"
@ -40,6 +41,7 @@
#include <algorithm>
#include <deque>
#include <set>
using namespace std;
using namespace lyx::support;
@ -645,22 +647,32 @@ void Undo::recordUndoFullBuffer(CursorData const & cur)
/// UndoGroupHelper class stuff
/** FIXME: handle restarted groups
* It may happen that the buffers are visited in order buffer1,
* buffer2, buffer1. In this case, we want to have only one undo group
* in buffer1. One solution is to replace buffer_ with a set<Buffer*>,
* but I am not sure yet how to do it. A use case is
* InsetLabel::updateReferences.
*/
class UndoGroupHelper::Impl {
friend class UndoGroupHelper;
set<Buffer *> buffers_;
};
UndoGroupHelper::UndoGroupHelper(Buffer * buf) : d(new UndoGroupHelper::Impl)
{
resetBuffer(buf);
}
UndoGroupHelper::~UndoGroupHelper()
{
for (Buffer * buf : d->buffers_)
if (theBufferList().isLoaded(buf) || theBufferList().isInternal(buf))
buf->undo().endUndoGroup();
delete d;
}
void UndoGroupHelper::resetBuffer(Buffer * buf)
{
if (buf == buffer_)
return;
if (buffer_)
buffer_->undo().endUndoGroup();
buffer_ = buf;
if (buffer_)
buffer_->undo().beginUndoGroup();
if (buf && d->buffers_.count(buf) == 0) {
d->buffers_.insert(buf);
buf->undo().beginUndoGroup();
}
}

View File

@ -135,15 +135,9 @@ private:
*/
class UndoGroupHelper {
public:
UndoGroupHelper(Buffer * buf = 0) : buffer_(0)
{
resetBuffer(buf);
}
UndoGroupHelper(Buffer * buf = 0);
~UndoGroupHelper()
{
resetBuffer(0);
}
~UndoGroupHelper();
/** Close the current undo group if necessary and create a new one
* for buffer \c buf.
@ -151,7 +145,8 @@ public:
void resetBuffer(Buffer * buf);
private:
Buffer * buffer_;
class Impl;
Impl * const d;
};

View File

@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \
Delegates.h \
KeyModifier.h \
KeySymbol.h \
NullPainter.h \
Painter.h \
Clipboard.h \
Selection.h \

109
src/frontends/NullPainter.h Normal file
View File

@ -0,0 +1,109 @@
// -*- C++ -*-
/**
* \file NullPainter.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author unknown
* \author John Levon
* \author Jean-Marc Lasgouttes
*
* Full author contact details are available in file CREDITS.
*/
#ifndef NULLPAINTER_H
#define NULLPAINTER_H
#include "Painter.h"
namespace lyx {
namespace frontend {
/**
* NullPainter - A painter instance that does nothing
*/
class NullPainter : public Painter {
public:
NullPainter() : Painter(1) {}
~NullPainter() {}
/// draw a line from point to point
void line(int, int, int, int, Color,
line_style = line_solid, int = thin_line) {}
///
void lines(int const *, int const *, int, Color,
fill_style = fill_none, line_style = line_solid,
int = thin_line) {}
///
void path(int const *, int const *, int const *, int const *,
int const *, int const *, int, Color,
fill_style = fill_none, line_style = line_solid, int = thin_line) {}
/// draw a rectangle
void rectangle(int, int, int, int, Color,
line_style = line_solid, int = thin_line) {}
/// draw a filled rectangle
void fillRectangle(int, int, int, int, Color) {}
/// draw an arc
void arc(int, int, unsigned int, unsigned int, int, int, Color) {}
/// draw a pixel
void point(int, int, Color) {}
/// draw an image from the image cache
void image(int, int, int, int, graphics::Image const &) {}
/// draw a string
void text(int, int, docstring const &, FontInfo const &) {}
/// draw a char
void text(int, int, char_type, FontInfo const &) {}
/// draw a string
void text(int, int, docstring const &, Font const &, double, double) {}
///
void text(int, int, docstring const &, Font const &,
Color, size_type, size_type, double, double) {}
/// This painter does not paint
bool isNull() const { return true; }
/// draw the underbar, strikeout, xout, uuline and uwave font attributes
void textDecoration(FontInfo const &, int, int, int) {}
/**
* Draw a string and enclose it inside a rectangle. If
* back color is specified, the background is cleared with
* the given color. If frame is specified, a thin frame is drawn
* around the text with the given color.
*/
void rectText(int, int, docstring const &,
FontInfo const &, Color, Color) {}
/// draw a string and enclose it inside a button frame
void buttonText(int, int, docstring const &,
FontInfo const &, Color, Color, int) {}
/// draw a character of a preedit string for cjk support.
int preeditText(int, int, char_type, FontInfo const &,
preedit_style) { return 0; }
/// start monochrome painting mode, i.e. map every color into [min,max]
void enterMonochromeMode(Color const &, Color const &) {}
/// leave monochrome painting mode
void leaveMonochromeMode() {}
/// draws a wavy line that can be used for underlining.
void wavyHorizontalLine(int, int, int, ColorCode) {}
};
} // namespace frontend
} // namespace lyx
#endif // NULLPAINTER_H

View File

@ -49,7 +49,7 @@ namespace frontend {
*/
class Painter {
public:
Painter(double pixel_ratio) : drawing_enabled_(true), pixel_ratio_(pixel_ratio) {}
Painter(double pixel_ratio) : pixel_ratio_(pixel_ratio) {}
static const int thin_line;
@ -147,11 +147,8 @@ public:
Color other, size_type from, size_type to,
double wordspacing, double textwidth) = 0;
void setDrawingEnabled(bool drawing_enabled)
{ drawing_enabled_ = drawing_enabled; }
/// Indicate wether real screen drawing shall be done or not.
bool isDrawingEnabled() const { return drawing_enabled_; }
// Returns true if the painter does not actually paint.
virtual bool isNull() const = 0;
double pixelRatio() const { return pixel_ratio_; }
@ -183,8 +180,6 @@ public:
/// draws a wavy line that can be used for underlining.
virtual void wavyHorizontalLine(int x, int y, int width, ColorCode col) = 0;
private:
///
bool drawing_enabled_;
/// Ratio between physical pixels and device-independent pixels
double pixel_ratio_;
};

View File

@ -36,8 +36,8 @@ public:
///
virtual ~WorkArea() {}
/// redraw the screen, without using existing pixmap
virtual void redraw(bool update_metrics) = 0;
/// Update metrics if needed and schedule a paint event
virtual void scheduleRedraw(bool update_metrics) = 0;
/// close this work area.
/// Slot for Buffer::closing signal.

View File

@ -35,7 +35,7 @@ void WorkAreaManager::remove(WorkArea * wa)
void WorkAreaManager::redrawAll(bool update_metrics)
{
for (WorkArea * wa : work_areas_)
wa->redraw(update_metrics);
wa->scheduleRedraw(update_metrics);
}

View File

@ -16,6 +16,8 @@
#include "LyXFileDialog.h"
#include "qt_helpers.h"
#include "LyXRC.h"
#include "support/debug.h"
#include "support/FileName.h"
#include "support/filetools.h"
@ -24,7 +26,9 @@
#include <string>
/** when this is defined, the code will use
#include <QApplication>
/** when LyXRC::use_native_filedialog is true, we use
* QFileDialog::getOpenFileName and friends to create filedialogs.
* Effects:
* - the dialog does not use the quick directory buttons (Button
@ -35,13 +39,6 @@
*
* Therefore there is a tradeoff in enabling or disabling this (JMarc)
*/
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#define USE_NATIVE_FILEDIALOG 1
#endif
#ifdef USE_NATIVE_FILEDIALOG
#include <QApplication>
#endif
namespace lyx {
@ -91,38 +88,38 @@ FileDialog::Result FileDialog::save(QString const & path,
FileDialog::Result result;
result.first = FileDialog::Chosen;
#ifdef USE_NATIVE_FILEDIALOG
QString const startsWith = makeAbsPath(suggested, path);
QString const name =
QFileDialog::getSaveFileName(qApp->focusWidget(),
title_, startsWith, filters.join(";;"),
selectedFilter, QFileDialog::DontConfirmOverwrite);
if (name.isNull())
result.first = FileDialog::Later;
else
result.second = toqstr(os::internal_path(fromqstr(name)));
#else
LyXFileDialog dlg(title_, path, filters, private_->b1, private_->b2);
dlg.setFileMode(QFileDialog::AnyFile);
dlg.setAcceptMode(QFileDialog::AcceptSave);
dlg.setConfirmOverwrite(false);
if (selectedFilter != 0 && !selectedFilter->isEmpty())
dlg.selectNameFilter(*selectedFilter);
if (lyxrc.use_native_filedialog) {
QString const startsWith = makeAbsPath(suggested, path);
QString const name =
QFileDialog::getSaveFileName(qApp->focusWidget(),
title_, startsWith, filters.join(";;"),
selectedFilter, QFileDialog::DontConfirmOverwrite);
if (name.isNull())
result.first = FileDialog::Later;
else
result.second = toqstr(os::internal_path(fromqstr(name)));
} else {
LyXFileDialog dlg(title_, path, filters, private_->b1, private_->b2);
dlg.setFileMode(QFileDialog::AnyFile);
dlg.setAcceptMode(QFileDialog::AcceptSave);
dlg.setConfirmOverwrite(false);
if (selectedFilter != 0 && !selectedFilter->isEmpty())
dlg.selectNameFilter(*selectedFilter);
if (!suggested.isEmpty())
dlg.selectFile(suggested);
if (!suggested.isEmpty())
dlg.selectFile(suggested);
LYXERR(Debug::GUI, "Synchronous FileDialog: ");
int res = dlg.exec();
LYXERR(Debug::GUI, "result " << res);
if (res == QDialog::Accepted)
result.second = internalPath(dlg.selectedFiles()[0]);
else
result.first = FileDialog::Later;
if (selectedFilter != 0)
*selectedFilter = dlg.selectedNameFilter();
dlg.hide();
#endif
LYXERR(Debug::GUI, "Synchronous FileDialog: ");
int res = dlg.exec();
LYXERR(Debug::GUI, "result " << res);
if (res == QDialog::Accepted)
result.second = internalPath(dlg.selectedFiles()[0]);
else
result.first = FileDialog::Later;
if (selectedFilter != 0)
*selectedFilter = dlg.selectedNameFilter();
dlg.hide();
}
return result;
}
@ -143,29 +140,29 @@ FileDialog::Result FileDialog::open(QString const & path,
FileDialog::Result result;
result.first = FileDialog::Chosen;
#ifdef USE_NATIVE_FILEDIALOG
QString const startsWith = makeAbsPath(suggested, path);
QString const file = QFileDialog::getOpenFileName(qApp->focusWidget(),
title_, startsWith, filters.join(";;"));
if (file.isNull())
result.first = FileDialog::Later;
else
result.second = internalPath(file);
#else
LyXFileDialog dlg(title_, path, filters, private_->b1, private_->b2);
if (lyxrc.use_native_filedialog) {
QString const startsWith = makeAbsPath(suggested, path);
QString const file = QFileDialog::getOpenFileName(qApp->focusWidget(),
title_, startsWith, filters.join(";;"));
if (file.isNull())
result.first = FileDialog::Later;
else
result.second = internalPath(file);
} else {
LyXFileDialog dlg(title_, path, filters, private_->b1, private_->b2);
if (!suggested.isEmpty())
dlg.selectFile(suggested);
if (!suggested.isEmpty())
dlg.selectFile(suggested);
LYXERR(Debug::GUI, "Synchronous FileDialog: ");
int res = dlg.exec();
LYXERR(Debug::GUI, "result " << res);
if (res == QDialog::Accepted)
result.second = internalPath(dlg.selectedFiles()[0]);
else
result.first = FileDialog::Later;
dlg.hide();
#endif
LYXERR(Debug::GUI, "Synchronous FileDialog: ");
int res = dlg.exec();
LYXERR(Debug::GUI, "result " << res);
if (res == QDialog::Accepted)
result.second = internalPath(dlg.selectedFiles()[0]);
else
result.first = FileDialog::Later;
dlg.hide();
}
return result;
}
@ -178,33 +175,33 @@ FileDialog::Result FileDialog::opendir(QString const & path,
FileDialog::Result result;
result.first = FileDialog::Chosen;
#ifdef USE_NATIVE_FILEDIALOG
QString const startsWith = toqstr(makeAbsPath(fromqstr(suggested),
fromqstr(path)).absFileName());
QString const dir = QFileDialog::getExistingDirectory(qApp->focusWidget(),
title_, startsWith);
if (dir.isNull())
result.first = FileDialog::Later;
else
result.second = toqstr(os::internal_path(fromqstr(dir)));
#else
LyXFileDialog dlg(title_, path, QStringList(qt_("Directories")),
private_->b1, private_->b2);
if (lyxrc.use_native_filedialog) {
QString const startsWith =
toqstr(makeAbsPath(fromqstr(suggested), fromqstr(path)).absFileName());
QString const dir =
QFileDialog::getExistingDirectory(qApp->focusWidget(), title_, startsWith);
if (dir.isNull())
result.first = FileDialog::Later;
else
result.second = toqstr(os::internal_path(fromqstr(dir)));
} else {
LyXFileDialog dlg(title_, path, QStringList(qt_("Directories")),
private_->b1, private_->b2);
dlg.setFileMode(QFileDialog::DirectoryOnly);
dlg.setFileMode(QFileDialog::DirectoryOnly);
if (!suggested.isEmpty())
dlg.selectFile(suggested);
if (!suggested.isEmpty())
dlg.selectFile(suggested);
LYXERR(Debug::GUI, "Synchronous FileDialog: ");
int res = dlg.exec();
LYXERR(Debug::GUI, "result " << res);
if (res == QDialog::Accepted)
result.second = internalPath(dlg.selectedFiles()[0]);
else
result.first = FileDialog::Later;
dlg.hide();
#endif
LYXERR(Debug::GUI, "Synchronous FileDialog: ");
int res = dlg.exec();
LYXERR(Debug::GUI, "result " << res);
if (res == QDialog::Accepted)
result.second = internalPath(dlg.selectedFiles()[0]);
else
result.first = FileDialog::Later;
dlg.hide();
}
return result;
}

View File

@ -65,8 +65,8 @@ FindAndReplaceWidget::FindAndReplaceWidget(GuiView & view)
replace_work_area_->setFrameStyle(QFrame::StyledPanel);
// We don't want two cursors blinking.
find_work_area_->stopBlinkingCursor();
replace_work_area_->stopBlinkingCursor();
find_work_area_->stopBlinkingCaret();
replace_work_area_->stopBlinkingCaret();
}

View File

@ -1395,9 +1395,9 @@ DispatchResult const & GuiApplication::dispatch(FuncRequest const & cmd)
if (current_view_ && current_view_->currentBufferView()) {
current_view_->currentBufferView()->cursor().saveBeforeDispatchPosXY();
buffer = &current_view_->currentBufferView()->buffer();
if (buffer)
buffer->undo().beginUndoGroup();
}
// This handles undo groups automagically
UndoGroupHelper ugh(buffer);
DispatchResult dr;
// redraw the screen at the end (first of the two drawing steps).
@ -1406,10 +1406,6 @@ DispatchResult const & GuiApplication::dispatch(FuncRequest const & cmd)
dispatch(cmd, dr);
updateCurrentView(cmd, dr);
// the buffer may have been closed by one action
if (theBufferList().isLoaded(buffer) || theBufferList().isInternal(buffer))
buffer->undo().endUndoGroup();
d->dispatch_result_ = dr;
return d->dispatch_result_;
}
@ -1439,7 +1435,7 @@ void GuiApplication::updateCurrentView(FuncRequest const & cmd, DispatchResult &
theSelection().haveSelection(bv->cursor().selection());
// update gui
current_view_->restartCursor();
current_view_->restartCaret();
}
if (dr.needMessageUpdate()) {
// Some messages may already be translated, so we cannot use _()
@ -1633,14 +1629,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
case LFUN_SCREEN_FONT_UPDATE: {
// handle the screen font changes.
d->font_loader_.update();
// Backup current_view_
GuiView * view = current_view_;
// Set current_view_ to zero to forbid GuiWorkArea::redraw()
// to skip the refresh.
current_view_ = 0;
theBufferList().changed(false);
// Restore current_view_
current_view_ = view;
dr.screenUpdate(Update::Force | Update::FitCursor);
break;
}
@ -1873,8 +1862,8 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
// FIXME: this LFUN should also work without any view.
Buffer * buffer = (current_view_ && current_view_->documentBufferView())
? &(current_view_->documentBufferView()->buffer()) : 0;
if (buffer)
buffer->undo().beginUndoGroup();
// This handles undo groups automagically
UndoGroupHelper ugh(buffer);
while (!arg.empty()) {
string first;
arg = split(arg, first, ';');
@ -1882,9 +1871,6 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
func.setOrigin(cmd.origin());
dispatch(func);
}
// the buffer may have been closed by one action
if (theBufferList().isLoaded(buffer) || theBufferList().isInternal(buffer))
buffer->undo().endUndoGroup();
break;
}
@ -2158,7 +2144,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
if (!keysym.isOK())
LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
if (current_view_)
current_view_->restartCursor();
current_view_->restartCaret();
return;
}
@ -2218,7 +2204,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
if (!isPrintable(encoded_last_key)) {
LYXERR(Debug::KEY, "Non-printable character! Omitting.");
if (current_view_)
current_view_->restartCursor();
current_view_->restartCaret();
return;
}
// The following modifier check is not needed on Mac.
@ -2240,7 +2226,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
{
if (current_view_) {
current_view_->message(_("Unknown function."));
current_view_->restartCursor();
current_view_->restartCaret();
}
return;
}
@ -2255,7 +2241,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
LYXERR(Debug::KEY, "Unknown Action and not isText() -- giving up");
if (current_view_) {
current_view_->message(_("Unknown function."));
current_view_->restartCursor();
current_view_->restartCaret();
}
return;
}

View File

@ -27,6 +27,7 @@
#include "TextClass.h"
#include "FuncRequest.h"
#include "insets/InsetCitation.h"
#include "insets/InsetCommand.h"
#include "support/debug.h"
@ -92,7 +93,7 @@ static vector<lyx::docstring> to_docstring_vector(QStringList const & qlist)
GuiCitation::GuiCitation(GuiView & lv)
: DialogView(lv, "citation", qt_("Citation")),
style_(QString()), literal_(false), params_(insetCode("citation"))
style_(QString()), params_(insetCode("citation"))
{
setupUi(this);
@ -232,12 +233,13 @@ void GuiCitation::on_restorePB_clicked()
{
init();
updateFilterHint();
filterPressed();
}
void GuiCitation::on_literalCB_clicked()
{
literal_ = literalCB->isChecked();
InsetCitation::last_literal = literalCB->isChecked();
changed();
}
@ -768,7 +770,7 @@ void GuiCitation::init()
// if this is a new citation, we set the literal checkbox
// to its last set value.
if (cited_keys_.isEmpty())
literalCB->setChecked(literal_);
literalCB->setChecked(InsetCitation::last_literal);
else
literalCB->setChecked(params_["literal"] == "true");
@ -1061,7 +1063,7 @@ void GuiCitation::saveSession(QSettings & settings) const
settings.setValue(
sessionKey() + "/citestyle", style_);
settings.setValue(
sessionKey() + "/literal", literal_);
sessionKey() + "/literal", InsetCitation::last_literal);
}
@ -1073,7 +1075,8 @@ void GuiCitation::restoreSession()
casesense_->setChecked(settings.value(sessionKey() + "/casesensitive").toBool());
instant_->setChecked(settings.value(sessionKey() + "/autofind", true).toBool());
style_ = settings.value(sessionKey() + "/citestyle").toString();
literal_ = settings.value(sessionKey() + "/literal", false).toBool();
InsetCitation::last_literal =
settings.value(sessionKey() + "/literal", false).toBool();
updateFilterHint();
}

View File

@ -183,9 +183,6 @@ private:
/// last used citation style
QString style_;
/// last set value for literal
/// this is used only for new citations
bool literal_;
///
GuiSelectionManager * selectionManager;
/// available keys.

View File

@ -470,6 +470,33 @@ PreambleModule::PreambleModule(QWidget * parent)
preambleTE->setWordWrapMode(QTextOption::NoWrap);
setFocusProxy(preambleTE);
connect(preambleTE, SIGNAL(textChanged()), this, SIGNAL(changed()));
connect(findLE, SIGNAL(textEdited(const QString &)), this, SLOT(checkFindButton()));
connect(findButtonPB, SIGNAL(clicked()), this, SLOT(findText()));
connect(findLE, SIGNAL(returnPressed()), this, SLOT(findText()));
checkFindButton();
// https://stackoverflow.com/questions/13027091/how-to-override-tab-width-in-qt
const int tabStop = 4;
QFontMetrics metrics(preambleTE->currentFont());
preambleTE->setTabStopWidth(tabStop * metrics.width(' '));
}
void PreambleModule::checkFindButton()
{
findButtonPB->setEnabled(!findLE->text().isEmpty());
}
void PreambleModule::findText()
{
bool const found = preambleTE->find(findLE->text());
if (!found) {
// wrap
QTextCursor qtcur = preambleTE->textCursor();
qtcur.movePosition(QTextCursor::Start);
preambleTE->setTextCursor(qtcur);
preambleTE->find(findLE->text());
}
}
@ -529,6 +556,8 @@ void PreambleModule::closeEvent(QCloseEvent * e)
LocalLayout::LocalLayout(QWidget * parent)
: UiWidget<Ui::LocalLayoutUi>(parent), current_id_(0), validated_(false)
{
locallayoutTE->setFont(guiApp->typewriterSystemFont());
locallayoutTE->setWordWrapMode(QTextOption::NoWrap);
connect(locallayoutTE, SIGNAL(textChanged()), this, SLOT(textChanged()));
connect(validatePB, SIGNAL(clicked()), this, SLOT(validatePressed()));
connect(convertPB, SIGNAL(clicked()), this, SLOT(convertPressed()));
@ -1217,7 +1246,7 @@ GuiDocument::GuiDocument(GuiView & lv)
setSectionResizeMode(mathsModule->packagesTW->horizontalHeader(), QHeaderView::Stretch);
map<string, string> const & packages = BufferParams::auto_packages();
mathsModule->packagesTW->setRowCount(packages.size());
int i = 0;
int packnum = 0;
for (map<string, string>::const_iterator it = packages.begin();
it != packages.end(); ++it) {
docstring const package = from_ascii(it->first);
@ -1248,11 +1277,35 @@ GuiDocument::GuiDocument(GuiView & lv)
autoRB->setToolTip(autoTooltip);
alwaysRB->setToolTip(alwaysTooltip);
neverRB->setToolTip(neverTooltip);
// Pack the buttons in a layout in order to get proper alignment
QWidget * autoRBWidget = new QWidget();
QHBoxLayout * autoRBLayout = new QHBoxLayout(autoRBWidget);
autoRBLayout->addWidget(autoRB);
autoRBLayout->setAlignment(Qt::AlignCenter);
autoRBLayout->setContentsMargins(0, 0, 0, 0);
autoRBWidget->setLayout(autoRBLayout);
QWidget * alwaysRBWidget = new QWidget();
QHBoxLayout * alwaysRBLayout = new QHBoxLayout(alwaysRBWidget);
alwaysRBLayout->addWidget(alwaysRB);
alwaysRBLayout->setAlignment(Qt::AlignCenter);
alwaysRBLayout->setContentsMargins(0, 0, 0, 0);
alwaysRBWidget->setLayout(alwaysRBLayout);
QWidget * neverRBWidget = new QWidget();
QHBoxLayout * neverRBLayout = new QHBoxLayout(neverRBWidget);
neverRBLayout->addWidget(neverRB);
neverRBLayout->setAlignment(Qt::AlignCenter);
neverRBLayout->setContentsMargins(0, 0, 0, 0);
neverRBWidget->setLayout(neverRBLayout);
QTableWidgetItem * pack = new QTableWidgetItem(toqstr(package));
mathsModule->packagesTW->setItem(i, 0, pack);
mathsModule->packagesTW->setCellWidget(i, 1, autoRB);
mathsModule->packagesTW->setCellWidget(i, 2, alwaysRB);
mathsModule->packagesTW->setCellWidget(i, 3, neverRB);
mathsModule->packagesTW->setItem(packnum, 0, pack);
mathsModule->packagesTW->setCellWidget(packnum, 1, autoRBWidget);
mathsModule->packagesTW->setCellWidget(packnum, 2, alwaysRBWidget);
mathsModule->packagesTW->setCellWidget(packnum, 3, neverRBWidget);
connect(autoRB, SIGNAL(clicked()),
this, SLOT(change_adaptor()));
@ -1260,7 +1313,7 @@ GuiDocument::GuiDocument(GuiView & lv)
this, SLOT(change_adaptor()));
connect(neverRB, SIGNAL(clicked()),
this, SLOT(change_adaptor()));
++i;
++packnum;
}
connect(mathsModule->allPackagesAutoPB, SIGNAL(clicked()),
this, SLOT(allPackagesAuto()));
@ -3001,17 +3054,19 @@ void GuiDocument::applyView()
if (!item)
continue;
int row = mathsModule->packagesTW->row(item);
QRadioButton * rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 1);
QRadioButton * rb =
(QRadioButton*)mathsModule->packagesTW->cellWidget(row, 1)->layout()->itemAt(0)->widget();
if (rb->isChecked()) {
bp_.use_package(it->first, BufferParams::package_auto);
continue;
}
rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 2);
rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 2)->layout()->itemAt(0)->widget();
if (rb->isChecked()) {
bp_.use_package(it->first, BufferParams::package_on);
continue;
}
rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 3);
rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 3)->layout()->itemAt(0)->widget();
if (rb->isChecked())
bp_.use_package(it->first, BufferParams::package_off);
}
@ -3546,17 +3601,20 @@ void GuiDocument::paramsToDialog()
int row = mathsModule->packagesTW->row(item);
switch (bp_.use_package(it->first)) {
case BufferParams::package_off: {
QRadioButton * rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 3);
QRadioButton * rb =
(QRadioButton*)mathsModule->packagesTW->cellWidget(row, 3)->layout()->itemAt(0)->widget();
rb->setChecked(true);
break;
}
case BufferParams::package_on: {
QRadioButton * rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 2);
QRadioButton * rb =
(QRadioButton*)mathsModule->packagesTW->cellWidget(row, 2)->layout()->itemAt(0)->widget();
rb->setChecked(true);
break;
}
case BufferParams::package_auto: {
QRadioButton * rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, 1);
QRadioButton * rb =
(QRadioButton*)mathsModule->packagesTW->cellWidget(row, 1)->layout()->itemAt(0)->widget();
rb->setChecked(true);
break;
}
@ -4328,7 +4386,8 @@ void GuiDocument::dispatchParams()
// We need a non-const buffer object.
Buffer & buf = const_cast<BufferView *>(bufferview())->buffer();
// There may be several undo records; group them (bug #8998)
buf.undo().beginUndoGroup();
// This handles undo groups automagically
UndoGroupHelper ugh(&buf);
// This must come first so that a language change is correctly noticed
setLanguage();
@ -4395,10 +4454,6 @@ void GuiDocument::dispatchParams()
// If we used an LFUN, we would not need these two lines:
BufferView * bv = const_cast<BufferView *>(bufferview());
bv->processUpdateFlags(Update::Force | Update::FitCursor);
// Don't forget to close the group. Note that it is important
// to check that there is no early return in the method.
buf.undo().endUndoGroup();
}
@ -4569,7 +4624,8 @@ void GuiDocument::allPackagesNot()
void GuiDocument::allPackages(int col)
{
for (int row = 0; row < mathsModule->packagesTW->rowCount(); ++row) {
QRadioButton * rb = (QRadioButton*)mathsModule->packagesTW->cellWidget(row, col);
QRadioButton * rb =
(QRadioButton*)mathsModule->packagesTW->cellWidget(row, col)->layout()->itemAt(0)->widget();
rb->setChecked(true);
}
}

View File

@ -332,10 +332,14 @@ private:
void closeEvent(QCloseEvent *);
void on_preambleTE_textChanged() { changed(); }
private:
typedef std::map<BufferId, std::pair<int,int> > Coords;
Coords preamble_coords_;
BufferId current_id_;
private Q_SLOTS:
///
void checkFindButton();
void findText();
};

View File

@ -493,6 +493,9 @@ static void getCrop(external::ClipData & data,
void GuiExternal::updateContents()
{
if (params_.filename.empty())
tab->setCurrentIndex(0);
string const name =
params_.filename.outputFileName(fromqstr(bufferFilePath()));
fileED->setText(toqstr(name));

View File

@ -31,9 +31,9 @@
#include "insets/InsetListingsParams.h"
#include "insets/InsetInclude.h"
#include <QPushButton>
#include <QCheckBox>
#include <QLineEdit>
#include <QPushButton>
#include <utility>

View File

@ -52,10 +52,10 @@ GuiPainter::GuiPainter(QPaintDevice * device, double pixel_ratio)
: QPainter(device), Painter(pixel_ratio),
use_pixmap_cache_(false)
{
// new QPainter has default QPen:
current_color_ = guiApp->colorCache().get(Color_black);
current_ls_ = line_solid;
current_lw_ = thin_line;
// set cache correctly
current_color_ = pen().color();
current_ls_ = pen().style() == Qt::DotLine ? line_onoffdash : line_solid;
current_lw_ = pen().width();
}
@ -171,9 +171,6 @@ void GuiPainter::leaveMonochromeMode()
void GuiPainter::point(int x, int y, Color col)
{
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col));
drawPoint(x, y);
}
@ -184,9 +181,6 @@ void GuiPainter::line(int x1, int y1, int x2, int y2,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col), ls, lw);
bool const do_antialiasing = renderHints() & TextAntialiasing
&& x1 != x2 && y1 != y2 && ls != line_solid_aliased;
@ -202,9 +196,6 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
// double the size if needed
// FIXME THREAD
static QVector<QPoint> points(32);
@ -247,9 +238,6 @@ void GuiPainter::path(int const * xp, int const * yp,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
QPainterPath bpath;
// This is the starting point, so its control points are meaningless
bpath.moveTo(xp[0], yp[0]);
@ -278,9 +266,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
line_style ls,
int lw)
{
if (!isDrawingEnabled())
return;
setQPainterPen(computeColor(col), ls, lw);
drawRect(x, y, w, h);
}
@ -288,9 +273,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
{
if (!isDrawingEnabled())
return;
fillRect(x, y, w, h, guiApp->colorCache().get(col));
}
@ -298,9 +280,6 @@ void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
int a1, int a2, Color col)
{
if (!isDrawingEnabled())
return;
// LyX usings 1/64ths degree, Qt usings 1/16th
setQPainterPen(computeColor(col));
bool const do_antialiasing = renderHints() & TextAntialiasing;
@ -317,9 +296,6 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
fillRectangle(x, y, w, h, Color_graphicsbg);
if (!isDrawingEnabled())
return;
QImage const image = qlimage.image();
QRectF const drect = QRectF(x, y, w, h);
QRectF const srect = QRectF(0, 0, image.width(), image.height());
@ -391,7 +367,7 @@ void GuiPainter::text(int x, int y, docstring const & s,
double const wordspacing, double const tw)
{
//LYXERR0("text: x=" << x << ", s=" << s);
if (s.empty() || !isDrawingEnabled())
if (s.empty())
return;
/* Caution: The following ucs4 to QString conversions work for symbol fonts

View File

@ -37,6 +37,9 @@ public:
GuiPainter(QPaintDevice *, double pixel_ratio);
virtual ~GuiPainter();
/// This painter paints
virtual bool isNull() const { return false; }
/// draw a line from point to point
virtual void line(
int x1, int y1,

View File

@ -1882,7 +1882,8 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
case LFUN_BUFFER_RELOAD:
enable = doc_buffer && !doc_buffer->isUnnamed()
&& doc_buffer->fileName().exists() && !doc_buffer->isClean();
&& doc_buffer->fileName().exists()
&& (!doc_buffer->isClean() || doc_buffer->notifiesExternalModification());
break;
case LFUN_BUFFER_CHILD_OPEN:
@ -2262,8 +2263,10 @@ Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles)
setBuffer(newBuffer);
newBuffer->errors("Parse");
if (tolastfiles)
if (tolastfiles) {
theSession().lastFiles().add(filename);
theSession().writeFile();
}
return newBuffer;
}
@ -2802,6 +2805,7 @@ bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
if (success) {
theSession().lastFiles().add(b.fileName());
theSession().writeFile();
return true;
}
@ -4191,7 +4195,7 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
// painting so we must reset it.
QPixmapCache::clear();
guiApp->fontLoader().update();
lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
dr.screenUpdate(Update::Force | Update::FitCursor);
break;
}
@ -4387,19 +4391,19 @@ Buffer const * GuiView::updateInset(Inset const * inset)
continue;
Buffer const * buffer = &(wa->bufferView().buffer());
if (inset_buffer == buffer)
wa->scheduleRedraw();
wa->scheduleRedraw(true);
}
return inset_buffer;
}
void GuiView::restartCursor()
void GuiView::restartCaret()
{
/* When we move around, or type, it's nice to be able to see
* the cursor immediately after the keypress.
* the caret immediately after the keypress.
*/
if (d.current_work_area_)
d.current_work_area_->startBlinkingCursor();
d.current_work_area_->startBlinkingCaret();
// Take this occasion to update the other GUI elements.
updateDialogs();
@ -4468,7 +4472,7 @@ void GuiView::resetDialogs()
// Now update controls with current buffer.
guiApp->setCurrentView(this);
restoreLayout();
restartCursor();
restartCaret();
}

View File

@ -116,7 +116,7 @@ public:
/// \return true if the \c FuncRequest has been dispatched.
void dispatch(FuncRequest const & cmd, DispatchResult & dr);
void restartCursor();
void restartCaret();
/// Update the completion popup and the inline completion state.
/// If \c start is true, then a new completion might be started.
/// If \c keep is true, an active completion will be kept active

View File

@ -16,7 +16,13 @@
#include "ColorCache.h"
#include "FontLoader.h"
#include "GuiApplication.h"
#include "GuiCompleter.h"
#include "GuiKeySymbol.h"
#include "GuiPainter.h"
#include "GuiView.h"
#include "Menus.h"
#include "qt_helpers.h"
#include "Buffer.h"
#include "BufferList.h"
@ -26,19 +32,14 @@
#include "Cursor.h"
#include "Font.h"
#include "FuncRequest.h"
#include "GuiApplication.h"
#include "GuiCompleter.h"
#include "GuiKeySymbol.h"
#include "GuiPainter.h"
#include "GuiView.h"
#include "KeySymbol.h"
#include "Language.h"
#include "LyX.h"
#include "LyXRC.h"
#include "LyXVC.h"
#include "qt_helpers.h"
#include "Text.h"
#include "TextMetrics.h"
#include "Undo.h"
#include "version.h"
#include "graphics/GraphicsImage.h"
@ -46,7 +47,6 @@
#include "support/convert.h"
#include "support/debug.h"
#include "support/gettext.h"
#include "support/lassert.h"
#include "support/TempFile.h"
@ -68,7 +68,6 @@
#include <QMenu>
#include <QPainter>
#include <QPalette>
#include <QPixmapCache>
#include <QScrollBar>
#include <QStyleOption>
#include <QStylePainter>
@ -77,8 +76,6 @@
#include <QToolTip>
#include <QMenuBar>
#include "support/bind.h"
#include <cmath>
#include <iostream>
@ -130,17 +127,15 @@ mouse_button::state q_motion_state(Qt::MouseButtons state)
namespace frontend {
class CursorWidget {
class CaretWidget {
public:
CursorWidget() : rtl_(false), l_shape_(false), completable_(false),
show_(false), x_(0), cursor_width_(0)
{
recomputeWidth();
}
CaretWidget() : rtl_(false), l_shape_(false), completable_(false),
x_(0), caret_width_(0)
{}
void draw(QPainter & painter)
{
if (!show_ || !rect_.isValid())
if (!rect_.isValid())
return;
int y = rect_.top();
@ -149,7 +144,7 @@ public:
int bot = rect_.bottom();
// draw vertical line
painter.fillRect(x_, y, cursor_width_, rect_.height(), color_);
painter.fillRect(x_, y, caret_width_, rect_.height(), color_);
// draw RTL/LTR indication
painter.setPen(color_);
@ -157,7 +152,7 @@ public:
if (rtl_)
painter.drawLine(x_, bot, x_ - l, bot);
else
painter.drawLine(x_, bot, x_ + cursor_width_ + r, bot);
painter.drawLine(x_, bot, x_ + caret_width_ + r, bot);
}
// draw completion triangle
@ -168,8 +163,8 @@ public:
painter.drawLine(x_ - 1, m - d, x_ - 1 - d, m);
painter.drawLine(x_ - 1, m + d, x_ - 1 - d, m);
} else {
painter.drawLine(x_ + cursor_width_, m - d, x_ + cursor_width_ + d, m);
painter.drawLine(x_ + cursor_width_, m + d, x_ + cursor_width_ + d, m);
painter.drawLine(x_ + caret_width_, m - d, x_ + caret_width_ + d, m);
painter.drawLine(x_ + caret_width_, m + d, x_ + caret_width_ + d, m);
}
}
}
@ -203,38 +198,32 @@ public:
r = max(r, TabIndicatorWidth);
}
// compute overall rectangle
rect_ = QRect(x - l, y, cursor_width_ + r + l, h);
}
void show(bool set_show = true) { show_ = set_show; }
void hide() { show_ = false; }
int cursorWidth() const { return cursor_width_; }
void recomputeWidth() {
cursor_width_ = lyxrc.cursor_width
//FIXME: LyXRC::cursor_width should be caret_width
caret_width_ = lyxrc.cursor_width
? lyxrc.cursor_width
: 1 + int((lyxrc.currentZoom + 50) / 200.0);
// compute overall rectangle
rect_ = QRect(x - l, y, caret_width_ + r + l, h);
}
QRect const & rect() { return rect_; }
private:
/// cursor is in RTL or LTR text
/// caret is in RTL or LTR text
bool rtl_;
/// indication for RTL or LTR
bool l_shape_;
/// triangle to show that a completion is available
bool completable_;
///
bool show_;
///
QColor color_;
/// rectangle, possibly with l_shape and completion triangle
QRect rect_;
/// x position (were the vertical line is drawn)
int x_;
int cursor_width_;
/// the width of the vertical blinking bar
int caret_width_;
};
@ -246,13 +235,35 @@ SyntheticMouseEvent::SyntheticMouseEvent()
GuiWorkArea::Private::Private(GuiWorkArea * parent)
: p(parent), screen_(0), buffer_view_(0), lyx_view_(0),
cursor_visible_(false), cursor_(0),
need_resize_(false), schedule_redraw_(false), preedit_lines_(1),
pixel_ratio_(1.0),
: p(parent), buffer_view_(0), lyx_view_(0),
caret_(0), caret_visible_(false),
need_resize_(false), preedit_lines_(1),
last_pixel_ratio_(1.0),
completer_(new GuiCompleter(p, p)), dialog_mode_(false), shell_escape_(false),
read_only_(false), clean_(true), externally_modified_(false)
{
int const time = QApplication::cursorFlashTime() / 2;
if (time > 0) {
caret_timeout_.setInterval(time);
caret_timeout_.start();
} else {
// let's initialize this just to be safe
caret_timeout_.setInterval(500);
}
}
GuiWorkArea::Private::~Private()
{
// If something is wrong with the buffer, we can ignore it safely
try {
buffer_view_->buffer().workAreaManager().remove(p);
} catch(...) {}
delete buffer_view_;
delete caret_;
// Completer has a QObject parent and is thus automatically destroyed.
// See #4758.
// delete completer_;
}
@ -287,24 +298,19 @@ double GuiWorkArea::pixelRatio() const
void GuiWorkArea::init()
{
// Setup the signals
connect(&d->cursor_timeout_, SIGNAL(timeout()),
this, SLOT(toggleCursor()));
connect(&d->caret_timeout_, SIGNAL(timeout()),
this, SLOT(toggleCaret()));
int const time = QApplication::cursorFlashTime() / 2;
if (time > 0) {
d->cursor_timeout_.setInterval(time);
d->cursor_timeout_.start();
} else {
// let's initialize this just to be safe
d->cursor_timeout_.setInterval(500);
}
// This connection is closed at the same time as this is destroyed.
d->synthetic_mouse_event_.timeout.timeout.connect([this](){
generateSyntheticMouseEvent();
});
d->resetScreen();
// With Qt4.5 a mouse event will happen before the first paint event
// so make sure that the buffer view has an up to date metrics.
d->buffer_view_->resize(viewport()->width(), viewport()->height());
d->cursor_ = new frontend::CursorWidget();
d->cursor_->hide();
d->caret_ = new frontend::CaretWidget();
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setAcceptDrops(true);
@ -313,63 +319,33 @@ void GuiWorkArea::init()
setFrameStyle(QFrame::NoFrame);
updateWindowTitle();
viewport()->setAutoFillBackground(false);
// We don't need double-buffering nor SystemBackground on
// the viewport because we have our own backing pixmap.
viewport()->setAttribute(Qt::WA_NoSystemBackground);
d->updateCursorShape();
// we paint our own background
viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
setFocusPolicy(Qt::StrongFocus);
d->setCursorShape(Qt::IBeamCursor);
// This connection is closed at the same time as this is destroyed.
d->synthetic_mouse_event_.timeout.timeout.connect([this](){
generateSyntheticMouseEvent();
});
LYXERR(Debug::GUI, "viewport width: " << viewport()->width()
<< " viewport height: " << viewport()->height());
// Enables input methods for asian languages.
// Must be set when creating custom text editing widgets.
setAttribute(Qt::WA_InputMethodEnabled, true);
d->dialog_mode_ = false;
}
GuiWorkArea::~GuiWorkArea()
{
// If something is wrong with the buffer, we can ignore it safely
try {
d->buffer_view_->buffer().workAreaManager().remove(this);
} catch(...) {}
delete d->screen_;
delete d->buffer_view_;
delete d->cursor_;
// Completer has a QObject parent and is thus automatically destroyed.
// See #4758.
// delete completer_;
delete d;
}
Qt::CursorShape GuiWorkArea::cursorShape() const
{
return viewport()->cursor().shape();
}
void GuiWorkArea::Private::setCursorShape(Qt::CursorShape shape)
{
p->viewport()->setCursor(shape);
}
void GuiWorkArea::Private::updateCursorShape()
{
setCursorShape(buffer_view_->clickableInset()
? Qt::PointingHandCursor : Qt::IBeamCursor);
bool const clickable = buffer_view_ && buffer_view_->clickableInset();
p->viewport()->setCursor(clickable ? Qt::PointingHandCursor
: Qt::IBeamCursor);
}
@ -436,14 +412,14 @@ BufferView const & GuiWorkArea::bufferView() const
}
void GuiWorkArea::stopBlinkingCursor()
void GuiWorkArea::stopBlinkingCaret()
{
d->cursor_timeout_.stop();
d->hideCursor();
d->caret_timeout_.stop();
d->hideCaret();
}
void GuiWorkArea::startBlinkingCursor()
void GuiWorkArea::startBlinkingCaret()
{
// do not show the cursor if the view is busy
if (view().busy())
@ -451,23 +427,32 @@ void GuiWorkArea::startBlinkingCursor()
Point p;
int h = 0;
d->buffer_view_->cursorPosAndHeight(p, h);
d->buffer_view_->caretPosAndHeight(p, h);
// Don't start blinking if the cursor isn't on screen.
if (!d->buffer_view_->cursorInView(p, h))
return;
d->showCursor();
d->showCaret();
//we're not supposed to cache this value.
int const time = QApplication::cursorFlashTime() / 2;
if (time <= 0)
return;
d->cursor_timeout_.setInterval(time);
d->cursor_timeout_.start();
d->caret_timeout_.setInterval(time);
d->caret_timeout_.start();
}
void GuiWorkArea::redraw(bool update_metrics)
void GuiWorkArea::toggleCaret()
{
if (d->caret_visible_)
d->hideCaret();
else
d->showCaret();
}
void GuiWorkArea::scheduleRedraw(bool update_metrics)
{
if (!isVisible())
// No need to redraw in this case.
@ -483,17 +468,14 @@ void GuiWorkArea::redraw(bool update_metrics)
d->buffer_view_->cursor().fixIfBroken();
}
// update cursor position, because otherwise it has to wait until
// update caret position, because otherwise it has to wait until
// the blinking interval is over
if (d->cursor_visible_) {
d->hideCursor();
d->showCursor();
}
d->updateCaretGeometry();
LYXERR(Debug::WORKAREA, "WorkArea::redraw screen");
d->updateScreen();
update(0, 0, viewport()->width(), viewport()->height());
viewport()->update();
/// FIXME: is this still true now that paintEvent does the actual painting?
/// \warning: scrollbar updating *must* be done after the BufferView is drawn
/// because \c BufferView::updateScrollbar() is called in \c BufferView::draw().
d->updateScrollbar();
@ -526,9 +508,9 @@ void GuiWorkArea::processKeySym(KeySymbol const & key, KeyModifier mod)
}
// In order to avoid bad surprise in the middle of an operation,
// we better stop the blinking cursor...
// the cursor gets restarted in GuiView::restartCursor()
stopBlinkingCursor();
// we better stop the blinking caret...
// the caret gets restarted in GuiView::restartCaret()
stopBlinkingCaret();
guiApp->processKeySym(key, mod);
}
@ -546,9 +528,9 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
cmd.action() != LFUN_MOUSE_MOTION || cmd.button() != mouse_button::none;
// In order to avoid bad surprise in the middle of an operation, we better stop
// the blinking cursor.
// the blinking caret.
if (notJustMovingTheMouse)
p->stopBlinkingCursor();
p->stopBlinkingCaret();
buffer_view_->mouseEventDispatch(cmd);
@ -568,8 +550,8 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
// FIXME: let GuiView take care of those.
lyx_view_->clearMessage();
// Show the cursor immediately after any operation
p->startBlinkingCursor();
// Show the caret immediately after any operation
p->startBlinkingCaret();
}
updateCursorShape();
@ -580,18 +562,18 @@ void GuiWorkArea::Private::resizeBufferView()
{
// WARNING: Please don't put any code that will trigger a repaint here!
// We are already inside a paint event.
p->stopBlinkingCursor();
p->stopBlinkingCaret();
// Warn our container (GuiView).
p->busy(true);
Point point;
int h = 0;
buffer_view_->cursorPosAndHeight(point, h);
bool const cursor_in_view = buffer_view_->cursorInView(point, h);
buffer_view_->caretPosAndHeight(point, h);
bool const caret_in_view = buffer_view_->cursorInView(point, h);
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
if (cursor_in_view)
if (caret_in_view)
buffer_view_->scrollToCursor();
updateScreen();
updateCaretGeometry();
// Update scrollbars which might have changed due different
// BufferView dimension. This is especially important when the
@ -601,23 +583,20 @@ void GuiWorkArea::Private::resizeBufferView()
need_resize_ = false;
p->busy(false);
// Eventually, restart the cursor after the resize event.
// Eventually, restart the caret after the resize event.
// We might be resizing even if the focus is on another widget so we only
// restart the cursor if we have the focus.
// restart the caret if we have the focus.
if (p->hasFocus())
QTimer::singleShot(50, p, SLOT(startBlinkingCursor()));
QTimer::singleShot(50, p, SLOT(startBlinkingCaret()));
}
void GuiWorkArea::Private::showCursor()
void GuiWorkArea::Private::updateCaretGeometry()
{
if (cursor_visible_)
return;
Point p;
Point point;
int h = 0;
buffer_view_->cursorPosAndHeight(p, h);
if (!buffer_view_->cursorInView(p, h))
buffer_view_->caretPosAndHeight(point, h);
if (!buffer_view_->cursorInView(point, h))
return;
// RTL or not RTL
@ -634,40 +613,41 @@ void GuiWorkArea::Private::showCursor()
if (realfont.language() == latex_language)
l_shape = false;
// show cursor on screen
// show caret on screen
Cursor & cur = buffer_view_->cursor();
bool completable = cur.inset().showCompletionCursor()
&& completer_->completionAvailable()
&& !completer_->popupVisible()
&& !completer_->inlineVisible();
cursor_visible_ = true;
cursor_->recomputeWidth();
caret_visible_ = true;
//int cur_x = buffer_view_->getPos(cur).x_;
// We may have decided to slide the cursor row so that cursor
// We may have decided to slide the cursor row so that caret
// is visible.
p.x_ -= buffer_view_->horizScrollOffset();
point.x_ -= buffer_view_->horizScrollOffset();
showCursor(p.x_, p.y_, h, l_shape, isrtl, completable);
caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable);
}
void GuiWorkArea::Private::hideCursor()
void GuiWorkArea::Private::showCaret()
{
if (!cursor_visible_)
if (caret_visible_)
return;
cursor_visible_ = false;
removeCursor();
updateCaretGeometry();
p->viewport()->update(caret_->rect());
}
void GuiWorkArea::toggleCursor()
void GuiWorkArea::Private::hideCaret()
{
if (d->cursor_visible_)
d->hideCursor();
else
d->showCursor();
if (!caret_visible_)
return;
caret_visible_ = false;
//if (!qApp->focusWidget())
p->viewport()->update(caret_->rect());
}
@ -690,7 +670,7 @@ void GuiWorkArea::Private::updateScrollbar()
void GuiWorkArea::scrollTo(int value)
{
stopBlinkingCursor();
stopBlinkingCaret();
d->buffer_view_->scrollDocView(value, true);
if (lyxrc.cursor_follows_scrollbar) {
@ -698,8 +678,8 @@ void GuiWorkArea::scrollTo(int value)
// FIXME: let GuiView take care of those.
d->lyx_view_->updateLayoutList();
}
// Show the cursor immediately after any operation.
startBlinkingCursor();
// Show the caret immediately after any operation.
startBlinkingCaret();
// FIXME QT5
#ifdef Q_WS_X11
QApplication::syncX();
@ -805,7 +785,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e)
d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer();
}
startBlinkingCursor();
startBlinkingCaret();
QAbstractScrollArea::focusInEvent(e);
}
@ -813,7 +793,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e)
void GuiWorkArea::focusOutEvent(QFocusEvent * e)
{
LYXERR(Debug::DEBUG, "GuiWorkArea::focusOutEvent(): " << this << endl);
stopBlinkingCursor();
stopBlinkingCaret();
QAbstractScrollArea::focusOutEvent(e);
}
@ -1176,157 +1156,31 @@ void GuiWorkArea::resizeEvent(QResizeEvent * ev)
}
void GuiWorkArea::Private::update(int x, int y, int w, int h)
void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
{
p->viewport()->update(x, y, w, h);
}
void GuiWorkArea::paintEvent(QPaintEvent * ev)
{
QRectF const rc = ev->rect();
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
if (d->needResize()) {
d->resetScreen();
d->resizeBufferView();
if (d->cursor_visible_) {
d->hideCursor();
d->showCursor();
}
}
QPainter pain(viewport());
double const pr = pixelRatio();
QRectF const rcs = QRectF(rc.x() * pr, rc.y() * pr, rc.width() * pr, rc.height() * pr);
if (lyxrc.use_qimage) {
QImage const & image = static_cast<QImage const &>(*d->screen_);
pain.drawImage(rc, image, rcs);
} else {
QPixmap const & pixmap = static_cast<QPixmap const &>(*d->screen_);
pain.drawPixmap(rc, pixmap, rcs);
}
d->cursor_->draw(pain);
ev->accept();
}
void GuiWorkArea::Private::updateScreen()
{
GuiPainter pain(screen_, p->pixelRatio());
buffer_view_->draw(pain);
}
void GuiWorkArea::Private::showCursor(int x, int y, int h,
bool l_shape, bool rtl, bool completable)
{
if (schedule_redraw_) {
// This happens when a graphic conversion is finished. As we don't know
// the size of the new graphics, it's better the update everything.
// We can't use redraw() here because this would trigger a infinite
// recursive loop with showCursor().
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
updateScreen();
updateScrollbar();
p->viewport()->update(QRect(0, 0, p->viewport()->width(), p->viewport()->height()));
schedule_redraw_ = false;
// Show the cursor immediately after the update.
hideCursor();
p->toggleCursor();
if (preedit_string_.empty())
return;
}
cursor_->update(x, y, h, l_shape, rtl, completable);
cursor_->show();
p->viewport()->update(cursor_->rect());
}
void GuiWorkArea::Private::removeCursor()
{
cursor_->hide();
//if (!qApp->focusWidget())
p->viewport()->update(cursor_->rect());
}
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
{
QString const & commit_string = e->commitString();
docstring const & preedit_string
= qstring_to_ucs4(e->preeditString());
if (!commit_string.isEmpty()) {
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
<< " commitString: " << e->commitString());
int key = 0;
// FIXME Iwami 04/01/07: we should take care also of UTF16 surrogates here.
for (int i = 0; i != commit_string.size(); ++i) {
QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, commit_string[i]);
keyPressEvent(&ev);
}
}
// Hide the cursor during the kana-kanji transformation.
if (preedit_string.empty())
startBlinkingCursor();
else
stopBlinkingCursor();
// last_width : for checking if last preedit string was/wasn't empty.
// FIXME THREAD && FIXME
// We could have more than one work area, right?
static bool last_width = false;
if (!last_width && preedit_string.empty()) {
// if last_width is last length of preedit string.
e->accept();
return;
}
GuiPainter pain(d->screen_, pixelRatio());
d->buffer_view_->updateMetrics();
d->buffer_view_->draw(pain);
// FIXME: shall we use real_current_font here? (see #10478)
FontInfo font = d->buffer_view_->cursor().getFont().fontInfo();
FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
FontMetrics const & fm = theFontMetrics(font);
int height = fm.maxHeight();
int cur_x = d->cursor_->rect().left();
int cur_y = d->cursor_->rect().bottom();
// redraw area of preedit string.
update(0, cur_y - height, viewport()->width(),
(height + 1) * d->preedit_lines_);
if (preedit_string.empty()) {
last_width = false;
d->preedit_lines_ = 1;
e->accept();
return;
}
last_width = true;
// att : stores an IM attribute.
QList<QInputMethodEvent::Attribute> const & att = e->attributes();
int const height = fm.maxHeight();
int cur_x = caret_->rect().left();
int cur_y = caret_->rect().bottom();
// get attributes of input method cursor.
// cursor_pos : cursor position in preedit string.
size_t cursor_pos = 0;
bool cursor_is_visible = false;
for (int i = 0; i != att.size(); ++i) {
if (att.at(i).type == QInputMethodEvent::Cursor) {
cursor_pos = att.at(i).start;
cursor_is_visible = att.at(i).length != 0;
for (auto const & attr : preedit_attr_) {
if (attr.type == QInputMethodEvent::Cursor) {
cursor_pos = attr.start;
cursor_is_visible = attr.length != 0;
break;
}
}
size_t preedit_length = preedit_string.length();
size_t const preedit_length = preedit_string_.length();
// get position of selection in input method.
// FIXME: isn't there a way to do this simplier?
@ -1335,12 +1189,12 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
// rLength : selected string length in IM.
size_t rLength = 0;
if (cursor_pos < preedit_length) {
for (int i = 0; i != att.size(); ++i) {
if (att.at(i).type == QInputMethodEvent::TextFormat) {
if (att.at(i).start <= int(cursor_pos)
&& int(cursor_pos) < att.at(i).start + att.at(i).length) {
rStart = att.at(i).start;
rLength = att.at(i).length;
for (auto const & attr : preedit_attr_) {
if (attr.type == QInputMethodEvent::TextFormat) {
if (attr.start <= int(cursor_pos)
&& int(cursor_pos) < attr.start + attr.length) {
rStart = attr.start;
rLength = attr.length;
if (!cursor_is_visible)
cursor_pos += rLength;
break;
@ -1353,20 +1207,20 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
rLength = 0;
}
int const right_margin = d->buffer_view_->rightMargin();
int const right_margin = buffer_view_->rightMargin();
Painter::preedit_style ps;
// Most often there would be only one line:
d->preedit_lines_ = 1;
preedit_lines_ = 1;
for (size_t pos = 0; pos != preedit_length; ++pos) {
char_type const typed_char = preedit_string[pos];
char_type const typed_char = preedit_string_[pos];
// reset preedit string style
ps = Painter::preedit_default;
// if we reached the right extremity of the screen, go to next line.
if (cur_x + fm.width(typed_char) > viewport()->width() - right_margin) {
if (cur_x + fm.width(typed_char) > p->viewport()->width() - right_margin) {
cur_x = right_margin;
cur_y += height + 1;
++d->preedit_lines_;
++preedit_lines_;
}
// preedit strings are displayed with dashed underline
// and partial strings are displayed white on black indicating
@ -1385,11 +1239,81 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
// draw one character and update cur_x.
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
}
}
// update the preedit string screen area.
update(0, cur_y - d->preedit_lines_*height, viewport()->width(),
void GuiWorkArea::paintEvent(QPaintEvent * ev)
{
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
if (d->need_resize_ || pixelRatio() != d->last_pixel_ratio_) {
d->resetScreen();
d->resizeBufferView();
}
d->last_pixel_ratio_ = pixelRatio();
GuiPainter pain(d->screenDevice(), pixelRatio());
d->buffer_view_->draw(pain, d->caret_visible_);
// The preedit text, if needed
d->paintPreeditText(pain);
// and the caret
if (d->caret_visible_)
d->caret_->draw(pain);
d->updateScreen(ev->rect());
ev->accept();
}
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
{
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
<< " commitString: " << e->commitString());
// insert the processed text in the document (handles undo)
if (!e->commitString().isEmpty()) {
d->buffer_view_->cursor().beginUndoGroup();
d->buffer_view_->cursor().insert(qstring_to_ucs4(e->commitString()));
d->buffer_view_->updateMetrics();
d->buffer_view_->cursor().endUndoGroup();
viewport()->update();
}
// Hide the caret during the test transformation.
if (e->preeditString().isEmpty())
startBlinkingCaret();
else
stopBlinkingCaret();
if (d->preedit_string_.empty() && e->preeditString().isEmpty()) {
// Nothing to do
e->accept();
return;
}
// The preedit text and its attributes will be used in paintPreeditText
d->preedit_string_ = qstring_to_ucs4(e->preeditString());
d->preedit_attr_ = e->attributes();
// redraw area of preedit string.
int height = d->caret_->rect().height();
int cur_y = d->caret_->rect().bottom();
viewport()->update(0, cur_y - height, viewport()->width(),
(height + 1) * d->preedit_lines_);
if (d->preedit_string_.empty()) {
d->preedit_lines_ = 1;
e->accept();
return;
}
// Don't forget to accept the event!
e->accept();
}
@ -1402,12 +1326,12 @@ QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
// this is the CJK-specific composition window position and
// the context menu position when the menu key is pressed.
case Qt::ImMicroFocus:
cur_r = d->cursor_->rect();
cur_r = d->caret_->rect();
if (d->preedit_lines_ != 1)
cur_r.moveLeft(10);
cur_r.moveBottom(cur_r.bottom()
+ cur_r.height() * (d->preedit_lines_ - 1));
// return lower right of cursor in LyX.
// return lower right of caret in LyX.
return cur_r;
default:
return QWidget::inputMethodQuery(query);
@ -1441,12 +1365,6 @@ bool GuiWorkArea::isFullScreen() const
}
void GuiWorkArea::scheduleRedraw()
{
d->schedule_redraw_ = true;
}
bool GuiWorkArea::inDialogMode() const
{
return d->dialog_mode_;
@ -1529,7 +1447,7 @@ QSize EmbeddedWorkArea::sizeHint () const
void EmbeddedWorkArea::disable()
{
stopBlinkingCursor();
stopBlinkingCaret();
if (view().currentWorkArea() != this)
return;
// No problem if currentMainWorkArea() is 0 (setCurrentWorkArea()
@ -1859,7 +1777,7 @@ void TabWorkArea::on_currentTabChanged(int i)
GuiWorkArea * wa = workArea(i);
LASSERT(wa, return);
wa->setUpdatesEnabled(true);
wa->redraw(true);
wa->scheduleRedraw(true);
wa->setFocus();
///
currentWorkAreaChanged(wa);

View File

@ -27,10 +27,6 @@ class QDropEvent;
class QToolButton;
class QWidget;
#ifdef CursorShape
#undef CursorShape
#endif
namespace lyx {
class Buffer;
@ -64,13 +60,11 @@ public:
/// is GuiView in fullscreen mode?
bool isFullScreen() const;
///
void scheduleRedraw();
///
BufferView & bufferView();
///
BufferView const & bufferView() const;
///
void redraw(bool update_metrics);
void scheduleRedraw(bool update_metrics);
/// return true if the key is part of a shortcut
bool queryKeySym(KeySymbol const & key, KeyModifier mod) const;
@ -81,8 +75,6 @@ public:
///
GuiCompleter & completer();
Qt::CursorShape cursorShape() const;
/// Return the GuiView this workArea belongs to
GuiView const & view() const;
GuiView & view();
@ -95,9 +87,9 @@ public Q_SLOTS:
/// This needs to be public because it is accessed externally by GuiView.
void processKeySym(KeySymbol const & key, KeyModifier mod);
///
void stopBlinkingCursor();
void stopBlinkingCaret();
///
void startBlinkingCursor();
void startBlinkingCaret();
Q_SIGNALS:
///
@ -118,8 +110,8 @@ private Q_SLOTS:
void scrollTo(int value);
/// timer to limit triple clicks
void doubleClickTimeout();
/// toggle the cursor's visibility
void toggleCursor();
/// toggle the caret's visibility
void toggleCaret();
/// close this work area.
/// Slot for Buffer::closing signal.
void close();

View File

@ -13,28 +13,19 @@
#define WORKAREA_PRIVATE_H
#include "FuncRequest.h"
#include "LyXRC.h"
#include "support/FileName.h"
#include "support/Timeout.h"
#include <QMouseEvent>
#include <QImage>
#include <QPixmap>
#include <QTimer>
class QContextMenuEvent;
class QDragEnterEvent;
class QDropEvent;
class QKeyEvent;
class QPaintEvent;
class QResizeEvent;
class QToolButton;
class QWheelEvent;
class QWidget;
#ifdef CursorShape
#undef CursorShape
#ifdef Q_OS_MAC
/* Qt on macOS does not respect the Qt::WA_OpaquePaintEvent attribute
* and resets the widget backing store at each update. Therefore, we
* use our own backing store in this case */
#define LYX_BACKINGSTORE 1
#include <QPainter>
#endif
namespace lyx {
@ -44,6 +35,7 @@ class Buffer;
namespace frontend {
class GuiCompleter;
class GuiPainter;
class GuiView;
class GuiWorkArea;
@ -86,96 +78,104 @@ public:
/**
* Implementation of the work area (buffer view GUI)
*/
class CursorWidget;
class CaretWidget;
struct GuiWorkArea::Private
{
///
Private(GuiWorkArea *);
/// update the passed area.
void update(int x, int y, int w, int h);
///
void updateScreen();
~Private();
///
void resizeBufferView();
/// paint the cursor and store the background
void showCursor(int x, int y, int h,
bool l_shape, bool rtl, bool completable);
/// hide the cursor
void removeCursor();
///
void dispatch(FuncRequest const & cmd0);
/// hide the visible cursor, if it is visible
void hideCursor();
/// show the cursor if it is not visible
void showCursor();
/// recompute the shape and position of the caret
void updateCaretGeometry();
/// show the caret if it is not visible
void showCaret();
/// hide the caret if it is visible
void hideCaret();
/// Set the range and value of the scrollbar and connect to its valueChanged
/// signal.
void updateScrollbar();
/// Change the cursor when the mouse hovers over a clickable inset
void updateCursorShape();
///
void setCursorShape(Qt::CursorShape shape);
bool needResize() const {
return need_resize_ || p->pixelRatio() != pixel_ratio_;
void paintPreeditText(GuiPainter & pain);
void resetScreen() {
#ifdef LYX_BACKINGSTORE
int const pr = p->pixelRatio();
screen_ = QImage(static_cast<int>(pr * p->viewport()->width()),
static_cast<int>(pr * p->viewport()->height()),
QImage::Format_ARGB32_Premultiplied);
# if QT_VERSION >= 0x050000
screen_.setDevicePixelRatio(pr);
# endif
#endif
}
void resetScreen()
{
delete screen_;
pixel_ratio_ = p->pixelRatio();
if (lyxrc.use_qimage) {
QImage *x =
new QImage(static_cast<int>(pixel_ratio_ * p->viewport()->width()),
static_cast<int>(pixel_ratio_ * p->viewport()->height()),
QImage::Format_ARGB32_Premultiplied);
#if QT_VERSION >= 0x050000
x->setDevicePixelRatio(pixel_ratio_);
QPaintDevice * screenDevice() {
#ifdef LYX_BACKINGSTORE
return &screen_;
#else
return p->viewport();
#endif
screen_ = x;
} else {
QPixmap *x =
new QPixmap(static_cast<int>(pixel_ratio_ * p->viewport()->width()),
static_cast<int>(pixel_ratio_ * p->viewport()->height()));
#if QT_VERSION >= 0x050000
x->setDevicePixelRatio(pixel_ratio_);
#endif
screen_ = x;
}
}
#ifdef LYX_BACKINGSTORE
void updateScreen(QRectF const & rc) {
QPainter qpain(p->viewport());
double const pr = p->pixelRatio();
QRectF const rcs = QRectF(rc.x() * pr, rc.y() * pr,
rc.width() * pr, rc.height() * pr);
qpain.drawImage(rc, screen_, rcs);
}
#else
void updateScreen(QRectF const & ) {}
#endif
///
GuiWorkArea * p;
///
QPaintDevice * screen_;
///
BufferView * buffer_view_;
///
GuiView * lyx_view_;
/// is the cursor currently displayed
bool cursor_visible_;
#ifdef LYX_BACKINGSTORE
///
QTimer cursor_timeout_;
QImage screen_;
#endif
///
CaretWidget * caret_;
/// is the caret currently displayed
bool caret_visible_;
///
QTimer caret_timeout_;
///
SyntheticMouseEvent synthetic_mouse_event_;
///
DoubleClick dc_event_;
///
CursorWidget * cursor_;
///
bool need_resize_;
///
bool schedule_redraw_;
///
/// the current preedit text of the input method
docstring preedit_string_;
/// Number of lines used by preedit text
int preedit_lines_;
/// the attributes of the preedit text
QList<QInputMethodEvent::Attribute> preedit_attr_;
/// Ratio between physical pixels and device-independent pixels
/// We save the last used value to detect changes of the
/// current pixel_ratio of the viewport.
double pixel_ratio_;
double last_pixel_ratio_;
///
GuiCompleter * completer_;

File diff suppressed because it is too large Load Diff

View File

@ -6,172 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<width>569</width>
<height>367</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="allPakcagesLA">
<property name="text">
<string>All packages:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="allPackagesAutoPB">
<property name="text">
<string>Load A&amp;utomatically</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="allPackagesAlwaysPB">
<property name="text">
<string>Load Alwa&amp;ys</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="allPackagesNotPB">
<property name="text">
<string>Do &amp;Not Load</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="MathIndentCB">
<property name="toolTip">
<string>Indent displayed formulas instead of centering</string>
</property>
<property name="text">
<string>Indent &amp;Formulas</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="MathIndentCO">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Size of the indentation</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>234</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="MathIndentLE">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="lyx::frontend::LengthCombo" name="MathIndentLengthCO">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
</widget>
</item>
<item row="3" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>153</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0">
<widget class="QLabel" name="MathNumberingPosL">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>115</width>
<height>18</height>
</size>
</property>
<property name="text">
<string>Formula numbering side:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="MathNumberingPosCO">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Side where formulas are numbered</string>
</property>
</widget>
</item>
<item row="4" column="2" colspan="2">
<spacer name="horizontalSpacer_1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>234</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="4">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QTableWidget" name="packagesTW">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -179,6 +22,9 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="columnCount">
<number>4</number>
</property>
@ -194,6 +40,171 @@
<column/>
</widget>
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="allPakcagesLA">
<property name="text">
<string>All packages:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="allPackagesAutoPB">
<property name="text">
<string>Load A&amp;utomatically</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="allPackagesAlwaysPB">
<property name="text">
<string>Load Alwa&amp;ys</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="allPackagesNotPB">
<property name="text">
<string>Do &amp;Not Load</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="MathIndentCB">
<property name="toolTip">
<string>Indent displayed formulas instead of centering</string>
</property>
<property name="text">
<string>Indent &amp;Formulas</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="MathIndentCO">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Size of the indentation</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>234</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="MathIndentLE">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="lyx::frontend::LengthCombo" name="MathIndentLengthCO">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string/>
</property>
</widget>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>153</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="MathNumberingPosL">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>115</width>
<height>18</height>
</size>
</property>
<property name="text">
<string>Formula numbering side:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="MathNumberingPosCO">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Side where formulas are numbered</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_1">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>234</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PreambleUi</class>
<widget class="QWidget" name="PreambleUi" >
<property name="geometry" >
<widget class="QWidget" name="PreambleUi">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,19 +10,38 @@
<height>278</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<layout class="QGridLayout">
<property name="leftMargin">
<number>11</number>
</property>
<property name="spacing" >
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0" >
<widget class="QTextEdit" name="preambleTE" >
<property name="acceptRichText" >
<item row="1" column="0">
<widget class="QLineEdit" name="findLE"/>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="findButtonPB">
<property name="text">
<string>Find</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QTextEdit" name="preambleTE">
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
@ -29,7 +49,7 @@
</layout>
</widget>
<includes>
<include location="local" >qt_i18n.h</include>
<include location="local">qt_i18n.h</include>
</includes>
<resources/>
<connections/>

View File

@ -249,6 +249,7 @@ docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
ParagraphList::const_iterator it = buffer.paragraphs().begin();
ParagraphList::const_iterator end = buffer.paragraphs().end();
bool is_literal = false;
for (; it != end; ++it) {
if (it->insetList().empty())
continue;
@ -274,11 +275,14 @@ docstring bibitemWidest(Buffer const & buffer, OutputParams const & runparams)
if (wx > w) {
w = wx;
lbl = label;
is_literal = (bitem->getParam("literal") == "true");
}
}
if (!lbl.empty()) {
InsetCommandParams p(BIBITEM_CODE);
if (is_literal)
p["literal"] = from_ascii("true");
return p.prepareCommand(runparams, lbl, ParamInfo::HANDLING_LATEXIFY);
}

View File

@ -59,6 +59,12 @@ InsetCitation::~InsetCitation()
}
// May well be over-ridden when session settings are loaded
// in GuiCitation. Unfortunately, that will not happen until
// such a dialog is created.
bool InsetCitation::last_literal = true;
ParamInfo const & InsetCitation::findInfo(string const & /* cmdName */)
{
static ParamInfo param_info_;

View File

@ -82,10 +82,14 @@ public:
static bool isCompatibleCommand(std::string const &);
//@}
///
void redoLabel() { cache.recalculate = true; }
///
CitationStyle getCitationStyle(BufferParams const & bp, std::string const & input,
std::vector<CitationStyle> const & valid_styles) const;
///
std::map<docstring, docstring> getQualifiedLists(docstring const p) const;
///
static bool last_literal;
private:
/// tries to make a pretty label and makes a basic one if not

View File

@ -1150,7 +1150,8 @@ void unifyGraphicsGroups(Buffer & b, string const & argument)
InsetGraphicsParams params;
InsetGraphics::string2params(argument, b, params);
b.undo().beginUndoGroup();
// This handles undo groups automagically
UndoGroupHelper ugh(&b);
Inset & inset = b.inset();
InsetIterator it = inset_iterator_begin(inset);
InsetIterator const end = inset_iterator_end(inset);
@ -1165,7 +1166,6 @@ void unifyGraphicsGroups(Buffer & b, string const & argument)
}
}
}
b.undo().endUndoGroup();
}

View File

@ -97,12 +97,12 @@ void InsetLabel::updateLabelAndRefs(docstring const & new_label,
if (label == old_label)
return;
buffer().undo().beginUndoGroup();
// This handles undo groups automagically
UndoGroupHelper ugh(&buffer());
if (cursor)
cursor->recordUndo();
setParam("name", label);
updateReferences(old_label, label);
buffer().undo().endUndoGroup();
}

View File

@ -157,6 +157,7 @@ void RenderGraphic::metrics(MetricsInfo & mi, Dimension & dim) const
dim.des = 0;
int font_width = 0;
int font_height = 0;
FontInfo msgFont(mi.base.font);
msgFont.setFamily(SANS_FAMILY);
@ -166,6 +167,7 @@ void RenderGraphic::metrics(MetricsInfo & mi, Dimension & dim) const
if (!justname.empty()) {
msgFont.setSize(FONT_SIZE_FOOTNOTE);
font_width = theFontMetrics(msgFont).width(justname);
font_height = theFontMetrics(msgFont).maxHeight();
}
docstring const msg = statusMessage(params_, loader_.status());
@ -173,9 +175,12 @@ void RenderGraphic::metrics(MetricsInfo & mi, Dimension & dim) const
msgFont.setSize(FONT_SIZE_TINY);
font_width = max(font_width,
theFontMetrics(msgFont).width(msg));
font_height += theFontMetrics(msgFont).maxAscent();
dim.des = theFontMetrics(msgFont).maxDescent();
}
dim.wid = max(50, font_width + 15);
dim.asc = max(50, font_height + 15);
dim_ = dim;
}

Some files were not shown because too many files have changed in this diff Show More